4 MPSL - Minimum Profit Scripting Language
5 Copyright (C) 2003/2007 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
)));
83 mpdm_t v
; /* a simple value */
84 mpdm_t ins
; /* an 'instruction': [ opcode, args ] */
87 %token
<v
> NULLV INTEGER REAL STRING SYMBOL LITERAL
88 %token WHILE IF SUB FOREACH LOCAL BREAK RETURN
93 %left INC DEC IADD ISUB IMUL IDIV IMOD
95 %left STRCAT STREQ NUMEQ STRNE NUMNE NUMGE NUMLE HASHPAIR RANGE
'>''<'
102 %type
<ins
> stmt expr sym_list stmt_list list hash compsym
119 /* null instruction */
123 /* expression, as is */
127 | WHILE
'(' expr
')' stmt
130 $$
= INS2
(L
"WHILE", $3, $5);
132 | IF
'(' expr
')' stmt %prec IFI
134 /* if - then construction */
135 $$
= INS2
(L
"IF", $3, $5);
137 | IF
'(' expr
')' stmt ELSE stmt
139 /* if - then - else construction */
140 $$
= INS3
(L
"IF", $3, $5, $7);
143 | SUB compsym
'{' stmt_list
'}'
145 /* subroutine definition,
147 $$
= INS2
(L
"ASSIGN", $2,
149 mpsl_x
($4, NULL
, 1)));
152 | SUB compsym
'(' ')' '{' stmt_list
'}'
154 /* subroutine definition,
155 without arguments (second
156 syntax, including parens) */
157 $$
= INS2
(L
"ASSIGN", $2,
159 mpsl_x
($6, NULL
, 1)));
162 | SUB compsym
'(' sym_list
')' '{' stmt_list
'}'
164 /* subroutine definition,
166 $$
= INS2
(L
"ASSIGN", $2,
171 | FOREACH
'(' compsym
',' expr
')' stmt
173 /* foreach construction */
174 $$
= INS3
(L
"FOREACH", $3, $5, $7);
176 | FOREACH
'(' LOCAL compsym
',' expr
')' stmt
178 /* foreach construction with local
179 definition of the iterator variable */
180 $$
= INS1
(L
"BLKFRAME",
183 INS3
(L
"FOREACH", $4, $6, $8)
188 |
'{' stmt_list
'}' {
189 /* block of instructions,
190 with local symbol table */
191 $$
= INS1
(L
"BLKFRAME", $2);
194 | LOCAL sym_list
';' {
195 /* local symbol creation */
196 $$
= INS1
(L
"LOCAL", $2);
198 | LOCAL SYMBOL
'=' expr
';'
200 /* contraction; local symbol
201 creation and assignation */
204 INS1
(L
"LITERAL", $2)),
206 INS1
(L
"LITERAL", $2),$4)
210 /* break (exit from loop) */
214 /* return from subroutine */
215 $$
= INS1
(L
"RETURN", $2);
218 /* return from subroutine (void) */
219 $$
= INS0
(L
"RETURN");
226 /* sequence of instructions */
227 $$
= INS2
(L
"MULTI", $1, $2);
233 $$
= INS1
(L
"LIST", $1);
236 /* build list from list of
238 $$
= INS2
(L
"LIST", $3, $1);
245 INS1
(L
"LITERAL", $1));
247 | sym_list
',' SYMBOL
{
248 /* comma-separated list of symbols */
250 INS1
(L
"LITERAL", $3), $1);
256 $$
= INS2
(L
"HASH", $1, $3);
258 | hash
',' expr HASHPAIR expr
260 /* build hash from list of
262 $$
= INS3
(L
"HASH", $3, $5, $1);
269 INS1
(L
"LITERAL", $1));
271 | compsym
'.' INTEGER
{
272 /* a.5 compound symbol */
274 INS1
(L
"LITERAL", $3), $1);
276 | compsym
'.' SYMBOL
{
277 /* a.b compound symbol */
279 INS1
(L
"LITERAL", $3), $1);
281 | compsym
'[' expr
']' {
282 /* a["b"] or a[5] compound symbol */
283 $$
= INS2
(L
"LIST", $3, $1);
289 /* literal integer */
290 $$
= INS1
(L
"LITERAL", $1);
294 $$
= INS1
(L
"LITERAL", $1);
297 /* literal real number */
298 $$
= INS1
(L
"LITERAL", $1);
301 /* compound symbol */
302 $$
= INS1
(L
"SYMVAL", $1);
306 $$
= INS1
(L
"LITERAL", NULL
);
309 |
'-' expr %prec UMINUS
{
311 $$
= INS1
(L
"UMINUS", $2);
314 /* math operations */
315 | expr
'+' expr
{ $$
= INS2
(L
"ADD", $1, $3); }
316 | expr
'-' expr
{ $$
= INS2
(L
"SUB", $1, $3); }
317 | expr
'*' expr
{ $$
= INS2
(L
"MUL", $1, $3); }
318 | expr
'/' expr
{ $$
= INS2
(L
"DIV", $1, $3); }
319 | expr MOD expr
{ $$
= INS2
(L
"MOD", $1, $3); }
322 | expr BITAND expr
{ $$
= INS2
(L
"BITAND", $1, $3); }
323 | expr BITOR expr
{ $$
= INS2
(L
"BITOR", $1, $3); }
324 | expr BITXOR expr
{ $$
= INS2
(L
"BITXOR", $1, $3); }
326 /* immediate math operations */
327 | INC compsym
{ $$
= INS1
(L
"PINC", $2); }
328 | compsym INC
{ $$
= INS1
(L
"SINC", $1); }
329 | DEC compsym
{ $$
= INS1
(L
"PDEC", $2); }
330 | compsym DEC
{ $$
= INS1
(L
"SDEC", $1); }
331 | compsym IADD expr
{ $$
= INS2
(L
"IADD", $1, $3); }
332 | compsym ISUB expr
{ $$
= INS2
(L
"ISUB", $1, $3); }
333 | compsym IMUL expr
{ $$
= INS2
(L
"IMUL", $1, $3); }
334 | compsym IDIV expr
{ $$
= INS2
(L
"IDIV", $1, $3); }
335 | compsym IMOD expr
{ $$
= INS2
(L
"IMOD", $1, $3); }
339 $$
= INS1
(L
"NOT", $2);
343 $$
= INS2
(L
"NUMLT", $1, $3);
346 /* bool greater than */
347 $$
= INS2
(L
"NUMGT", $1, $3);
350 /* bool less or equal than */
351 $$
= INS2
(L
"NUMLE", $1, $3);
354 /* bool greater or equal than */
355 $$
= INS2
(L
"NUMGE", $1, $3);
358 /* bool numeric equal */
359 $$
= INS2
(L
"NUMEQ", $1, $3);
362 /* bool numeric non-equal */
364 INS2
(L
"NUMEQ", $1, $3));
368 /* string concatenation */
369 $$
= INS2
(L
"STRCAT", $1, $3);
372 /* bool string equal */
373 $$
= INS2
(L
"STREQ", $1, $3);
376 /* bool string non-equal */
378 INS2
(L
"STREQ", $1, $3));
381 | expr BOOLAND expr
{
383 $$
= INS2
(L
"AND", $1, $3);
387 $$
= INS2
(L
"OR", $1, $3);
390 | SUB
'{' stmt_list
'}' {
391 /* anonymous subroutine (without args) */
392 $$
= INS1
(L
"LITERAL", mpsl_x
($3, NULL
, 0));
395 | SUB
'(' sym_list
')' '{' stmt_list
'}'
397 /* anonymous subroutine (with args) */
398 $$
= INS1
(L
"LITERAL", mpsl_x
($6, $3, 0));
402 /* parenthesized expression */
408 $$
= INS1
(L
"LITERAL", MPDM_A
(0));
414 |
'[' expr RANGE expr
']'
416 /* build range from expressions */
417 $$
= INS2
(L
"RANGE", $2, $4);
422 $$
= INS1
(L
"LITERAL", MPDM_H
(0));
430 /* function call (without args) */
431 $$
= INS1
(L
"EXECSYM", $1);
433 | compsym
'(' list
')' {
434 /* function call (with args) */
435 $$
= INS2
(L
"EXECSYM", $1, $3);
438 /* simple assignation */
439 $$
= INS2
(L
"ASSIGN", $1, $3);
446 void yyerror(char * s
)
450 snprintf
(tmp
, sizeof
(tmp
), "%s in %s, line %d",
451 s
, mpsl_filename
, mpsl_line
+ 1);
453 mpsl_error
(MPDM_MBS
(tmp
));
457 static FILE * inc_fopen
(char * filename
, mpdm_t inc
)
458 /* loads filename, searching in INC if not directly accesible */
464 /* loop through INC, prepending each path
466 for
(n
= 0; n
< mpdm_size
(inc
); n
++) {
467 mpdm_t v
= mpdm_aget
(inc
, n
);
469 v
= MPDM_2MBS
(v
->data
);
470 snprintf
(tmp
, sizeof
(tmp
), "%s/%s",
471 (char *)v
->data
, filename
);
473 if
((f
= fopen
(tmp
, "r")) != NULL
)
481 static mpdm_t do_parse
(char * filename
, wchar_t * code
, FILE * file
)
482 /* calls yyparse() after doing some initialisations, and returns
483 the compiled code as an executable value */
491 /* reset last bytecode */
492 mpsl_bytecode
= NULL
;
495 mpsl_next_char
= code
;
498 if
(mpsl_filename
!= NULL
)
501 mpsl_filename
= strdup
(filename
);
503 /* cache some values */
504 v
= mpdm_hget_s
(mpdm_root
(), L
"MPSL");
505 mpsl_opcodes
= mpdm_hget_s
(v
, L
"OPCODE");
506 mpsl_lc
= mpdm_hget_s
(v
, L
"LC");
509 if
(yyparse() == 0 && mpsl_bytecode
!= NULL
)
510 x
= mpsl_x
(mpsl_bytecode
, NULL
, 1);
512 /* clean back cached values */
521 * mpsl_compile - Compiles a string of MPSL code.
522 * @code: A value containing a string of MPSL code
524 * Compiles a string of MPSL code and returns an mpdm value executable
525 * by mpdm_exec(). If there is a syntax (or other type) error, NULL
526 * is returned instead.
528 mpdm_t mpsl_compile
(mpdm_t code
)
533 x
= do_parse
("<INLINE>", (wchar_t *) code
->data
, NULL
);
541 * mpsl_compile_file - Compiles a file of MPSL code.
542 * @file: File stream or file name.
544 * Compiles a source file of MPSL code and returns an mpdm value
545 * executable by mpdm_exec(). If @file is an MPSL file descriptor,
546 * it's read as is and compiled; otherwise, it's assumed to be a
547 * file name, that will be searched for in any of the paths defined
548 * in the INC MPSL global array (take note that the current
549 * directory is NOT searched by default). If the file cannot be found
550 * or there is any other error, NULL is returned instead.
552 mpdm_t mpsl_compile_file
(mpdm_t file
)
556 char * filename
= NULL
;
558 if
(file
->flags
& MPDM_FILE
) {
561 /* it's an open file; just store the stream */
562 /* FIXME: this is a hack; there should exist
563 a way to retrieve the FILE handle */
569 mpdm_t inc
= mpsl_get_symbol
(MPDM_LS
(L
"INC"));
571 /* it's a filename; open it */
572 file
= MPDM_2MBS
(file
->data
);
574 filename
= file
->data
;
576 if
((f
= inc_fopen
(filename
, inc
)) == NULL
) {
579 snprintf
(tmp
, sizeof
(tmp
) - 1,
580 "File '%s' not found in INC",
582 mpsl_error
(MPDM_MBS
(tmp
));
590 x
= do_parse
(filename
, NULL
, f
);
599 * mpsl_eval - Evaluates MSPL code.
600 * @code: A value containing a string of MPSL code, or executable code
601 * @args: optional arguments for @code
603 * Evaluates a piece of code. The @code can be a string containing MPSL source
604 * code (that will be compiled) or a direct executable value. If the compilation
605 * or the execution gives an error, the ERROR variable will be set to a printable
606 * value and NULL returned. Otherwise, the exit value from the code is returned
607 * and ERROR set to NULL. The abort flag is reset on exit.
609 mpdm_t mpsl_eval
(mpdm_t code
, mpdm_t args
)
617 /* if code is not executable, try to compile */
618 if
(!MPDM_IS_EXEC
(code
))
619 code
= mpsl_compile
(code
);
621 /* execute, if possible */
622 if
(MPDM_IS_EXEC
(code
))
623 v
= mpdm_exec
(code
, args
);
625 /* reset the abort flag */