write() accepts a variable number of arguments (Closes: #1111).
[mpsl.git] / mpsl_c.c
blobbfc14f19c49de328ef7ed1b7be01d6ba3977d561
1 /*
3 MPSL - Minimum Profit Scripting Language
4 Copyright (C) 2003/2009 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_symtbl = 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) {
72 wchar_t * ptr = (wchar_t *)v->data;
74 /* ... and it's "" or the "0" string, it's false */
75 if(*ptr == L'\0' || (*ptr == L'0' && *(ptr + 1) == L'\0'))
76 return 0;
79 /* any other case is true */
80 return 1;
84 /**
85 * mpsl_boolean - Returns 'true' or 'false' MPSL stock values.
86 * @b: boolean selector
88 * Returns MPSL's 'false' or 'true' values depending on the value in @b.
90 mpdm_t mpsl_boolean(int b)
92 return b ? mpdm_hget_s(mpdm_root(), L"TRUE") : NULL;
96 static mpdm_t find_local_symtbl(mpdm_t s, mpdm_t l)
97 /* finds the local symbol table hash that holds l */
99 int n;
100 mpdm_t v = NULL;
102 /* no local symbol table? nothing to find */
103 if(l == NULL)
104 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--) {
112 mpdm_t h = mpdm_aget(l, n);
114 if (mpdm_exists(h, s)) {
115 v = h;
116 break;
120 return v;
124 static void set_local_symbols(mpdm_t s, mpdm_t v, mpdm_t l)
125 /* sets (or creates) a list of local symbols with a list of values */
127 mpdm_t h;
129 if (s == NULL || l == NULL)
130 return;
132 /* gets the top local variable frame */
133 h = mpdm_aget(l, -1);
135 if (MPDM_IS_ARRAY(s)) {
136 int n;
138 for (n = 0; n < mpdm_size(s); n++)
139 mpdm_hset(h, mpdm_aget(s, n), mpdm_aget(v, n));
141 else
142 mpdm_hset(h, s, v);
146 static mpdm_t get_symbol(mpdm_t s, mpdm_t l)
147 /* gets a symbol from a local symbol table, or the global */
149 return mpdm_sget(find_local_symtbl(s, l), s);
153 static mpdm_t set_symbol(mpdm_t s, mpdm_t v, mpdm_t l)
154 /* sets a symbol in a local symbol table, or the global */
156 mpdm_sset(find_local_symtbl(s, l), s, v);
157 return v;
162 * mpsl_set_symbol - Sets value to a symbol.
163 * @s: symbol name
164 * @v: value
166 * Assigns the value @v to the @s symbol. If the value exists as
167 * a local symbol, it's assigned to it; otherwise, it's set as a global
168 * symbol (and created if it does not exist).
170 * This function is only meant to be executed from inside an MPSL
171 * program; from outside, it's exactly the same as calling mpdm_sset()
172 * (as the local symbol table won't exist).
174 mpdm_t mpsl_set_symbol(mpdm_t s, mpdm_t v)
176 return set_symbol(s, v, local_symtbl);
181 * mpsl_get_symbol - Gets the value of a symbol.
182 * @s: symbol name
184 * Gets the value of a symbol. The symbol can be local or global
185 * (if the symbol exists in both tables, the local value will be returned).
187 * This function is only meant to be executed from inside an MPSL
188 * program; from outside, it's exactly the same as calling mpdm_sget()
189 * (as the local symbol table won't exist).
191 mpdm_t mpsl_get_symbol(mpdm_t s)
193 return get_symbol(s, local_symtbl);
198 * mpsl_error - Generates an error.
199 * @err: the error message
201 * Generates an error. The @err error message is stored in the ERROR
202 * mpsl variable and the mpsl_abort global flag is set, so no further
203 * mpsl code can be executed until reset.
205 mpdm_t mpsl_error(mpdm_t err)
207 /* abort further execution */
208 mpsl_abort = 1;
210 /* set the error */
211 return mpdm_hset_s(mpdm_root(), L"ERROR", err);
214 /** opcodes **/
216 #define O_TYPE static mpdm_t
217 #define O_ARGS mpdm_t c, mpdm_t a, mpdm_t l, int * f
219 O_TYPE mpsl_exec_i(O_ARGS);
221 #define C(n) mpdm_aget(c, n)
222 #define C0 C(0)
223 #define C1 C(1)
225 #define M(n) mpsl_exec_i(C(n), a, l, f)
226 #define M1 M(1)
227 #define M2 M(2)
228 #define M3 M(3)
230 #define R(x) mpdm_rval(x)
231 #define I(x) mpdm_ival(x)
233 #define RM1 mpdm_rval(M(1))
234 #define RM2 mpdm_rval(M(2))
235 #define IM1 mpdm_ival(M(1))
236 #define IM2 mpdm_ival(M(2))
238 #define GET(m) get_symbol(m, l)
239 #define SET(m, v) set_symbol(m, v, l)
240 #define BOOL mpsl_boolean
241 #define ISTRU mpsl_is_true
243 #define RF(v) mpdm_ref(v)
244 #define UF(v) mpdm_unref(v)
246 O_TYPE O_literal(O_ARGS) { return mpdm_clone(C1); }
247 O_TYPE O_multi(O_ARGS) { mpdm_t v = RF(M1); if (!*f) v = M2; else UF(v); return v; }
248 O_TYPE O_imulti(O_ARGS) { mpdm_t v = RF(M1); if (!*f) M2; return UF(v); }
249 O_TYPE O_symval(O_ARGS) { return GET(M1); }
250 O_TYPE O_assign(O_ARGS) { mpdm_t v = RF(M1); mpdm_t r = SET(v, M2); UF(v); return r; }
251 O_TYPE O_if(O_ARGS) { return ISTRU(M1) ? M2 : M3; }
252 O_TYPE O_local(O_ARGS) { set_local_symbols(M1, NULL, l); return NULL; }
253 O_TYPE O_uminus(O_ARGS) { return MPDM_R(-RM1); }
254 O_TYPE O_add(O_ARGS) { return MPDM_R(RM1 + RM2); }
255 O_TYPE O_sub(O_ARGS) { return MPDM_R(RM1 - RM2); }
256 O_TYPE O_mul(O_ARGS) { return MPDM_R(RM1 * RM2); }
257 O_TYPE O_div(O_ARGS) { return MPDM_R(RM1 / RM2); }
258 O_TYPE O_mod(O_ARGS) { return MPDM_I(IM1 % IM2); }
259 O_TYPE O_not(O_ARGS) { return BOOL(! ISTRU(M1)); }
260 O_TYPE O_and(O_ARGS) { mpdm_t r = M1; return ISTRU(r) ? M2 : r; }
261 O_TYPE O_or(O_ARGS) { mpdm_t r = M1; return ISTRU(r) ? r : M2; }
262 O_TYPE O_bitand(O_ARGS) { return MPDM_I(IM1 & IM2); }
263 O_TYPE O_bitor(O_ARGS) { return MPDM_I(IM1 | IM2); }
264 O_TYPE O_bitxor(O_ARGS) { return MPDM_I(IM1 ^ IM2); }
265 O_TYPE O_numlt(O_ARGS) { return BOOL(RM1 < RM2); }
266 O_TYPE O_numle(O_ARGS) { return BOOL(RM1 <= RM2); }
267 O_TYPE O_numgt(O_ARGS) { return BOOL(RM1 > RM2); }
268 O_TYPE O_numge(O_ARGS) { return BOOL(RM1 >= RM2); }
269 O_TYPE O_strcat(O_ARGS) { mpdm_t v = RF(M1); mpdm_t r = mpdm_strcat(v, M2); UF(v); return r; }
270 O_TYPE O_streq(O_ARGS) { mpdm_t v = RF(M1); mpdm_t r = BOOL(mpdm_cmp(v, M2) == 0); UF(v); return r; }
271 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))); }
272 O_TYPE O_break(O_ARGS) { *f = 1; return NULL; }
273 O_TYPE O_return(O_ARGS) { mpdm_t v = M1; *f = -1; return v; }
275 O_TYPE O_execsym(O_ARGS)
276 /* executes the value of a symbol */
278 mpdm_t s, v, r = NULL;
280 /* gets the symbol name */
281 s = RF(M1);
283 /* gets the symbol value */
284 v = GET(s);
286 if (!MPDM_IS_EXEC(v)) {
287 /* not found or NULL value? error */
288 mpdm_t t;
289 char tmp[128];
291 t = mpdm_join(MPDM_LS(L"."), s);
292 t = MPDM_2MBS((wchar_t *) t->data);
294 snprintf(tmp, sizeof(tmp), "Undefined function %s()",
295 (char *)t->data);
297 mpsl_error(MPDM_MBS(tmp));
299 else {
300 /* save current local symbol table */
301 mpdm_t t = local_symtbl;
303 /* substitute with this one */
304 local_symtbl = l;
306 /* execute */
307 r = mpdm_exec(v, M2);
309 /* and get back to the original one */
310 local_symtbl = t;
313 UF(s);
315 return r;
319 O_TYPE O_while(O_ARGS)
320 /* while loop */
322 mpdm_t r = NULL;
324 while (! *f && ISTRU(M1)) {
325 UF(r);
326 r = RF(M2);
329 if (*f == 1)
330 *f = 0;
332 return UF(r);
336 O_TYPE O_foreach(O_ARGS)
337 /* foreach loop */
339 mpdm_t s = RF(M1);
340 mpdm_t v = RF(M2);
341 mpdm_t r = NULL;
342 int n;
344 for (n = 0; n < mpdm_size(v) && ! *f; n++) {
345 SET(s, mpdm_aget(v, n));
346 UF(r);
347 r = RF(M3);
350 if (*f == 1)
351 *f = 0;
353 UF(s); UF(v);
355 return UF(r);
359 O_TYPE O_range(O_ARGS)
360 /* build list from range of two numeric values */
362 double n;
363 double v1 = RM1;
364 double v2 = RM2;
365 mpdm_t ret = MPDM_A(0);
367 if (v1 < v2)
368 for (n = v1; n <= v2; n++)
369 mpdm_push(ret, MPDM_R(n));
370 else
371 for (n = v1; n >= v2; n--)
372 mpdm_push(ret, MPDM_R(n));
374 return ret;
378 O_TYPE O_list(O_ARGS)
379 /* build list from instructions */
381 mpdm_t ret = RF(mpdm_size(c) == 2 ? MPDM_A(0) : M(2));
383 mpdm_push(ret, M(1));
384 return UF(ret);
388 O_TYPE O_hash(O_ARGS)
389 /* build hash from instructions */
391 mpdm_t k, v;
392 mpdm_t ret = RF(mpdm_size(c) == 3 ? MPDM_H(0) : M(3));
394 k = RF(M(1)); v = RF(M(2));
395 mpdm_hset(ret, UF(k), UF(v));
396 return UF(ret);
400 O_TYPE generic_frame(O_ARGS)
401 /* runs an instruction under a frame */
403 mpdm_t ret;
405 /* if l is NULL (usually for subroutine frames),
406 create a new array for holding local symbol tables */
407 if (l == NULL)
408 l = MPDM_A(0);
410 RF(l);
412 /* create a new local symbol table */
413 mpdm_push(l, MPDM_H(0));
415 /* creates the arguments (if any) as local variables */
416 set_local_symbols(M2, a, l);
418 /* execute instruction */
419 ret = M1;
421 UF(l);
423 /* destroy the local symbol table */
424 mpdm_pop(l);
426 return ret;
430 O_TYPE O_blkframe(O_ARGS)
431 /* runs an instruction under a block frame */
433 return generic_frame(c, a, l, f);
437 O_TYPE O_subframe(O_ARGS)
438 /* runs an instruction inside a subroutine frame */
440 /* don't propagate the local symbol table,
441 triggering a new subroutine frame */
442 return generic_frame(c, a, NULL, f);
446 static struct mpsl_op_s {
447 wchar_t * name;
448 int foldable;
449 mpdm_t (* func)(O_ARGS);
450 } op_table[] = {
451 { L"LITERAL", 0, O_literal }, /* *must* be the zeroth */
452 { L"MULTI", 0, O_multi },
453 { L"IMULTI", 0, O_imulti },
454 { L"SYMVAL", 0, O_symval },
455 { L"ASSIGN", 0, O_assign },
456 { L"EXECSYM", 0, O_execsym },
457 { L"IF", 0, O_if },
458 { L"WHILE", 0, O_while },
459 { L"FOREACH", 0, O_foreach },
460 { L"SUBFRAME", 0, O_subframe },
461 { L"BLKFRAME", 0, O_blkframe },
462 { L"BREAK", 0, O_break },
463 { L"RETURN", 0, O_return },
464 { L"LOCAL", 0, O_local },
465 { L"LIST", 1, O_list },
466 { L"HASH", 1, O_hash },
467 { L"RANGE", 1, O_range },
468 { L"UMINUS", 1, O_uminus },
469 { L"ADD", 1, O_add },
470 { L"SUB", 1, O_sub },
471 { L"MUL", 1, O_mul },
472 { L"DIV", 1, O_div },
473 { L"MOD", 1, O_mod },
474 { L"NOT", 1, O_not },
475 { L"AND", 1, O_and },
476 { L"OR", 1, O_or },
477 { L"NUMEQ", 1, O_numeq },
478 { L"NUMLT", 1, O_numlt },
479 { L"NUMLE", 1, O_numle },
480 { L"NUMGT", 1, O_numgt },
481 { L"NUMGE", 1, O_numge },
482 { L"STRCAT", 1, O_strcat },
483 { L"STREQ", 1, O_streq },
484 { L"BITAND", 1, O_bitand },
485 { L"BITOR", 1, O_bitor },
486 { L"BITXOR", 1, O_bitxor },
487 { NULL, 0, NULL }
491 O_TYPE mpsl_exec_i(O_ARGS)
492 /* Executes one MPSL instruction in the MPSL virtual machine. Called
493 from mpsl_exec_p() (which holds the flow control status variable) */
495 struct mpsl_op_s * o;
496 mpdm_t ret = NULL;
498 /* if aborted or NULL, do nothing */
499 if (mpsl_abort || c == NULL)
500 return NULL;
502 /* sweep some values */
503 if (sweep_on_exec_i)
504 mpdm_sweep(0);
506 /* gets the opcode */
507 o = &op_table[mpdm_ival(C0)];
509 /* blindly call it, or crash */
510 ret = o->func(c, a, l, f);
512 if(mpsl_trace)
513 printf("** %ls: %ls\n", mpdm_string(C0), mpdm_string(ret));
515 return ret;
519 mpdm_t mpsl_exec_p(mpdm_t c, mpdm_t a)
520 /* executes an MPSL instruction stream */
522 int f = 0;
524 /* execute first instruction with a new flow control variable */
525 return mpsl_exec_i(c, a, local_symtbl, &f);
529 static mpdm_t constant_fold(mpdm_t i)
530 /* tries to fold complex but constant expressions into a literal */
532 int n;
534 /* get the number opcode */
535 n = mpdm_ival(mpdm_aget(i, 0));
537 if (op_table[n].foldable) {
538 /* test if all arguments are literal (opcode 0) */
539 for (n = 1; n < mpdm_size(i); n++) {
540 mpdm_t t = mpdm_aget(i, n);
542 /* if it's not LITERAL, abort immediately */
543 if (mpdm_ival(mpdm_aget(t, 0)) != 0)
544 return i;
547 /* avoid sweeping */
548 sweep_on_exec_i = 0;
550 /* execute the instruction and convert to LITERAL */
551 i = mpsl_exec_p(i, NULL);
552 i = mpsl_mkins(L"LITERAL", 1, i, NULL, NULL);
554 /* sweep again */
555 sweep_on_exec_i = 1;
558 return i;
562 mpdm_t mpsl_mkins(wchar_t * opcode, int args, mpdm_t a1, mpdm_t a2, mpdm_t a3)
563 /* creates an instruction */
565 mpdm_t o;
566 mpdm_t v;
568 v = MPDM_A(args + 1);
570 /* inserts the opcode */
571 o = mpdm_hget_s(mpsl_opcodes, opcode);
572 mpdm_aset(v, o, 0);
574 switch (args) {
575 case 3: mpdm_aset(v, a3, 3);
576 case 2: mpdm_aset(v, a2, 2);
577 case 1: mpdm_aset(v, a1, 1);
580 v = constant_fold(v);
582 return v;
586 mpdm_t mpsl_build_opcodes(void)
587 /* builds the table of opcodes */
589 int n;
590 mpdm_t r = MPDM_H(0);
592 for (n = 0; op_table[n].name != NULL; n++) {
593 mpdm_t v = MPDM_LS(op_table[n].name);
595 mpdm_set_ival(v, n);
597 /* keys and values are the same */
598 mpdm_hset(r, v, v);
601 return r;