4 MPSL - Minimum Profit Scripting Language
5 Copyright (C) 2003/2009 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
37 /* the bytecode being generated */
38 static mpdm_t mpsl_bytecode
= NULL
;
40 /* pointer to source code being compiled */
41 extern
wchar_t * mpsl_next_char
;
43 /* pointer to file being compiled */
44 extern
FILE * mpsl_file
;
49 /* compiled filename (for errors) */
50 static char * mpsl_filename
= NULL
;
52 /* cached value MPSL.OPCODE */
53 extern mpdm_t mpsl_opcodes
;
55 /* cached value MPSL.LC */
56 extern mpdm_t mpsl_lc
;
63 void yyerror(char * s
);
65 #define INS0(o) mpsl_mkins(o, 0, NULL, NULL, NULL)
66 #define INS1(o,a1) mpsl_mkins(o, 1, a1, NULL, NULL)
67 #define INS2(o,a1,a2) mpsl_mkins(o, 2, a1, a2, NULL)
68 #define INS3(o,a1,a2,a3) mpsl_mkins(o, 3, a1, a2, a3)
70 static mpdm_t mpsl_x
(mpdm_t a1
, mpdm_t a2
, int sf
)
71 /* creates an executable value with the MPSL executor as the first
72 argument and a compiled stream as the second */
74 return MPDM_X2
(mpsl_exec_p
,
75 mpsl_mkins
(sf ? L
"SUBFRAME" : L
"BLKFRAME",
76 a2
== NULL ?
1 : 2, a1
, a2
, NULL
));
80 static void compiler_warning
(char * str
)
82 fprintf
(stderr
, "WARNING: %s.\n", str
);
89 mpdm_t v
; /* a simple value */
90 mpdm_t ins
; /* an 'instruction': [ opcode, args ] */
93 %token
<v
> NULLV INTEGER REAL STRING SYMBOL LITERAL
94 %token WHILE IF SUB FOREACH LOCAL BREAK RETURN
99 %left INC DEC IADD ISUB IMUL IDIV IMOD IBITAND IBITOR IBITXOR ISHR ISHL
101 %left STRCAT STREQ NUMEQ STRNE NUMNE NUMGE NUMLE HASHPAIR RANGE
'>''<' INVCALL
109 %type
<ins
> stmt expr sym_list stmt_list list hash compsym
126 /* null instruction */
130 /* expression, as is */
134 | WHILE
'(' expr
')' stmt
137 $$
= INS2
(L
"WHILE", $3, $5);
139 | IF
'(' expr
')' stmt %prec IFI
141 /* if - then construction */
142 $$
= INS2
(L
"IF", $3, $5);
144 | IF
'(' expr
')' stmt ELSE stmt
146 /* if - then - else construction */
147 $$
= INS3
(L
"IF", $3, $5, $7);
150 | SUB compsym
'{' stmt_list
'}'
152 /* subroutine definition,
154 $$
= INS2
(L
"ASSIGN", $2,
156 mpsl_x
($4, NULL
, 1)));
159 | SUB compsym
'(' ')' '{' stmt_list
'}'
161 /* subroutine definition,
162 without arguments (second
163 syntax, including parens) */
164 $$
= INS2
(L
"ASSIGN", $2,
166 mpsl_x
($6, NULL
, 1)));
169 | SUB compsym
'(' sym_list
')' '{' stmt_list
'}'
171 /* subroutine definition,
173 $$
= INS2
(L
"ASSIGN", $2,
178 | FOREACH
'(' compsym
',' expr
')' stmt
180 /* foreach construction */
181 /* a block frame is created, the iterator
182 created as local, and the foreach executed */
183 $$
= INS1
(L
"BLKFRAME",
186 INS3
(L
"FOREACH", $3, $5, $7)
190 | FOREACH
'(' LOCAL compsym
',' expr
')' stmt
192 compiler_warning
("useless use of local in foreach loop");
194 $$
= INS1
(L
"BLKFRAME",
197 INS3
(L
"FOREACH", $4, $6, $8)
202 |
'{' stmt_list
'}' {
203 /* block of instructions,
204 with local symbol table */
205 $$
= INS1
(L
"BLKFRAME", $2);
208 | LOCAL sym_list
';' {
209 /* local symbol creation */
210 $$
= INS1
(L
"LOCAL", $2);
212 | LOCAL SYMBOL
'=' expr
';'
214 /* contraction; local symbol
215 creation and assignation */
218 INS1
(L
"LITERAL", $2)),
220 INS1
(L
"LITERAL", $2),$4)
224 /* break (exit from loop) */
228 /* return from subroutine */
229 $$
= INS1
(L
"RETURN", $2);
232 /* return from subroutine (void) */
233 $$
= INS0
(L
"RETURN");
240 /* sequence of instructions */
241 $$
= INS2
(L
"MULTI", $1, $2);
247 $$
= INS1
(L
"LIST", $1);
250 /* build list from list of
252 $$
= INS2
(L
"LIST", $3, $1);
259 INS1
(L
"LITERAL", $1));
261 | sym_list
',' SYMBOL
{
262 /* comma-separated list of symbols */
264 INS1
(L
"LITERAL", $3), $1);
270 $$
= INS2
(L
"HASH", $1, $3);
272 | hash
',' expr HASHPAIR expr
274 /* build hash from list of
276 $$
= INS3
(L
"HASH", $3, $5, $1);
283 INS1
(L
"LITERAL", $1));
285 | compsym
'.' INTEGER
{
286 /* a.5 compound symbol */
288 INS1
(L
"LITERAL", $3), $1);
290 | compsym
'.' SYMBOL
{
291 /* a.b compound symbol */
293 INS1
(L
"LITERAL", $3), $1);
295 | compsym
'[' expr
']' {
296 /* a["b"] or a[5] compound symbol */
297 $$
= INS2
(L
"LIST", $3, $1);
303 /* literal integer */
304 $$
= INS1
(L
"LITERAL", $1);
308 $$
= INS1
(L
"LITERAL", $1);
311 /* literal real number */
312 $$
= INS1
(L
"LITERAL", $1);
315 /* compound symbol */
316 $$
= INS1
(L
"SYMVAL", $1);
320 $$
= INS1
(L
"LITERAL", NULL
);
323 |
'-' expr %prec UMINUS
{
325 $$
= INS1
(L
"UMINUS", $2);
328 /* math operations */
329 | expr
'+' expr
{ $$
= INS2
(L
"ADD", $1, $3); }
330 | expr
'-' expr
{ $$
= INS2
(L
"SUB", $1, $3); }
331 | expr
'*' expr
{ $$
= INS2
(L
"MUL", $1, $3); }
332 | expr
'/' expr
{ $$
= INS2
(L
"DIV", $1, $3); }
333 | expr MOD expr
{ $$
= INS2
(L
"MOD", $1, $3); }
336 | expr BITAND expr
{ $$
= INS2
(L
"BITAND", $1, $3); }
337 | expr BITOR expr
{ $$
= INS2
(L
"BITOR", $1, $3); }
338 | expr BITXOR expr
{ $$
= INS2
(L
"BITXOR", $1, $3); }
339 | expr SHL expr
{ $$
= INS2
(L
"SHL", $1, $3); }
340 | expr SHR expr
{ $$
= INS2
(L
"SHR", $1, $3); }
342 /* increment and decrement (prefix) */
343 | INC compsym
{ $$
= INS2
(L
"ASSIGN", $2,
346 INS1
(L
"LITERAL", MPDM_I
(1))
350 | DEC compsym
{ $$
= INS2
(L
"ASSIGN", $2,
353 INS1
(L
"LITERAL", MPDM_I
(1))
358 /* increment and decrement (suffix) */
359 | compsym INC
{ $$
= INS2
(L
"IMULTI",
364 INS1
(L
"LITERAL", MPDM_I
(1))
369 | compsym DEC
{ $$
= INS2
(L
"IMULTI",
374 INS1
(L
"LITERAL", MPDM_I
(1))
380 /* immediate math operations */
381 | compsym IADD expr
{ $$
= INS2
(L
"ASSIGN", $1,
387 | compsym ISUB expr
{ $$
= INS2
(L
"ASSIGN", $1,
393 | compsym IMUL expr
{ $$
= INS2
(L
"ASSIGN", $1,
399 | compsym IDIV expr
{ $$
= INS2
(L
"ASSIGN", $1,
405 | compsym IMOD expr
{ $$
= INS2
(L
"ASSIGN", $1,
411 | compsym IBITAND expr
{ $$
= INS2
(L
"ASSIGN", $1,
417 | compsym IBITOR expr
{ $$
= INS2
(L
"ASSIGN", $1,
423 | compsym IBITXOR expr
{ $$
= INS2
(L
"ASSIGN", $1,
429 | compsym ISHL expr
{ $$
= INS2
(L
"ASSIGN", $1,
435 | compsym ISHR expr
{ $$
= INS2
(L
"ASSIGN", $1,
444 $$
= INS1
(L
"NOT", $2);
448 $$
= INS2
(L
"NUMLT", $1, $3);
451 /* bool greater than */
452 $$
= INS2
(L
"NUMGT", $1, $3);
455 /* bool less or equal than */
456 $$
= INS2
(L
"NUMLE", $1, $3);
459 /* bool greater or equal than */
460 $$
= INS2
(L
"NUMGE", $1, $3);
463 /* bool numeric equal */
464 $$
= INS2
(L
"NUMEQ", $1, $3);
467 /* bool numeric non-equal */
469 INS2
(L
"NUMEQ", $1, $3));
473 /* string concatenation */
474 $$
= INS2
(L
"STRCAT", $1, $3);
477 /* bool string equal */
478 $$
= INS2
(L
"STREQ", $1, $3);
481 /* bool string non-equal */
483 INS2
(L
"STREQ", $1, $3));
486 | expr BOOLAND expr
{
488 $$
= INS2
(L
"AND", $1, $3);
492 $$
= INS2
(L
"OR", $1, $3);
495 | SUB
'{' stmt_list
'}' {
496 /* anonymous subroutine (without args) */
497 $$
= INS1
(L
"LITERAL", mpsl_x
($3, NULL
, 0));
500 | SUB
'(' sym_list
')' '{' stmt_list
'}'
502 /* anonymous subroutine (with args) */
503 $$
= INS1
(L
"LITERAL", mpsl_x
($6, $3, 0));
507 /* parenthesized expression */
513 $$
= INS1
(L
"LITERAL", MPDM_A
(0));
519 |
'[' expr RANGE expr
']'
521 /* build range from expressions */
522 $$
= INS2
(L
"RANGE", $2, $4);
527 $$
= INS1
(L
"LITERAL", MPDM_H
(0));
535 /* function call (without args) */
536 $$
= INS1
(L
"EXECSYM", $1);
538 | compsym
'(' list
')' {
539 /* function call (with args) */
540 $$
= INS2
(L
"EXECSYM", $1, $3);
542 | expr INVCALL compsym
'(' ')' {
543 /* function call with only an inverse argument */
544 $$
= INS2
(L
"EXECSYM", $3, INS1
(L
"ILIST", $1));
546 | expr INVCALL compsym
'(' list
')' {
547 /* function call with inverse argument and other ones */
548 $$
= INS2
(L
"EXECSYM", $3, INS2
(L
"ILIST", $1, $5));
551 /* simple assignation */
552 $$
= INS2
(L
"ASSIGN", $1, $3);
559 void yyerror(char * s
)
563 snprintf
(tmp
, sizeof
(tmp
), "%s in %s, line %d",
564 s
, mpsl_filename
, mpsl_line
+ 1);
566 mpsl_error
(MPDM_MBS
(tmp
));
570 static FILE * inc_fopen
(const char * filename
, mpdm_t inc
)
571 /* loads filename, searching in INC if not directly accesible */
577 /* loop through INC, prepending each path
579 for
(n
= 0; n
< mpdm_size
(inc
); n
++) {
580 mpdm_t v
= mpdm_aget
(inc
, n
);
582 v
= MPDM_2MBS
(v
->data
);
583 snprintf
(tmp
, sizeof
(tmp
), "%s/%s",
584 (char *)v
->data
, filename
);
586 if
((f
= fopen
(tmp
, "r")) != NULL
)
594 static mpdm_t do_parse
(const char * filename
, wchar_t * code
, FILE * file
)
595 /* calls yyparse() after doing some initialisations, and returns
596 the compiled code as an executable value */
604 /* reset last bytecode */
605 mpsl_bytecode
= NULL
;
608 mpsl_next_char
= code
;
611 if
(mpsl_filename
!= NULL
)
614 mpsl_filename
= strdup
(filename
);
616 /* cache some values */
617 v
= mpdm_hget_s
(mpdm_root
(), L
"MPSL");
618 mpsl_opcodes
= mpdm_hget_s
(v
, L
"OPCODE");
619 mpsl_lc
= mpdm_hget_s
(v
, L
"LC");
622 if
(yyparse() == 0 && mpsl_bytecode
!= NULL
)
623 x
= mpsl_x
(mpsl_bytecode
, NULL
, 1);
625 /* clean back cached values */
634 * mpsl_compile - Compiles a string of MPSL code.
635 * @code: A value containing a string of MPSL code
637 * Compiles a string of MPSL code and returns an mpdm value executable
638 * by mpdm_exec(). If there is a syntax (or other type) error, NULL
639 * is returned instead.
641 mpdm_t mpsl_compile
(mpdm_t code
)
646 x
= do_parse
("<INLINE>", (wchar_t *) code
->data
, NULL
);
654 * mpsl_compile_file - Compiles a file of MPSL code.
655 * @file: File stream or file name.
657 * Compiles a source file of MPSL code and returns an mpdm value
658 * executable by mpdm_exec(). If @file is an MPSL file descriptor,
659 * it's read as is and compiled; otherwise, it's assumed to be a
660 * file name, that will be searched for in any of the paths defined
661 * in the INC MPSL global array (take note that the current
662 * directory is NOT searched by default). If the file cannot be found
663 * or there is any other error, NULL is returned instead.
665 mpdm_t mpsl_compile_file
(mpdm_t file
)
669 const char * filename
= NULL
;
671 if
((f
= mpdm_get_filehandle
(file
)) != NULL
) {
675 mpdm_t inc
= mpsl_get_symbol
(MPDM_LS
(L
"INC"));
677 /* it's a filename; open it */
678 file
= MPDM_2MBS
(file
->data
);
680 filename
= file
->data
;
682 if
((f
= inc_fopen
(filename
, inc
)) == NULL
) {
685 snprintf
(tmp
, sizeof
(tmp
) - 1,
686 "File '%s' not found in INC",
688 mpsl_error
(MPDM_MBS
(tmp
));
696 x
= do_parse
(filename
, NULL
, f
);
705 * mpsl_eval - Evaluates MSPL code.
706 * @code: A value containing a string of MPSL code, or executable code
707 * @args: optional arguments for @code
709 * Evaluates a piece of code. The @code can be a string containing MPSL source
710 * code (that will be compiled) or a direct executable value. If the compilation
711 * or the execution gives an error, the ERROR variable will be set to a printable
712 * value and NULL returned. Otherwise, the exit value from the code is returned
713 * and ERROR set to NULL. The abort flag is reset on exit.
715 mpdm_t mpsl_eval
(mpdm_t code
, mpdm_t args
)
723 /* if code is not executable, try to compile */
724 if
(!MPDM_IS_EXEC
(code
))
725 code
= mpsl_compile
(code
);
727 /* execute, if possible */
728 if
(MPDM_IS_EXEC
(code
))
729 v
= mpdm_exec
(code
, args
);
731 /* reset the abort flag */