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
101 %left STRCAT STREQ NUMEQ STRNE NUMNE NUMGE NUMLE HASHPAIR RANGE
'>''<'
108 %type
<ins
> stmt expr sym_list stmt_list list hash compsym
125 /* null instruction */
129 /* expression, as is */
133 | WHILE
'(' expr
')' stmt
136 $$
= INS2
(L
"WHILE", $3, $5);
138 | IF
'(' expr
')' stmt %prec IFI
140 /* if - then construction */
141 $$
= INS2
(L
"IF", $3, $5);
143 | IF
'(' expr
')' stmt ELSE stmt
145 /* if - then - else construction */
146 $$
= INS3
(L
"IF", $3, $5, $7);
149 | SUB compsym
'{' stmt_list
'}'
151 /* subroutine definition,
153 $$
= INS2
(L
"ASSIGN", $2,
155 mpsl_x
($4, NULL
, 1)));
158 | SUB compsym
'(' ')' '{' stmt_list
'}'
160 /* subroutine definition,
161 without arguments (second
162 syntax, including parens) */
163 $$
= INS2
(L
"ASSIGN", $2,
165 mpsl_x
($6, NULL
, 1)));
168 | SUB compsym
'(' sym_list
')' '{' stmt_list
'}'
170 /* subroutine definition,
172 $$
= INS2
(L
"ASSIGN", $2,
177 | FOREACH
'(' compsym
',' expr
')' stmt
179 /* foreach construction */
180 /* a block frame is created, the iterator
181 created as local, and the foreach executed */
182 $$
= INS1
(L
"BLKFRAME",
185 INS3
(L
"FOREACH", $3, $5, $7)
189 | FOREACH
'(' LOCAL compsym
',' expr
')' stmt
191 compiler_warning
("useless use of local in foreach loop");
193 $$
= INS1
(L
"BLKFRAME",
196 INS3
(L
"FOREACH", $4, $6, $8)
201 |
'{' stmt_list
'}' {
202 /* block of instructions,
203 with local symbol table */
204 $$
= INS1
(L
"BLKFRAME", $2);
207 | LOCAL sym_list
';' {
208 /* local symbol creation */
209 $$
= INS1
(L
"LOCAL", $2);
211 | LOCAL SYMBOL
'=' expr
';'
213 /* contraction; local symbol
214 creation and assignation */
217 INS1
(L
"LITERAL", $2)),
219 INS1
(L
"LITERAL", $2),$4)
223 /* break (exit from loop) */
227 /* return from subroutine */
228 $$
= INS1
(L
"RETURN", $2);
231 /* return from subroutine (void) */
232 $$
= INS0
(L
"RETURN");
239 /* sequence of instructions */
240 $$
= INS2
(L
"MULTI", $1, $2);
246 $$
= INS1
(L
"LIST", $1);
249 /* build list from list of
251 $$
= INS2
(L
"LIST", $3, $1);
258 INS1
(L
"LITERAL", $1));
260 | sym_list
',' SYMBOL
{
261 /* comma-separated list of symbols */
263 INS1
(L
"LITERAL", $3), $1);
269 $$
= INS2
(L
"HASH", $1, $3);
271 | hash
',' expr HASHPAIR expr
273 /* build hash from list of
275 $$
= INS3
(L
"HASH", $3, $5, $1);
282 INS1
(L
"LITERAL", $1));
284 | compsym
'.' INTEGER
{
285 /* a.5 compound symbol */
287 INS1
(L
"LITERAL", $3), $1);
289 | compsym
'.' SYMBOL
{
290 /* a.b compound symbol */
292 INS1
(L
"LITERAL", $3), $1);
294 | compsym
'[' expr
']' {
295 /* a["b"] or a[5] compound symbol */
296 $$
= INS2
(L
"LIST", $3, $1);
302 /* literal integer */
303 $$
= INS1
(L
"LITERAL", $1);
307 $$
= INS1
(L
"LITERAL", $1);
310 /* literal real number */
311 $$
= INS1
(L
"LITERAL", $1);
314 /* compound symbol */
315 $$
= INS1
(L
"SYMVAL", $1);
319 $$
= INS1
(L
"LITERAL", NULL
);
322 |
'-' expr %prec UMINUS
{
324 $$
= INS1
(L
"UMINUS", $2);
327 /* math operations */
328 | expr
'+' expr
{ $$
= INS2
(L
"ADD", $1, $3); }
329 | expr
'-' expr
{ $$
= INS2
(L
"SUB", $1, $3); }
330 | expr
'*' expr
{ $$
= INS2
(L
"MUL", $1, $3); }
331 | expr
'/' expr
{ $$
= INS2
(L
"DIV", $1, $3); }
332 | expr MOD expr
{ $$
= INS2
(L
"MOD", $1, $3); }
335 | expr BITAND expr
{ $$
= INS2
(L
"BITAND", $1, $3); }
336 | expr BITOR expr
{ $$
= INS2
(L
"BITOR", $1, $3); }
337 | expr BITXOR expr
{ $$
= INS2
(L
"BITXOR", $1, $3); }
339 /* increment and decrement (prefix) */
340 | INC compsym
{ $$
= INS2
(L
"ASSIGN", $2,
343 INS1
(L
"LITERAL", MPDM_I
(1))
347 | DEC compsym
{ $$
= INS2
(L
"ASSIGN", $2,
350 INS1
(L
"LITERAL", MPDM_I
(1))
355 /* increment and decrement (suffix) */
356 | compsym INC
{ $$
= INS2
(L
"IMULTI",
361 INS1
(L
"LITERAL", MPDM_I
(1))
366 | compsym DEC
{ $$
= INS2
(L
"IMULTI",
371 INS1
(L
"LITERAL", MPDM_I
(1))
377 /* immediate math operations */
378 | compsym IADD expr
{ $$
= INS2
(L
"ASSIGN", $1,
384 | compsym ISUB expr
{ $$
= INS2
(L
"ASSIGN", $1,
390 | compsym IMUL expr
{ $$
= INS2
(L
"ASSIGN", $1,
396 | compsym IDIV expr
{ $$
= INS2
(L
"ASSIGN", $1,
402 | compsym IMOD expr
{ $$
= INS2
(L
"ASSIGN", $1,
411 $$
= INS1
(L
"NOT", $2);
415 $$
= INS2
(L
"NUMLT", $1, $3);
418 /* bool greater than */
419 $$
= INS2
(L
"NUMGT", $1, $3);
422 /* bool less or equal than */
423 $$
= INS2
(L
"NUMLE", $1, $3);
426 /* bool greater or equal than */
427 $$
= INS2
(L
"NUMGE", $1, $3);
430 /* bool numeric equal */
431 $$
= INS2
(L
"NUMEQ", $1, $3);
434 /* bool numeric non-equal */
436 INS2
(L
"NUMEQ", $1, $3));
440 /* string concatenation */
441 $$
= INS2
(L
"STRCAT", $1, $3);
444 /* bool string equal */
445 $$
= INS2
(L
"STREQ", $1, $3);
448 /* bool string non-equal */
450 INS2
(L
"STREQ", $1, $3));
453 | expr BOOLAND expr
{
455 $$
= INS2
(L
"AND", $1, $3);
459 $$
= INS2
(L
"OR", $1, $3);
462 | SUB
'{' stmt_list
'}' {
463 /* anonymous subroutine (without args) */
464 $$
= INS1
(L
"LITERAL", mpsl_x
($3, NULL
, 0));
467 | SUB
'(' sym_list
')' '{' stmt_list
'}'
469 /* anonymous subroutine (with args) */
470 $$
= INS1
(L
"LITERAL", mpsl_x
($6, $3, 0));
474 /* parenthesized expression */
480 $$
= INS1
(L
"LITERAL", MPDM_A
(0));
486 |
'[' expr RANGE expr
']'
488 /* build range from expressions */
489 $$
= INS2
(L
"RANGE", $2, $4);
494 $$
= INS1
(L
"LITERAL", MPDM_H
(0));
502 /* function call (without args) */
503 $$
= INS1
(L
"EXECSYM", $1);
505 | compsym
'(' list
')' {
506 /* function call (with args) */
507 $$
= INS2
(L
"EXECSYM", $1, $3);
510 /* simple assignation */
511 $$
= INS2
(L
"ASSIGN", $1, $3);
518 void yyerror(char * s
)
522 snprintf
(tmp
, sizeof
(tmp
), "%s in %s, line %d",
523 s
, mpsl_filename
, mpsl_line
+ 1);
525 mpsl_error
(MPDM_MBS
(tmp
));
529 static FILE * inc_fopen
(const char * filename
, mpdm_t inc
)
530 /* loads filename, searching in INC if not directly accesible */
536 /* loop through INC, prepending each path
538 for
(n
= 0; n
< mpdm_size
(inc
); n
++) {
539 mpdm_t v
= mpdm_aget
(inc
, n
);
541 v
= MPDM_2MBS
(v
->data
);
542 snprintf
(tmp
, sizeof
(tmp
), "%s/%s",
543 (char *)v
->data
, filename
);
545 if
((f
= fopen
(tmp
, "r")) != NULL
)
553 static mpdm_t do_parse
(const char * filename
, wchar_t * code
, FILE * file
)
554 /* calls yyparse() after doing some initialisations, and returns
555 the compiled code as an executable value */
563 /* reset last bytecode */
564 mpsl_bytecode
= NULL
;
567 mpsl_next_char
= code
;
570 if
(mpsl_filename
!= NULL
)
573 mpsl_filename
= strdup
(filename
);
575 /* cache some values */
576 v
= mpdm_hget_s
(mpdm_root
(), L
"MPSL");
577 mpsl_opcodes
= mpdm_hget_s
(v
, L
"OPCODE");
578 mpsl_lc
= mpdm_hget_s
(v
, L
"LC");
581 if
(yyparse() == 0 && mpsl_bytecode
!= NULL
)
582 x
= mpsl_x
(mpsl_bytecode
, NULL
, 1);
584 /* clean back cached values */
593 * mpsl_compile - Compiles a string of MPSL code.
594 * @code: A value containing a string of MPSL code
596 * Compiles a string of MPSL code and returns an mpdm value executable
597 * by mpdm_exec(). If there is a syntax (or other type) error, NULL
598 * is returned instead.
600 mpdm_t mpsl_compile
(mpdm_t code
)
605 x
= do_parse
("<INLINE>", (wchar_t *) code
->data
, NULL
);
613 * mpsl_compile_file - Compiles a file of MPSL code.
614 * @file: File stream or file name.
616 * Compiles a source file of MPSL code and returns an mpdm value
617 * executable by mpdm_exec(). If @file is an MPSL file descriptor,
618 * it's read as is and compiled; otherwise, it's assumed to be a
619 * file name, that will be searched for in any of the paths defined
620 * in the INC MPSL global array (take note that the current
621 * directory is NOT searched by default). If the file cannot be found
622 * or there is any other error, NULL is returned instead.
624 mpdm_t mpsl_compile_file
(mpdm_t file
)
628 const char * filename
= NULL
;
630 if
((f
= mpdm_get_filehandle
(file
)) != NULL
) {
634 mpdm_t inc
= mpsl_get_symbol
(MPDM_LS
(L
"INC"));
636 /* it's a filename; open it */
637 file
= MPDM_2MBS
(file
->data
);
639 filename
= file
->data
;
641 if
((f
= inc_fopen
(filename
, inc
)) == NULL
) {
644 snprintf
(tmp
, sizeof
(tmp
) - 1,
645 "File '%s' not found in INC",
647 mpsl_error
(MPDM_MBS
(tmp
));
655 x
= do_parse
(filename
, NULL
, f
);
664 * mpsl_eval - Evaluates MSPL code.
665 * @code: A value containing a string of MPSL code, or executable code
666 * @args: optional arguments for @code
668 * Evaluates a piece of code. The @code can be a string containing MPSL source
669 * code (that will be compiled) or a direct executable value. If the compilation
670 * or the execution gives an error, the ERROR variable will be set to a printable
671 * value and NULL returned. Otherwise, the exit value from the code is returned
672 * and ERROR set to NULL. The abort flag is reset on exit.
674 mpdm_t mpsl_eval
(mpdm_t code
, mpdm_t args
)
682 /* if code is not executable, try to compile */
683 if
(!MPDM_IS_EXEC
(code
))
684 code
= mpsl_compile
(code
);
686 /* execute, if possible */
687 if
(MPDM_IS_EXEC
(code
))
688 v
= mpdm_exec
(code
, args
);
690 /* reset the abort flag */