If random() uses a 0 argument, it restarts the seed and returns 0.
[mpsl.git] / mpsl.y
blob117efb52c0ae3bf253f1552869d3f2010c5c2851
1 %{
2 /*
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
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));
80 static void compiler_warning(char * str)
82 fprintf(stderr, "WARNING: %s.\n", str);
88 %union {
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
95 %nonassoc IFI
96 %nonassoc ELSE
98 %left BOOLAND BOOLOR
99 %left INC DEC IADD ISUB IMUL IDIV IMOD IBITAND IBITOR IBITXOR ISHR ISHL
100 %left '!'
101 %left STRCAT STREQ NUMEQ STRNE NUMNE NUMGE NUMLE HASHPAIR RANGE '>''<' INVCALL
102 %left BITAND
103 %left BITOR BITXOR
104 %left SHL SHR
105 %left '+' '-'
106 %left '*' '/' MOD
107 %nonassoc UMINUS
109 %type <ins> stmt expr sym_list stmt_list list hash compsym
113 program:
114 function { ; }
117 function:
118 function stmt_list {
119 mpsl_bytecode = $2;
121 | /* NULL */
124 stmt:
125 ';' {
126 /* null instruction */
127 $$ = INS0(L"MULTI");
129 | expr ';' {
130 /* expression, as is */
131 $$ = $1;
134 | WHILE '(' expr ')' stmt
136 /* while loop */
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,
153 without arguments */
154 $$ = INS2(L"ASSIGN", $2,
155 INS1(L"LITERAL",
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,
165 INS1(L"LITERAL",
166 mpsl_x($6, NULL, 1)));
169 | SUB compsym '(' sym_list ')' '{' stmt_list '}'
171 /* subroutine definition,
172 with arguments */
173 $$ = INS2(L"ASSIGN", $2,
174 INS1(L"LITERAL",
175 mpsl_x($7, $4, 1)));
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",
184 INS2(L"MULTI",
185 INS1(L"LOCAL", $3),
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",
195 INS2(L"MULTI",
196 INS1(L"LOCAL", $4),
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 */
216 $$ = INS2(L"MULTI",
217 INS1(L"LOCAL",
218 INS1(L"LITERAL", $2)),
219 INS2(L"ASSIGN",
220 INS1(L"LITERAL", $2),$4)
223 | BREAK ';' {
224 /* break (exit from loop) */
225 $$ = INS0(L"BREAK");
227 | RETURN expr ';' {
228 /* return from subroutine */
229 $$ = INS1(L"RETURN", $2);
231 | RETURN ';' {
232 /* return from subroutine (void) */
233 $$ = INS0(L"RETURN");
237 stmt_list:
238 stmt { $$ = $1; }
239 | stmt_list stmt {
240 /* sequence of instructions */
241 $$ = INS2(L"MULTI", $1, $2);
245 list:
246 expr {
247 $$ = INS1(L"LIST", $1);
249 | list ',' expr {
250 /* build list from list of
251 instructions */
252 $$ = INS2(L"LIST", $3, $1);
256 sym_list:
257 SYMBOL {
258 $$ = INS1(L"LIST",
259 INS1(L"LITERAL", $1));
261 | sym_list ',' SYMBOL {
262 /* comma-separated list of symbols */
263 $$ = INS2(L"LIST",
264 INS1(L"LITERAL", $3), $1);
268 hash:
269 expr HASHPAIR expr {
270 $$ = INS2(L"HASH", $1, $3);
272 | hash ',' expr HASHPAIR expr
274 /* build hash from list of
275 instructions */
276 $$ = INS3(L"HASH", $3, $5, $1);
280 compsym:
281 SYMBOL {
282 $$ = INS1(L"LIST",
283 INS1(L"LITERAL", $1));
285 | compsym '.' INTEGER {
286 /* a.5 compound symbol */
287 $$ = INS2(L"LIST",
288 INS1(L"LITERAL", $3), $1);
290 | compsym '.' SYMBOL {
291 /* a.b compound symbol */
292 $$ = INS2(L"LIST",
293 INS1(L"LITERAL", $3), $1);
295 | compsym '[' expr ']' {
296 /* a["b"] or a[5] compound symbol */
297 $$ = INS2(L"LIST", $3, $1);
301 expr:
302 INTEGER {
303 /* literal integer */
304 $$ = INS1(L"LITERAL", $1);
306 | STRING {
307 /* literal string */
308 $$ = INS1(L"LITERAL", $1);
310 | REAL {
311 /* literal real number */
312 $$ = INS1(L"LITERAL", $1);
314 | compsym {
315 /* compound symbol */
316 $$ = INS1(L"SYMVAL", $1);
318 | NULLV {
319 /* NULL value */
320 $$ = INS1(L"LITERAL", NULL);
323 | '-' expr %prec UMINUS {
324 /* unary minus */
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); }
335 /* bit operations */
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,
344 INS2(L"ADD",
345 INS1(L"SYMVAL", $2),
346 INS1(L"LITERAL", MPDM_I(1))
350 | DEC compsym { $$ = INS2(L"ASSIGN", $2,
351 INS2(L"SUB",
352 INS1(L"SYMVAL", $2),
353 INS1(L"LITERAL", MPDM_I(1))
358 /* increment and decrement (suffix) */
359 | compsym INC { $$ = INS2(L"IMULTI",
360 INS1(L"SYMVAL", $1),
361 INS2(L"ASSIGN", $1,
362 INS2(L"ADD",
363 INS1(L"SYMVAL", $1),
364 INS1(L"LITERAL", MPDM_I(1))
369 | compsym DEC { $$ = INS2(L"IMULTI",
370 INS1(L"SYMVAL", $1),
371 INS2(L"ASSIGN", $1,
372 INS2(L"SUB",
373 INS1(L"SYMVAL", $1),
374 INS1(L"LITERAL", MPDM_I(1))
380 /* immediate math operations */
381 | compsym IADD expr { $$ = INS2(L"ASSIGN", $1,
382 INS2(L"ADD",
383 INS1(L"SYMVAL", $1),
387 | compsym ISUB expr { $$ = INS2(L"ASSIGN", $1,
388 INS2(L"SUB",
389 INS1(L"SYMVAL", $1),
393 | compsym IMUL expr { $$ = INS2(L"ASSIGN", $1,
394 INS2(L"MUL",
395 INS1(L"SYMVAL", $1),
399 | compsym IDIV expr { $$ = INS2(L"ASSIGN", $1,
400 INS2(L"DIV",
401 INS1(L"SYMVAL", $1),
405 | compsym IMOD expr { $$ = INS2(L"ASSIGN", $1,
406 INS2(L"MOD",
407 INS1(L"SYMVAL", $1),
411 | compsym IBITAND expr { $$ = INS2(L"ASSIGN", $1,
412 INS2(L"BITAND",
413 INS1(L"SYMVAL", $1),
417 | compsym IBITOR expr { $$ = INS2(L"ASSIGN", $1,
418 INS2(L"BITOR",
419 INS1(L"SYMVAL", $1),
423 | compsym IBITXOR expr { $$ = INS2(L"ASSIGN", $1,
424 INS2(L"BITXOR",
425 INS1(L"SYMVAL", $1),
429 | compsym ISHL expr { $$ = INS2(L"ASSIGN", $1,
430 INS2(L"SHL",
431 INS1(L"SYMVAL", $1),
435 | compsym ISHR expr { $$ = INS2(L"ASSIGN", $1,
436 INS2(L"SHR",
437 INS1(L"SYMVAL", $1),
442 | '!' expr {
443 /* boolean not */
444 $$ = INS1(L"NOT", $2);
446 | expr '<' expr {
447 /* bool less than */
448 $$ = INS2(L"NUMLT", $1, $3);
450 | expr '>' expr {
451 /* bool greater than */
452 $$ = INS2(L"NUMGT", $1, $3);
454 | expr NUMLE expr {
455 /* bool less or equal than */
456 $$ = INS2(L"NUMLE", $1, $3);
458 | expr NUMGE expr {
459 /* bool greater or equal than */
460 $$ = INS2(L"NUMGE", $1, $3);
462 | expr NUMEQ expr {
463 /* bool numeric equal */
464 $$ = INS2(L"NUMEQ", $1, $3);
466 | expr NUMNE expr {
467 /* bool numeric non-equal */
468 $$ = INS1(L"NOT",
469 INS2(L"NUMEQ", $1, $3));
472 | expr STRCAT expr {
473 /* string concatenation */
474 $$ = INS2(L"STRCAT", $1, $3);
476 | expr STREQ expr {
477 /* bool string equal */
478 $$ = INS2(L"STREQ", $1, $3);
480 | expr STRNE expr {
481 /* bool string non-equal */
482 $$ = INS1(L"NOT",
483 INS2(L"STREQ", $1, $3));
486 | expr BOOLAND expr {
487 /* boolean and */
488 $$ = INS2(L"AND", $1, $3);
490 | expr BOOLOR expr {
491 /* boolean or */
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));
506 | '(' expr ')' {
507 /* parenthesized expression */
508 $$ = $2;
511 | '[' ']' {
512 /* empty list */
513 $$ = INS1(L"LITERAL", MPDM_A(0));
515 | '[' list ']' {
516 /* non-empty list */
517 $$ = $2;
519 | '[' expr RANGE expr ']'
521 /* build range from expressions */
522 $$ = INS2(L"RANGE", $2, $4);
525 | '{' '}' {
526 /* empty hash */
527 $$ = INS1(L"LITERAL", MPDM_H(0));
529 | '{' hash '}' {
530 /* non-empty hash */
531 $$ = $2;
534 | compsym '(' ')' {
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));
550 | compsym '=' expr {
551 /* simple assignation */
552 $$ = INS2(L"ASSIGN", $1, $3);
559 void yyerror(char * s)
561 char tmp[1024];
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 */
573 FILE * f = NULL;
574 char tmp[1024];
575 int n;
577 /* loop through INC, prepending each path
578 to the filename */
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)
587 break;
590 return f;
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 */
598 mpdm_t v;
599 mpdm_t x = NULL;
601 /* first line */
602 mpsl_line = 0;
604 /* reset last bytecode */
605 mpsl_bytecode = NULL;
607 /* set globals */
608 mpsl_next_char = code;
609 mpsl_file = file;
611 if (mpsl_filename != NULL)
612 free(mpsl_filename);
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");
621 /* compile! */
622 if (yyparse() == 0 && mpsl_bytecode != NULL)
623 x = mpsl_x(mpsl_bytecode, NULL, 1);
625 /* clean back cached values */
626 mpsl_opcodes = NULL;
627 mpsl_lc = NULL;
629 return x;
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)
643 mpdm_t x = NULL;
645 mpdm_ref(code);
646 x = do_parse("<INLINE>", (wchar_t *) code->data, NULL);
647 mpdm_unref(code);
649 return x;
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)
667 mpdm_t x = NULL;
668 FILE * f = NULL;
669 const char * filename = NULL;
671 if ((f = mpdm_get_filehandle(file)) != NULL) {
672 filename = "<FILE>";
674 else {
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) {
683 char tmp[128];
685 snprintf(tmp, sizeof(tmp) - 1,
686 "File '%s' not found in INC",
687 filename);
688 mpsl_error(MPDM_MBS(tmp));
690 return (NULL);
693 file = MPDM_F(f);
696 x = do_parse(filename, NULL, f);
698 mpdm_close(file);
700 return x;
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)
717 mpdm_t v = NULL;
719 /* reset error */
720 mpsl_error(NULL);
721 mpsl_abort = 0;
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 */
732 mpsl_abort = 0;
734 return v;