Mercurial > hg-old > index.cgi
annotate src/expr.c @ 67:d5fe306f1ab1
Fixed numerous bugs in macro handling
author | lost |
---|---|
date | Mon, 05 Jan 2009 05:40:33 +0000 |
parents | 73423b66e511 |
children | 2fe5fd7d65a3 |
rev | line source |
---|---|
13
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
1 /* |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
2 expr.c |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
3 Copyright © 2008 William Astle |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
4 |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
5 This file is part of LWASM. |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
6 |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
7 LWASM is free software: you can redistribute it and/or modify it under the |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
8 terms of the GNU General Public License as published by the Free Software |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
9 Foundation, either version 3 of the License, or (at your option) any later |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
10 version. |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
11 |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
12 This program is distributed in the hope that it will be useful, but WITHOUT |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
15 more details. |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
16 |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
17 You should have received a copy of the GNU General Public License along with |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
18 this program. If not, see <http://www.gnu.org/licenses/>. |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
19 */ |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
20 |
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
21 /* |
18 | 22 This file contains the actual expression evaluator |
13
05d4115b4860
Started work on new expression evaluator system and major code re-work for next release
lost
parents:
diff
changeset
|
23 */ |
14 | 24 |
25 #define __expr_c_seen__ | |
15 | 26 |
18 | 27 #include <ctype.h> |
15 | 28 #include <stdlib.h> |
18 | 29 #include <string.h> |
15 | 30 |
14 | 31 #include "expr.h" |
17 | 32 #include "util.h" |
40
d2cee0c335e7
adjusted symbol rules to accept symbols starting with @ but not @<digit>
lost
parents:
39
diff
changeset
|
33 #include "lwasm.h" |
39 | 34 |
17 | 35 lwasm_expr_stack_t *lwasm_expr_stack_create(void) |
36 { | |
37 lwasm_expr_stack_t *s; | |
38 | |
39 s = lwasm_alloc(sizeof(lwasm_expr_stack_t)); | |
40 s -> head = NULL; | |
41 s -> tail = NULL; | |
42 return s; | |
43 } | |
15 | 44 |
17 | 45 void lwasm_expr_stack_free(lwasm_expr_stack_t *s) |
46 { | |
47 while (s -> head) | |
48 { | |
49 s -> tail = s -> head; | |
50 s -> head = s -> head -> next; | |
51 lwasm_expr_term_free(s -> tail -> term); | |
52 lwasm_free(s -> tail); | |
53 } | |
54 lwasm_free(s); | |
55 } | |
14 | 56 |
17 | 57 void lwasm_expr_term_free(lwasm_expr_term_t *t) |
58 { | |
59 if (t) | |
60 { | |
44 | 61 if (t -> term_type == LWASM_TERM_SYM) |
17 | 62 lwasm_free(t -> symbol); |
63 lwasm_free(t); | |
64 } | |
65 } | |
66 | |
67 lwasm_expr_term_t *lwasm_expr_term_create_oper(int oper) | |
68 { | |
69 lwasm_expr_term_t *t; | |
37
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
70 |
39 | 71 debug_message(10, "Creating operator term: %d", oper); |
17 | 72 |
73 t = lwasm_alloc(sizeof(lwasm_expr_term_t)); | |
74 t -> term_type = LWASM_TERM_OPER; | |
75 t -> value = oper; | |
76 return t; | |
77 } | |
15 | 78 |
17 | 79 lwasm_expr_term_t *lwasm_expr_term_create_int(int val) |
14 | 80 { |
17 | 81 lwasm_expr_term_t *t; |
39 | 82 debug_message(10, "Creating integer term: %d", val); |
17 | 83 |
84 t = lwasm_alloc(sizeof(lwasm_expr_term_t)); | |
85 t -> term_type = LWASM_TERM_INT; | |
86 t -> value = val; | |
87 return t; | |
88 } | |
89 | |
90 lwasm_expr_term_t *lwasm_expr_term_create_sym(char *sym) | |
91 { | |
92 lwasm_expr_term_t *t; | |
14 | 93 |
39 | 94 debug_message(10, "Creating symbol term: %s", sym); |
95 | |
17 | 96 t = lwasm_alloc(sizeof(lwasm_expr_term_t)); |
97 t -> term_type = LWASM_TERM_SYM; | |
98 t -> symbol = lwasm_strdup(sym); | |
99 return t; | |
100 } | |
15 | 101 |
17 | 102 lwasm_expr_term_t *lwasm_expr_term_dup(lwasm_expr_term_t *t) |
103 { | |
104 switch (t -> term_type) | |
15 | 105 { |
17 | 106 case LWASM_TERM_INT: |
107 return lwasm_expr_term_create_int(t -> value); | |
108 | |
109 case LWASM_TERM_OPER: | |
110 return lwasm_expr_term_create_oper(t -> value); | |
111 | |
112 case LWASM_TERM_SYM: | |
113 return lwasm_expr_term_create_sym(t -> symbol); | |
114 | |
115 default: | |
39 | 116 debug_message(0, "lwasm_expr_term_dup(): invalid term type %d", t -> term_type); |
17 | 117 exit(1); |
118 } | |
119 // can't get here | |
120 } | |
121 | |
122 void lwasm_expr_stack_push(lwasm_expr_stack_t *s, lwasm_expr_term_t *t) | |
123 { | |
124 lwasm_expr_stack_node_t *n; | |
125 | |
126 if (!s) | |
127 { | |
39 | 128 debug_message(0, "lwasm_expr_stack_push(): invalid stack pointer"); |
17 | 129 exit(1); |
15 | 130 } |
131 | |
17 | 132 n = lwasm_alloc(sizeof(lwasm_expr_stack_node_t)); |
133 n -> next = NULL; | |
134 n -> prev = s -> tail; | |
135 n -> term = lwasm_expr_term_dup(t); | |
136 | |
137 if (s -> head) | |
138 { | |
139 s -> tail -> next = n; | |
140 s -> tail = n; | |
141 } | |
142 else | |
15 | 143 { |
17 | 144 s -> head = n; |
145 s -> tail = n; | |
146 } | |
147 } | |
148 | |
149 lwasm_expr_term_t *lwasm_expr_stack_pop(lwasm_expr_stack_t *s) | |
150 { | |
151 lwasm_expr_term_t *t; | |
152 lwasm_expr_stack_node_t *n; | |
153 | |
154 if (!(s -> tail)) | |
155 return NULL; | |
156 | |
157 n = s -> tail; | |
158 s -> tail = n -> prev; | |
159 if (!(n -> prev)) | |
160 { | |
161 s -> head = NULL; | |
15 | 162 } |
14 | 163 |
17 | 164 t = n -> term; |
165 n -> term = NULL; | |
166 | |
167 lwasm_free(n); | |
168 | |
169 return t; | |
14 | 170 } |
18 | 171 |
172 // the following two functions are co-routines which actually parse | |
173 // an infix expression onto the expression stack, each returns -1 | |
174 // if an error is encountered | |
175 | |
176 /* | |
177 parse a term and push it onto the stack | |
178 | |
179 this function handles unary prefix operators (-, +, .not., .com.) | |
180 as well as () | |
181 */ | |
182 int lwasm_expr_parse_term(lwasm_expr_stack_t *s, const char **p) | |
183 { | |
184 lwasm_expr_term_t *t; | |
39 | 185 debug_message(2, "Expression string %s", *p); |
186 | |
18 | 187 eval_next: |
61 | 188 if (!**p || isspace(**p) || **p == ')' || **p == ']') |
189 return -1; | |
18 | 190 if (**p == '(') |
191 { | |
39 | 192 debug_message(3, "Starting paren"); |
18 | 193 (*p)++; |
194 lwasm_expr_parse_expr(s, p, 0); | |
195 if (**p != ')') | |
196 return -1; | |
197 (*p)++; | |
198 return 0; | |
199 } | |
200 | |
201 if (**p == '+') | |
202 { | |
39 | 203 debug_message(3, "Unary +"); |
18 | 204 (*p)++; |
205 goto eval_next; | |
206 } | |
207 | |
208 if (**p == '-') | |
209 { | |
210 // parse expression following "-" | |
211 (*p)++; | |
212 if (lwasm_expr_parse_expr(s, p, 200) < 0) | |
213 return -1; | |
214 t = lwasm_expr_term_create_oper(LWASM_OPER_NEG); | |
215 lwasm_expr_stack_push(s, t); | |
216 lwasm_expr_term_free(t); | |
217 return 0; | |
218 } | |
219 | |
23 | 220 if (**p == '^') |
221 { | |
222 // parse expression following "^" | |
223 (*p)++; | |
224 if (lwasm_expr_parse_expr(s, p, 200) < 0) | |
225 return -1; | |
226 t = lwasm_expr_term_create_oper(LWASM_OPER_COM); | |
227 lwasm_expr_stack_push(s, t); | |
228 lwasm_expr_term_free(t); | |
229 return 0; | |
230 } | |
231 | |
18 | 232 /* |
233 we have an actual term here so evaluate it | |
234 | |
235 it could be one of the following: | |
236 | |
237 1. a decimal constant | |
238 2. a hexadecimal constant | |
239 3. an octal constant | |
240 4. a binary constant | |
241 5. a symbol reference | |
242 6. the "current" instruction address (*) | |
243 7. the "current" data address (.) | |
244 8. a "back reference" (<) | |
245 9. a "forward reference" (>) | |
246 | |
247 items 6 through 9 are stored as symbol references | |
248 | |
249 (a . followed by a . or a alpha char or number is a symbol) | |
250 */ | |
251 if (**p == '*' | |
252 || ( | |
253 **p == '.' | |
254 && (*p)[1] != '.' | |
255 && !((*p)[1] >= 'A' && (*p)[1] <= 'Z') | |
256 && !((*p)[1] >= 'a' && (*p)[1] <= 'z') | |
257 && !((*p)[1] >= '0' && (*p)[1] <= '9') | |
258 ) | |
259 || **p == '<' | |
260 || **p == '>') | |
261 { | |
262 char tstr[2]; | |
263 tstr[0] = **p; | |
264 tstr[1] = '\0'; | |
265 t = lwasm_expr_term_create_sym(tstr); | |
266 lwasm_expr_stack_push(s, t); | |
267 lwasm_expr_term_free(t); | |
268 (*p)++; | |
269 return 0; | |
270 } | |
271 | |
272 /* | |
273 - a symbol will be a string of characters introduced by a letter, ".", | |
274 "_" but NOT a number | |
275 - a decimal constant will consist of only digits, optionally prefixed | |
276 with "&" | |
277 - a binary constant will consist of only 0s and 1s either prefixed with % | |
278 or suffixed with "B" | |
279 - a hex constant will consist of 0-9A-F either prefixed with $ or | |
280 suffixed with "H"; a hex number starting with A-F must be prefixed | |
281 with $ or start with 0 and end with H | |
282 - an octal constant will consist of 0-7 either prefixed with @ or | |
283 suffixed with "O" or "Q" | |
284 - an ascii constant will be a single character prefixed with a ' | |
285 - a double ascii constant will be two characters prefixed with a " | |
286 | |
287 */ | |
288 if (**p == '"') | |
289 { | |
290 // double ascii constant | |
291 int val; | |
292 (*p)++; | |
293 if (!**p) | |
294 return -1; | |
295 if (!*((*p)+1)) | |
296 return -1; | |
297 val = **p << 8 | *((*p) + 1); | |
298 (*p) += 2; | |
299 t = lwasm_expr_term_create_int(val); | |
300 lwasm_expr_stack_push(s, t); | |
301 lwasm_expr_term_free(t); | |
302 return 0; | |
303 } | |
304 else if (**p == '\'') | |
305 { | |
306 // single ascii constant | |
307 int val; | |
308 (*p)++; | |
61 | 309 debug_message(3, "Single ascii character constant '%c'", **p); |
18 | 310 if (!**p) |
311 return -1; | |
312 val = **p; | |
313 (*p)++; | |
314 t = lwasm_expr_term_create_int(val); | |
315 lwasm_expr_stack_push(s, t); | |
316 lwasm_expr_term_free(t); | |
61 | 317 return 0; |
18 | 318 } |
319 else if (**p == '&') | |
320 { | |
321 // decimal constant | |
322 int val = 0; | |
323 | |
324 (*p)++; | |
47
804d7465e0f9
Implemented ORG and fixed problems with constants using $, &, or @ to specify base
lost
parents:
44
diff
changeset
|
325 while (**p && strchr("0123456789", **p)) |
18 | 326 { |
327 val = val * 10 + (**p - '0'); | |
328 (*p)++; | |
329 } | |
330 t = lwasm_expr_term_create_int(val); | |
331 lwasm_expr_stack_push(s, t); | |
332 lwasm_expr_term_free(t); | |
333 return 0; | |
334 } | |
335 else if (**p == '%') | |
336 { | |
337 // binary constant | |
338 int val = 0; | |
339 | |
340 (*p)++; | |
341 while (**p == '0' || **p == '1') | |
342 { | |
343 val = val * 2 + (**p - '0'); | |
344 (*p)++; | |
345 } | |
346 t = lwasm_expr_term_create_int(val); | |
347 lwasm_expr_stack_push(s, t); | |
348 lwasm_expr_term_free(t); | |
349 return 0; | |
350 } | |
351 else if (**p == '$') | |
352 { | |
353 // hexadecimal constant | |
354 int val = 0, val2; | |
355 | |
356 (*p)++; | |
47
804d7465e0f9
Implemented ORG and fixed problems with constants using $, &, or @ to specify base
lost
parents:
44
diff
changeset
|
357 debug_message(3, "Found prefix hex constant: %s", *p); |
804d7465e0f9
Implemented ORG and fixed problems with constants using $, &, or @ to specify base
lost
parents:
44
diff
changeset
|
358 while (**p && strchr("0123456789ABCDEFabcdef", **p)) |
18 | 359 { |
360 val2 = toupper(**p) - '0'; | |
361 if (val2 > 9) | |
362 val2 -= 7; | |
47
804d7465e0f9
Implemented ORG and fixed problems with constants using $, &, or @ to specify base
lost
parents:
44
diff
changeset
|
363 debug_message(3, "Got char: %c (%d)", **p, val2); |
18 | 364 val = val * 16 + val2; |
365 (*p)++; | |
366 } | |
367 t = lwasm_expr_term_create_int(val); | |
368 lwasm_expr_stack_push(s, t); | |
369 lwasm_expr_term_free(t); | |
370 return 0; | |
371 } | |
40
d2cee0c335e7
adjusted symbol rules to accept symbols starting with @ but not @<digit>
lost
parents:
39
diff
changeset
|
372 // an @ followed by a digit is an octal number |
d2cee0c335e7
adjusted symbol rules to accept symbols starting with @ but not @<digit>
lost
parents:
39
diff
changeset
|
373 // but if it's followed by anything else, it is a symbol |
d2cee0c335e7
adjusted symbol rules to accept symbols starting with @ but not @<digit>
lost
parents:
39
diff
changeset
|
374 else if (**p == '@' && isdigit(*(*p + 1))) |
18 | 375 { |
376 // octal constant | |
377 int val = 0; | |
378 | |
379 (*p)++; | |
47
804d7465e0f9
Implemented ORG and fixed problems with constants using $, &, or @ to specify base
lost
parents:
44
diff
changeset
|
380 while (**p && strchr("01234567", **p)) |
18 | 381 { |
382 val = val * 8 + (**p - '0'); | |
383 (*p)++; | |
384 } | |
385 t = lwasm_expr_term_create_int(val); | |
386 lwasm_expr_stack_push(s, t); | |
387 lwasm_expr_term_free(t); | |
388 return 0; | |
389 } | |
390 | |
391 // symbol or bare decimal or suffix identified constant here | |
392 // all numbers will start with a digit at this point | |
393 if (**p < '0' || **p > '9') | |
394 { | |
395 int l = 0; | |
396 char *sb; | |
397 | |
398 // evaluate a symbol here | |
41 | 399 static const char *symchars = "_.$@?abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; |
400 while ((*p)[l] && strchr(symchars, (*p)[l])) | |
18 | 401 l++; |
402 | |
403 if (l == 0) | |
404 return -1; | |
405 | |
406 sb = lwasm_alloc(l + 1); | |
407 sb[l] = '\0'; | |
408 memcpy(sb, *p, l); | |
409 t = lwasm_expr_term_create_sym(sb); | |
410 lwasm_expr_stack_push(s, t); | |
411 lwasm_expr_term_free(t); | |
41 | 412 (*p) += l; |
413 debug_message(3, "Symbol: '%s'; (%s)", sb, *p); | |
18 | 414 lwasm_free(sb); |
415 return 0; | |
416 } | |
417 | |
418 if (!**p) | |
419 return -1; | |
420 | |
421 // evaluate a suffix based constant | |
422 { | |
423 int decval = 0, binval = 0, hexval = 0, octval = 0; | |
424 int valtype = 15; // 1 = bin, 2 = oct, 4 = dec, 8 = hex | |
425 int bindone = 0; | |
426 int val; | |
427 int dval; | |
428 | |
429 while (1) | |
430 { | |
431 if (!**p || !strchr("0123456789ABCDEFabcdefqhoQHO", **p)) | |
432 { | |
433 // we can legally have bin or decimal here | |
434 if (bindone) | |
435 { | |
436 // we just finished a binary value | |
437 val = binval; | |
438 break; | |
439 } | |
440 else if (valtype & 4) | |
441 { | |
442 // otherwise we must be decimal (if we're still allowed one) | |
443 val = decval; | |
39 | 444 debug_message(3, "End of decimal value"); |
18 | 445 break; |
446 } | |
447 else | |
448 { | |
449 // bad value | |
450 return -1; | |
451 } | |
452 } | |
453 | |
454 dval = toupper(**p); | |
455 (*p)++; | |
456 | |
457 if (bindone) | |
458 { | |
459 // any characters past "B" means it is not binary | |
460 bindone = 0; | |
461 valtype &= 14; | |
462 } | |
463 | |
464 switch (dval) | |
465 { | |
466 case 'Q': | |
467 case 'O': | |
468 if (valtype & 2) | |
469 { | |
470 val = octval; | |
471 valtype = -1; | |
472 break; | |
473 } | |
474 else | |
475 { | |
476 // not a valid octal value | |
477 return -1; | |
478 } | |
479 /* can't get here */ | |
480 | |
481 case 'H': | |
482 if (valtype & 8) | |
483 { | |
484 val = hexval; | |
485 valtype = -1; | |
486 break; | |
487 } | |
488 else | |
489 { | |
490 // not a valid hex number | |
491 return -1; | |
492 } | |
493 /* can't get here */ | |
494 | |
495 case 'B': | |
496 // this is a bit of a sticky one since B is a legit hex | |
497 // digit so this may or may not be the end of the number | |
498 // so we fall through to the digit case | |
499 | |
500 if (valtype & 1) | |
501 { | |
502 // could still be binary | |
503 bindone = 1; | |
504 valtype = 9; // hex and binary | |
505 } | |
506 /* fall through intentional */ | |
507 | |
508 default: | |
509 // digit | |
510 dval -= '0'; | |
511 if (dval > 9) | |
512 dval -= 7; | |
39 | 513 debug_message(3, "Got digit: %d", dval); |
37
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
514 // if (dval > 1) |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
515 // valtype &= 14; |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
516 // if (dval > 7) |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
517 // valtype &= 12; |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
518 // if (dval > 9) |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
519 // valtype &= 8; |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
520 |
18 | 521 if (valtype & 8) |
522 { | |
523 hexval = hexval * 16 + dval; | |
524 } | |
525 if (valtype & 4) | |
526 { | |
527 if (dval > 9) | |
528 valtype &= 11; | |
529 else | |
530 decval = decval * 10 + dval; | |
531 } | |
532 if (valtype & 2) | |
533 { | |
534 if (dval > 7) | |
535 valtype &= 13; | |
536 else | |
537 octval = octval * 8 + dval; | |
538 } | |
539 if (valtype & 1) | |
540 { | |
541 if (dval > 1) | |
542 valtype &= 14; | |
543 else | |
544 binval = binval * 2 + dval; | |
545 } | |
546 } | |
547 // break out if we have a return value | |
37
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
548 if (valtype == -1) |
18 | 549 break; |
550 // return if no more valid possibilities! | |
551 if (valtype == 0) | |
552 return -1; | |
37
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
553 val = decval; // in case we fall through |
18 | 554 } |
555 | |
556 // we get here when we have a value to return | |
557 t = lwasm_expr_term_create_int(val); | |
558 lwasm_expr_stack_push(s, t); | |
559 lwasm_expr_term_free(t); | |
560 return 0; | |
561 } | |
562 /* can't get here */ | |
563 } | |
564 | |
565 // parse an expression and push the result onto the stack | |
566 // if an operator of lower precedence than the value of "prec" is found, | |
567 int lwasm_expr_parse_expr(lwasm_expr_stack_t *s, const char **p, int prec) | |
568 { | |
569 static const struct operinfo | |
570 { | |
571 int opernum; | |
572 char *operstr; | |
573 int operprec; | |
574 } operators[] = | |
575 { | |
576 { LWASM_OPER_PLUS, "+", 100 }, | |
577 { LWASM_OPER_MINUS, "-", 100 }, | |
578 { LWASM_OPER_TIMES, "*", 150 }, | |
579 { LWASM_OPER_DIVIDE, "/", 150 }, | |
580 { LWASM_OPER_MOD, "%", 150 }, | |
581 { LWASM_OPER_INTDIV, "\\", 150 }, | |
582 | |
583 { LWASM_OPER_NONE, "", 0 } | |
584 }; | |
585 int opern, i; | |
586 lwasm_expr_term_t *operterm; | |
587 | |
588 // return if we are at the end of the expression or a subexpression | |
61 | 589 if (!**p || isspace(**p) || **p == ')' || **p == ',' || **p == ']') |
18 | 590 return 0; |
591 | |
592 if (lwasm_expr_parse_term(s, p) < 0) | |
593 return -1; | |
37
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
594 |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
595 eval_next: |
61 | 596 if (!**p || isspace(**p) || **p == ')' || **p == ',' || **p == ']') |
37
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
597 return 0; |
18 | 598 |
599 // expecting an operator here | |
600 for (opern = 0; operators[opern].opernum != LWASM_OPER_NONE; opern++) | |
601 { | |
602 for (i = 0; (*p)[i] && operators[opern].operstr[i] && (*p[i] == operators[opern].operstr[i]); i++) | |
603 /* do nothing */ ; | |
604 if (operators[opern].operstr[i] == '\0') | |
605 break; | |
606 } | |
607 if (operators[opern].opernum == LWASM_OPER_NONE) | |
608 { | |
609 // unrecognized operator | |
610 return -1; | |
611 } | |
612 | |
613 // the operator number in question is in opern; i is the length of the | |
614 // operator string | |
615 | |
616 // logic: | |
617 // if the precedence of this operation is <= to the "prec" flag, | |
618 // we simply return without advancing the input pointer; the operator | |
619 // will be evaluated again in the enclosing function call | |
620 if (operators[opern].operprec <= prec) | |
621 return 0; | |
622 | |
623 // logic: | |
624 // we have a higher precedence operator here so we will advance the | |
625 // input pointer to the next term and let the expression evaluator | |
626 // loose on it after which time we will push our operator onto the | |
627 // stack and then go on with the expression evaluation | |
628 (*p) += i; // advance input pointer | |
629 | |
630 // evaluate next expression(s) of higher precedence | |
631 if (lwasm_expr_parse_expr(s, p, operators[opern].operprec) < 0) | |
632 return -1; | |
633 | |
634 operterm = lwasm_expr_term_create_oper(operators[opern].opernum); | |
635 lwasm_expr_stack_push(s, operterm); | |
636 lwasm_expr_term_free(operterm); | |
637 | |
638 // return if we are at the end of the expression or a subexpression | |
639 if (!**p || isspace(**p) || **p == ')') | |
640 return 0; | |
641 | |
642 // continue evaluating | |
643 goto eval_next; | |
644 } | |
645 | |
646 /* | |
647 actually evaluate an expression | |
648 | |
649 This happens in two stages. The first stage merely parses the expression into | |
650 a lwasm_expr_stack_t * which is then evaluated as much as possible before the | |
651 result is returned. | |
652 | |
653 Returns NULL on a parse error or otherwise invalid expression. *outp will | |
654 contain the pointer to the next character after the expression if and only | |
655 if there is no error. In the case of an error, *outp is undefined. | |
656 */ | |
37
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
657 lwasm_expr_stack_t *lwasm_expr_eval(const char *inp, const char **outp, int (*sfunc)(char *sym, void *state, int *val), void *state) |
18 | 658 { |
659 lwasm_expr_stack_t *s; | |
660 const char *p; | |
37
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
661 int rval; |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
662 |
18 | 663 // actually parse the expression |
664 p = inp; | |
665 s = lwasm_expr_stack_create(); | |
37
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
666 |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
667 rval = lwasm_expr_parse_expr(s, &p, 0); |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
668 if (rval < 0) |
18 | 669 goto cleanup_error; |
670 | |
671 // save end of expression | |
672 if (outp) | |
673 (*outp) = p; | |
674 | |
675 // return potentially partial expression | |
37
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
676 if (lwasm_expr_reval(s, sfunc, state) < 0) |
18 | 677 goto cleanup_error; |
37
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
678 |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
679 if (lwasm_expr_is_constant(s)) |
39 | 680 debug_message(3, "Constant expression evaluates to: %d", lwasm_expr_get_value(s)); |
18 | 681 |
682 return s; | |
683 | |
684 cleanup_error: | |
685 lwasm_expr_stack_free(s); | |
686 return NULL; | |
687 } | |
688 | |
689 /* | |
690 take an expression stack s and scan for operations that can be completed | |
691 | |
692 return -1 on error, 0 on no error | |
693 | |
694 possible errors are: division by zero or unknown operator | |
695 | |
696 theory of operation: | |
697 | |
698 scan the stack for an operator which has two constants preceding it (binary) | |
699 or 1 constant preceding it (unary) and if found, perform the calculation | |
700 and replace the operator and its operands with the result | |
701 | |
702 repeat the scan until no futher simplications are found or if there are no | |
703 further operators or only a single term remains | |
704 | |
705 */ | |
37
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
706 int lwasm_expr_reval(lwasm_expr_stack_t *s, int (*sfunc)(char *sym, void *state, int *val), void *state) |
18 | 707 { |
708 lwasm_expr_stack_node_t *n; | |
37
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
709 int sval; |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
710 |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
711 // resolve symbols |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
712 // symbols that do not resolve to a constant are left alone |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
713 for (n = s -> head; n; n = n -> next) |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
714 { |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
715 if (n -> term -> term_type == LWASM_TERM_SYM) |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
716 { |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
717 if (sfunc(n -> term -> symbol, state, &sval) == 0) |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
718 { |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
719 n -> term -> term_type = LWASM_TERM_INT; |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
720 n -> term -> value = sval; |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
721 lwasm_free(n -> term -> symbol); |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
722 n -> term -> symbol = NULL; |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
723 } |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
724 } |
538e15927776
Added symbol handling to expression subsystem; adpated instruction handlers to the new scheme; misc fixes
lost
parents:
23
diff
changeset
|
725 } |
18 | 726 |
727 next_iter: | |
728 // a single term | |
729 if (s -> head == s -> tail) | |
730 return 0; | |
731 | |
732 // search for an operator | |
733 for (n = s -> head; n; n = n -> next) | |
734 { | |
735 if (n -> term -> term_type == LWASM_TERM_OPER) | |
736 { | |
737 if (n -> term -> value == LWASM_OPER_NEG | |
738 || n -> term -> value == LWASM_OPER_COM | |
739 ) | |
740 { | |
741 // unary operator | |
742 if (n -> prev && n -> prev -> term -> term_type == LWASM_TERM_INT) | |
743 { | |
744 // a unary operator we can resolve | |
745 // we do the op then remove the term "n" is pointing at | |
746 if (n -> term -> value == LWASM_OPER_NEG) | |
747 { | |
748 n -> prev -> term -> value = -(n -> prev -> term -> value); | |
749 } | |
750 else if (n -> term -> value == LWASM_OPER_COM) | |
751 { | |
752 n -> prev -> term -> value = ~(n -> prev -> term -> value); | |
753 } | |
754 n -> prev -> next = n -> next; | |
755 if (n -> next) | |
756 n -> next -> prev = n -> prev; | |
757 else | |
758 s -> tail = n -> prev; | |
759 | |
760 lwasm_expr_term_free(n -> term); | |
761 lwasm_free(n); | |
762 break; | |
763 } | |
764 } | |
765 else | |
766 { | |
767 // binary operator | |
768 if (n -> prev && n -> prev -> prev && n -> prev -> term -> term_type == LWASM_TERM_INT && n -> prev -> prev -> term -> term_type == LWASM_TERM_INT) | |
769 { | |
770 // a binary operator we can resolve | |
771 switch (n -> term -> value) | |
772 { | |
773 case LWASM_OPER_PLUS: | |
774 n -> prev -> prev -> term -> value += n -> prev -> term -> value; | |
775 break; | |
776 | |
777 case LWASM_OPER_MINUS: | |
778 n -> prev -> prev -> term -> value -= n -> prev -> term -> value; | |
779 break; | |
780 | |
781 case LWASM_OPER_TIMES: | |
782 n -> prev -> prev -> term -> value *= n -> prev -> term -> value; | |
783 break; | |
784 | |
785 case LWASM_OPER_DIVIDE: | |
786 if (n -> prev -> term -> value == 0) | |
787 return -1; | |
788 n -> prev -> prev -> term -> value /= n -> prev -> term -> value; | |
789 break; | |
790 | |
791 case LWASM_OPER_MOD: | |
792 if (n -> prev -> term -> value == 0) | |
793 return -1; | |
794 n -> prev -> prev -> term -> value %= n -> prev -> term -> value; | |
795 break; | |
796 | |
797 case LWASM_OPER_INTDIV: | |
798 if (n -> prev -> term -> value == 0) | |
799 return -1; | |
800 n -> prev -> prev -> term -> value /= n -> prev -> term -> value; | |
801 break; | |
802 | |
803 case LWASM_OPER_BWAND: | |
804 n -> prev -> prev -> term -> value &= n -> prev -> term -> value; | |
805 break; | |
806 | |
807 case LWASM_OPER_BWOR: | |
808 n -> prev -> prev -> term -> value |= n -> prev -> term -> value; | |
809 break; | |
810 | |
811 case LWASM_OPER_BWXOR: | |
812 n -> prev -> prev -> term -> value ^= n -> prev -> term -> value; | |
813 break; | |
814 | |
815 case LWASM_OPER_AND: | |
816 n -> prev -> prev -> term -> value = (n -> prev -> term -> value && n -> prev -> prev -> term -> value) ? 1 : 0; | |
817 break; | |
818 | |
819 case LWASM_OPER_OR: | |
820 n -> prev -> prev -> term -> value = (n -> prev -> term -> value || n -> prev -> prev -> term -> value) ? 1 : 0; | |
821 break; | |
822 | |
823 default: | |
824 // return error if unknown operator! | |
825 return -1; | |
826 } | |
827 | |
828 // now remove the two unneeded entries from the stack | |
829 n -> prev -> prev -> next = n -> next; | |
830 if (n -> next) | |
831 n -> next -> prev = n -> prev -> prev; | |
832 else | |
833 s -> tail = n -> prev -> prev; | |
834 | |
835 lwasm_expr_term_free(n -> term); | |
836 lwasm_expr_term_free(n -> prev -> term); | |
837 lwasm_free(n -> prev); | |
838 lwasm_free(n); | |
839 break; | |
840 } | |
841 } | |
842 } | |
843 } | |
844 // note for the terminally confused about dynamic memory and pointers: | |
845 // n will not be NULL even after the lwasm_free calls above so | |
846 // this test will still work (n will be a dangling pointer) | |
847 // (n will only be NULL if we didn't find any operators to simplify) | |
848 if (n) | |
849 goto next_iter; | |
850 | |
851 return 0; | |
852 } |