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 */
21bcip_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 */
71var_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
83var_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 */
101var_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
133var_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 */
183var_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 */
207var_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
230var_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 */
249var_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 */
277int 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
329var_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 */
347void 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