More fixings toward 2.0.
[mpsl.git] / mpsl_c.c
blob0be16fe5beb88a534a61974c23882c2b5ea04319
1 /*
3 MPSL - Minimum Profit Scripting Language
4 Copyright (C) 2003/2010 Angel Ortega <angel@triptico.com>
6 mpsl_c.c - Minimum Profit Scripting Language Core
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 http://www.triptico.com
26 #include "config.h"
28 #include <stdio.h>
29 #include <wchar.h>
30 #include <math.h>
31 #include "mpdm.h"
32 #include "mpsl.h"
35 /** data **/
37 /* global abort flag */
38 int mpsl_abort = 0;
40 /* temporary storage for the local symbol table */
41 static mpdm_t local_symtbl = NULL;
43 /* temporary storage for the opcode table
44 (only usable while compiling) */
45 mpdm_t mpsl_opcodes = NULL;
47 /* flag to mark if an execution is from inside constant folding */
48 static int in_constant_folding = 0;
50 /* pointer to a trap function */
51 static mpdm_t mpsl_trap_func = NULL;
54 /** code **/
56 /**
57 * mpsl_is_true - Tests if a value is true.
58 * @v: the value
60 * If @v is a valid MPSL 'false' value (NULL, "" or the "0" string),
61 * returns zero, or nonzero otherwise.
63 int mpsl_is_true(mpdm_t v)
65 /* if value is NULL, it's false */
66 if (v == NULL)
67 return 0;
69 /* if it's a printable string... */
70 if (v->flags & MPDM_STRING) {
71 wchar_t * ptr = (wchar_t *)v->data;
73 /* ... and it's "" or the "0" string, it's false */
74 if(*ptr == L'\0' || (*ptr == L'0' && *(ptr + 1) == L'\0'))
75 return 0;
78 /* any other case is true */
79 return 1;
83 /**
84 * mpsl_boolean - Returns 'true' or 'false' MPSL stock values.
85 * @b: boolean selector
87 * Returns MPSL's 'false' or 'true' values depending on the value in @b.
89 mpdm_t mpsl_boolean(int b)
91 return b ? mpdm_hget_s(mpdm_root(), L"TRUE") : NULL;
95 static mpdm_t find_local_symtbl(mpdm_t s, mpdm_t l)
96 /* finds the local symbol table hash that holds l */
98 int n;
99 mpdm_t v = NULL;
101 /* no local symbol table? nothing to find */
102 if(l == NULL)
103 return NULL;
105 /* if s is multiple, take just the first element */
106 if(MPDM_IS_ARRAY(s))
107 s = mpdm_aget(s, 0);
109 /* travel the local symbol table trying to find it */
110 for (n = mpdm_size(l) - 1; n >= 0; n--) {
111 mpdm_t h = mpdm_aget(l, n);
113 if (mpdm_exists(h, s)) {
114 v = h;
115 break;
119 return v;
123 static void set_local_symbols(mpdm_t s, mpdm_t v, mpdm_t l)
124 /* sets (or creates) a list of local symbols with a list of values */
126 mpdm_t h;
128 if (l == NULL)
129 return;
131 /* gets the top local variable frame */
132 h = mpdm_aget(l, -1);
134 if (MPDM_IS_ARRAY(s) || MPDM_IS_ARRAY(v)) {
135 int n;
136 mpdm_t a;
138 for (n = 0; n < mpdm_size(s); n++)
139 mpdm_hset(h, mpdm_aget(s, n), mpdm_aget(v, n));
141 /* store the rest of arguments into _ */
142 a = MPDM_A(0);
144 for (; n < mpdm_size(v); n++)
145 mpdm_push(a, mpdm_aget(v, n));
147 mpdm_hset_s(h, L"_", a);
149 else
150 mpdm_hset(h, s, v);
154 static mpdm_t get_symbol(mpdm_t s, mpdm_t l)
155 /* gets a symbol from a local symbol table, or the global */
157 return mpdm_sget(find_local_symtbl(s, l), s);
161 static mpdm_t set_symbol(mpdm_t s, mpdm_t v, mpdm_t l)
162 /* sets a symbol in a local symbol table, or the global */
164 mpdm_sset(find_local_symtbl(s, l), s, v);
165 return v;
170 * mpsl_set_symbol - Sets value to a symbol.
171 * @s: symbol name
172 * @v: value
174 * Assigns the value @v to the @s symbol. If the value exists as
175 * a local symbol, it's assigned to it; otherwise, it's set as a global
176 * symbol (and created if it does not exist).
178 * This function is only meant to be executed from inside an MPSL
179 * program; from outside, it's exactly the same as calling mpdm_sset()
180 * (as the local symbol table won't exist).
182 mpdm_t mpsl_set_symbol(mpdm_t s, mpdm_t v)
184 return set_symbol(s, v, local_symtbl);
189 * mpsl_get_symbol - Gets the value of a symbol.
190 * @s: symbol name
192 * Gets the value of a symbol. The symbol can be local or global
193 * (if the symbol exists in both tables, the local value will be returned).
195 * This function is only meant to be executed from inside an MPSL
196 * program; from outside, it's exactly the same as calling mpdm_sget()
197 * (as the local symbol table won't exist).
199 mpdm_t mpsl_get_symbol(mpdm_t s)
201 return get_symbol(s, local_symtbl);
206 * mpsl_error - Generates an error.
207 * @err: the error message
209 * Generates an error. The @err error message is stored in the ERROR
210 * mpsl variable and the mpsl_abort global flag is set, so no further
211 * mpsl code can be executed until reset.
213 mpdm_t mpsl_error(mpdm_t err)
215 /* abort further execution */
216 mpsl_abort = 1;
218 /* set the error */
219 return mpdm_hset_s(mpdm_root(), L"ERROR", err);
223 /** opcode macro helpers **/
225 #define O_TYPE static mpdm_t
226 #define O_ARGS mpdm_t c, mpdm_t a, mpdm_t l, int * f
228 O_TYPE mpsl_exec_i(O_ARGS);
230 #define C(n) mpdm_aget(c, n)
231 #define C0 C(0)
232 #define C1 C(1)
234 #define M(n) mpsl_exec_i(C(n), a, l, f)
235 #define M1 M(1)
236 #define M2 M(2)
237 #define M3 M(3)
239 #define R(x) mpdm_rval(x)
240 #define I(x) mpdm_ival(x)
242 #define RM1 mpdm_rval(M(1))
243 #define RM2 mpdm_rval(M(2))
244 #define IM1 mpdm_ival(M(1))
245 #define IM2 mpdm_ival(M(2))
247 #define GET(m) get_symbol(m, l)
248 #define SET(m, v) set_symbol(m, v, l)
249 #define BOOL mpsl_boolean
251 #define RF(v) mpdm_ref(v)
252 #define UF(v) v = mpdm_unref(v)
253 #define UFND(v) mpdm_unrefnd(v)
255 static int is_true_uf(mpdm_t v)
257 int r;
259 RF(v);
260 r = mpsl_is_true(v);
261 UF(v);
263 return r;
267 /** opcodes **/
269 O_TYPE O_literal(O_ARGS) {
270 return mpdm_clone(C1);
273 O_TYPE O_multi(O_ARGS) {
274 mpdm_t v = M1;
276 if (!*f) {
277 mpdm_t t = v;
279 RF(t);
280 v = M2;
281 UF(t);
284 return v;
287 O_TYPE O_imulti(O_ARGS) {
288 mpdm_t v = RF(M1);
290 if (!*f) {
291 mpdm_t w = RF(M2);
292 UF(w);
295 return UFND(v);
298 O_TYPE O_symval(O_ARGS) {
299 mpdm_t v = RF(M1);
300 mpdm_t r = GET(v);
301 UF(v);
303 return r;
306 O_TYPE O_assign(O_ARGS) {
307 mpdm_t v = RF(M1);
308 mpdm_t r = SET(v, M2);
309 UF(v);
311 return r;
314 O_TYPE O_if(O_ARGS) {
315 mpdm_t r;
317 if (is_true_uf(M1))
318 r = M2;
319 else
320 r = M3;
322 return r;
325 O_TYPE O_local(O_ARGS) {
326 mpdm_t v = RF(M1);
327 set_local_symbols(v, NULL, l);
328 UF(v);
330 return NULL;
333 O_TYPE O_uminus(O_ARGS) {
334 mpdm_t v = RF(M1);
335 mpdm_t r = MPDM_R(-mpdm_rval(v));
336 UF(v);
338 return r;
341 O_TYPE O_add(O_ARGS) { return MPDM_R(RM1 + RM2); }
342 O_TYPE O_sub(O_ARGS) { return MPDM_R(RM1 - RM2); }
343 O_TYPE O_mul(O_ARGS) { return MPDM_R(RM1 * RM2); }
344 O_TYPE O_div(O_ARGS) { return MPDM_R(RM1 / RM2); }
345 O_TYPE O_mod(O_ARGS) { return MPDM_I(IM1 % IM2); }
347 O_TYPE O_not(O_ARGS) {
348 return BOOL(!is_true_uf(M1));
351 O_TYPE O_and(O_ARGS) {
352 mpdm_t v = M1;
353 mpdm_t r;
355 if (mpsl_is_true(v)) {
356 RF(v);
357 r = M2;
358 UF(v);
360 else
361 r = v;
363 return r;
366 O_TYPE O_or(O_ARGS) {
367 mpdm_t v = M1;
368 mpdm_t r;
370 if (!mpsl_is_true(v)) {
371 RF(v);
372 r = M2;
373 UF(v);
375 else
376 r = v;
378 return r;
381 O_TYPE O_bitand(O_ARGS) { return MPDM_I(IM1 & IM2); }
382 O_TYPE O_bitor(O_ARGS) { return MPDM_I(IM1 | IM2); }
383 O_TYPE O_bitxor(O_ARGS) { return MPDM_I(IM1 ^ IM2); }
384 O_TYPE O_shl(O_ARGS) { return MPDM_I(IM1 << IM2); }
385 O_TYPE O_shr(O_ARGS) { return MPDM_I(IM1 >> IM2); }
386 O_TYPE O_pow(O_ARGS) { return MPDM_R(pow(RM1, RM2)); }
387 O_TYPE O_numlt(O_ARGS) { return BOOL(RM1 < RM2); }
388 O_TYPE O_numle(O_ARGS) { return BOOL(RM1 <= RM2); }
389 O_TYPE O_numgt(O_ARGS) { return BOOL(RM1 > RM2); }
390 O_TYPE O_numge(O_ARGS) { return BOOL(RM1 >= RM2); }
392 O_TYPE O_strcat(O_ARGS) {
393 mpdm_t v1 = RF(M1);
394 mpdm_t v2 = RF(M2);
396 mpdm_t r = mpdm_strcat(v1, v2);
398 UF(v2);
399 UF(v1);
401 return r;
404 O_TYPE O_streq(O_ARGS) {
405 mpdm_t v1 = RF(M1);
406 mpdm_t v2 = RF(M2);
408 mpdm_t r = BOOL(mpdm_cmp(v1, v2) == 0);
410 UF(v2);
411 UF(v1);
413 return r;
416 O_TYPE O_numeq(O_ARGS) { mpdm_t v1 = RF(M1); mpdm_t v2 = M2; UF(v1); return BOOL((v1 == NULL || v2 == NULL) ? (v1 == v2) : (R(v1) == R(v2))); }
418 O_TYPE O_break(O_ARGS) {
419 *f = 1;
420 return NULL;
423 O_TYPE O_return(O_ARGS) {
424 mpdm_t v = M1;
425 *f = -1;
426 return v;
429 O_TYPE O_execsym(O_ARGS)
430 /* executes the value of a symbol */
432 mpdm_t s, v, w, r = NULL;
434 /* gets the symbol name */
435 s = RF(M1);
437 /* gets the symbol value */
438 v = GET(s);
440 if (!MPDM_IS_EXEC(v)) {
441 /* not found or NULL value? error */
442 mpdm_t t, w;
443 char tmp[128];
445 w = RF(mpdm_join_s(L".", s));
446 t = RF(MPDM_2MBS((wchar_t *) w->data));
448 snprintf(tmp, sizeof(tmp), "Undefined function %s()",
449 (char *)t->data);
451 mpsl_error(MPDM_MBS(tmp));
453 UF(w);
454 UF(t);
456 else {
457 /* save current local symbol table */
458 mpdm_t t = local_symtbl;
460 /* substitute with this one */
461 local_symtbl = l;
463 /* execute */
464 w = RF(M2);
465 r = mpdm_exec(v, w);
466 UF(w);
468 /* and get back to the original one */
469 local_symtbl = t;
472 UF(s);
474 return r;
478 O_TYPE O_while(O_ARGS)
479 /* while loop */
481 mpdm_t r = NULL;
483 while (!*f && is_true_uf(M1)) {
484 UF(r);
485 r = RF(M2);
488 if (*f == 1)
489 *f = 0;
491 return UFND(r);
495 O_TYPE O_foreach(O_ARGS)
496 /* foreach loop */
498 mpdm_t s = RF(M1);
499 mpdm_t v = RF(M2);
500 mpdm_t r = NULL;
501 int n;
503 for (n = 0; n < mpdm_size(v) && ! *f; n++) {
504 SET(s, mpdm_aget(v, n));
505 UF(r);
506 r = RF(M3);
509 if (*f == 1)
510 *f = 0;
512 UF(s); UF(v);
514 return UFND(r);
518 O_TYPE O_range(O_ARGS)
519 /* build list from range of two numeric values */
521 double n;
522 double v1 = RM1;
523 double v2 = RM2;
524 mpdm_t ret = MPDM_A(0);
526 if (v1 < v2)
527 for (n = v1; n <= v2; n++)
528 mpdm_push(ret, MPDM_R(n));
529 else
530 for (n = v1; n >= v2; n--)
531 mpdm_push(ret, MPDM_R(n));
533 return ret;
537 O_TYPE O_list(O_ARGS)
538 /* build list from instructions */
540 mpdm_t ret = RF(mpdm_size(c) == 2 ? MPDM_A(0) : M(2));
542 mpdm_push(ret, M(1));
543 return UFND(ret);
547 O_TYPE O_ilist(O_ARGS)
548 /* build and inverse list from instructions */
550 mpdm_t ret = RF(mpdm_size(c) == 2 ? MPDM_A(0) : M(2));
552 mpdm_ins(ret, M(1), 0);
553 return UFND(ret);
557 O_TYPE O_hash(O_ARGS)
558 /* build hash from instructions */
560 mpdm_t k, v;
561 mpdm_t ret = RF(mpdm_size(c) == 3 ? MPDM_H(0) : M(3));
563 k = RF(M1);
564 v = RF(M2);
565 mpdm_hset(ret, k, v);
566 UF(k);
567 UF(v);
569 return UFND(ret);
573 O_TYPE generic_frame(O_ARGS)
574 /* runs an instruction under a frame */
576 mpdm_t ret;
578 /* if l is NULL (usually for subroutine frames),
579 create a new array for holding local symbol tables */
580 if (l == NULL)
581 l = MPDM_A(0);
583 RF(l);
585 /* create a new local symbol table */
586 mpdm_push(l, MPDM_H(0));
588 /* creates the arguments (if any) as local variables */
589 set_local_symbols(M2, a, l);
591 /* execute instruction */
592 ret = M1;
594 /* destroy the local symbol table */
595 mpdm_adel(l, -1);
597 UF(l);
599 return ret;
603 O_TYPE O_blkframe(O_ARGS)
604 /* runs an instruction under a block frame */
606 return generic_frame(c, a, l, f);
610 O_TYPE O_subframe(O_ARGS)
611 /* runs an instruction inside a subroutine frame */
613 /* don't propagate the local symbol table,
614 triggering a new subroutine frame */
615 return generic_frame(c, a, NULL, f);
619 static struct mpsl_op_s {
620 wchar_t * name;
621 int foldable;
622 mpdm_t (* func)(O_ARGS);
623 } op_table[] = {
624 { L"LITERAL", 0, O_literal }, /* *must* be the zeroth */
625 { L"MULTI", 0, O_multi },
626 { L"IMULTI", 0, O_imulti },
627 { L"SYMVAL", 0, O_symval },
628 { L"ASSIGN", 0, O_assign },
629 { L"EXECSYM", 0, O_execsym },
630 { L"IF", 0, O_if },
631 { L"WHILE", 0, O_while },
632 { L"FOREACH", 0, O_foreach },
633 { L"SUBFRAME", 0, O_subframe },
634 { L"BLKFRAME", 0, O_blkframe },
635 { L"BREAK", 0, O_break },
636 { L"RETURN", 0, O_return },
637 { L"LOCAL", 0, O_local },
638 { L"LIST", 1, O_list },
639 { L"ILIST", 1, O_ilist },
640 { L"HASH", 1, O_hash },
641 { L"RANGE", 1, O_range },
642 { L"UMINUS", 1, O_uminus },
643 { L"ADD", 1, O_add },
644 { L"SUB", 1, O_sub },
645 { L"MUL", 1, O_mul },
646 { L"DIV", 1, O_div },
647 { L"MOD", 1, O_mod },
648 { L"NOT", 1, O_not },
649 { L"AND", 1, O_and },
650 { L"OR", 1, O_or },
651 { L"NUMEQ", 1, O_numeq },
652 { L"NUMLT", 1, O_numlt },
653 { L"NUMLE", 1, O_numle },
654 { L"NUMGT", 1, O_numgt },
655 { L"NUMGE", 1, O_numge },
656 { L"STRCAT", 1, O_strcat },
657 { L"STREQ", 1, O_streq },
658 { L"BITAND", 1, O_bitand },
659 { L"BITOR", 1, O_bitor },
660 { L"BITXOR", 1, O_bitxor },
661 { L"SHL", 1, O_shl },
662 { L"SHR", 1, O_shr },
663 { L"POW", 1, O_pow },
664 { NULL, 0, NULL }
668 O_TYPE mpsl_exec_i(O_ARGS)
669 /* Executes one MPSL instruction in the MPSL virtual machine. Called
670 from mpsl_exec_p() (which holds the flow control status variable) */
672 struct mpsl_op_s * o;
673 mpdm_t ret = NULL;
675 /* if aborted or NULL, do nothing */
676 if (mpsl_abort || c == NULL)
677 return NULL;
679 /* sweep some values */
680 if (!in_constant_folding)
681 mpdm_sweep(0);
683 /* gets the opcode */
684 o = &op_table[mpdm_ival(C0)];
686 /* blindly call it, or crash */
687 ret = o->func(c, a, l, f);
689 if (mpsl_trap_func != NULL && !in_constant_folding) {
690 mpdm_t f = mpsl_trap_func;
692 mpsl_trap_func = NULL;
693 mpdm_exec_3(f, c, a, ret);
694 mpsl_trap_func = f;
697 return ret;
701 mpdm_t mpsl_exec_p(mpdm_t c, mpdm_t a)
702 /* executes an MPSL instruction stream */
704 int f = 0;
706 /* execute first instruction with a new flow control variable */
707 return mpsl_exec_i(c, a, local_symtbl, &f);
711 static mpdm_t constant_fold(mpdm_t i)
712 /* tries to fold complex but constant expressions into a literal */
714 int n;
715 mpdm_t v, w;
717 /* get the number opcode */
718 n = mpdm_ival(mpdm_aget(i, 0));
720 if (op_table[n].foldable) {
721 /* test if all arguments are literal (opcode 0) */
722 for (n = 1; n < mpdm_size(i); n++) {
723 mpdm_t t = mpdm_aget(i, n);
725 /* if it's not LITERAL, abort immediately */
726 if (mpdm_ival(mpdm_aget(t, 0)) != 0)
727 return i;
730 in_constant_folding = 1;
732 /* execute the instruction and convert to LITERAL */
733 v = RF(i);
734 w = RF(mpsl_exec_p(v, NULL));
735 i = mpsl_mkins(L"LITERAL", 1, w, NULL, NULL);
736 UF(w);
737 UF(v);
739 in_constant_folding = 0;
742 return i;
746 mpdm_t mpsl_mkins(wchar_t * opcode, int args, mpdm_t a1, mpdm_t a2, mpdm_t a3)
747 /* creates an instruction */
749 mpdm_t o;
750 mpdm_t v;
752 v = MPDM_A(args + 1);
754 /* inserts the opcode */
755 o = mpdm_hget_s(mpsl_opcodes, opcode);
756 mpdm_aset(v, o, 0);
758 switch (args) {
759 case 3: mpdm_aset(v, a3, 3);
760 case 2: mpdm_aset(v, a2, 2);
761 case 1: mpdm_aset(v, a1, 1);
764 v = constant_fold(v);
766 return v;
770 mpdm_t mpsl_build_opcodes(void)
771 /* builds the table of opcodes */
773 int n;
774 mpdm_t r = MPDM_H(0);
776 for (n = 0; op_table[n].name != NULL; n++) {
777 mpdm_t v = MPDM_LS(op_table[n].name);
779 mpdm_set_ival(v, n);
781 /* keys and values are the same */
782 mpdm_hset(r, v, v);
785 return r;
790 * mpsl_trap - Install a trapping function.
791 * @trap_func: The trapping MPSL code
793 * Installs a trapping function. The function is an MPSL
794 * executable value receiving 3 arguments: the code stream,
795 * the arguments and the return value of the executed code.
797 * Returns NULL (previous versions returned the previous
798 * trapping function).
800 mpdm_t mpsl_trap(mpdm_t trap_func)
802 mpdm_ref(trap_func);
803 mpdm_unref(mpsl_trap_func);
804 mpsl_trap_func = trap_func;
806 return NULL;
811 * mpsl_argv - Fills the ARGV global array.
812 * @argc: number of arguments
813 * @argv: array of string values
815 * Fills the ARGV global MPSL array with an array of arguments. These
816 * are usually the ones sent to main().
818 void mpsl_argv(int argc, char * argv[])
820 int n;
821 mpdm_t ARGV;
823 /* create the ARGV array */
824 ARGV = MPDM_A(0);
826 for (n = 0; n < argc; n++)
827 mpdm_push(ARGV, MPDM_MBS(argv[n]));
829 mpdm_hset_s(mpdm_root(), L"ARGV", ARGV);
833 /* in mpsl_f.c */
834 mpdm_t mpsl_build_funcs(void);
838 * mpsl_startup - Initializes MPSL.
840 * Initializes the Minimum Profit Scripting Language. Returns 0 if
841 * everything went OK.
843 int mpsl_startup(void)
845 mpdm_t r;
846 mpdm_t m;
848 /* startup MPDM */
849 mpdm_startup();
851 r = mpdm_root();
853 /* creates INC, unless already defined */
854 if (mpdm_hget_s(r, L"INC") == NULL)
855 mpdm_hset_s(r, L"INC", MPDM_A(0));
857 /* the TRUE value */
858 mpdm_hset_s(r, L"TRUE", MPDM_I(1));
860 /* standard file descriptors */
861 mpdm_hset_s(r, L"STDIN", MPDM_F(stdin));
862 mpdm_hset_s(r, L"STDOUT", MPDM_F(stdout));
863 mpdm_hset_s(r, L"STDERR", MPDM_F(stderr));
865 /* home and application directories */
866 mpdm_hset_s(r, L"HOMEDIR", mpdm_home_dir());
867 mpdm_hset_s(r, L"APPDIR", mpdm_app_dir());
869 /* fill now the MPSL hash */
870 m = MPDM_H(0);
871 mpdm_hset_s(r, L"MPSL", m);
873 /* store things there */
874 mpdm_hset_s(m, L"VERSION", MPDM_MBS(VERSION));
875 mpdm_hset_s(m, L"OPCODE", mpsl_build_opcodes());
876 mpdm_hset_s(m, L"LC", MPDM_H(0));
877 mpdm_hset_s(m, L"CORE", mpsl_build_funcs());
879 mpdm_dump_1 = mpsl_dump_1;
881 return 0;
886 * mpsl_shutdown - Shuts down MPSL.
888 * Shuts down MPSL. No MPSL functions should be used from now on.
890 void mpsl_shutdown(void)
892 mpdm_shutdown();