New stress test for future foreach() local iterator.
[mpsl.git] / mpsl.y
blobab4d6e62210d09328a42e27490779d2cc109481e
1 %{
2 /*
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
27 #include <stdio.h>
28 #include <string.h>
29 #include <wchar.h>
30 #include "mpdm.h"
31 #include "mpsl.h"
33 /*******************
34 Data
35 ********************/
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;
46 /* line number */
47 extern int mpsl_line;
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;
58 /*******************
59 Code
60 ********************/
62 int yylex(void);
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)));
82 %union {
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
89 %nonassoc IFI
90 %nonassoc ELSE
92 %left BOOLAND BOOLOR
93 %left INC DEC IADD ISUB IMUL IDIV IMOD
94 %left '!'
95 %left STRCAT STREQ NUMEQ STRNE NUMNE NUMGE NUMLE HASHPAIR RANGE '>''<'
96 %left BITAND
97 %left BITOR BITXOR
98 %left '+' '-'
99 %left '*' '/' MOD
100 %nonassoc UMINUS
102 %type <ins> stmt expr sym_list stmt_list list hash compsym
106 program:
107 function { ; }
110 function:
111 function stmt_list {
112 mpsl_bytecode = $2;
114 | /* NULL */
117 stmt:
118 ';' {
119 /* null instruction */
120 $$ = INS0(L"MULTI");
122 | expr ';' {
123 /* expression, as is */
124 $$ = $1;
127 | WHILE '(' expr ')' stmt
129 /* while loop */
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,
146 without arguments */
147 $$ = INS2(L"ASSIGN", $2,
148 INS1(L"LITERAL",
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,
158 INS1(L"LITERAL",
159 mpsl_x($6, NULL, 1)));
162 | SUB compsym '(' sym_list ')' '{' stmt_list '}'
164 /* subroutine definition,
165 with arguments */
166 $$ = INS2(L"ASSIGN", $2,
167 INS1(L"LITERAL",
168 mpsl_x($7, $4, 1)));
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",
181 INS2(L"MULTI",
182 INS1(L"LOCAL", $4),
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 */
202 $$ = INS2(L"MULTI",
203 INS1(L"LOCAL",
204 INS1(L"LITERAL", $2)),
205 INS2(L"ASSIGN",
206 INS1(L"LITERAL", $2),$4)
209 | BREAK ';' {
210 /* break (exit from loop) */
211 $$ = INS0(L"BREAK");
213 | RETURN expr ';' {
214 /* return from subroutine */
215 $$ = INS1(L"RETURN", $2);
217 | RETURN ';' {
218 /* return from subroutine (void) */
219 $$ = INS0(L"RETURN");
223 stmt_list:
224 stmt { $$ = $1; }
225 | stmt_list stmt {
226 /* sequence of instructions */
227 $$ = INS2(L"MULTI", $1, $2);
231 list:
232 expr {
233 $$ = INS1(L"LIST", $1);
235 | list ',' expr {
236 /* build list from list of
237 instructions */
238 $$ = INS2(L"LIST", $3, $1);
242 sym_list:
243 SYMBOL {
244 $$ = INS1(L"LIST",
245 INS1(L"LITERAL", $1));
247 | sym_list ',' SYMBOL {
248 /* comma-separated list of symbols */
249 $$ = INS2(L"LIST",
250 INS1(L"LITERAL", $3), $1);
254 hash:
255 expr HASHPAIR expr {
256 $$ = INS2(L"HASH", $1, $3);
258 | hash ',' expr HASHPAIR expr
260 /* build hash from list of
261 instructions */
262 $$ = INS3(L"HASH", $3, $5, $1);
266 compsym:
267 SYMBOL {
268 $$ = INS1(L"LIST",
269 INS1(L"LITERAL", $1));
271 | compsym '.' INTEGER {
272 /* a.5 compound symbol */
273 $$ = INS2(L"LIST",
274 INS1(L"LITERAL", $3), $1);
276 | compsym '.' SYMBOL {
277 /* a.b compound symbol */
278 $$ = INS2(L"LIST",
279 INS1(L"LITERAL", $3), $1);
281 | compsym '[' expr ']' {
282 /* a["b"] or a[5] compound symbol */
283 $$ = INS2(L"LIST", $3, $1);
287 expr:
288 INTEGER {
289 /* literal integer */
290 $$ = INS1(L"LITERAL", $1);
292 | STRING {
293 /* literal string */
294 $$ = INS1(L"LITERAL", $1);
296 | REAL {
297 /* literal real number */
298 $$ = INS1(L"LITERAL", $1);
300 | compsym {
301 /* compound symbol */
302 $$ = INS1(L"SYMVAL", $1);
304 | NULLV {
305 /* NULL value */
306 $$ = INS1(L"LITERAL", NULL);
309 | '-' expr %prec UMINUS {
310 /* unary minus */
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); }
321 /* bit operations */
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); }
337 | '!' expr {
338 /* boolean not */
339 $$ = INS1(L"NOT", $2);
341 | expr '<' expr {
342 /* bool less than */
343 $$ = INS2(L"NUMLT", $1, $3);
345 | expr '>' expr {
346 /* bool greater than */
347 $$ = INS2(L"NUMGT", $1, $3);
349 | expr NUMLE expr {
350 /* bool less or equal than */
351 $$ = INS2(L"NUMLE", $1, $3);
353 | expr NUMGE expr {
354 /* bool greater or equal than */
355 $$ = INS2(L"NUMGE", $1, $3);
357 | expr NUMEQ expr {
358 /* bool numeric equal */
359 $$ = INS2(L"NUMEQ", $1, $3);
361 | expr NUMNE expr {
362 /* bool numeric non-equal */
363 $$ = INS1(L"NOT",
364 INS2(L"NUMEQ", $1, $3));
367 | expr STRCAT expr {
368 /* string concatenation */
369 $$ = INS2(L"STRCAT", $1, $3);
371 | expr STREQ expr {
372 /* bool string equal */
373 $$ = INS2(L"STREQ", $1, $3);
375 | expr STRNE expr {
376 /* bool string non-equal */
377 $$ = INS1(L"NOT",
378 INS2(L"STREQ", $1, $3));
381 | expr BOOLAND expr {
382 /* boolean and */
383 $$ = INS2(L"AND", $1, $3);
385 | expr BOOLOR expr {
386 /* boolean or */
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));
401 | '(' expr ')' {
402 /* parenthesized expression */
403 $$ = $2;
406 | '[' ']' {
407 /* empty list */
408 $$ = INS1(L"LITERAL", MPDM_A(0));
410 | '[' list ']' {
411 /* non-empty list */
412 $$ = $2;
414 | '[' expr RANGE expr ']'
416 /* build range from expressions */
417 $$ = INS2(L"RANGE", $2, $4);
420 | '{' '}' {
421 /* empty hash */
422 $$ = INS1(L"LITERAL", MPDM_H(0));
424 | '{' hash '}' {
425 /* non-empty hash */
426 $$ = $2;
429 | compsym '(' ')' {
430 /* function call (without args) */
431 $$ = INS1(L"EXECSYM", $1);
433 | compsym '(' list ')' {
434 /* function call (with args) */
435 $$ = INS2(L"EXECSYM", $1, $3);
437 | compsym '=' expr {
438 /* simple assignation */
439 $$ = INS2(L"ASSIGN", $1, $3);
446 void yyerror(char * s)
448 char tmp[1024];
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 */
460 FILE * f = NULL;
461 char tmp[1024];
462 int n;
464 /* loop through INC, prepending each path
465 to the filename */
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)
474 break;
477 return (f);
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 */
485 mpdm_t v;
486 mpdm_t x = NULL;
488 /* first line */
489 mpsl_line = 0;
491 /* reset last bytecode */
492 mpsl_bytecode = NULL;
494 /* set globals */
495 mpsl_next_char = code;
496 mpsl_file = file;
498 if (mpsl_filename != NULL)
499 free(mpsl_filename);
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");
508 /* compile! */
509 if (yyparse() == 0 && mpsl_bytecode != NULL)
510 x = mpsl_x(mpsl_bytecode, NULL, 1);
512 /* clean back cached values */
513 mpsl_opcodes = NULL;
514 mpsl_lc = NULL;
516 return (x);
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)
530 mpdm_t x = NULL;
532 mpdm_ref(code);
533 x = do_parse("<INLINE>", (wchar_t *) code->data, NULL);
534 mpdm_unref(code);
536 return (x);
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)
554 mpdm_t x = NULL;
555 FILE * f = NULL;
556 char * filename = NULL;
558 if ((f = mpdm_get_filehandle(file)) != NULL) {
559 filename = "<FILE>";
561 else {
562 mpdm_t inc = mpsl_get_symbol(MPDM_LS(L"INC"));
564 /* it's a filename; open it */
565 file = MPDM_2MBS(file->data);
567 filename = file->data;
569 if ((f = inc_fopen(filename, inc)) == NULL) {
570 char tmp[128];
572 snprintf(tmp, sizeof(tmp) - 1,
573 "File '%s' not found in INC",
574 filename);
575 mpsl_error(MPDM_MBS(tmp));
577 return (NULL);
580 file = MPDM_F(f);
583 x = do_parse(filename, NULL, f);
585 mpdm_close(file);
587 return (x);
592 * mpsl_eval - Evaluates MSPL code.
593 * @code: A value containing a string of MPSL code, or executable code
594 * @args: optional arguments for @code
596 * Evaluates a piece of code. The @code can be a string containing MPSL source
597 * code (that will be compiled) or a direct executable value. If the compilation
598 * or the execution gives an error, the ERROR variable will be set to a printable
599 * value and NULL returned. Otherwise, the exit value from the code is returned
600 * and ERROR set to NULL. The abort flag is reset on exit.
602 mpdm_t mpsl_eval(mpdm_t code, mpdm_t args)
604 mpdm_t v = NULL;
606 /* reset error */
607 mpsl_error(NULL);
608 mpsl_abort = 0;
610 /* if code is not executable, try to compile */
611 if (!MPDM_IS_EXEC(code))
612 code = mpsl_compile(code);
614 /* execute, if possible */
615 if (MPDM_IS_EXEC(code))
616 v = mpdm_exec(code, args);
618 /* reset the abort flag */
619 mpsl_abort = 0;
621 return (v);