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
13static bc_t *bc_in;
14static 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
24void cev_log(void);
25
26void cev_udp(void) {
27 sc_raise("(EXPR): UDP INSIDE EXPR");
28}
29
30void cev_missing_rp(void) {
31 sc_raise("(EXPR): MISSING ')'");
32}
33
34void cev_opr_err(void) {
35 sc_raise("(EXPR): SYNTAX ERROR (%d)", CODE(IP));
36}
37
38void 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
47void 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
56void 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
96void cev_empty_args() {
97 cev_add1(kwTYPE_LEVEL_BEGIN);
98 cev_add1(kwTYPE_LEVEL_END);
99 IP += 2;
100}
101
102// function [(...)]
103void 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
145void 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 */
162void 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 */
222void 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 */
238void 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 */
258void 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 */
276void 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 */
297void 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 */
320void 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 */
341void 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 */
372void 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