1// This file is part of SmallBASIC
2//
3// SmallBASIC variable manager.
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/sys.h"
11#include "common/sberr.h"
12
13#define INT_STR_LEN 64
14#define VAR_POOL_SIZE 8192
15
16var_t var_pool[VAR_POOL_SIZE];
17var_t *var_pool_head;
18
19void v_init_pool() {
20 for (uint32_t i = 0; i < VAR_POOL_SIZE; i++) {
21 v_init(&var_pool[i]);
22 var_pool[i].pooled = 1;
23 if (i + 1 < VAR_POOL_SIZE) {
24 var_pool[i].v.pool_next = &var_pool[i + 1];
25 } else {
26 var_pool[i].v.pool_next = NULL;
27 }
28 }
29 var_pool_head = &var_pool[0];
30}
31
32/*
33 * creates and returns a new variable
34 */
35var_t *v_new() {
36 var_t *result = var_pool_head;
37 if (result != NULL) {
38 // remove an item from the free-list
39 var_pool_head = result->v.pool_next;
40 } else {
41 // pool exhausted
42 result = (var_t *)malloc(sizeof(var_t));
43 result->pooled = 0;
44 }
45 v_init(result);
46 return result;
47}
48
49void v_pool_free(var_t *var) {
50 // insert back into the free list
51 var->v.pool_next = var_pool_head;
52 var_pool_head = var;
53}
54
55uint32_t v_get_capacity(uint32_t size) {
56 return size + (size / 2) + 1;
57}
58
59// allocate capacity in the array container
60void v_alloc_capacity(var_t *var, uint32_t size) {
61 uint32_t capacity = v_get_capacity(size);
62 v_capacity(var) = capacity;
63 v_asize(var) = size;
64 v_data(var) = (var_t *)malloc(sizeof(var_t) * capacity);
65 if (!v_data(var)) {
66 err_memory();
67 } else {
68 for (uint32_t i = 0; i < capacity; i++) {
69 var_t *e = v_elem(var, i);
70 e->pooled = 0;
71 v_init(e);
72 }
73 }
74}
75
76// create an new empty array
77void v_init_array(var_t *var) {
78 v_capacity(var) = 0;
79 v_asize(var) = 0;
80 v_data(var) = NULL;
81 v_maxdim(var) = 1;
82 v_ubound(var, 0) = opt_base;
83 v_lbound(var, 0) = opt_base;
84}
85
86// create an array of the given size
87void v_new_array(var_t *var, uint32_t size) {
88 var->type = V_ARRAY;
89 v_alloc_capacity(var, size);
90}
91
92void v_set_array1_size(var_t *var, uint32_t size) {
93 v_asize(var) = size;
94 v_maxdim(var) = 1;
95 v_ubound(var, 0) = v_lbound(var, 0) + (size - 1);
96}
97
98void v_copy_array(var_t *dest, const var_t *src) {
99 dest->type = V_ARRAY;
100 v_alloc_capacity(dest, v_asize(src));
101
102 // copy dimensions
103 v_maxdim(dest) = v_maxdim(src);
104 for (int i = 0; i < v_maxdim(src); i++) {
105 v_ubound(dest, i) = v_ubound(src, i);
106 v_lbound(dest, i) = v_lbound(src, i);
107 }
108
109 // copy each element
110 uint32_t v_size = v_asize(src);
111 for (uint32_t i = 0; i < v_size; i++) {
112 var_t *dest_vp = v_elem(dest, i);
113 v_init(dest_vp);
114 v_set(dest_vp, v_elem(src, i));
115 }
116}
117
118void v_array_free(var_t *var) {
119 uint32_t v_size = v_capacity(var);
120 if (v_size && v_data(var)) {
121 for (uint32_t i = 0; i < v_size; i++) {
122 v_free(v_elem(var, i));
123 }
124 free(var->v.a.data);
125 }
126}
127
128void v_init_str(var_t *var, int length) {
129 var->type = V_STR;
130 var->v.p.ptr = malloc(length + 1);
131 var->v.p.ptr[0] = '\0';
132 var->v.p.length = length + 1;
133 var->v.p.owner = 1;
134}
135
136void v_move_str(var_t *var, char *str) {
137 var->type = V_STR;
138 var->v.p.ptr = str;
139 var->v.p.length = strlen(str) + 1;
140 var->v.p.owner = 1;
141}
142
143/*
144 * returns true if the user's program must use this var as an empty var
145 * this is usefull for arrays
146 */
147int v_isempty(var_t *var) {
148 switch (var->type) {
149 case V_INT:
150 return (var->v.i == 0);
151 case V_NUM:
152 return (var->v.n == 0.0);
153 case V_STR:
154 return (v_strlen(var) == 0);
155 case V_ARRAY:
156 return (v_asize(var) == 0);
157 case V_PTR:
158 return (var->v.ap.p == 0);
159 case V_MAP:
160 return map_is_empty(var);
161 case V_REF:
162 return v_isempty(var->v.ref);
163 }
164
165 return 1;
166}
167
168int v_strlen(const var_t *v) {
169 int result;
170 if (v->type == V_STR) {
171 result = v->v.p.length;
172 if (result && v->v.p.ptr[result - 1] == '\0') {
173 result--;
174 }
175 } else {
176 result = 0;
177 }
178 return result;
179}
180
181/*
182 * returns the length of the variable
183 */
184int v_length(var_t *var) {
185 char tmpsb[INT_STR_LEN];
186
187 switch (var->type) {
188 case V_INT:
189 ltostr(var->v.i, tmpsb);
190 return strlen(tmpsb);
191 case V_NUM:
192 ftostr(var->v.n, tmpsb);
193 return strlen(tmpsb);
194 case V_STR:
195 return v_strlen(var);
196 case V_ARRAY:
197 return v_asize(var);
198 case V_PTR:
199 ltostr(var->v.ap.p, tmpsb);
200 return strlen(tmpsb);
201 case V_MAP:
202 return map_length(var);
203 case V_REF:
204 return v_length(var->v.ref);
205 }
206 return 0;
207}
208
209/*
210 * resize an existing array
211 */
212void v_resize_array(var_t *v, uint32_t size) {
213 if (v->type != V_ARRAY) {
214 err_varisnotarray();
215 } else if ((int)size < 0) {
216 err_evargerr();
217 } else if (size == v_asize(v)) {
218 // already at target size
219 } else if (size == 0) {
220 v_free(v);
221 v_init_array(v);
222 v->type = V_ARRAY;
223 } else if (size < v_asize(v)) {
224 // resize down. free discarded elements
225 uint32_t v_size = v_asize(v);
226 for (uint32_t i = size; i < v_size; i++) {
227 v_free(v_elem(v, i));
228 }
229 v_set_array1_size(v, size);
230 } else if (size <= v_capacity(v)) {
231 // use existing capacity
232 v_set_array1_size(v, size);
233 } else {
234 // insufficient capacity
235 uint32_t prev_size = v_asize(v);
236 if (prev_size == 0) {
237 v_alloc_capacity(v, size);
238 } else if (prev_size < size) {
239 // resize & copy
240 uint32_t capacity = v_get_capacity(size);
241 v_capacity(v) = capacity;
242 v_data(v) = (var_t *)realloc(v_data(v), sizeof(var_t) * capacity);
243 for (uint32_t i = prev_size; i < capacity; i++) {
244 var_t *e = v_elem(v, i);
245 e->pooled = 0;
246 v_init(e);
247 }
248 }
249
250 // init vars
251 for (uint32_t i = prev_size; i < size; i++) {
252 v_init(v_elem(v, i));
253 }
254
255 v_set_array1_size(v, size);
256 }
257}
258
259/*
260 * create RxC array
261 */
262void v_tomatrix(var_t *v, int r, int c) {
263 v_free(v);
264 v_new_array(v, r * c);
265 v_maxdim(v) = 2;
266 v_lbound(v, 0) = v_lbound(v, 1) = opt_base;
267 v_ubound(v, 0) = opt_base + (r - 1);
268 v_ubound(v, 1) = opt_base + (c - 1);
269}
270
271/*
272 * create array
273 */
274void v_toarray1(var_t *v, uint32_t r) {
275 v_free(v);
276 v->type = V_ARRAY;
277 if (r > 0) {
278 v_new_array(v, r);
279 v_maxdim(v) = 1;
280 v_lbound(v, 0) = opt_base;
281 v_ubound(v, 0) = opt_base + (r - 1);
282 } else {
283 v_init_array(v);
284 }
285}
286
287/*
288 * returns true if the variable v is not empty (0 for nums)
289 */
290int v_is_nonzero(var_t *v) {
291 switch (v->type) {
292 case V_INT:
293 return (v->v.i != 0);
294 case V_NUM:
295 return (ABS(v->v.n) > 1E-308);
296 case V_STR:
297 return (v->v.p.length != 0);
298 case V_ARRAY:
299 return (v_asize(v) != 0);
300 case V_PTR:
301 return (v->v.ap.p != 0);
302 case V_MAP:
303 return !map_is_empty(v);
304 };
305 return 0;
306}
307
308/*
309 * compare the variable a with the variable b
310 * returns
311 * -1 a < b
312 * +1 a > b
313 * 0 a = b
314 */
315int v_compare(var_t *a, var_t *b) {
316 var_num_t dt;
317 var_int_t di;
318 if (a == 0 || b == 0) {
319 err_evsyntax();
320 return 0;
321 }
322
323 if (a->type == V_INT && b->type == V_INT) {
324 di = (a->v.i - b->v.i);
325 return (di < 0 ? -1 : di > 0 ? 1 : 0);
326 } else if ((a->type == V_INT || a->type == V_NUM) &&
327 (b->type == V_INT || b->type == V_NUM)) {
328 var_num_t left = (a->type == V_NUM) ? a->v.n : a->v.i;
329 var_num_t right = (b->type == V_NUM) ? b->v.n : b->v.i;
330 if (fabs(left - right) < EPSILON) {
331 return 0;
332 } else {
333 return (left - right) < 0.0 ? -1 : 1;
334 }
335 }
336 if ((a->type == V_STR) && (b->type == V_STR)) {
337 return strcmp(a->v.p.ptr, b->v.p.ptr);
338 }
339 if ((a->type == V_STR) && (b->type == V_NUM)) {
340 if (a->v.p.ptr[0] == '\0' || is_number(a->v.p.ptr)) {
341 // compare nums
342 dt = v_getval(a);
343 return (dt < b->v.n) ? -1 : ((dt == b->v.n) ? 0 : 1);
344 }
345 return 1;
346 }
347 if ((a->type == V_NUM) && (b->type == V_STR)) {
348 if (b->v.p.ptr[0] == '\0' || is_number(b->v.p.ptr)) {
349 // compare nums
350 dt = v_getval(b);
351 return (dt < a->v.n) ? 1 : ((dt == a->v.n) ? 0 : -1);
352 }
353 return - 1;
354 }
355 if ((a->type == V_STR) && (b->type == V_INT)) {
356 if (a->v.p.ptr[0] == '\0' || is_number(a->v.p.ptr)) {
357 // compare nums
358 di = v_igetval(a);
359 return (di < b->v.i) ? -1 : ((di == b->v.i) ? 0 : 1);
360 }
361 return 1;
362 }
363 if ((a->type == V_INT) && (b->type == V_STR)) {
364 if (b->v.p.ptr[0] == '\0' || is_number(b->v.p.ptr)) {
365 // compare nums
366 di = v_igetval(b);
367 return (di < a->v.i) ? 1 : ((di == a->v.i) ? 0 : -1);
368 }
369 return - 1;
370 }
371 if ((a->type == V_ARRAY) && (b->type == V_ARRAY)) {
372 // check size
373 if (v_asize(a) < v_asize(b)) {
374 return -1;
375 } else if (v_asize(a) > v_asize(b)) {
376 return 1;
377 }
378 // check every element
379 for (uint32_t i = 0; i < v_asize(a); i++) {
380 var_t *ea = v_elem(a, i);
381 var_t *eb = v_elem(b, i);
382 int ci = v_compare(ea, eb);
383 if (ci != 0) {
384 return ci;
385 }
386 }
387 // equal
388 return 0;
389 }
390
391 if (a->type == V_MAP && b->type == V_MAP) {
392 return map_compare(a, b);
393 }
394
395 if (a->type == V_NIL || b->type == V_NIL) {
396 // return equal (0) when both NONE
397 return (a->type != b->type);
398 }
399
400 err_evtype();
401 return 1;
402}
403
404/*
405 * add two variables
406 * result = a + b
407 */
408void v_add(var_t *result, var_t *a, var_t *b) {
409 char tmpsb[INT_STR_LEN];
410
411 if (a->type == V_STR && b->type == V_STR) {
412 int length = strlen(a->v.p.ptr) + strlen(b->v.p.ptr);
413 v_init_str(result, length);
414 strcpy(result->v.p.ptr, a->v.p.ptr);
415 strcat(result->v.p.ptr, b->v.p.ptr);
416 return;
417 } else if (a->type == V_INT && b->type == V_INT) {
418 result->type = V_INT;
419 result->v.i = a->v.i + b->v.i;
420 return;
421 } else if (a->type == V_NUM && b->type == V_NUM) {
422 result->type = V_NUM;
423 result->v.n = a->v.n + b->v.n;
424 return;
425 } else if (a->type == V_NUM && b->type == V_INT) {
426 result->type = V_NUM;
427 result->v.n = a->v.n + b->v.i;
428 return;
429 } else if (a->type == V_INT && b->type == V_NUM) {
430 result->type = V_NUM;
431 result->v.n = a->v.i + b->v.n;
432 return;
433 } else if (a->type == V_STR && (b->type == V_INT || b->type == V_NUM)) {
434 if (is_number(a->v.p.ptr)) {
435 result->type = V_NUM;
436 if (b->type == V_INT) {
437 result->v.n = b->v.i + v_getval(a);
438 } else {
439 result->v.n = b->v.n + v_getval(a);
440 }
441 } else {
442 v_init_str(result, strlen(a->v.p.ptr) + INT_STR_LEN);
443 strcpy(result->v.p.ptr, a->v.p.ptr);
444 if (b->type == V_INT) {
445 ltostr(b->v.i, tmpsb);
446 } else {
447 ftostr(b->v.n, tmpsb);
448 }
449 strcat(result->v.p.ptr, tmpsb);
450 result->v.p.length = strlen(result->v.p.ptr) + 1;
451 }
452 } else if ((a->type == V_INT || a->type == V_NUM) && b->type == V_STR) {
453 if (is_number(b->v.p.ptr)) {
454 result->type = V_NUM;
455 if (a->type == V_INT) {
456 result->v.n = a->v.i + v_getval(b);
457 } else {
458 result->v.n = a->v.n + v_getval(b);
459 }
460 } else {
461 v_init_str(result, strlen(b->v.p.ptr) + INT_STR_LEN);
462 if (a->type == V_INT) {
463 ltostr(a->v.i, tmpsb);
464 } else {
465 ftostr(a->v.n, tmpsb);
466 }
467 strcpy(result->v.p.ptr, tmpsb);
468 strcat(result->v.p.ptr, b->v.p.ptr);
469 result->v.p.length = strlen(result->v.p.ptr) + 1;
470 }
471 } else if (b->type == V_MAP) {
472 char *map = map_to_str(b);
473 v_set(result, a);
474 v_strcat(result, map);
475 free(map);
476 }
477}
478
479/*
480 * assign (dest = src)
481 */
482void v_set(var_t *dest, const var_t *src) {
483 v_free(dest);
484 dest->const_flag = 0;
485 dest->type = src->type;
486
487 switch (src->type) {
488 case V_INT:
489 dest->v.i = src->v.i;
490 break;
491 case V_NUM:
492 dest->v.n = src->v.n;
493 break;
494 case V_STR:
495 if (src->v.p.owner) {
496 dest->v.p.length = v_strlen(src) + 1;
497 dest->v.p.ptr = (char *)malloc(dest->v.p.length);
498 dest->v.p.owner = 1;
499 strcpy(dest->v.p.ptr, src->v.p.ptr);
500 } else {
501 dest->v.p.length = src->v.p.length;
502 dest->v.p.ptr = src->v.p.ptr;
503 dest->v.p.owner = 0;
504 }
505 break;
506 case V_ARRAY:
507 if (v_asize(src)) {
508 v_copy_array(dest, src);
509 } else {
510 v_init_array(dest);
511 }
512 break;
513 case V_PTR:
514 dest->v.ap.p = src->v.ap.p;
515 dest->v.ap.v = src->v.ap.v;
516 break;
517 case V_MAP:
518 // reset type since not yet a map
519 dest->type = 0;
520 map_set(dest, (const var_p_t)src);
521 break;
522 case V_REF:
523 dest->v.ref = src->v.ref;
524 break;
525 case V_FUNC:
526 dest->v.fn.cb = src->v.fn.cb;
527 dest->v.fn.id = src->v.fn.id;
528 break;
529 case V_NIL:
530 dest->type = V_NIL;
531 dest->const_flag = 1;
532 break;
533 }
534}
535
536/*
537 * assign (dest = src)
538 */
539void v_move(var_t *dest, const var_t *src) {
540 v_free(dest);
541 dest->const_flag = 0;
542 dest->type = src->type;
543
544 switch (src->type) {
545 case V_INT:
546 dest->v.i = src->v.i;
547 break;
548 case V_NUM:
549 dest->v.n = src->v.n;
550 break;
551 case V_STR:
552 dest->v.p.ptr = src->v.p.ptr;
553 dest->v.p.length = src->v.p.length;
554 dest->v.p.owner = src->v.p.owner;
555 break;
556 case V_ARRAY:
557 memcpy(&dest->v.a, &src->v.a, sizeof(src->v.a));
558 break;
559 case V_PTR:
560 dest->v.ap.p = src->v.ap.p;
561 dest->v.ap.v = src->v.ap.v;
562 break;
563 case V_MAP:
564 dest->v.m.map = src->v.m.map;
565 dest->v.m.count = src->v.m.count;
566 dest->v.m.size = src->v.m.size;
567 dest->v.m.id = src->v.m.id;
568 break;
569 case V_REF:
570 dest->v.ref = src->v.ref;
571 break;
572 case V_FUNC:
573 dest->v.fn.cb = src->v.fn.cb;
574 dest->v.fn.id = src->v.fn.id;
575 break;
576 case V_NIL:
577 dest->type = V_NIL;
578 dest->const_flag = 1;
579 break;
580 }
581}
582
583/*
584 * return a full copy of the 'source'
585 */
586var_t *v_clone(const var_t *source) {
587 var_t *vnew = v_new();
588 v_set(vnew, source);
589 return vnew;
590}
591
592/*
593 * add b to a
594 */
595void v_inc(var_t *a, var_t *b) {
596 if (a->type == V_INT && b->type == V_INT) {
597 a->v.i += b->v.i;
598 } else if (a->type == V_NUM && b->type == V_NUM) {
599 a->v.n += b->v.n;
600 } else if (a->type == V_NUM && b->type == V_INT) {
601 a->v.n += b->v.i;
602 } else if (a->type == V_INT && b->type == V_NUM) {
603 a->type = V_NUM;
604 a->v.n = a->v.i + b->v.n;
605 } else {
606 err_varnotnum();
607 }
608}
609
610/*
611 * returns the sign of a variable
612 */
613int v_sign(var_t *x) {
614 if (x->type == V_INT) {
615 return (x->v.i < 0) ? -1 : ((x->v.i == 0) ? 0 : 1);
616 } else if (x->type == V_NUM) {
617 return (x->v.n < 0) ? -1 : ((x->v.n == 0) ? 0 : 1);
618 }
619 err_varnotnum();
620 return 0;
621}
622
623/*
624 * setup a string variable
625 */
626void v_createstr(var_t *v, const char *src) {
627 v_init_str(v, strlen(src));
628 strcpy(v->v.p.ptr, src);
629}
630
631/*
632 * returns the string representation of the variable
633 */
634char *v_str(var_t *arg) {
635 char *buffer;
636 switch (arg->type) {
637 case V_INT:
638 buffer = malloc(INT_STR_LEN);
639 ltostr(arg->v.i, buffer);
640 break;
641 case V_NUM:
642 buffer = malloc(INT_STR_LEN);
643 ftostr(arg->v.n, buffer);
644 break;
645 case V_STR:
646 buffer = strdup(arg->v.p.ptr);
647 break;
648 case V_ARRAY:
649 case V_MAP:
650 buffer = map_to_str(arg);
651 break;
652 case V_FUNC:
653 case V_PTR:
654 buffer = malloc(5);
655 strcpy(buffer, "func");
656 break;
657 case V_NIL:
658 buffer = malloc(5);
659 strcpy(buffer, SB_KW_NONE_STR);
660 break;
661 default:
662 buffer = malloc(1);
663 buffer[0] = '\0';
664 break;
665 }
666 return buffer;
667}
668
669/*
670 * converts the variable to string-variable
671 */
672void v_tostr(var_t *arg) {
673 if (arg->type != V_STR) {
674 char *tmp = v_str(arg);
675 v_free(arg);
676 v_init_str(arg, strlen(tmp));
677 strcpy(arg->v.p.ptr, tmp);
678 free(tmp);
679 }
680}
681
682/*
683 * set the value of 'var' to string
684 */
685void v_setstr(var_t *var, const char *str) {
686 if (var->type != V_STR || strcmp(str, var->v.p.ptr) != 0) {
687 v_free(var);
688 v_init_str(var, strlen(str));
689 strcpy(var->v.p.ptr, str);
690 }
691}
692
693void v_setstrn(var_t *var, const char *str, int len) {
694 if (var->type != V_STR || strncmp(str, var->v.p.ptr, len) != 0) {
695 v_free(var);
696 v_init_str(var, len);
697 strlcpy(var->v.p.ptr, str, len + 1);
698 }
699}
700
701/*
702 * adds a string to current string value
703 */
704void v_strcat(var_t *var, const char *str) {
705 if (var->type == V_INT || var->type == V_NUM) {
706 v_tostr(var);
707 }
708 if (var->type == V_STR) {
709 if (var->v.p.owner) {
710 var->v.p.length = strlen(var->v.p.ptr) + strlen(str) + 1;
711 var->v.p.ptr = realloc(var->v.p.ptr, var->v.p.length);
712 strcat(var->v.p.ptr, str);
713 } else {
714 // mutate into owner string
715 char *p = var->v.p.ptr;
716 int len = var->v.p.length + strlen(str);
717 v_init_str(var, len);
718 strcpy(var->v.p.ptr, p);
719 strcat(var->v.p.ptr, str);
720 }
721
722 } else {
723 err_typemismatch();
724 }
725}
726
727/*
728 * set the value of 'var' to n
729 */
730void v_setreal(var_t *var, var_num_t n) {
731 v_free(var);
732 var->type = V_NUM;
733 var->v.n = n;
734}
735
736/*
737 * set the value of 'var' to i
738 */
739void v_setint(var_t *var, var_int_t i) {
740 v_free(var);
741 var->type = V_INT;
742 var->v.i = i;
743}
744
745/*
746 * return the string value of 'var'
747 */
748char *v_getstr(var_t *var) {
749 if (var->type != V_STR) {
750 v_tostr(var);
751 }
752 return var->v.p.ptr;
753}
754
755/*
756 * set an empty string
757 */
758void v_zerostr(var_t *r) {
759 v_free(r);
760 v_init_str(r, 0);
761 r->v.p.ptr[0] = '\0';
762}
763
764/*
765 * convert's a user's string to variable
766 *
767 * its decides in what format to store the value
768 * its used mostly by 'input' functions
769 */
770void v_input2var(const char *str, var_t *var) {
771 v_free(var);
772
773 if (!str || str[0] == '\0') {
774 // no data
775 v_setstr(var, str);
776 } else {
777 int type;
778 var_int_t lv;
779 var_num_t dv;
780
781 char *buf = strdup(str);
782 char *sb = strdup(str);
783 char *np = get_numexpr(sb, buf, &type, &lv, &dv);
784
785 if (type == 1 && *np == '\0') {
786 v_setint(var, lv);
787 } else if (type == 2 && *np == '\0') {
788 v_setreal(var, dv);
789 } else {
790 v_setstr(var, str);
791 }
792 free(sb);
793 free(buf);
794 }
795}
796
797void v_create_func(var_p_t map, const char *name, method cb) {
798 var_p_t v_func = map_add_var(map, name, 0);
799 v_func->type = V_FUNC;
800 v_func->v.fn.cb = cb;
801 v_func->v.fn.id = 0;
802}
803