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 opcode table
41 (only usable while compiling) */
42 mpdm_t mpsl_opcodes
= NULL
;
44 /* pointer to a trap function */
45 static mpdm_t mpsl_trap_func
= NULL
;
51 * mpsl_is_true - Tests if a value is true.
54 * If @v is a valid MPSL 'false' value (NULL, "" or the "0" string),
55 * returns zero, or nonzero otherwise.
57 int mpsl_is_true(mpdm_t v
)
59 /* if value is NULL, it's false */
63 /* if it's a printable string... */
64 if (v
->flags
& MPDM_STRING
) {
65 wchar_t *ptr
= (wchar_t *) v
->data
;
67 /* ... and it's "" or the "0" string, it's false */
68 if (*ptr
== L
'\0' || (*ptr
== L
'0' && *(ptr
+ 1) == L
'\0'))
72 /* any other case is true */
78 * mpsl_boolean - Returns 'true' or 'false' MPSL stock values.
79 * @b: boolean selector
81 * Returns MPSL's 'false' or 'true' values depending on the value in @b.
83 mpdm_t
mpsl_boolean(int b
)
85 return b
? mpdm_hget_s(mpdm_root(), L
"TRUE") : NULL
;
89 static mpdm_t
find_local_symtbl(mpdm_t s
, mpdm_t l
)
90 /* finds the local symbol table hash that holds l */
95 /* no local symbol table? nothing to find */
99 /* if s is multiple, take just the first element */
100 if (MPDM_IS_ARRAY(s
))
103 /* travel the local symbol table trying to find it */
104 for (n
= mpdm_size(l
) - 1; n
>= 0; n
--) {
105 mpdm_t h
= mpdm_aget(l
, n
);
107 if (mpdm_exists(h
, s
)) {
117 static void set_local_symbols(mpdm_t s
, mpdm_t v
, mpdm_t l
)
118 /* sets (or creates) a list of local symbols with a list of values */
127 /* gets the top local variable frame */
128 h
= mpdm_aget(l
, -1);
130 if (MPDM_IS_ARRAY(s
) || MPDM_IS_ARRAY(v
)) {
134 for (n
= 0; n
< mpdm_size(s
); n
++)
135 mpdm_hset(h
, mpdm_aget(s
, n
), mpdm_aget(v
, n
));
137 if (n
< mpdm_size(v
)) {
138 /* store the rest of arguments into _ */
139 a
= mpdm_hset_s(h
, L
"_", MPDM_A(0));
141 for (; n
< mpdm_size(v
); n
++)
142 mpdm_push(a
, mpdm_aget(v
, n
));
156 * mpsl_set_symbol - Sets value to a symbol.
159 * @l: local symbol table
161 * Assigns the value @v to the @s symbol. If the value exists as
162 * a local symbol, it's assigned to it; otherwise, it's set as a global
163 * symbol (and created if it does not exist).
165 mpdm_t
mpsl_set_symbol(mpdm_t s
, mpdm_t v
, mpdm_t l
)
173 r
= mpdm_sset(find_local_symtbl(s
, l
), s
, v
);
183 mpdm_t
mpsl_get_symbol_i(mpdm_t s
, mpdm_t l
, int i
)
190 r
= mpdm_sget_i(find_local_symtbl(s
, l
), s
, i
);
200 * mpsl_get_symbol - Gets the value of a symbol.
202 * @l: local symbol table
204 * Gets the value of a symbol. The symbol can be local or global
205 * (if the symbol exists in both tables, the local value will be returned).
207 mpdm_t
mpsl_get_symbol(mpdm_t s
, mpdm_t l
)
209 return mpsl_get_symbol_i(s
, l
, 0);
214 * mpsl_error - Generates an error.
215 * @err: the error message
217 * Generates an error. The @err error message is stored in the ERROR
218 * mpsl variable and the mpsl_abort global flag is set, so no further
219 * mpsl code can be executed until reset.
221 mpdm_t
mpsl_error(mpdm_t err
)
223 /* abort further execution */
227 return mpdm_hset_s(mpdm_root(), L
"ERROR", err
);
231 /** opcode macro helpers **/
233 #define O_TYPE static mpdm_t
234 #define O_ARGS mpdm_t c, mpdm_t a, mpdm_t l, int * f
236 O_TYPE
mpsl_exec_i(O_ARGS
);
238 #define C(n) mpdm_aget(c, n)
242 #define M(n) mpsl_exec_i(C(n), a, l, f)
248 #define R(x) mpdm_rval(x)
249 #define I(x) mpdm_ival(x)
251 #define RM1 mpdm_rval(M(1))
252 #define RM2 mpdm_rval(M(2))
253 #define IM1 mpdm_ival(M(1))
254 #define IM2 mpdm_ival(M(2))
256 #define GET(m) mpsl_get_symbol(m, l)
257 #define SET(m, v) mpsl_set_symbol(m, v, l)
258 #define BOOL mpsl_boolean
260 #define RF(v) mpdm_ref(v)
261 #define UF(v) v = mpdm_unref(v)
262 #define UFND(v) mpdm_unrefnd(v)
264 static int is_true_uf(mpdm_t v
)
277 O_TYPE
O_literal(O_ARGS
)
279 return mpdm_clone(C1
);
282 O_TYPE
O_multi(O_ARGS
)
294 O_TYPE
O_imulti(O_ARGS
)
304 O_TYPE
O_symval(O_ARGS
)
309 O_TYPE
O_assign(O_ARGS
)
316 return is_true_uf(M1
) ? M2
: M3
;
319 O_TYPE
O_local(O_ARGS
)
321 set_local_symbols(M1
, NULL
, l
);
326 O_TYPE
O_uminus(O_ARGS
)
328 return MPDM_R(-mpdm_rval(M1
));
333 return MPDM_R(RM1
+ RM2
);
338 return MPDM_R(RM1
- RM2
);
343 return MPDM_R(RM1
* RM2
);
348 return MPDM_R(RM1
/ RM2
);
353 return MPDM_I(IM1
% IM2
);
358 return BOOL(!is_true_uf(M1
));
366 if (mpsl_is_true(v
)) {
381 if (!mpsl_is_true(v
)) {
391 O_TYPE
O_bitand(O_ARGS
)
393 return MPDM_I(IM1
& IM2
);
396 O_TYPE
O_bitor(O_ARGS
)
398 return MPDM_I(IM1
| IM2
);
401 O_TYPE
O_bitxor(O_ARGS
)
403 return MPDM_I(IM1
^ IM2
);
408 return MPDM_I(IM1
<< IM2
);
413 return MPDM_I(IM1
>> IM2
);
418 return MPDM_R(pow(RM1
, RM2
));
421 O_TYPE
O_numlt(O_ARGS
)
423 return BOOL(RM1
< RM2
);
426 O_TYPE
O_numle(O_ARGS
)
428 return BOOL(RM1
<= RM2
);
431 O_TYPE
O_numgt(O_ARGS
)
433 return BOOL(RM1
> RM2
);
436 O_TYPE
O_numge(O_ARGS
)
438 return BOOL(RM1
>= RM2
);
441 O_TYPE
O_strcat(O_ARGS
)
443 return mpdm_strcat(M1
, M2
);
446 O_TYPE
O_streq(O_ARGS
)
448 return BOOL(mpdm_cmp(M1
, M2
) == 0);
451 O_TYPE
O_numeq(O_ARGS
)
456 mpdm_t r
= BOOL((v1
== NULL
|| v2
== NULL
) ?
457 (v1
== v2
) : (R(v1
) == R(v2
))
466 O_TYPE
O_break(O_ARGS
)
473 O_TYPE
O_return(O_ARGS
)
482 O_TYPE
execsym(O_ARGS
, int th
)
484 mpdm_t s
, v
, r
= NULL
;
486 /* gets the symbol name */
489 /* gets the symbol value */
492 if (!MPDM_IS_EXEC(v
)) {
493 /* not found or NULL value? error */
497 w
= RF(mpdm_join_s(s
, L
"."));
498 t
= RF(MPDM_2MBS((wchar_t *) w
->data
));
500 snprintf(tmp
, sizeof(tmp
), "Undefined function %s()",
503 mpsl_error(MPDM_MBS(tmp
));
511 /* does the symbol have more than one part? */
512 if (MPDM_IS_ARRAY(s
) && mpdm_size(s
) > 1) {
513 /* if so, store the prefix into local variable 'this' */
514 mpdm_push(l
, MPDM_H(0));
515 mpsl_set_symbol(MPDM_LS(L
"this"), mpsl_get_symbol_i(s
, l
, 1), l
);
521 r
= th
? mpdm_exec_thread(v
, M2
, l
) : mpdm_exec(v
, M2
, l
);
533 O_TYPE
O_execsym(O_ARGS
)
534 /* executes the value of a symbol */
536 return execsym(c
, a
, l
, f
, 0);
540 O_TYPE
O_threadsym(O_ARGS
)
541 /* executes the value of a symbol in a new thread */
543 return execsym(c
, a
, l
, f
, 1);
547 O_TYPE
O_while(O_ARGS
)
552 for (mpdm_void(M3
); !*f
&& is_true_uf(M1
); mpdm_void(M4
)) {
564 O_TYPE
O_foreach(O_ARGS
)
572 for (n
= 0; n
< mpdm_size(v
) && !*f
; n
++) {
573 SET(s
, mpdm_aget(v
, n
));
588 O_TYPE
O_range(O_ARGS
)
589 /* build list from range of two numeric values */
594 mpdm_t r
= RF(MPDM_A(0));
597 for (n
= v1
; n
<= v2
; n
++)
598 mpdm_push(r
, MPDM_R(n
));
600 for (n
= v1
; n
>= v2
; n
--)
601 mpdm_push(r
, MPDM_R(n
));
609 O_TYPE
O_list(O_ARGS
)
610 /* build list from instructions */
612 mpdm_t ret
= RF(mpdm_size(c
) == 2 ? MPDM_A(0) : M(2));
614 mpdm_push(ret
, M(1));
620 O_TYPE
O_ilist(O_ARGS
)
621 /* build and inverse list from instructions */
623 mpdm_t ret
= RF(mpdm_size(c
) == 2 ? MPDM_A(0) : M(2));
625 mpdm_ins(ret
, M(1), 0);
631 O_TYPE
O_hash(O_ARGS
)
632 /* build hash from instructions */
634 mpdm_t ret
= RF(mpdm_size(c
) == 3 ? MPDM_H(0) : M(3));
636 mpdm_hset(ret
, M1
, M2
);
642 O_TYPE
O_blkframe(O_ARGS
)
643 /* runs an instruction under a block frame */
647 /* no context? create one */
653 /* create a new local symbol table */
654 mpdm_push(l
, MPDM_H(0));
656 /* creates the arguments (if any) as local variables */
657 set_local_symbols(M2
, a
, l
);
659 /* execute instruction */
662 /* destroy the local symbol table */
671 O_TYPE
O_subframe(O_ARGS
)
672 /* runs an instruction inside a subroutine frame */
674 /* like a block frame, but with its own symbol table */
675 return O_blkframe(c
, a
, MPDM_A(0), f
);
679 static struct mpsl_op_s
{
682 mpdm_t(*func
) (O_ARGS
);
684 { L
"LITERAL", 0, O_literal
}, /* *must* be the zeroth */
685 { L
"MULTI", 0, O_multi
},
686 { L
"IMULTI", 0, O_imulti
},
687 { L
"SYMVAL", 0, O_symval
},
688 { L
"ASSIGN", 0, O_assign
},
689 { L
"EXECSYM", 0, O_execsym
},
690 { L
"THREADSYM", 0, O_threadsym
},
692 { L
"WHILE", 0, O_while
},
693 { L
"FOREACH", 0, O_foreach
},
694 { L
"SUBFRAME", 0, O_subframe
},
695 { L
"BLKFRAME", 0, O_blkframe
},
696 { L
"BREAK", 0, O_break
},
697 { L
"RETURN", 0, O_return
},
698 { L
"LOCAL", 0, O_local
},
699 { L
"LIST", 1, O_list
},
700 { L
"ILIST", 1, O_ilist
},
701 { L
"HASH", 1, O_hash
},
702 { L
"RANGE", 1, O_range
},
703 { L
"UMINUS", 1, O_uminus
},
704 { L
"ADD", 1, O_add
},
705 { L
"SUB", 1, O_sub
},
706 { L
"MUL", 1, O_mul
},
707 { L
"DIV", 1, O_div
},
708 { L
"MOD", 1, O_mod
},
709 { L
"NOT", 1, O_not
},
710 { L
"AND", 1, O_and
},
712 { L
"NUMEQ", 1, O_numeq
},
713 { L
"NUMLT", 1, O_numlt
},
714 { L
"NUMLE", 1, O_numle
},
715 { L
"NUMGT", 1, O_numgt
},
716 { L
"NUMGE", 1, O_numge
},
717 { L
"STRCAT", 1, O_strcat
},
718 { L
"STREQ", 1, O_streq
},
719 { L
"BITAND", 1, O_bitand
},
720 { L
"BITOR", 1, O_bitor
},
721 { L
"BITXOR", 1, O_bitxor
},
722 { L
"SHL", 1, O_shl
},
723 { L
"SHR", 1, O_shr
},
724 { L
"POW", 1, O_pow
},
729 O_TYPE
mpsl_exec_i(O_ARGS
)
730 /* Executes one MPSL instruction in the MPSL virtual machine. Called
731 from mpsl_exec_p() (which holds the flow control status variable) */
739 /* if aborted or NULL, do nothing */
740 if (!mpsl_abort
&& c
!= NULL
) {
741 /* gets the opcode and calls it */
742 ret
= op_table
[mpdm_ival(C0
)].func(c
, a
, l
, f
);
744 if (mpsl_trap_func
!= NULL
) {
745 mpdm_t f
= mpsl_trap_func
;
749 mpsl_trap_func
= NULL
;
750 mpdm_exec_3(f
, c
, a
, ret
, l
);
765 mpdm_t
mpsl_exec_p(mpdm_t c
, mpdm_t a
, mpdm_t ctxt
)
766 /* executes an MPSL instruction stream */
770 /* execute first instruction with a new flow control variable */
771 return mpsl_exec_i(c
, a
, ctxt
, &f
);
775 static mpdm_t
constant_fold(mpdm_t i
)
776 /* tries to fold complex but constant expressions into a literal */
781 /* get the number opcode */
782 n
= mpdm_ival(mpdm_aget(i
, 0));
784 if (op_table
[n
].foldable
) {
785 /* test if all arguments are literal (opcode 0) */
786 for (n
= 1; n
< mpdm_size(i
); n
++) {
787 mpdm_t t
= mpdm_aget(i
, n
);
789 /* if it's not LITERAL, abort immediately */
790 if (mpdm_ival(mpdm_aget(t
, 0)) != 0)
794 /* execute the instruction and convert to LITERAL */
795 v
= mpsl_exec_p(i
, NULL
, NULL
);
796 i
= mpsl_mkins(L
"LITERAL", 1, v
, NULL
, NULL
, NULL
);
803 mpdm_t
mpsl_mkins(wchar_t * opcode
, int args
, mpdm_t a1
, mpdm_t a2
,
804 mpdm_t a3
, mpdm_t a4
)
805 /* creates an instruction */
810 v
= MPDM_A(args
+ 1);
813 /* inserts the opcode */
814 o
= mpdm_hget_s(mpsl_opcodes
, opcode
);
818 case 4: mpdm_aset(v
, a4
, 4); /* no break */
819 case 3: mpdm_aset(v
, a3
, 3); /* no break */
820 case 2: mpdm_aset(v
, a2
, 2); /* no break */
821 case 1: mpdm_aset(v
, a1
, 1); /* no break */
826 v
= constant_fold(v
);
832 mpdm_t
mpsl_build_opcodes(void)
833 /* builds the table of opcodes */
836 mpdm_t r
= MPDM_H(0);
840 for (n
= 0; op_table
[n
].name
!= NULL
; n
++) {
841 mpdm_t v
= MPDM_LS(op_table
[n
].name
);
845 /* keys and values are the same */
856 * mpsl_trap - Install a trapping function.
857 * @trap_func: The trapping MPSL code
859 * Installs a trapping function. The function is an MPSL
860 * executable value receiving 3 arguments: the code stream,
861 * the arguments and the return value of the executed code.
863 * Returns NULL (previous versions returned the previous
864 * trapping function).
866 mpdm_t
mpsl_trap(mpdm_t trap_func
)
869 mpdm_unref(mpsl_trap_func
);
870 mpsl_trap_func
= trap_func
;
877 * mpsl_argv - Fills the ARGV global array.
878 * @argc: number of arguments
879 * @argv: array of string values
881 * Fills the ARGV global MPSL array with an array of arguments. These
882 * are usually the ones sent to main().
884 void mpsl_argv(int argc
, char *argv
[])
889 /* create the ARGV array */
890 ARGV
= mpdm_hset_s(mpdm_root(), L
"ARGV", MPDM_A(0));
892 for (n
= 0; n
< argc
; n
++)
893 mpdm_push(ARGV
, MPDM_MBS(argv
[n
]));
898 mpdm_t
mpsl_build_funcs(void);
902 * mpsl_startup - Initializes MPSL.
904 * Initializes the Minimum Profit Scripting Language. Returns 0 if
905 * everything went OK.
907 int mpsl_startup(void)
917 /* creates INC, unless already defined */
918 if (mpdm_hget_s(r
, L
"INC") == NULL
)
919 mpdm_hset_s(r
, L
"INC", MPDM_A(0));
922 mpdm_hset_s(r
, L
"TRUE", MPDM_I(1));
924 /* standard file descriptors */
925 mpdm_hset_s(r
, L
"STDIN", MPDM_F(stdin
));
926 mpdm_hset_s(r
, L
"STDOUT", MPDM_F(stdout
));
927 mpdm_hset_s(r
, L
"STDERR", MPDM_F(stderr
));
929 /* home and application directories */
930 mpdm_hset_s(r
, L
"HOMEDIR", mpdm_home_dir());
931 mpdm_hset_s(r
, L
"APPDIR", mpdm_app_dir());
933 /* fill now the MPSL hash */
935 mpdm_hset_s(r
, L
"MPSL", m
);
937 /* store things there */
938 mpdm_hset_s(m
, L
"VERSION", MPDM_MBS(VERSION
));
939 mpdm_hset_s(m
, L
"OPCODE", mpsl_build_opcodes());
940 mpdm_hset_s(m
, L
"LC", MPDM_H(0));
941 mpdm_hset_s(m
, L
"CORE", mpsl_build_funcs());
943 mpdm_dump_1
= mpsl_dump_1
;
950 * mpsl_shutdown - Shuts down MPSL.
952 * Shuts down MPSL. No MPSL functions should be used from now on.
954 void mpsl_shutdown(void)