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