4 MPSL - Minimum Profit Scripting Language
5 Copyright (C) 2003/2010 Angel Ortega <angel@triptico.com>
7 mpsl.y - Minimum Profit Scripting Language YACC parser
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 http://www.triptico.com
35 /* the bytecode being generated */
36 static mpdm_t mpsl_bytecode
= NULL
;
38 /* pointer to source code being compiled */
39 extern
wchar_t *mpsl_next_char
;
41 /* pointer to file being compiled */
42 extern
FILE *mpsl_file
;
47 /* compiled filename (for errors) */
48 static char *mpsl_filename
= NULL
;
50 /* cached value MPSL.OPCODE */
51 extern mpdm_t mpsl_opcodes
;
53 /* cached value MPSL.LC */
54 extern mpdm_t mpsl_lc
;
60 void yyerror(char *s
);
62 #define INS0(o) mpsl_mkins(o, 0, NULL, NULL, NULL, NULL)
63 #define INS1(o,a1) mpsl_mkins(o, 1, a1, NULL, NULL, NULL)
64 #define INS2(o,a1,a2) mpsl_mkins(o, 2, a1, a2, NULL, NULL)
65 #define INS3(o,a1,a2,a3) mpsl_mkins(o, 3, a1, a2, a3, NULL)
66 #define INS4(o,a1,a2,a3,a4) mpsl_mkins(o, 4, a1, a2, a3, a4)
68 static mpdm_t mpsl_x
(mpdm_t a1
, mpdm_t a2
, int sf
)
69 /* creates an executable value with the MPSL executor as the first
70 argument and a compiled stream as the second */
72 return MPDM_X2
(mpsl_exec_p
,
73 mpsl_mkins
(sf ? L
"SUBFRAME" : L
"BLKFRAME",
74 a2
== NULL ?
1 : 2, a1
, a2
, NULL
, NULL
));
78 static void compiler_warning
(char *str
)
80 fprintf
(stderr
, "WARNING: %s.\n", str
);
87 mpdm_t v
; /* a simple value */
88 mpdm_t ins
; /* an 'instruction': [ opcode, args ] */
91 %token
<v
> NULLV INTEGER REAL STRING SYMBOL LITERAL
92 %token WHILE FOR IF SUB FOREACH LOCAL BREAK RETURN
97 %left INC DEC IADD ISUB IMUL IDIV IMOD IBITAND IBITOR IBITXOR ISHR ISHL
99 %left STRCAT STREQ NUMEQ STRNE NUMNE NUMGE NUMLE ARROW
':' RANGE
'>''<' INVCALL
104 %left
'*' '/' MOD POW
107 %type
<ins
> stmt expr sym_list stmt_list list hash compsym
124 /* null instruction */
128 /* expression, as is */
132 | WHILE
'(' expr
')' stmt
135 $$
= INS2
(L
"WHILE", $3, $5);
137 | FOR
'(' expr
';' expr
';' expr
')' stmt
140 $$
= INS4
(L
"WHILE", $5, $9, $3, $7);
142 | FOR
'(' ';' ';' ')' stmt
145 $$
= INS2
(L
"WHILE", INS1
(L
"LITERAL", MPDM_I
(1)), $6);
147 | IF
'(' expr
')' stmt %prec IFI
149 /* if - then construction */
150 $$
= INS2
(L
"IF", $3, $5);
152 | IF
'(' expr
')' stmt ELSE stmt
154 /* if - then - else construction */
155 $$
= INS3
(L
"IF", $3, $5, $7);
158 | SUB compsym
'{' stmt_list
'}'
160 /* subroutine definition,
162 $$
= INS2
(L
"ASSIGN", $2,
164 mpsl_x
($4, NULL
, 1)));
167 | SUB compsym
'(' ')' '{' stmt_list
'}'
169 /* subroutine definition,
170 without arguments (second
171 syntax, including parens) */
172 $$
= INS2
(L
"ASSIGN", $2,
174 mpsl_x
($6, NULL
, 1)));
177 | SUB compsym
'(' sym_list
')' '{' stmt_list
'}'
179 /* subroutine definition,
181 $$
= INS2
(L
"ASSIGN", $2,
186 | FOREACH
'(' compsym
',' expr
')' stmt
188 /* foreach construction */
189 /* a block frame is created, the iterator
190 created as local, and the foreach executed */
191 $$
= INS1
(L
"BLKFRAME",
194 INS3
(L
"FOREACH", $3, $5, $7)
198 | FOREACH
'(' LOCAL compsym
',' expr
')' stmt
200 compiler_warning
("useless use of local in foreach loop");
202 $$
= INS1
(L
"BLKFRAME",
205 INS3
(L
"FOREACH", $4, $6, $8)
210 |
'{' stmt_list
'}' {
211 /* block of instructions,
212 with local symbol table */
213 $$
= INS1
(L
"BLKFRAME", $2);
216 | LOCAL sym_list
';' {
217 /* local symbol creation */
218 $$
= INS1
(L
"LOCAL", $2);
220 | LOCAL SYMBOL
'=' expr
';'
222 /* contraction; local symbol
223 creation and assignation */
226 INS1
(L
"LITERAL", $2)),
228 INS1
(L
"LITERAL", $2),$4)
232 /* break (exit from loop) */
236 /* return from subroutine */
237 $$
= INS1
(L
"RETURN", $2);
240 /* return from subroutine (void) */
241 $$
= INS0
(L
"RETURN");
248 /* sequence of instructions */
249 $$
= INS2
(L
"MULTI", $1, $2);
255 $$
= INS1
(L
"LIST", $1);
258 /* build list from list of
260 $$
= INS2
(L
"LIST", $3, $1);
267 INS1
(L
"LITERAL", $1));
269 | sym_list
',' SYMBOL
{
270 /* comma-separated list of symbols */
272 INS1
(L
"LITERAL", $3), $1);
278 $$
= INS2
(L
"HASH", $1, $3);
281 $$
= INS2
(L
"HASH", INS1
(L
"LITERAL", $1), $3);
283 | hash
',' expr ARROW expr
285 /* build hash from list of
287 $$
= INS3
(L
"HASH", $3, $5, $1);
289 | hash
',' SYMBOL
':' expr
291 /* build hash from list of
293 $$
= INS3
(L
"HASH", INS1
(L
"LITERAL", $3), $5, $1);
300 INS1
(L
"LITERAL", $1));
302 | compsym
'.' INTEGER
{
303 /* a.5 compound symbol */
305 INS1
(L
"LITERAL", $3), $1);
307 | compsym
'.' SYMBOL
{
308 /* a.b compound symbol */
310 INS1
(L
"LITERAL", $3), $1);
312 | compsym
'[' expr
']' {
313 /* a["b"] or a[5] compound symbol */
314 $$
= INS2
(L
"LIST", $3, $1);
320 /* literal integer */
321 $$
= INS1
(L
"LITERAL", $1);
325 $$
= INS1
(L
"LITERAL", $1);
328 /* literal real number */
329 $$
= INS1
(L
"LITERAL", $1);
332 /* compound symbol */
333 $$
= INS1
(L
"SYMVAL", $1);
337 $$
= INS1
(L
"LITERAL", NULL
);
340 |
'-' expr %prec UMINUS
{
342 $$
= INS1
(L
"UMINUS", $2);
345 /* math operations */
346 | expr
'+' expr
{ $$
= INS2
(L
"ADD", $1, $3); }
347 | expr
'-' expr
{ $$
= INS2
(L
"SUB", $1, $3); }
348 | expr
'*' expr
{ $$
= INS2
(L
"MUL", $1, $3); }
349 | expr
'/' expr
{ $$
= INS2
(L
"DIV", $1, $3); }
350 | expr MOD expr
{ $$
= INS2
(L
"MOD", $1, $3); }
351 | expr POW expr
{ $$
= INS2
(L
"POW", $1, $3); }
354 | expr AMPERSAND expr
{ $$
= INS2
(L
"BITAND", $1, $3); }
355 | expr BITOR expr
{ $$
= INS2
(L
"BITOR", $1, $3); }
356 | expr BITXOR expr
{ $$
= INS2
(L
"BITXOR", $1, $3); }
357 | expr SHL expr
{ $$
= INS2
(L
"SHL", $1, $3); }
358 | expr SHR expr
{ $$
= INS2
(L
"SHR", $1, $3); }
360 /* increment and decrement (prefix) */
361 | INC compsym
{ $$
= INS2
(L
"ASSIGN", $2,
364 INS1
(L
"LITERAL", MPDM_I
(1))
368 | DEC compsym
{ $$
= INS2
(L
"ASSIGN", $2,
371 INS1
(L
"LITERAL", MPDM_I
(1))
376 /* increment and decrement (suffix) */
377 | compsym INC
{ $$
= INS2
(L
"IMULTI",
382 INS1
(L
"LITERAL", MPDM_I
(1))
387 | compsym DEC
{ $$
= INS2
(L
"IMULTI",
392 INS1
(L
"LITERAL", MPDM_I
(1))
398 /* immediate math operations */
399 | compsym IADD expr
{ $$
= INS2
(L
"ASSIGN", $1,
405 | compsym ISUB expr
{ $$
= INS2
(L
"ASSIGN", $1,
411 | compsym IMUL expr
{ $$
= INS2
(L
"ASSIGN", $1,
417 | compsym IDIV expr
{ $$
= INS2
(L
"ASSIGN", $1,
423 | compsym IMOD expr
{ $$
= INS2
(L
"ASSIGN", $1,
429 | compsym IBITAND expr
{ $$
= INS2
(L
"ASSIGN", $1,
435 | compsym IBITOR expr
{ $$
= INS2
(L
"ASSIGN", $1,
441 | compsym IBITXOR expr
{ $$
= INS2
(L
"ASSIGN", $1,
447 | compsym ISHL expr
{ $$
= INS2
(L
"ASSIGN", $1,
453 | compsym ISHR expr
{ $$
= INS2
(L
"ASSIGN", $1,
462 $$
= INS1
(L
"NOT", $2);
466 $$
= INS2
(L
"NUMLT", $1, $3);
469 /* bool greater than */
470 $$
= INS2
(L
"NUMGT", $1, $3);
473 /* bool less or equal than */
474 $$
= INS2
(L
"NUMLE", $1, $3);
477 /* bool greater or equal than */
478 $$
= INS2
(L
"NUMGE", $1, $3);
481 /* bool numeric equal */
482 $$
= INS2
(L
"NUMEQ", $1, $3);
485 /* bool numeric non-equal */
487 INS2
(L
"NUMEQ", $1, $3));
491 /* string concatenation */
492 $$
= INS2
(L
"STRCAT", $1, $3);
495 /* bool string equal */
496 $$
= INS2
(L
"STREQ", $1, $3);
499 /* bool string non-equal */
501 INS2
(L
"STREQ", $1, $3));
504 | expr BOOLAND expr
{
506 $$
= INS2
(L
"AND", $1, $3);
510 $$
= INS2
(L
"OR", $1, $3);
513 | SUB
'{' stmt_list
'}' {
514 /* anonymous subroutine (without args) */
515 $$
= INS1
(L
"LITERAL", mpsl_x
($3, NULL
, 0));
518 | SUB
'(' sym_list
')' '{' stmt_list
'}'
520 /* anonymous subroutine (with args) */
521 $$
= INS1
(L
"LITERAL", mpsl_x
($6, $3, 0));
525 /* parenthesized expression */
531 $$
= INS1
(L
"LITERAL", MPDM_A
(0));
537 |
'[' expr RANGE expr
']'
539 /* build range from expressions */
540 $$
= INS2
(L
"RANGE", $2, $4);
545 $$
= INS1
(L
"LITERAL", MPDM_H
(0));
553 /* function call (without args) */
554 $$
= INS1
(L
"EXECSYM", $1);
556 | compsym
'(' list
')' {
557 /* function call (with args) */
558 $$
= INS2
(L
"EXECSYM", $1, $3);
560 | expr INVCALL compsym
'(' ')' {
561 /* function call with only an inverse argument */
562 $$
= INS2
(L
"EXECSYM", $3, INS1
(L
"ILIST", $1));
564 | expr INVCALL compsym
'(' list
')' {
565 /* function call with inverse argument and other ones */
566 $$
= INS2
(L
"EXECSYM", $3, INS2
(L
"ILIST", $1, $5));
568 | AMPERSAND compsym
'(' ')' {
569 /* function call as a new thread (without args) */
570 $$
= INS1
(L
"THREADSYM", $2);
572 | AMPERSAND compsym
'(' list
')' {
573 /* function call as a new thread (with args) */
574 $$
= INS2
(L
"THREADSYM", $2, $4);
577 /* simple assignation */
578 $$
= INS2
(L
"ASSIGN", $1, $3);
585 void yyerror(char *s
)
589 snprintf
(tmp
, sizeof
(tmp
), "%s in %s, line %d",
590 s
, mpsl_filename
, mpsl_line
+ 1);
592 mpsl_error
(MPDM_MBS
(tmp
));
596 static FILE *inc_fopen
(const char *filename
, mpdm_t inc
)
597 /* loads filename, searching in INC if not directly accesible */
603 /* loop through INC, prepending each path
605 for
(n
= 0; n
< mpdm_size
(inc
); n
++) {
606 mpdm_t v
= mpdm_aget
(inc
, n
);
608 v
= mpdm_ref
(MPDM_2MBS
(v
->data
));
609 snprintf
(tmp
, sizeof
(tmp
), "%s/%s", (char *) v
->data
, filename
);
612 if
((f
= fopen
(tmp
, "r")) != NULL
)
620 static mpdm_t do_parse
(const char *filename
, wchar_t * code
, FILE * file
)
621 /* calls yyparse() after doing some initialisations, and returns
622 the compiled code as an executable value */
630 /* reset last bytecode */
631 mpsl_bytecode
= NULL
;
634 mpsl_next_char
= code
;
637 if
(mpsl_filename
!= NULL
)
640 mpsl_filename
= strdup
(filename
);
642 /* cache some values */
643 v
= mpdm_hget_s
(mpdm_root
(), L
"MPSL");
644 mpsl_opcodes
= mpdm_hget_s
(v
, L
"OPCODE");
645 mpsl_lc
= mpdm_hget_s
(v
, L
"LC");
648 if
(yyparse() == 0 && mpsl_bytecode
!= NULL
)
649 x
= mpsl_x
(mpsl_bytecode
, NULL
, 1);
651 /* clean back cached values */
660 * mpsl_compile - Compiles a string of MPSL code.
661 * @code: A value containing a string of MPSL code
663 * Compiles a string of MPSL code and returns an mpdm value executable
664 * by mpdm_exec(). If there is a syntax (or other type) error, NULL
665 * is returned instead.
667 mpdm_t mpsl_compile
(mpdm_t code
)
672 x
= do_parse
("<INLINE>", (wchar_t *) code
->data
, NULL
);
680 * mpsl_compile_file - Compiles a file of MPSL code.
681 * @file: File stream or file name.
682 * @inc: search path for source files.
684 * Compiles a source file of MPSL code and returns an mpdm value
685 * executable by mpdm_exec(). If @file is an MPSL file descriptor,
686 * it's read as is and compiled; otherwise, it's assumed to be a
687 * file name, that will be searched for in any of the paths defined
688 * in the @inc array. If the file cannot be found
689 * or there is any other error, NULL is returned instead.
691 mpdm_t mpsl_compile_file
(mpdm_t file
, mpdm_t inc
)
696 const char *filename
= NULL
;
701 if
((f
= mpdm_get_filehandle
(file
)) != NULL
) {
708 /* it's a filename; open it */
709 v
= mpdm_ref
(MPDM_2MBS
(file
->data
));
713 if
((f
= inc_fopen
(filename
, inc
)) == NULL
) {
716 snprintf
(tmp
, sizeof
(tmp
) - 1,
717 "File '%s' not found in INC", filename
);
718 mpsl_error
(MPDM_MBS
(tmp
));
727 x
= do_parse
(filename
, NULL
, f
);
739 * mpsl_eval - Evaluates MSPL code.
740 * @code: A value containing a string of MPSL code, or executable code
741 * @args: optional arguments for @code
742 * @ctxt: context for @code
744 * Evaluates a piece of code. The @code can be a string containing MPSL source
745 * code (that will be compiled) or a direct executable value. If the compilation
746 * or the execution gives an error, the ERROR variable will be set to a printable
747 * value and NULL returned. Otherwise, the exit value from the code is returned
748 * and ERROR set to NULL. The abort flag is reset on exit.
750 mpdm_t mpsl_eval
(mpdm_t code
, mpdm_t args
, mpdm_t ctxt
)
760 /* if code is not executable, try to compile */
761 if
(!MPDM_IS_EXEC
(code
)) {
764 /* get the eval cache */
765 if
((c
= mpdm_hget_s
(mpdm_root
(), L
"__EVAL__")) == NULL
)
766 c
= mpdm_hset_s
(mpdm_root
(), L
"__EVAL__", MPDM_H
(0));
768 /* this code still not compiled? do it */
769 if
((cs
= mpdm_hget
(c
, code
)) == NULL
)
770 cs
= mpdm_hset
(c
, code
, mpsl_compile
(code
));
775 /* execute, if possible */
776 if
(MPDM_IS_EXEC
(cs
))
777 r
= mpdm_exec
(cs
, args
, ctxt
);
781 /* reset the abort flag */