1 | // This file is part of SmallBASIC |
2 | // |
3 | // pseudo-compiler: expressions (warning: the input is byte-code segment) |
4 | // |
5 | // This program is distributed under the terms of the GPL v2.0 or later |
6 | // Download the GNU Public License (GPL) from www.gnu.org |
7 | // |
8 | // Copyright(C) 2000 Nicholas Christopoulos |
9 | |
10 | #include "common/smbas.h" |
11 | #include "common/bc.h" |
12 | |
13 | static bc_t *bc_in; |
14 | static bc_t *bc_out; |
15 | |
16 | #define cev_add1(x) bc_add_code(bc_out, (x)) |
17 | #define cev_add2(x, y) { bc_add1(bc_out, (x)); bc_add1(bc_out, (y)); } |
18 | #define cev_add_addr(x) bc_add_addr(bc_out, (x)) |
19 | #define IP bc_in->cp |
20 | #define CODE(x) bc_in->ptr[(x)] |
21 | #define CODE_PEEK() CODE(IP) |
22 | #define IF_ERR_RTN if (comp_error) return |
23 | |
24 | void cev_log(void); |
25 | |
26 | void cev_udp(void) { |
27 | sc_raise("(EXPR): UDP INSIDE EXPR" ); |
28 | } |
29 | |
30 | void cev_missing_rp(void) { |
31 | sc_raise("(EXPR): MISSING ')'" ); |
32 | } |
33 | |
34 | void cev_opr_err(void) { |
35 | sc_raise("(EXPR): SYNTAX ERROR (%d)" , CODE(IP)); |
36 | } |
37 | |
38 | void cev_prim_str() { |
39 | uint32_t len; |
40 | memcpy(&len, bc_in->ptr + bc_in->cp, OS_STRLEN); |
41 | IP += OS_STRLEN; |
42 | bc_add_dword(bc_out, len); |
43 | bc_add_n(bc_out, bc_in->ptr + bc_in->cp, len); |
44 | IP += len; |
45 | } |
46 | |
47 | void cev_prim_uds() { |
48 | while (CODE_PEEK() == kwTYPE_UDS_EL) { |
49 | cev_add1(kwTYPE_UDS_EL); |
50 | cev_add1(kwTYPE_STR); |
51 | IP += 2; |
52 | cev_prim_str(); |
53 | } |
54 | } |
55 | |
56 | void cev_prim_var() { |
57 | bc_add_n(bc_out, bc_in->ptr + bc_in->cp, ADDRSZ); |
58 | IP += ADDRSZ; |
59 | |
60 | cev_prim_uds(); |
61 | |
62 | // support multiple () |
63 | while (CODE_PEEK() == kwTYPE_LEVEL_BEGIN) { |
64 | cev_add1(kwTYPE_LEVEL_BEGIN); |
65 | IP++; |
66 | if (CODE_PEEK() == kwTYPE_LEVEL_END) { |
67 | // NULL ARRAYS |
68 | cev_add1(kwTYPE_LEVEL_END); |
69 | IP++; |
70 | } else { |
71 | cev_log(); |
72 | |
73 | while (CODE_PEEK() == kwTYPE_SEP || CODE_PEEK() == kwTO) { |
74 | // DIM X(A TO B) |
75 | if (CODE_PEEK() == kwTYPE_SEP) { |
76 | cev_add1(CODE(IP)); |
77 | IP++; |
78 | } |
79 | cev_add1(CODE(IP)); |
80 | IP++; |
81 | |
82 | cev_log(); |
83 | } |
84 | |
85 | if (CODE_PEEK() != kwTYPE_LEVEL_END) { |
86 | cev_missing_rp(); |
87 | } else { |
88 | cev_add1(kwTYPE_LEVEL_END); |
89 | IP++; |
90 | cev_prim_uds(); |
91 | } |
92 | } |
93 | } |
94 | } |
95 | |
96 | void cev_empty_args() { |
97 | cev_add1(kwTYPE_LEVEL_BEGIN); |
98 | cev_add1(kwTYPE_LEVEL_END); |
99 | IP += 2; |
100 | } |
101 | |
102 | // function [(...)] |
103 | void cev_prim_args() { |
104 | cev_add1(kwTYPE_LEVEL_BEGIN); |
105 | IP++; |
106 | |
107 | if (CODE_PEEK() == kwTYPE_CALL_PTR) { |
108 | cev_add1(CODE(IP)); |
109 | IP++; |
110 | } |
111 | |
112 | if (CODE_PEEK() != kwTYPE_SEP) { |
113 | // empty parameter |
114 | cev_log(); |
115 | } |
116 | while (CODE_PEEK() == kwTYPE_SEP) { |
117 | // while parameters |
118 | cev_add1(CODE(IP)); |
119 | IP++; |
120 | cev_add1(CODE(IP)); |
121 | IP++; |
122 | |
123 | if (CODE_PEEK() != kwTYPE_LEVEL_END) { |
124 | if (CODE_PEEK() != kwTYPE_SEP) { |
125 | cev_log(); |
126 | } |
127 | } |
128 | } |
129 | |
130 | // after (), check for UDS field, eg foo(10).x |
131 | if (CODE_PEEK() == kwTYPE_UDS_EL) { |
132 | cev_prim_uds(); |
133 | cev_log(); |
134 | } |
135 | |
136 | if (CODE_PEEK() != kwTYPE_LEVEL_END) { |
137 | cev_missing_rp(); |
138 | } else { |
139 | cev_add1(kwTYPE_LEVEL_END); |
140 | IP++; |
141 | } |
142 | } |
143 | |
144 | // test for repeated primatives |
145 | void cev_check_dup_prim() { |
146 | switch (CODE(IP)) { |
147 | case kwTYPE_INT: |
148 | case kwTYPE_NUM: |
149 | case kwTYPE_STR: |
150 | case kwTYPE_VAR: |
151 | case kwTYPE_CALLF: |
152 | cev_opr_err(); |
153 | break; |
154 | default: |
155 | break; |
156 | } |
157 | } |
158 | |
159 | /* |
160 | * prim |
161 | */ |
162 | void cev_prim() { |
163 | IF_ERR_RTN; |
164 | byte code = CODE(IP); |
165 | IP++; |
166 | cev_add1(code); |
167 | switch (code) { |
168 | case kwTYPE_INT: |
169 | bc_add_n(bc_out, bc_in->ptr + bc_in->cp, OS_INTSZ); |
170 | IP += OS_INTSZ; |
171 | cev_check_dup_prim(); |
172 | break; |
173 | case kwTYPE_NUM: |
174 | bc_add_n(bc_out, bc_in->ptr + bc_in->cp, OS_REALSZ); |
175 | IP += OS_REALSZ; |
176 | cev_check_dup_prim(); |
177 | break; |
178 | case kwTYPE_STR: |
179 | cev_prim_str(); |
180 | cev_check_dup_prim(); |
181 | break; |
182 | case kwTYPE_CALL_UDP: |
183 | cev_udp(); |
184 | cev_check_dup_prim(); |
185 | break; |
186 | case kwTYPE_PTR: |
187 | bc_add_n(bc_out, bc_in->ptr + bc_in->cp, ADDRSZ); // addr |
188 | IP += ADDRSZ; |
189 | bc_add_n(bc_out, bc_in->ptr + bc_in->cp, ADDRSZ); // return var |
190 | IP += ADDRSZ; |
191 | cev_check_dup_prim(); |
192 | break; |
193 | case kwTYPE_VAR: |
194 | cev_prim_var(); |
195 | cev_check_dup_prim(); |
196 | break; |
197 | case kwTYPE_CALL_UDF: // [udf1][addr2] |
198 | case kwTYPE_CALLEXTF: // [lib][index] |
199 | bc_add_n(bc_out, bc_in->ptr + bc_in->cp, ADDRSZ); |
200 | IP += ADDRSZ; // no break here |
201 | case kwTYPE_CALLF: // [code] |
202 | bc_add_n(bc_out, bc_in->ptr + bc_in->cp, ADDRSZ); |
203 | IP += ADDRSZ; // no break here |
204 | default: |
205 | if (CODE_PEEK() == kwTYPE_LEVEL_BEGIN) { |
206 | if (CODE(IP + 1) == kwTYPE_LEVEL_END) { |
207 | cev_empty_args(); |
208 | } else { |
209 | cev_prim_args(); |
210 | } |
211 | } |
212 | if (code != kwBYREF) { |
213 | cev_check_dup_prim(); |
214 | } |
215 | break; |
216 | }; |
217 | } |
218 | |
219 | /* |
220 | * parenthesis |
221 | */ |
222 | void cev_parenth() { |
223 | IF_ERR_RTN; |
224 | if (CODE_PEEK() == kwTYPE_LEVEL_BEGIN) { |
225 | if (CODE(IP + 1) == kwTYPE_LEVEL_END) { |
226 | cev_empty_args(); |
227 | } else { |
228 | cev_prim_args(); |
229 | } |
230 | } else { |
231 | cev_prim(); |
232 | } |
233 | } |
234 | |
235 | /* |
236 | * unary |
237 | */ |
238 | void cev_unary() { |
239 | char op; |
240 | |
241 | IF_ERR_RTN; |
242 | if (CODE(IP) == kwTYPE_UNROPR || CODE(IP) == kwTYPE_ADDOPR) { |
243 | op = CODE(IP + 1); |
244 | IP += 2; |
245 | } else { |
246 | op = 0; |
247 | } |
248 | cev_parenth(); // R = cev_parenth |
249 | if (op) { |
250 | cev_add1(kwTYPE_UNROPR); |
251 | cev_add1(op); // R = op R |
252 | } |
253 | } |
254 | |
255 | /* |
256 | * pow |
257 | */ |
258 | void cev_pow() { |
259 | cev_unary(); // R = cev_unary |
260 | |
261 | IF_ERR_RTN; |
262 | while (CODE(IP) == kwTYPE_POWOPR) { |
263 | IP += 2; |
264 | |
265 | cev_add1(kwTYPE_EVPUSH); // PUSH R |
266 | cev_unary(); // R = cev_unary |
267 | IF_ERR_RTN; |
268 | cev_add1(kwTYPE_EVPOP); // POP LEFT |
269 | cev_add2(kwTYPE_POWOPR, '^'); // R = LEFT op R |
270 | } |
271 | } |
272 | |
273 | /* |
274 | * mul | div | mod |
275 | */ |
276 | void cev_mul() { |
277 | cev_pow(); // R = cev_pow() |
278 | |
279 | IF_ERR_RTN; |
280 | while (CODE(IP) == kwTYPE_MULOPR) { |
281 | char op; |
282 | |
283 | op = CODE(++IP); |
284 | IP++; |
285 | cev_add1(kwTYPE_EVPUSH); // PUSH R |
286 | |
287 | cev_pow(); |
288 | IF_ERR_RTN; |
289 | cev_add1(kwTYPE_EVPOP); // POP LEFT |
290 | cev_add2(kwTYPE_MULOPR, op); // R = LEFT op R |
291 | } |
292 | } |
293 | |
294 | /* |
295 | * add | sub |
296 | */ |
297 | void cev_add() { |
298 | cev_mul(); // R = cev_mul() |
299 | |
300 | IF_ERR_RTN; |
301 | while (CODE(IP) == kwTYPE_ADDOPR) { |
302 | char op; |
303 | |
304 | IP++; |
305 | op = CODE(IP); |
306 | IP++; |
307 | cev_add1(kwTYPE_EVPUSH); // PUSH R |
308 | |
309 | cev_mul(); // R = cev_mul |
310 | IF_ERR_RTN; |
311 | |
312 | cev_add1(kwTYPE_EVPOP); // POP LEFT |
313 | cev_add2(kwTYPE_ADDOPR, op); // R = LEFT op R |
314 | } |
315 | } |
316 | |
317 | /* |
318 | * compare |
319 | */ |
320 | void cev_cmp() { |
321 | cev_add(); // R = cev_add() |
322 | |
323 | IF_ERR_RTN; |
324 | while (CODE(IP) == kwTYPE_CMPOPR) { |
325 | char op; |
326 | |
327 | IP++; |
328 | op = CODE(IP); |
329 | IP++; |
330 | cev_add1(kwTYPE_EVPUSH); // PUSH R |
331 | cev_add(); // R = cev_add() |
332 | IF_ERR_RTN; |
333 | cev_add1(kwTYPE_EVPOP); // POP LEFT |
334 | cev_add2(kwTYPE_CMPOPR, op); // R = LEFT op R |
335 | } |
336 | } |
337 | |
338 | /* |
339 | * logical |
340 | */ |
341 | void cev_log(void) { |
342 | cev_cmp(); // R = cev_cmp() |
343 | IF_ERR_RTN; |
344 | while (CODE(IP) == kwTYPE_LOGOPR) { |
345 | byte op; |
346 | bcip_t shortcut; |
347 | bcip_t shortcut_offs; |
348 | |
349 | IP++; |
350 | op = CODE(IP); |
351 | IP++; |
352 | |
353 | cev_add1(kwTYPE_EVPUSH); // PUSH R (push the left side result |
354 | cev_add1(kwTYPE_EVAL_SC); |
355 | cev_add2(kwTYPE_LOGOPR, op); |
356 | shortcut = bc_out->count; // shortcut jump target (calculated below) |
357 | cev_add_addr(0); |
358 | |
359 | cev_cmp(); // right seg // R = cev_cmp() |
360 | IF_ERR_RTN; |
361 | cev_add1(kwTYPE_EVPOP); // POP LEFT |
362 | cev_add2(kwTYPE_LOGOPR, op); // R = LEFT op R |
363 | |
364 | shortcut_offs = bc_out->count - shortcut; |
365 | memcpy(bc_out->ptr + shortcut, &shortcut_offs, ADDRSZ); |
366 | } |
367 | } |
368 | |
369 | /* |
370 | * main |
371 | */ |
372 | void expr_parser(bc_t *bc_src) { |
373 | // init |
374 | bc_in = bc_src; |
375 | bc_out = malloc(sizeof(bc_t)); |
376 | bc_create(bc_out); |
377 | |
378 | byte code = CODE_PEEK(); |
379 | |
380 | // |
381 | // empty! |
382 | // |
383 | if (code == kwTYPE_LINE || code == kwTYPE_EOC) { |
384 | bc_destroy(bc_out); |
385 | free(bc_out); |
386 | return; |
387 | } |
388 | // |
389 | // LET|CONST special code |
390 | // |
391 | if (code == kwTYPE_CMPOPR) { |
392 | IP++; |
393 | if (CODE(IP) != '=') { |
394 | cev_opr_err(); |
395 | bc_destroy(bc_out); |
396 | free(bc_out); |
397 | return; |
398 | } else { |
399 | IP++; |
400 | cev_add2(kwTYPE_CMPOPR, '='); |
401 | } |
402 | } |
403 | // start |
404 | code = CODE_PEEK(); |
405 | while (code != kwTYPE_EOC && code != kwTYPE_LINE && !comp_error) { |
406 | if (kw_check_evexit(code)) { // separator |
407 | cev_add1(code); |
408 | IP++; // add sep. |
409 | if (code == kwUSE) { |
410 | cev_add_addr(0); |
411 | // USE needs 2 ips |
412 | cev_add_addr(0); |
413 | IP += (ADDRSZ + ADDRSZ); |
414 | } else if (code == kwAS) { |
415 | if (CODE_PEEK() == kwTYPE_SEP) { // OPEN ... AS #1 |
416 | cev_add1(kwTYPE_SEP); |
417 | IP++; |
418 | cev_add1(CODE(IP)); |
419 | IP++; |
420 | } |
421 | } else { |
422 | if (code == kwTYPE_SEP) { // Normal separator (,;) |
423 | cev_add1(CODE(IP)); |
424 | IP++; |
425 | } |
426 | } |
427 | code = CODE_PEEK(); // next |
428 | continue; |
429 | } |
430 | cev_log(); // do it |
431 | code = CODE_PEEK(); // next |
432 | } |
433 | |
434 | // finish |
435 | if (bc_out->count) { |
436 | bc_in->count = 0; |
437 | bc_append(bc_in, bc_out); |
438 | } |
439 | |
440 | bc_destroy(bc_out); |
441 | free(bc_out); |
442 | } |
443 | |