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)
63 #define INS1(o,a1) mpsl_mkins(o, 1, a1, NULL, NULL)
64 #define INS2(o,a1,a2) mpsl_mkins(o, 2, a1, a2, NULL)
65 #define INS3(o,a1,a2,a3) mpsl_mkins(o, 3, a1, a2, a3)
67 static mpdm_t mpsl_x
(mpdm_t a1
, mpdm_t a2
, int sf
)
68 /* creates an executable value with the MPSL executor as the first
69 argument and a compiled stream as the second */
71 return MPDM_X2
(mpsl_exec_p
,
72 mpsl_mkins
(sf ? L
"SUBFRAME" : L
"BLKFRAME",
73 a2
== NULL ?
1 : 2, a1
, a2
, NULL
));
77 static void compiler_warning
(char *str
)
79 fprintf
(stderr
, "WARNING: %s.\n", str
);
86 mpdm_t v
; /* a simple value */
87 mpdm_t ins
; /* an 'instruction': [ opcode, args ] */
90 %token
<v
> NULLV INTEGER REAL STRING SYMBOL LITERAL
91 %token WHILE IF SUB FOREACH LOCAL BREAK RETURN
96 %left INC DEC IADD ISUB IMUL IDIV IMOD IBITAND IBITOR IBITXOR ISHR ISHL
98 %left STRCAT STREQ NUMEQ STRNE NUMNE NUMGE NUMLE HASHPAIR RANGE
'>''<' INVCALL
103 %left
'*' '/' MOD POW
106 %type
<ins
> stmt expr sym_list stmt_list list hash compsym
123 /* null instruction */
127 /* expression, as is */
131 | WHILE
'(' expr
')' stmt
134 $$
= INS2
(L
"WHILE", $3, $5);
136 | IF
'(' expr
')' stmt %prec IFI
138 /* if - then construction */
139 $$
= INS2
(L
"IF", $3, $5);
141 | IF
'(' expr
')' stmt ELSE stmt
143 /* if - then - else construction */
144 $$
= INS3
(L
"IF", $3, $5, $7);
147 | SUB compsym
'{' stmt_list
'}'
149 /* subroutine definition,
151 $$
= INS2
(L
"ASSIGN", $2,
153 mpsl_x
($4, NULL
, 1)));
156 | SUB compsym
'(' ')' '{' stmt_list
'}'
158 /* subroutine definition,
159 without arguments (second
160 syntax, including parens) */
161 $$
= INS2
(L
"ASSIGN", $2,
163 mpsl_x
($6, NULL
, 1)));
166 | SUB compsym
'(' sym_list
')' '{' stmt_list
'}'
168 /* subroutine definition,
170 $$
= INS2
(L
"ASSIGN", $2,
175 | FOREACH
'(' compsym
',' expr
')' stmt
177 /* foreach construction */
178 /* a block frame is created, the iterator
179 created as local, and the foreach executed */
180 $$
= INS1
(L
"BLKFRAME",
183 INS3
(L
"FOREACH", $3, $5, $7)
187 | FOREACH
'(' LOCAL compsym
',' expr
')' stmt
189 compiler_warning
("useless use of local in foreach loop");
191 $$
= INS1
(L
"BLKFRAME",
194 INS3
(L
"FOREACH", $4, $6, $8)
199 |
'{' stmt_list
'}' {
200 /* block of instructions,
201 with local symbol table */
202 $$
= INS1
(L
"BLKFRAME", $2);
205 | LOCAL sym_list
';' {
206 /* local symbol creation */
207 $$
= INS1
(L
"LOCAL", $2);
209 | LOCAL SYMBOL
'=' expr
';'
211 /* contraction; local symbol
212 creation and assignation */
215 INS1
(L
"LITERAL", $2)),
217 INS1
(L
"LITERAL", $2),$4)
221 /* break (exit from loop) */
225 /* return from subroutine */
226 $$
= INS1
(L
"RETURN", $2);
229 /* return from subroutine (void) */
230 $$
= INS0
(L
"RETURN");
237 /* sequence of instructions */
238 $$
= INS2
(L
"MULTI", $1, $2);
244 $$
= INS1
(L
"LIST", $1);
247 /* build list from list of
249 $$
= INS2
(L
"LIST", $3, $1);
256 INS1
(L
"LITERAL", $1));
258 | sym_list
',' SYMBOL
{
259 /* comma-separated list of symbols */
261 INS1
(L
"LITERAL", $3), $1);
267 $$
= INS2
(L
"HASH", $1, $3);
269 | hash
',' expr HASHPAIR expr
271 /* build hash from list of
273 $$
= INS3
(L
"HASH", $3, $5, $1);
280 INS1
(L
"LITERAL", $1));
282 | compsym
'.' INTEGER
{
283 /* a.5 compound symbol */
285 INS1
(L
"LITERAL", $3), $1);
287 | compsym
'.' SYMBOL
{
288 /* a.b compound symbol */
290 INS1
(L
"LITERAL", $3), $1);
292 | compsym
'[' expr
']' {
293 /* a["b"] or a[5] compound symbol */
294 $$
= INS2
(L
"LIST", $3, $1);
300 /* literal integer */
301 $$
= INS1
(L
"LITERAL", $1);
305 $$
= INS1
(L
"LITERAL", $1);
308 /* literal real number */
309 $$
= INS1
(L
"LITERAL", $1);
312 /* compound symbol */
313 $$
= INS1
(L
"SYMVAL", $1);
317 $$
= INS1
(L
"LITERAL", NULL
);
320 |
'-' expr %prec UMINUS
{
322 $$
= INS1
(L
"UMINUS", $2);
325 /* math operations */
326 | expr
'+' expr
{ $$
= INS2
(L
"ADD", $1, $3); }
327 | expr
'-' expr
{ $$
= INS2
(L
"SUB", $1, $3); }
328 | expr
'*' expr
{ $$
= INS2
(L
"MUL", $1, $3); }
329 | expr
'/' expr
{ $$
= INS2
(L
"DIV", $1, $3); }
330 | expr MOD expr
{ $$
= INS2
(L
"MOD", $1, $3); }
331 | expr POW expr
{ $$
= INS2
(L
"POW", $1, $3); }
334 | expr AMPERSAND expr
{ $$
= INS2
(L
"BITAND", $1, $3); }
335 | expr BITOR expr
{ $$
= INS2
(L
"BITOR", $1, $3); }
336 | expr BITXOR expr
{ $$
= INS2
(L
"BITXOR", $1, $3); }
337 | expr SHL expr
{ $$
= INS2
(L
"SHL", $1, $3); }
338 | expr SHR expr
{ $$
= INS2
(L
"SHR", $1, $3); }
340 /* increment and decrement (prefix) */
341 | INC compsym
{ $$
= INS2
(L
"ASSIGN", $2,
344 INS1
(L
"LITERAL", MPDM_I
(1))
348 | DEC compsym
{ $$
= INS2
(L
"ASSIGN", $2,
351 INS1
(L
"LITERAL", MPDM_I
(1))
356 /* increment and decrement (suffix) */
357 | compsym INC
{ $$
= INS2
(L
"IMULTI",
362 INS1
(L
"LITERAL", MPDM_I
(1))
367 | compsym DEC
{ $$
= INS2
(L
"IMULTI",
372 INS1
(L
"LITERAL", MPDM_I
(1))
378 /* immediate math operations */
379 | compsym IADD expr
{ $$
= INS2
(L
"ASSIGN", $1,
385 | compsym ISUB expr
{ $$
= INS2
(L
"ASSIGN", $1,
391 | compsym IMUL expr
{ $$
= INS2
(L
"ASSIGN", $1,
397 | compsym IDIV expr
{ $$
= INS2
(L
"ASSIGN", $1,
403 | compsym IMOD expr
{ $$
= INS2
(L
"ASSIGN", $1,
409 | compsym IBITAND expr
{ $$
= INS2
(L
"ASSIGN", $1,
415 | compsym IBITOR expr
{ $$
= INS2
(L
"ASSIGN", $1,
421 | compsym IBITXOR expr
{ $$
= INS2
(L
"ASSIGN", $1,
427 | compsym ISHL expr
{ $$
= INS2
(L
"ASSIGN", $1,
433 | compsym ISHR expr
{ $$
= INS2
(L
"ASSIGN", $1,
442 $$
= INS1
(L
"NOT", $2);
446 $$
= INS2
(L
"NUMLT", $1, $3);
449 /* bool greater than */
450 $$
= INS2
(L
"NUMGT", $1, $3);
453 /* bool less or equal than */
454 $$
= INS2
(L
"NUMLE", $1, $3);
457 /* bool greater or equal than */
458 $$
= INS2
(L
"NUMGE", $1, $3);
461 /* bool numeric equal */
462 $$
= INS2
(L
"NUMEQ", $1, $3);
465 /* bool numeric non-equal */
467 INS2
(L
"NUMEQ", $1, $3));
471 /* string concatenation */
472 $$
= INS2
(L
"STRCAT", $1, $3);
475 /* bool string equal */
476 $$
= INS2
(L
"STREQ", $1, $3);
479 /* bool string non-equal */
481 INS2
(L
"STREQ", $1, $3));
484 | expr BOOLAND expr
{
486 $$
= INS2
(L
"AND", $1, $3);
490 $$
= INS2
(L
"OR", $1, $3);
493 | SUB
'{' stmt_list
'}' {
494 /* anonymous subroutine (without args) */
495 $$
= INS1
(L
"LITERAL", mpsl_x
($3, NULL
, 0));
498 | SUB
'(' sym_list
')' '{' stmt_list
'}'
500 /* anonymous subroutine (with args) */
501 $$
= INS1
(L
"LITERAL", mpsl_x
($6, $3, 0));
505 /* parenthesized expression */
511 $$
= INS1
(L
"LITERAL", MPDM_A
(0));
517 |
'[' expr RANGE expr
']'
519 /* build range from expressions */
520 $$
= INS2
(L
"RANGE", $2, $4);
525 $$
= INS1
(L
"LITERAL", MPDM_H
(0));
533 /* function call (without args) */
534 $$
= INS1
(L
"EXECSYM", $1);
536 | compsym
'(' list
')' {
537 /* function call (with args) */
538 $$
= INS2
(L
"EXECSYM", $1, $3);
540 | expr INVCALL compsym
'(' ')' {
541 /* function call with only an inverse argument */
542 $$
= INS2
(L
"EXECSYM", $3, INS1
(L
"ILIST", $1));
544 | expr INVCALL compsym
'(' list
')' {
545 /* function call with inverse argument and other ones */
546 $$
= INS2
(L
"EXECSYM", $3, INS2
(L
"ILIST", $1, $5));
548 | AMPERSAND compsym
'(' ')' {
549 /* function call as a new thread (without args) */
550 $$
= INS1
(L
"THREADSYM", $2);
552 | AMPERSAND compsym
'(' list
')' {
553 /* function call as a new thread (with args) */
554 $$
= INS2
(L
"THREADSYM", $2, $4);
557 /* simple assignation */
558 $$
= INS2
(L
"ASSIGN", $1, $3);
565 void yyerror(char *s
)
569 snprintf
(tmp
, sizeof
(tmp
), "%s in %s, line %d",
570 s
, mpsl_filename
, mpsl_line
+ 1);
572 mpsl_error
(MPDM_MBS
(tmp
));
576 static FILE *inc_fopen
(const char *filename
, mpdm_t inc
)
577 /* loads filename, searching in INC if not directly accesible */
583 /* loop through INC, prepending each path
585 for
(n
= 0; n
< mpdm_size
(inc
); n
++) {
586 mpdm_t v
= mpdm_aget
(inc
, n
);
588 v
= mpdm_ref
(MPDM_2MBS
(v
->data
));
589 snprintf
(tmp
, sizeof
(tmp
), "%s/%s", (char *) v
->data
, filename
);
592 if
((f
= fopen
(tmp
, "r")) != NULL
)
600 static mpdm_t do_parse
(const char *filename
, wchar_t * code
, FILE * file
)
601 /* calls yyparse() after doing some initialisations, and returns
602 the compiled code as an executable value */
610 /* reset last bytecode */
611 mpsl_bytecode
= NULL
;
614 mpsl_next_char
= code
;
617 if
(mpsl_filename
!= NULL
)
620 mpsl_filename
= strdup
(filename
);
622 /* cache some values */
623 v
= mpdm_hget_s
(mpdm_root
(), L
"MPSL");
624 mpsl_opcodes
= mpdm_hget_s
(v
, L
"OPCODE");
625 mpsl_lc
= mpdm_hget_s
(v
, L
"LC");
628 if
(yyparse() == 0 && mpsl_bytecode
!= NULL
)
629 x
= mpsl_x
(mpsl_bytecode
, NULL
, 1);
631 /* clean back cached values */
640 * mpsl_compile - Compiles a string of MPSL code.
641 * @code: A value containing a string of MPSL code
643 * Compiles a string of MPSL code and returns an mpdm value executable
644 * by mpdm_exec(). If there is a syntax (or other type) error, NULL
645 * is returned instead.
647 mpdm_t mpsl_compile
(mpdm_t code
)
652 x
= do_parse
("<INLINE>", (wchar_t *) code
->data
, NULL
);
660 * mpsl_compile_file - Compiles a file of MPSL code.
661 * @file: File stream or file name.
662 * @inc: search path for source files.
664 * Compiles a source file of MPSL code and returns an mpdm value
665 * executable by mpdm_exec(). If @file is an MPSL file descriptor,
666 * it's read as is and compiled; otherwise, it's assumed to be a
667 * file name, that will be searched for in any of the paths defined
668 * in the @inc array. If the file cannot be found
669 * or there is any other error, NULL is returned instead.
671 mpdm_t mpsl_compile_file
(mpdm_t file
, mpdm_t inc
)
676 const char *filename
= NULL
;
681 if
((f
= mpdm_get_filehandle
(file
)) != NULL
) {
688 /* it's a filename; open it */
689 v
= mpdm_ref
(MPDM_2MBS
(file
->data
));
693 if
((f
= inc_fopen
(filename
, inc
)) == NULL
) {
696 snprintf
(tmp
, sizeof
(tmp
) - 1,
697 "File '%s' not found in INC", filename
);
698 mpsl_error
(MPDM_MBS
(tmp
));
707 x
= do_parse
(filename
, NULL
, f
);
719 * mpsl_eval - Evaluates MSPL code.
720 * @code: A value containing a string of MPSL code, or executable code
721 * @args: optional arguments for @code
722 * @ctxt: context for @code
724 * Evaluates a piece of code. The @code can be a string containing MPSL source
725 * code (that will be compiled) or a direct executable value. If the compilation
726 * or the execution gives an error, the ERROR variable will be set to a printable
727 * value and NULL returned. Otherwise, the exit value from the code is returned
728 * and ERROR set to NULL. The abort flag is reset on exit.
730 mpdm_t mpsl_eval
(mpdm_t code
, mpdm_t args
, mpdm_t ctxt
)
740 /* if code is not executable, try to compile */
741 if
(!MPDM_IS_EXEC
(code
)) {
744 /* get the eval cache */
745 if
((c
= mpdm_hget_s
(mpdm_root
(), L
"__EVAL__")) == NULL
)
746 c
= mpdm_hset_s
(mpdm_root
(), L
"__EVAL__", MPDM_H
(0));
748 /* this code still not compiled? do it */
749 if
((cs
= mpdm_hget
(c
, code
)) == NULL
)
750 cs
= mpdm_hset
(c
, code
, mpsl_compile
(code
));
755 /* execute, if possible */
756 if
(MPDM_IS_EXEC
(cs
))
757 r
= mpdm_exec
(cs
, args
, ctxt
);
761 /* reset the abort flag */