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
37 /* global abort flag */
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
;
57 * mpsl_is_true - Tests if a value is true.
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 */
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'))
78 /* any other case is true */
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 */
101 /* no local symbol table? nothing to find */
105 /* if s is multiple, take just the first element */
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
)) {
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 */
131 /* gets the top local variable frame */
132 h
= mpdm_aget(l
, -1);
134 if (MPDM_IS_ARRAY(s
) || MPDM_IS_ARRAY(v
)) {
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 _ */
144 for (; n
< mpdm_size(v
); n
++)
145 mpdm_push(a
, mpdm_aget(v
, n
));
147 mpdm_hset_s(h
, L
"_", a
);
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
);
170 * mpsl_set_symbol - Sets value to a symbol.
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.
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 */
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)
234 #define M(n) mpsl_exec_i(C(n), a, l, f)
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
)
269 O_TYPE
O_literal(O_ARGS
) {
270 return mpdm_clone(C1
);
273 O_TYPE
O_multi(O_ARGS
) {
287 O_TYPE
O_imulti(O_ARGS
) {
298 O_TYPE
O_symval(O_ARGS
) {
306 O_TYPE
O_assign(O_ARGS
) {
308 mpdm_t r
= SET(v
, M2
);
314 O_TYPE
O_if(O_ARGS
) {
325 O_TYPE
O_local(O_ARGS
) {
327 set_local_symbols(v
, NULL
, l
);
333 O_TYPE
O_uminus(O_ARGS
) {
335 mpdm_t r
= MPDM_R(-mpdm_rval(v
));
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
) {
355 if (mpsl_is_true(v
)) {
366 O_TYPE
O_or(O_ARGS
) {
370 if (!mpsl_is_true(v
)) {
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
) {
396 mpdm_t r
= mpdm_strcat(v1
, v2
);
404 O_TYPE
O_streq(O_ARGS
) {
408 mpdm_t r
= BOOL(mpdm_cmp(v1
, v2
) == 0);
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
) {
423 O_TYPE
O_return(O_ARGS
) {
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 */
437 /* gets the symbol value */
440 if (!MPDM_IS_EXEC(v
)) {
441 /* not found or NULL value? error */
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()",
451 mpsl_error(MPDM_MBS(tmp
));
457 /* save current local symbol table */
458 mpdm_t t
= local_symtbl
;
460 /* substitute with this one */
468 /* and get back to the original one */
478 O_TYPE
O_while(O_ARGS
)
483 while (!*f
&& is_true_uf(M1
)) {
495 O_TYPE
O_foreach(O_ARGS
)
503 for (n
= 0; n
< mpdm_size(v
) && ! *f
; n
++) {
504 SET(s
, mpdm_aget(v
, n
));
518 O_TYPE
O_range(O_ARGS
)
519 /* build list from range of two numeric values */
524 mpdm_t ret
= MPDM_A(0);
527 for (n
= v1
; n
<= v2
; n
++)
528 mpdm_push(ret
, MPDM_R(n
));
530 for (n
= v1
; n
>= v2
; n
--)
531 mpdm_push(ret
, MPDM_R(n
));
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));
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);
557 O_TYPE
O_hash(O_ARGS
)
558 /* build hash from instructions */
561 mpdm_t ret
= RF(mpdm_size(c
) == 3 ? MPDM_H(0) : M(3));
565 mpdm_hset(ret
, k
, v
);
573 O_TYPE
generic_frame(O_ARGS
)
574 /* runs an instruction under a frame */
578 /* if l is NULL (usually for subroutine frames),
579 create a new array for holding local symbol tables */
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 */
594 /* destroy the local symbol table */
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
{
622 mpdm_t (* func
)(O_ARGS
);
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
},
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
},
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
},
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
;
675 /* if aborted or NULL, do nothing */
676 if (mpsl_abort
|| c
== NULL
)
679 /* sweep some values */
680 if (!in_constant_folding
)
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
);
701 mpdm_t
mpsl_exec_p(mpdm_t c
, mpdm_t a
)
702 /* executes an MPSL instruction stream */
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 */
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)
730 in_constant_folding
= 1;
732 /* execute the instruction and convert to LITERAL */
734 w
= RF(mpsl_exec_p(v
, NULL
));
735 i
= mpsl_mkins(L
"LITERAL", 1, w
, NULL
, NULL
);
739 in_constant_folding
= 0;
746 mpdm_t
mpsl_mkins(wchar_t * opcode
, int args
, mpdm_t a1
, mpdm_t a2
, mpdm_t a3
)
747 /* creates an instruction */
752 v
= MPDM_A(args
+ 1);
754 /* inserts the opcode */
755 o
= mpdm_hget_s(mpsl_opcodes
, opcode
);
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
);
770 mpdm_t
mpsl_build_opcodes(void)
771 /* builds the table of opcodes */
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
);
781 /* keys and values are the same */
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
)
803 mpdm_unref(mpsl_trap_func
);
804 mpsl_trap_func
= trap_func
;
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
[])
823 /* create the ARGV array */
826 for (n
= 0; n
< argc
; n
++)
827 mpdm_push(ARGV
, MPDM_MBS(argv
[n
]));
829 mpdm_hset_s(mpdm_root(), L
"ARGV", ARGV
);
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)
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));
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 */
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
;
886 * mpsl_shutdown - Shuts down MPSL.
888 * Shuts down MPSL. No MPSL functions should be used from now on.
890 void mpsl_shutdown(void)