New opcode HASH2, that is optimizable and will substitute HASH.
[mpsl.git] / mpsl_c.c
blob937d86d233820a16198de06d267dd5137dc1c78d
1 /*
3 MPSL - Minimum Profit Scripting Language
4 Copyright (C) 2003/2006 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 "mpdm.h"
31 #include "mpsl.h"
33 /*******************
34 Data
35 ********************/
37 /* instruction execution tracing flag */
38 int mpsl_trace = 0;
40 /* global abort flag */
41 int mpsl_abort = 0;
43 /* temporary storage for the local symbol table */
44 static mpdm_t local_symbol_table = NULL;
46 /* temporary storage for the opcode table
47 (only usable while compiling) */
48 mpdm_t mpsl_opcodes = NULL;
50 /* flag to control calls to mpdm_sweep() from inside mpsl_exec_i() */
51 static int sweep_on_exec_i = 1;
53 /*******************
54 Code
55 ********************/
57 /**
58 * mpsl_is_true - Tests if a value is true.
59 * @v: the value
61 * If @v is a valid MPSL 'false' value (NULL, "" or the "0" string),
62 * returns zero, or nonzero otherwise.
64 int mpsl_is_true(mpdm_t v)
66 /* if value is NULL, it's false */
67 if(v == NULL)
68 return(0);
70 /* if it's a printable string... */
71 if(v->flags & MPDM_STRING)
73 wchar_t * ptr = (wchar_t *)v->data;
75 /* ... and it's "" or the "0" string, it's false */
76 if(*ptr == L'\0' || (*ptr == L'0' && *(ptr + 1) == L'\0'))
77 return(0);
80 /* any other case is true */
81 return(1);
85 /**
86 * mpsl_boolean - Returns 'true' or 'false' MPSL stock values.
87 * @b: boolean selector
89 * Returns MPSL's 'false' or 'true' values depending on the value in @b.
91 mpdm_t mpsl_boolean(int b)
93 return(b ? mpdm_hget_s(mpdm_root(), L"TRUE") : NULL);
97 static mpdm_t find_local_symbol(mpdm_t s, mpdm_t l)
98 /* finds a symbol in the local symbol table */
100 int n;
101 mpdm_t v = NULL;
103 /* no local symbol table? nothing to find */
104 if(l == NULL) return(NULL);
106 /* if s is multiple, take just the first element */
107 if(MPDM_IS_ARRAY(s))
108 s = mpdm_aget(s, 0);
110 /* travel the local symbol table trying to find it */
111 for(n = mpdm_size(l) - 1;n >= 0;n--)
113 mpdm_t h = mpdm_aget(l, n);
115 if(mpdm_exists(h, s))
117 v = h;
118 break;
122 return(v);
126 static void set_local_symbols(mpdm_t s, mpdm_t v, mpdm_t l)
127 /* sets (or creates) a list of local symbols with a list of values */
129 mpdm_t h;
131 if(s == NULL || l == NULL)
132 return;
134 /* gets the top local variable frame */
135 h = mpdm_aget(l, -1);
137 if(MPDM_IS_ARRAY(s))
139 int n;
141 for(n = 0;n < mpdm_size(s);n++)
142 mpdm_hset(h, mpdm_aget(s, n), mpdm_aget(v, n));
144 else
145 mpdm_hset(h, s, v);
149 static mpdm_t get_symbol(mpdm_t s, mpdm_t l)
150 /* gets a symbol from a local symbol table, or the global */
152 return(mpdm_sget(find_local_symbol(s, l), s));
156 static mpdm_t set_symbol(mpdm_t s, mpdm_t v, mpdm_t l)
157 /* sets a symbol in a local symbol table, or the global */
159 mpdm_sset(find_local_symbol(s, l), s, v);
160 return(v);
165 * mpsl_set_symbol - Sets value to a symbol.
166 * @s: symbol name
167 * @v: value
169 * Assigns the value @v to the @s symbol. If the value exists as
170 * a local symbol, it's assigned to it; otherwise, it's set as a global
171 * symbol (and created if it does not exist).
173 * This function is only meant to be executed from inside an MPSL
174 * program; from outside, it's exactly the same as calling mpdm_sset()
175 * (as the local symbol table won't exist).
177 mpdm_t mpsl_set_symbol(mpdm_t s, mpdm_t v)
179 return(set_symbol(s, v, local_symbol_table));
184 * mpsl_get_symbol - Gets the value of a symbol.
185 * @s: symbol name
187 * Gets the value of a symbol. The symbol can be local or global
188 * (if the symbol exists in both tables, the local value will be returned).
190 * This function is only meant to be executed from inside an MPSL
191 * program; from outside, it's exactly the same as calling mpdm_sget()
192 * (as the local symbol table won't exist).
194 mpdm_t mpsl_get_symbol(mpdm_t s)
196 return(get_symbol(s, local_symbol_table));
201 * mpsl_error - Generates an error.
202 * @err: the error message
204 * Generates an error. The @err error message is stored in the ERROR
205 * mpsl variable and the mpsl_abort global flag is set, so no further
206 * mpsl code can be executed until reset.
208 mpdm_t mpsl_error(mpdm_t err)
210 /* abort further execution */
211 mpsl_abort = 1;
213 /* set the error */
214 return(mpdm_hset_s(mpdm_root(), L"ERROR", err));
217 /** opcodes **/
219 #define O_TYPE static mpdm_t
220 #define O_ARGS mpdm_t c, mpdm_t a, mpdm_t l, int * f
222 O_TYPE mpsl_exec_i(O_ARGS);
224 #define C(n) mpdm_aget(c, n)
225 #define C0 C(0)
226 #define C1 C(1)
228 #define M(n) mpsl_exec_i(C(n), a, l, f)
229 #define M1 M(1)
230 #define M2 M(2)
231 #define M3 M(3)
233 #define R(x) mpdm_rval(x)
234 #define I(x) mpdm_ival(x)
236 #define RM1 mpdm_rval(M(1))
237 #define RM2 mpdm_rval(M(2))
238 #define IM1 mpdm_ival(M(1))
239 #define IM2 mpdm_ival(M(2))
241 #define GET(m) get_symbol(m, l)
242 #define SET(m, v) set_symbol(m, v, l)
243 #define BOOL mpsl_boolean
244 #define ISTRU mpsl_is_true
246 #define RF(v) mpdm_ref(v)
247 #define UF(v) mpdm_unref(v)
249 O_TYPE O_literal(O_ARGS) { return(mpdm_clone(C1)); }
250 O_TYPE O_multi(O_ARGS) { mpdm_t v = RF(M1); if(!*f) v = M2; else UF(v); return(v); }
251 O_TYPE O_symval(O_ARGS) { return(GET(M1)); }
252 O_TYPE O_assign(O_ARGS) { mpdm_t v = RF(M1); mpdm_t r = SET(v, M2); UF(v); return(r); }
253 O_TYPE O_if(O_ARGS) { return(ISTRU(M1) ? M2 : M3); }
254 O_TYPE O_local(O_ARGS) { set_local_symbols(M1, NULL, l); return(NULL); }
255 O_TYPE O_uminus(O_ARGS) { return(MPDM_R(-RM1)); }
256 O_TYPE O_add(O_ARGS) { return(MPDM_R(RM1 + RM2)); }
257 O_TYPE O_sub(O_ARGS) { return(MPDM_R(RM1 - RM2)); }
258 O_TYPE O_mul(O_ARGS) { return(MPDM_R(RM1 * RM2)); }
259 O_TYPE O_div(O_ARGS) { return(MPDM_R(RM1 / RM2)); }
260 O_TYPE O_mod(O_ARGS) { return(MPDM_I(IM1 % IM2)); }
261 O_TYPE O_not(O_ARGS) { return(BOOL(! ISTRU(M1))); }
262 O_TYPE O_and(O_ARGS) { mpdm_t r = M1; return(ISTRU(r) ? M2 : r); }
263 O_TYPE O_or(O_ARGS) { mpdm_t r = M1; return(ISTRU(r) ? r : M2); }
264 O_TYPE O_bitand(O_ARGS) { return(MPDM_I(IM1 & IM2)); }
265 O_TYPE O_bitor(O_ARGS) { return(MPDM_I(IM1 | IM2)); }
266 O_TYPE O_bitxor(O_ARGS) { return(MPDM_I(IM1 ^ IM2)); }
267 O_TYPE O_numlt(O_ARGS) { return(BOOL(RM1 < RM2)); }
268 O_TYPE O_numle(O_ARGS) { return(BOOL(RM1 <= RM2)); }
269 O_TYPE O_numgt(O_ARGS) { return(BOOL(RM1 > RM2)); }
270 O_TYPE O_numge(O_ARGS) { return(BOOL(RM1 >= RM2)); }
271 O_TYPE O_strcat(O_ARGS) { mpdm_t v = RF(M1); mpdm_t r = mpdm_strcat(v, M2); UF(v); return(r); }
272 O_TYPE O_streq(O_ARGS) { mpdm_t v = RF(M1); mpdm_t r = BOOL(mpdm_cmp(v, M2) == 0); UF(v); return(r); }
273 O_TYPE O_immpinc(O_ARGS) { mpdm_t s=M1; return(SET(s, MPDM_R(R(GET(s)) + 1))); }
274 O_TYPE O_immpdec(O_ARGS) { mpdm_t s=M1; return(SET(s, MPDM_R(R(GET(s)) - 1))); }
275 O_TYPE O_immadd(O_ARGS) { mpdm_t s = RF(M1); mpdm_t r = SET(s, MPDM_R(R(GET(s)) + RM2)); UF(s); return(r); }
276 O_TYPE O_immsub(O_ARGS) { mpdm_t s = RF(M1); mpdm_t r = SET(s, MPDM_R(R(GET(s)) - RM2)); UF(s); return(r); }
277 O_TYPE O_immmul(O_ARGS) { mpdm_t s = RF(M1); mpdm_t r = SET(s, MPDM_R(R(GET(s)) * RM2)); UF(s); return(r); }
278 O_TYPE O_immdiv(O_ARGS) { mpdm_t s = RF(M1); mpdm_t r = SET(s, MPDM_R(R(GET(s)) / RM2)); UF(s); return(r); }
279 O_TYPE O_immmod(O_ARGS) { mpdm_t s = RF(M1); mpdm_t r = SET(s, MPDM_R(I(GET(s)) % IM2)); UF(s); return(r); }
280 O_TYPE O_immsinc(O_ARGS) { mpdm_t s = M1; mpdm_t v = GET(s); SET(s, MPDM_R(R(v) + 1)); return(v); }
281 O_TYPE O_immsdec(O_ARGS) { mpdm_t s = M1; mpdm_t v = GET(s); SET(s, MPDM_R(R(v) - 1)); return(v); }
282 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)))); }
283 O_TYPE O_break(O_ARGS) { *f = 1; return(NULL); }
284 O_TYPE O_return(O_ARGS) { mpdm_t v = M1; *f = -1; return(v); }
286 O_TYPE O_exec(O_ARGS)
287 /* executes the value of a symbol */
289 mpdm_t s, v, r = NULL;
291 /* gets the symbol name */
292 s = RF(M1);
294 /* gets the symbol value */
295 v = GET(s);
297 if (!MPDM_IS_EXEC(v)) {
298 /* not found or NULL value? error */
299 mpdm_t t;
300 char tmp[128];
302 t = mpdm_join(MPDM_LS(L"."), s);
303 t = MPDM_2MBS((wchar_t *) t->data);
305 snprintf(tmp, sizeof(tmp), "Undefined function %s()",
306 (char *)t->data);
308 mpsl_error(MPDM_MBS(tmp));
310 else
312 local_symbol_table = l;
314 /* executes */
315 r = mpdm_exec(v, M2);
317 local_symbol_table = NULL;
320 UF(s);
322 return(r);
326 O_TYPE O_while(O_ARGS)
327 /* while loop */
329 mpdm_t r = NULL;
331 while(! *f && ISTRU(M1))
333 UF(r);
334 r = RF(M2);
337 if(*f == 1) *f = 0;
339 return(UF(r));
343 O_TYPE O_foreach(O_ARGS)
344 /* foreach loop */
346 mpdm_t s = RF(M1);
347 mpdm_t v = RF(M2);
348 mpdm_t r = NULL;
349 int n;
351 for(n = 0;n < mpdm_size(v) && ! *f;n++)
353 SET(s, mpdm_aget(v, n));
354 UF(r);
355 r = RF(M3);
358 if(*f == 1) *f = 0;
360 UF(s); UF(v);
362 return(UF(r));
366 O_TYPE O_range(O_ARGS)
367 /* build list from range of two numeric values */
369 double n;
370 double v1 = RM1;
371 double v2 = RM2;
372 mpdm_t ret = MPDM_A(0);
374 if(v1 < v2)
375 for(n = v1;n <= v2;n++)
376 mpdm_push(ret, MPDM_R(n));
377 else
378 for(n = v1;n >= v2;n--)
379 mpdm_push(ret, MPDM_R(n));
381 return(ret);
385 O_TYPE O_list(O_ARGS)
386 /* build list from instructions */
388 int n;
389 mpdm_t ret = RF(MPDM_A(mpdm_size(c) - 1));
391 for(n = 1;n < mpdm_size(c);n++)
392 mpdm_aset(ret, M(n), n - 1);
394 return(UF(ret));
398 O_TYPE O_list2(O_ARGS)
400 mpdm_t ret = RF(mpdm_size(c) == 2 ? MPDM_A(0) : M(2));
402 mpdm_push(ret, M(1));
403 return(UF(ret));
407 O_TYPE O_hash(O_ARGS)
408 /* build hash from instructions */
410 int n;
411 mpdm_t ret = RF(MPDM_H(0));
413 for(n = 1;n < mpdm_size(c);n += 2)
415 mpdm_t v = RF(M(n));
417 mpdm_hset(ret, v, M(n + 1));
419 UF(v);
422 return(UF(ret));
426 O_TYPE O_hash2(O_ARGS)
428 mpdm_t k, v;
429 mpdm_t ret = RF(mpdm_size(c) == 3 ? MPDM_H(0) : M(3));
431 k = RF(M(1)); v = RF(M(2));
432 mpdm_hset(ret, UF(k), UF(v));
433 return(UF(ret));
436 O_TYPE O_subframe(O_ARGS)
437 /* runs an instruction inside a subroutine frame */
439 mpdm_t ret;
441 /* create a new local symbol table */
442 l = mpdm_ref(MPDM_A(1));
443 mpdm_aset(l, MPDM_H(0), 0);
445 /* creates the arguments (if any) as local variables */
446 set_local_symbols(M2, a, l);
448 /* execute instruction */
449 ret = M1;
451 /* this local symbol table is no longer needed */
452 mpdm_unref(l);
454 return(ret);
458 O_TYPE O_blkframe(O_ARGS)
459 /* runs an instruction under a block frame */
461 mpdm_t ret;
463 mpdm_push(l, MPDM_H(0));
464 ret = M1;
465 mpdm_pop(l);
467 return(ret);
471 static struct mpsl_op_s
473 wchar_t * name;
474 int foldable;
475 mpdm_t (* func)(O_ARGS);
476 } op_table[]=
478 { L"LITERAL", 0, O_literal }, /* 0 */
479 { L"MULTI", 0, O_multi },
480 { L"SYMVAL", 0, O_symval },
481 { L"ASSIGN", 0, O_assign },
482 { L"EXEC", 0, O_exec },
483 { L"IF", 0, O_if },
484 { L"WHILE", 0, O_while },
485 { L"FOREACH", 0, O_foreach },
486 { L"SUBFRAME", 0, O_subframe },
487 { L"BLKFRAME", 0, O_blkframe },
488 { L"BREAK", 0, O_break },
489 { L"RETURN", 0, O_return },
490 { L"LOCAL", 0, O_local },
491 { L"LIST", 0, O_list }, /* should be */
492 { L"LIST2", 1, O_list2 }, /* should be */
493 { L"HASH", 0, O_hash }, /* should be */
494 { L"HASH2", 1, O_hash2 }, /* should be */
495 { L"RANGE", 1, O_range },
496 { L"UMINUS", 1, O_uminus },
497 { L"ADD", 1, O_add },
498 { L"SUB", 1, O_sub },
499 { L"MUL", 1, O_mul },
500 { L"DIV", 1, O_div },
501 { L"MOD", 1, O_mod },
502 { L"SINC", 0, O_immsinc },
503 { L"SDEC", 0, O_immsdec },
504 { L"PINC", 0, O_immpinc },
505 { L"PDEC", 0, O_immpdec },
506 { L"IADD", 0, O_immadd },
507 { L"ISUB", 0, O_immsub },
508 { L"IMUL", 0, O_immmul },
509 { L"IDIV", 0, O_immdiv },
510 { L"IMOD", 0, O_immmod },
511 { L"NOT", 1, O_not },
512 { L"AND", 1, O_and },
513 { L"OR", 1, O_or },
514 { L"NUMEQ", 1, O_numeq },
515 { L"NUMLT", 1, O_numlt },
516 { L"NUMLE", 1, O_numle },
517 { L"NUMGT", 1, O_numgt },
518 { L"NUMGE", 1, O_numge },
519 { L"STRCAT", 1, O_strcat },
520 { L"STREQ", 1, O_streq },
521 { L"BITAND", 1, O_bitand },
522 { L"BITOR", 1, O_bitor },
523 { L"BITXOR", 1, O_bitxor },
524 { NULL, 0, NULL }
528 O_TYPE mpsl_exec_i(O_ARGS)
529 /* Executes one MPSL instruction in the MPSL virtual machine. Called
530 from mpsl_exec() (which holds the flow control status variable) */
532 mpdm_t ret = NULL;
534 /* if aborted, do nothing */
535 if(mpsl_abort) return(NULL);
537 /* reference code, arguments and local symbol table */
538 mpdm_ref(c);
539 mpdm_ref(a);
540 mpdm_ref(l);
542 if(c != NULL)
544 int op;
546 /* gets the opcode */
547 op = mpdm_ival(C0);
549 /* if it's a valid opcode... */
550 if(op >= 0 && op < sizeof(op_table) / sizeof(struct mpsl_op_s))
552 struct mpsl_op_s * o = &op_table[op];
554 /* and call it if existent */
555 if(o->func != NULL)
556 ret = mpdm_ref(o->func(c, a, l, f));
560 if(mpsl_trace)
561 printf("** %ls: %ls\n", mpdm_string(C0), mpdm_string(ret));
563 /* unreference */
564 mpdm_unref(l);
565 mpdm_unref(a);
566 mpdm_unref(c);
568 /* sweep some values */
569 if(sweep_on_exec_i) mpdm_sweep(0);
571 return(mpdm_unref(ret));
575 mpdm_t mpsl_exec_p(mpdm_t c, mpdm_t a)
576 /* executes an MPSL instruction stream */
578 static int exec_level = 0;
579 int f = 0;
580 mpdm_t v;
582 exec_level++;
584 /* execute first instruction */
585 v = mpsl_exec_i(c, a, NULL, &f);
587 exec_level--;
589 return(v);
593 static mpdm_t constant_fold(mpdm_t i)
594 /* tries to fold complex but constant expressions into a literal */
596 int n;
598 /* get the number opcode */
599 n = mpdm_ival(mpdm_aget(i, 0));
601 if(op_table[n].foldable)
603 /* test if all arguments are literal (opcode 0) */
604 for(n = 1;n < mpdm_size(i);n++)
606 mpdm_t t = mpdm_aget(i, n);
608 /* if it's not LITERAL, abort immediately */
609 if(mpdm_ival(mpdm_aget(t, 0)) != 0)
610 return(i);
613 /* avoid sweeping */
614 sweep_on_exec_i = 0;
616 /* execute the instruction and convert to LITERAL */
617 i = mpsl_exec_p(i, NULL);
618 i = mpsl_mkins(L"LITERAL", 1, i, NULL, NULL);
620 /* sweep again */
621 sweep_on_exec_i = 1;
624 return(i);
628 mpdm_t mpsl_mkins(wchar_t * opcode, int args, mpdm_t a1, mpdm_t a2, mpdm_t a3)
629 /* creates an instruction */
631 mpdm_t o;
632 mpdm_t v;
634 v = MPDM_A(args + 1);
636 /* inserts the opcode */
637 o = mpdm_hget_s(mpsl_opcodes, opcode);
638 mpdm_aset(v, o, 0);
640 if(args > 0) mpdm_aset(v, a1, 1);
641 if(args > 1) mpdm_aset(v, a2, 2);
642 if(args > 2) mpdm_aset(v, a3, 3);
644 v = constant_fold(v);
646 return(v);
650 mpdm_t mpsl_build_opcodes(void)
651 /* builds the table of opcodes */
653 int n;
654 mpdm_t r = MPDM_H(0);
656 for(n = 0;op_table[n].name != NULL;n++)
658 mpdm_t v = MPDM_LS(op_table[n].name);
659 v->ival = n;
660 v->flags |= MPDM_IVAL;
662 /* keys and values are the same */
663 mpdm_hset(r, v, v);
666 return(r);