1 | // This file is part of SmallBASIC |
2 | // |
3 | // Evaluate variables from bytecode |
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) 2010-2014 Chris Warren-Smith. [http://tinyurl.com/ja2ss] |
9 | |
10 | #include "common/sys.h" |
11 | #include "common/kw.h" |
12 | #include "common/pproc.h" |
13 | #include "common/sberr.h" |
14 | #include "common/smbas.h" |
15 | #include "common/var.h" |
16 | #include "common/var_eval.h" |
17 | |
18 | /** |
19 | * Convert multi-dim index to one-dim index |
20 | */ |
21 | bcip_t get_array_idx(var_t *array) { |
22 | bcip_t idx = 0; |
23 | bcip_t lev = 0; |
24 | |
25 | do { |
26 | var_t var; |
27 | v_init(&var); |
28 | eval(&var); |
29 | |
30 | if (prog_error) { |
31 | break; |
32 | } else if (var.type == V_STR || array->type == V_MAP) { |
33 | err_varnotnum(); |
34 | break; |
35 | } else { |
36 | bcip_t idim = v_getint(&var); |
37 | |
38 | v_free(&var); |
39 | idim = idim - v_lbound(array, lev); |
40 | |
41 | bcip_t m = idim; |
42 | for (bcip_t i = lev + 1; i < v_maxdim(array); i++) { |
43 | m = m * (ABS(v_ubound(array, i) - v_lbound(array, i)) + 1); |
44 | } |
45 | idx += m; |
46 | |
47 | // skip separator |
48 | byte code = code_peek(); |
49 | if (code == kwTYPE_SEP) { |
50 | code_skipnext(); |
51 | if (code_getnext() != ',') { |
52 | err_missing_comma(); |
53 | } |
54 | } |
55 | // next |
56 | lev++; |
57 | } |
58 | } while (!prog_error && code_peek() != kwTYPE_LEVEL_END); |
59 | |
60 | if (!prog_error) { |
61 | if ((int) v_maxdim(array) != lev) { |
62 | err_missing_sep(); |
63 | } |
64 | } |
65 | return idx; |
66 | } |
67 | |
68 | /** |
69 | * Returns the map field along with the immediate parent map |
70 | */ |
71 | var_t *code_getvarptr_map(var_t **var_map) { |
72 | var_t *var_p = NULL; |
73 | if (code_peek() == kwTYPE_VAR) { |
74 | code_skipnext(); |
75 | *var_map = tvar[code_getaddr()]; |
76 | if (code_peek() == kwTYPE_UDS_EL) { |
77 | var_p = map_resolve_fields(*var_map, var_map); |
78 | } |
79 | } |
80 | return var_p; |
81 | } |
82 | |
83 | var_t *v_set_self(var_t *map) { |
84 | var_t *self = tvar[SYSVAR_SELF]; |
85 | var_t *result = (self->type == V_REF) ? self->v.ref : NULL; |
86 | if (map != NULL) { |
87 | self->const_flag = 0; |
88 | self->type = V_REF; |
89 | self->v.ref = map; |
90 | } else { |
91 | self->const_flag = 1; |
92 | self->type = V_INT; |
93 | self->v.i = 0; |
94 | } |
95 | return result; |
96 | } |
97 | |
98 | /** |
99 | * Used by code_getvarptr() to retrieve an element ptr of an array |
100 | */ |
101 | var_t *code_getvarptr_arridx(var_t *basevar_p) { |
102 | var_t *var_p = NULL; |
103 | |
104 | if (code_peek() != kwTYPE_LEVEL_BEGIN) { |
105 | err_arrmis_lp(); |
106 | } else { |
107 | code_skipnext(); |
108 | bcip_t array_index = get_array_idx(basevar_p); |
109 | if (!prog_error) { |
110 | if ((int) array_index < v_asize(basevar_p) && (int) array_index >= 0) { |
111 | var_p = v_elem(basevar_p, array_index); |
112 | if (code_peek() == kwTYPE_LEVEL_END) { |
113 | code_skipnext(); |
114 | if (code_peek() == kwTYPE_LEVEL_BEGIN) { |
115 | // there is a second array inside |
116 | if (var_p->type != V_ARRAY) { |
117 | err_varisnotarray(); |
118 | } else { |
119 | return code_getvarptr_arridx(var_p); |
120 | } |
121 | } |
122 | } else { |
123 | err_arrmis_rp(); |
124 | } |
125 | } else { |
126 | err_arridx(array_index, v_asize(basevar_p)); |
127 | } |
128 | } |
129 | } |
130 | return var_p; |
131 | } |
132 | |
133 | var_t *code_get_map_element(var_t *map, var_t *field) { |
134 | var_t *result = NULL; |
135 | |
136 | if (code_peek() != kwTYPE_LEVEL_BEGIN) { |
137 | err_arrmis_lp(); |
138 | } else if (field->type == V_PTR) { |
139 | prog_ip = cmd_push_args(kwFUNC, field->v.ap.p, field->v.ap.v); |
140 | var_t *self = v_set_self(map); |
141 | bc_loop(2); |
142 | v_set_self(self); |
143 | |
144 | if (!prog_error) { |
145 | stknode_t udf_rv; |
146 | code_pop(&udf_rv, kwTYPE_RET); |
147 | if (udf_rv.type != kwTYPE_RET) { |
148 | err_stackmess(); |
149 | } else { |
150 | // result must exist until processed in eval() |
151 | var_p_t var = map_get(map, MAP_TMP_FIELD); |
152 | if (var == NULL) { |
153 | var = map_add_var(map, MAP_TMP_FIELD, 0); |
154 | } |
155 | v_move(var, udf_rv.x.vdvar.vptr); |
156 | v_detach(udf_rv.x.vdvar.vptr); |
157 | result = var; |
158 | } |
159 | } |
160 | } else if (field->type == V_ARRAY) { |
161 | result = code_getvarptr_arridx(field); |
162 | } else { |
163 | code_skipnext(); |
164 | var_t var; |
165 | v_init(&var); |
166 | eval(&var); |
167 | if (!prog_error) { |
168 | map_get_value(field, &var, &result); |
169 | if (code_peek() == kwTYPE_LEVEL_END) { |
170 | code_skipnext(); |
171 | } else { |
172 | err_missing_sep(); |
173 | } |
174 | } |
175 | v_free(&var); |
176 | } |
177 | return result; |
178 | } |
179 | |
180 | /** |
181 | * resolve a composite variable reference, eg: ar.ch(0).foo |
182 | */ |
183 | var_t *code_resolve_varptr(var_t *var_p, int until_parens) { |
184 | int deref = 1; |
185 | while (deref && var_p != NULL) { |
186 | switch (code_peek()) { |
187 | case kwTYPE_LEVEL_BEGIN: |
188 | if (until_parens) { |
189 | deref = 0; |
190 | } else { |
191 | var_p = code_getvarptr_arridx(var_p); |
192 | } |
193 | break; |
194 | case kwTYPE_UDS_EL: |
195 | var_p = map_resolve_fields(var_p, NULL); |
196 | break; |
197 | default: |
198 | deref = 0; |
199 | } |
200 | } |
201 | return var_p; |
202 | } |
203 | |
204 | /** |
205 | * resolve a composite variable reference, eg: ar.ch(0).foo |
206 | */ |
207 | var_t *code_resolve_map(var_t *var_p, int until_parens) { |
208 | int deref = 1; |
209 | var_t *v_parent = var_p; |
210 | while (deref && var_p != NULL) { |
211 | switch (code_peek()) { |
212 | case kwTYPE_LEVEL_BEGIN: |
213 | if (until_parens) { |
214 | deref = 0; |
215 | } else { |
216 | var_p = code_get_map_element(v_parent, var_p); |
217 | } |
218 | break; |
219 | case kwTYPE_UDS_EL: |
220 | var_p = map_resolve_fields(var_p, &v_parent); |
221 | break; |
222 | default: |
223 | deref = 0; |
224 | } |
225 | var_p = eval_ref_var(var_p); |
226 | } |
227 | return var_p; |
228 | } |
229 | |
230 | var_t *resolve_var_ref(var_t *var_p, int *is_ptr) { |
231 | switch (code_peek()) { |
232 | case kwTYPE_LEVEL_BEGIN: |
233 | if (var_p->type == V_PTR) { |
234 | *is_ptr = 1; |
235 | } else { |
236 | var_p = resolve_var_ref(code_getvarptr_arridx(var_p), is_ptr); |
237 | } |
238 | break; |
239 | case kwTYPE_UDS_EL: |
240 | var_p = resolve_var_ref(map_resolve_fields(var_p, NULL), is_ptr); |
241 | break; |
242 | } |
243 | return var_p; |
244 | } |
245 | |
246 | /** |
247 | * resolve the map without invoking any V_PTRs |
248 | */ |
249 | var_t *code_isvar_resolve_map(var_t *var_p, int *is_ptr) { |
250 | int deref = 1; |
251 | var_t *v_parent = var_p; |
252 | while (deref && var_p != NULL) { |
253 | switch (code_peek()) { |
254 | case kwTYPE_LEVEL_BEGIN: |
255 | if (var_p->type == V_PTR) { |
256 | *is_ptr = 1; |
257 | deref = 0; |
258 | } else { |
259 | var_p = code_get_map_element(v_parent, var_p); |
260 | } |
261 | break; |
262 | case kwTYPE_UDS_EL: |
263 | var_p = map_resolve_fields(var_p, NULL); |
264 | break; |
265 | default: |
266 | deref = 0; |
267 | } |
268 | var_p = eval_ref_var(var_p); |
269 | } |
270 | return var_p; |
271 | } |
272 | |
273 | /** |
274 | * returns true if the next code is a variable. if the following code is an |
275 | * expression (no matter if the first item is a variable), returns false |
276 | */ |
277 | int code_isvar() { |
278 | if (code_peek() == kwTYPE_VAR) { |
279 | int is_ptr; |
280 | var_t *basevar_p; |
281 | bcip_t cur_ip = prog_ip; |
282 | |
283 | code_skipnext(); |
284 | var_t *var_p = basevar_p = tvar[code_getaddr()]; |
285 | switch (basevar_p->type) { |
286 | case V_MAP: |
287 | is_ptr = 0; |
288 | var_p = code_isvar_resolve_map(var_p, &is_ptr); |
289 | if (is_ptr) { |
290 | // restore IP |
291 | prog_ip = cur_ip; |
292 | return 1; |
293 | } |
294 | break; |
295 | case V_ARRAY: |
296 | var_p = code_resolve_varptr(var_p, 0); |
297 | break; |
298 | case V_REF: |
299 | is_ptr = 0; |
300 | var_p = resolve_var_ref(var_p, &is_ptr); |
301 | if (is_ptr) { |
302 | prog_ip = cur_ip; |
303 | return 1; |
304 | } |
305 | break; |
306 | default: |
307 | if (code_peek() == kwTYPE_LEVEL_BEGIN) { |
308 | var_p = NULL; |
309 | } |
310 | } |
311 | if (var_p) { |
312 | byte code = code_peek(); |
313 | if (code == kwTYPE_EOC || |
314 | code == kwTYPE_SEP || |
315 | code == kwTYPE_LEVEL_END || |
316 | kw_check_evexit(code)) { |
317 | // restore IP |
318 | prog_ip = cur_ip; |
319 | return 1; |
320 | } |
321 | } |
322 | |
323 | // restore IP |
324 | prog_ip = cur_ip; |
325 | } |
326 | return 0; |
327 | } |
328 | |
329 | var_t *eval_ref_var(var_t *var_p) { |
330 | var_t *result = var_p; |
331 | while (result != NULL && result->type == V_REF) { |
332 | if (result->v.ref == var_p) { |
333 | // circular referance error |
334 | result = NULL; |
335 | err_ref_circ_var(); |
336 | break; |
337 | } else { |
338 | result = result->v.ref; |
339 | } |
340 | } |
341 | return result; |
342 | } |
343 | |
344 | /* |
345 | * evaluate the pcode string |
346 | */ |
347 | void v_eval_str(var_p_t v) { |
348 | int len = code_getstrlen(); |
349 | v->type = V_STR; |
350 | v->v.p.length = len; |
351 | v->v.p.ptr = (char *)&prog_source[prog_ip]; |
352 | v->v.p.owner = 0; |
353 | prog_ip += len; |
354 | } |
355 | |
356 | |