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
37 /* instruction execution tracing flag */
40 /* global abort flag */
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;
58 * mpsl_is_true - Tests if a value is true.
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 */
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'))
80 /* any other case is true */
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 */
103 /* no local symbol table? nothing to find */
104 if(l
== NULL
) return(NULL
);
106 /* if s is multiple, take just the first element */
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
))
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 */
131 if(s
== NULL
|| l
== NULL
)
134 /* gets the top local variable frame */
135 h
= mpdm_aget(l
, -1);
141 for(n
= 0;n
< mpdm_size(s
);n
++)
142 mpdm_hset(h
, mpdm_aget(s
, n
), mpdm_aget(v
, n
));
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
);
165 * mpsl_set_symbol - Sets value to a symbol.
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.
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 */
214 return(mpdm_hset_s(mpdm_root(), L
"ERROR", err
));
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)
228 #define M(n) mpsl_exec_i(C(n), a, l, f)
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 */
294 /* gets the symbol value */
297 if (!MPDM_IS_EXEC(v
)) {
298 /* not found or NULL value? error */
302 t
= mpdm_join(MPDM_LS(L
"."), s
);
303 t
= MPDM_2MBS((wchar_t *) t
->data
);
305 snprintf(tmp
, sizeof(tmp
), "Undefined function %s()",
308 mpsl_error(MPDM_MBS(tmp
));
312 local_symbol_table
= l
;
315 r
= mpdm_exec(v
, M2
);
317 local_symbol_table
= NULL
;
326 O_TYPE
O_while(O_ARGS
)
331 while(! *f
&& ISTRU(M1
))
343 O_TYPE
O_foreach(O_ARGS
)
351 for(n
= 0;n
< mpdm_size(v
) && ! *f
;n
++)
353 SET(s
, mpdm_aget(v
, n
));
366 O_TYPE
O_range(O_ARGS
)
367 /* build list from range of two numeric values */
372 mpdm_t ret
= MPDM_A(0);
375 for(n
= v1
;n
<= v2
;n
++)
376 mpdm_push(ret
, MPDM_R(n
));
378 for(n
= v1
;n
>= v2
;n
--)
379 mpdm_push(ret
, MPDM_R(n
));
385 O_TYPE
O_list(O_ARGS
)
386 /* build list from instructions */
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);
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));
407 O_TYPE
O_hash(O_ARGS
)
408 /* build hash from instructions */
411 mpdm_t ret
= RF(MPDM_H(0));
413 for(n
= 1;n
< mpdm_size(c
);n
+= 2)
417 mpdm_hset(ret
, v
, M(n
+ 1));
426 O_TYPE
O_hash2(O_ARGS
)
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
));
436 O_TYPE
O_subframe(O_ARGS
)
437 /* runs an instruction inside a subroutine frame */
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 */
451 /* this local symbol table is no longer needed */
458 O_TYPE
O_blkframe(O_ARGS
)
459 /* runs an instruction under a block frame */
463 mpdm_push(l
, MPDM_H(0));
471 static struct mpsl_op_s
475 mpdm_t (* func
)(O_ARGS
);
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
},
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
},
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
},
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) */
534 /* if aborted, do nothing */
535 if(mpsl_abort
) return(NULL
);
537 /* reference code, arguments and local symbol table */
546 /* gets the opcode */
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 */
556 ret
= mpdm_ref(o
->func(c
, a
, l
, f
));
561 printf("** %ls: %ls\n", mpdm_string(C0
), mpdm_string(ret
));
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;
584 /* execute first instruction */
585 v
= mpsl_exec_i(c
, a
, NULL
, &f
);
593 static mpdm_t
constant_fold(mpdm_t i
)
594 /* tries to fold complex but constant expressions into a literal */
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)
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
);
628 mpdm_t
mpsl_mkins(wchar_t * opcode
, int args
, mpdm_t a1
, mpdm_t a2
, mpdm_t a3
)
629 /* creates an instruction */
634 v
= MPDM_A(args
+ 1);
636 /* inserts the opcode */
637 o
= mpdm_hget_s(mpsl_opcodes
, opcode
);
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
);
650 mpdm_t
mpsl_build_opcodes(void)
651 /* builds the table of opcodes */
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
);
660 v
->flags
|= MPDM_IVAL
;
662 /* keys and values are the same */