Revert bs-a and bs-b patch.
[mpsl.git] / mpsl.y
blob51b604f2e4a5839a2437bab177923b59a1743525
1 %{
2 /*
4 MPSL - Minimum Profit Scripting Language
5 Copyright (C) 2003/2010 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 /** data **/
35 /* the bytecode being generated */
36 static mpdm_t mpsl_bytecode = NULL;
38 /* pointer to source code being compiled */
39 extern wchar_t *mpsl_next_char;
41 /* pointer to file being compiled */
42 extern FILE *mpsl_file;
44 /* line number */
45 extern int mpsl_line;
47 /* compiled filename (for errors) */
48 static char *mpsl_filename = NULL;
50 /* cached value MPSL.OPCODE */
51 extern mpdm_t mpsl_opcodes;
53 /* cached value MPSL.LC */
54 extern mpdm_t mpsl_lc;
57 /** code **/
59 int yylex(void);
60 void yyerror(char *s);
62 #define INS0(o) mpsl_mkins(o, 0, NULL, NULL, NULL, NULL)
63 #define INS1(o,a1) mpsl_mkins(o, 1, a1, NULL, NULL, NULL)
64 #define INS2(o,a1,a2) mpsl_mkins(o, 2, a1, a2, NULL, NULL)
65 #define INS3(o,a1,a2,a3) mpsl_mkins(o, 3, a1, a2, a3, NULL)
66 #define INS4(o,a1,a2,a3,a4) mpsl_mkins(o, 4, a1, a2, a3, a4)
68 static mpdm_t mpsl_x(mpdm_t a1, mpdm_t a2, int sf)
69 /* creates an executable value with the MPSL executor as the first
70 argument and a compiled stream as the second */
72 return MPDM_X2(mpsl_exec_p,
73 mpsl_mkins(sf ? L"SUBFRAME" : L"BLKFRAME",
74 a2 == NULL ? 1 : 2, a1, a2, NULL, NULL));
78 static void compiler_warning(char *str)
80 fprintf(stderr, "WARNING: %s.\n", str);
86 %union {
87 mpdm_t v; /* a simple value */
88 mpdm_t ins; /* an 'instruction': [ opcode, args ] */
91 %token <v> NULLV INTEGER REAL STRING SYMBOL LITERAL
92 %token WHILE FOR IF SUB FOREACH LOCAL BREAK RETURN
93 %nonassoc IFI
94 %nonassoc ELSE
96 %left BOOLAND BOOLOR
97 %left INC DEC IADD ISUB IMUL IDIV IMOD IBITAND IBITOR IBITXOR ISHR ISHL
98 %left '!'
99 %left STRCAT STREQ NUMEQ STRNE NUMNE NUMGE NUMLE ARROW ':' RANGE '>''<' INVCALL
100 %left AMPERSAND
101 %left BITOR BITXOR
102 %left SHL SHR
103 %left '+' '-'
104 %left '*' '/' MOD POW
105 %nonassoc UMINUS
107 %type <ins> stmt expr sym_list stmt_list list hash compsym
111 program:
112 function { ; }
115 function:
116 function stmt_list {
117 mpsl_bytecode = $2;
119 | /* NULL */
122 stmt:
123 ';' {
124 /* null instruction */
125 $$ = INS0(L"MULTI");
127 | expr ';' {
128 /* expression, as is */
129 $$ = $1;
132 | WHILE '(' expr ')' stmt
134 /* while loop */
135 $$ = INS2(L"WHILE", $3, $5);
137 | FOR '(' expr ';' expr ';' expr ')' stmt
139 /* for loop */
140 $$ = INS4(L"WHILE", $5, $9, $3, $7);
142 | FOR '(' ';' ';' ')' stmt
144 /* infinite loop */
145 $$ = INS2(L"WHILE", INS1(L"LITERAL", MPDM_I(1)), $6);
147 | IF '(' expr ')' stmt %prec IFI
149 /* if - then construction */
150 $$ = INS2(L"IF", $3, $5);
152 | IF '(' expr ')' stmt ELSE stmt
154 /* if - then - else construction */
155 $$ = INS3(L"IF", $3, $5, $7);
158 | SUB compsym '{' stmt_list '}'
160 /* subroutine definition,
161 without arguments */
162 $$ = INS2(L"ASSIGN", $2,
163 INS1(L"LITERAL",
164 mpsl_x($4, NULL, 1)));
167 | SUB compsym '(' ')' '{' stmt_list '}'
169 /* subroutine definition,
170 without arguments (second
171 syntax, including parens) */
172 $$ = INS2(L"ASSIGN", $2,
173 INS1(L"LITERAL",
174 mpsl_x($6, NULL, 1)));
177 | SUB compsym '(' sym_list ')' '{' stmt_list '}'
179 /* subroutine definition,
180 with arguments */
181 $$ = INS2(L"ASSIGN", $2,
182 INS1(L"LITERAL",
183 mpsl_x($7, $4, 1)));
186 | FOREACH '(' compsym ',' expr ')' stmt
188 /* foreach construction */
189 /* a block frame is created, the iterator
190 created as local, and the foreach executed */
191 $$ = INS1(L"BLKFRAME",
192 INS2(L"MULTI",
193 INS1(L"LOCAL", $3),
194 INS3(L"FOREACH", $3, $5, $7)
198 | FOREACH '(' LOCAL compsym ',' expr ')' stmt
200 compiler_warning("useless use of local in foreach loop");
202 $$ = INS1(L"BLKFRAME",
203 INS2(L"MULTI",
204 INS1(L"LOCAL", $4),
205 INS3(L"FOREACH", $4, $6, $8)
210 | '{' stmt_list '}' {
211 /* block of instructions,
212 with local symbol table */
213 $$ = INS1(L"BLKFRAME", $2);
216 | LOCAL sym_list ';' {
217 /* local symbol creation */
218 $$ = INS1(L"LOCAL", $2);
220 | LOCAL SYMBOL '=' expr ';'
222 /* contraction; local symbol
223 creation and assignation */
224 $$ = INS2(L"MULTI",
225 INS1(L"LOCAL",
226 INS1(L"LITERAL", $2)),
227 INS2(L"ASSIGN",
228 INS1(L"LITERAL", $2),$4)
231 | BREAK ';' {
232 /* break (exit from loop) */
233 $$ = INS0(L"BREAK");
235 | RETURN expr ';' {
236 /* return from subroutine */
237 $$ = INS1(L"RETURN", $2);
239 | RETURN ';' {
240 /* return from subroutine (void) */
241 $$ = INS0(L"RETURN");
245 stmt_list:
246 stmt { $$ = $1; }
247 | stmt_list stmt {
248 /* sequence of instructions */
249 $$ = INS2(L"MULTI", $1, $2);
253 list:
254 expr {
255 $$ = INS1(L"LIST", $1);
257 | list ',' expr {
258 /* build list from list of
259 instructions */
260 $$ = INS2(L"LIST", $3, $1);
264 sym_list:
265 SYMBOL {
266 $$ = INS1(L"LIST",
267 INS1(L"LITERAL", $1));
269 | sym_list ',' SYMBOL {
270 /* comma-separated list of symbols */
271 $$ = INS2(L"LIST",
272 INS1(L"LITERAL", $3), $1);
276 hash:
277 expr ARROW expr {
278 $$ = INS2(L"HASH", $1, $3);
280 | SYMBOL ':' expr {
281 $$ = INS2(L"HASH", INS1(L"LITERAL", $1), $3);
283 | hash ',' expr ARROW expr
285 /* build hash from list of
286 instructions */
287 $$ = INS3(L"HASH", $3, $5, $1);
289 | hash ',' SYMBOL ':' expr
291 /* build hash from list of
292 instructions */
293 $$ = INS3(L"HASH", INS1(L"LITERAL", $3), $5, $1);
297 compsym:
298 SYMBOL {
299 $$ = INS1(L"LIST",
300 INS1(L"LITERAL", $1));
302 | compsym '.' INTEGER {
303 /* a.5 compound symbol */
304 $$ = INS2(L"LIST",
305 INS1(L"LITERAL", $3), $1);
307 | compsym '.' SYMBOL {
308 /* a.b compound symbol */
309 $$ = INS2(L"LIST",
310 INS1(L"LITERAL", $3), $1);
312 | compsym '[' expr ']' {
313 /* a["b"] or a[5] compound symbol */
314 $$ = INS2(L"LIST", $3, $1);
318 expr:
319 INTEGER {
320 /* literal integer */
321 $$ = INS1(L"LITERAL", $1);
323 | STRING {
324 /* literal string */
325 $$ = INS1(L"LITERAL", $1);
327 | REAL {
328 /* literal real number */
329 $$ = INS1(L"LITERAL", $1);
331 | compsym {
332 /* compound symbol */
333 $$ = INS1(L"SYMVAL", $1);
335 | NULLV {
336 /* NULL value */
337 $$ = INS1(L"LITERAL", NULL);
340 | '-' expr %prec UMINUS {
341 /* unary minus */
342 $$ = INS1(L"UMINUS", $2);
345 /* math operations */
346 | expr '+' expr { $$ = INS2(L"ADD", $1, $3); }
347 | expr '-' expr { $$ = INS2(L"SUB", $1, $3); }
348 | expr '*' expr { $$ = INS2(L"MUL", $1, $3); }
349 | expr '/' expr { $$ = INS2(L"DIV", $1, $3); }
350 | expr MOD expr { $$ = INS2(L"MOD", $1, $3); }
351 | expr POW expr { $$ = INS2(L"POW", $1, $3); }
353 /* bit operations */
354 | expr AMPERSAND expr { $$ = INS2(L"BITAND", $1, $3); }
355 | expr BITOR expr { $$ = INS2(L"BITOR", $1, $3); }
356 | expr BITXOR expr { $$ = INS2(L"BITXOR", $1, $3); }
357 | expr SHL expr { $$ = INS2(L"SHL", $1, $3); }
358 | expr SHR expr { $$ = INS2(L"SHR", $1, $3); }
360 /* increment and decrement (prefix) */
361 | INC compsym { $$ = INS2(L"ASSIGN", $2,
362 INS2(L"ADD",
363 INS1(L"SYMVAL", $2),
364 INS1(L"LITERAL", MPDM_I(1))
368 | DEC compsym { $$ = INS2(L"ASSIGN", $2,
369 INS2(L"SUB",
370 INS1(L"SYMVAL", $2),
371 INS1(L"LITERAL", MPDM_I(1))
376 /* increment and decrement (suffix) */
377 | compsym INC { $$ = INS2(L"IMULTI",
378 INS1(L"SYMVAL", $1),
379 INS2(L"ASSIGN", $1,
380 INS2(L"ADD",
381 INS1(L"SYMVAL", $1),
382 INS1(L"LITERAL", MPDM_I(1))
387 | compsym DEC { $$ = INS2(L"IMULTI",
388 INS1(L"SYMVAL", $1),
389 INS2(L"ASSIGN", $1,
390 INS2(L"SUB",
391 INS1(L"SYMVAL", $1),
392 INS1(L"LITERAL", MPDM_I(1))
398 /* immediate math operations */
399 | compsym IADD expr { $$ = INS2(L"ASSIGN", $1,
400 INS2(L"ADD",
401 INS1(L"SYMVAL", $1),
405 | compsym ISUB expr { $$ = INS2(L"ASSIGN", $1,
406 INS2(L"SUB",
407 INS1(L"SYMVAL", $1),
411 | compsym IMUL expr { $$ = INS2(L"ASSIGN", $1,
412 INS2(L"MUL",
413 INS1(L"SYMVAL", $1),
417 | compsym IDIV expr { $$ = INS2(L"ASSIGN", $1,
418 INS2(L"DIV",
419 INS1(L"SYMVAL", $1),
423 | compsym IMOD expr { $$ = INS2(L"ASSIGN", $1,
424 INS2(L"MOD",
425 INS1(L"SYMVAL", $1),
429 | compsym IBITAND expr { $$ = INS2(L"ASSIGN", $1,
430 INS2(L"BITAND",
431 INS1(L"SYMVAL", $1),
435 | compsym IBITOR expr { $$ = INS2(L"ASSIGN", $1,
436 INS2(L"BITOR",
437 INS1(L"SYMVAL", $1),
441 | compsym IBITXOR expr { $$ = INS2(L"ASSIGN", $1,
442 INS2(L"BITXOR",
443 INS1(L"SYMVAL", $1),
447 | compsym ISHL expr { $$ = INS2(L"ASSIGN", $1,
448 INS2(L"SHL",
449 INS1(L"SYMVAL", $1),
453 | compsym ISHR expr { $$ = INS2(L"ASSIGN", $1,
454 INS2(L"SHR",
455 INS1(L"SYMVAL", $1),
460 | '!' expr {
461 /* boolean not */
462 $$ = INS1(L"NOT", $2);
464 | expr '<' expr {
465 /* bool less than */
466 $$ = INS2(L"NUMLT", $1, $3);
468 | expr '>' expr {
469 /* bool greater than */
470 $$ = INS2(L"NUMGT", $1, $3);
472 | expr NUMLE expr {
473 /* bool less or equal than */
474 $$ = INS2(L"NUMLE", $1, $3);
476 | expr NUMGE expr {
477 /* bool greater or equal than */
478 $$ = INS2(L"NUMGE", $1, $3);
480 | expr NUMEQ expr {
481 /* bool numeric equal */
482 $$ = INS2(L"NUMEQ", $1, $3);
484 | expr NUMNE expr {
485 /* bool numeric non-equal */
486 $$ = INS1(L"NOT",
487 INS2(L"NUMEQ", $1, $3));
490 | expr STRCAT expr {
491 /* string concatenation */
492 $$ = INS2(L"STRCAT", $1, $3);
494 | expr STREQ expr {
495 /* bool string equal */
496 $$ = INS2(L"STREQ", $1, $3);
498 | expr STRNE expr {
499 /* bool string non-equal */
500 $$ = INS1(L"NOT",
501 INS2(L"STREQ", $1, $3));
504 | expr BOOLAND expr {
505 /* boolean and */
506 $$ = INS2(L"AND", $1, $3);
508 | expr BOOLOR expr {
509 /* boolean or */
510 $$ = INS2(L"OR", $1, $3);
513 | SUB '{' stmt_list '}' {
514 /* anonymous subroutine (without args) */
515 $$ = INS1(L"LITERAL", mpsl_x($3, NULL, 0));
518 | SUB '(' sym_list ')' '{' stmt_list '}'
520 /* anonymous subroutine (with args) */
521 $$ = INS1(L"LITERAL", mpsl_x($6, $3, 0));
524 | '(' expr ')' {
525 /* parenthesized expression */
526 $$ = $2;
529 | '[' ']' {
530 /* empty list */
531 $$ = INS1(L"LITERAL", MPDM_A(0));
533 | '[' list ']' {
534 /* non-empty list */
535 $$ = $2;
537 | '[' expr RANGE expr ']'
539 /* build range from expressions */
540 $$ = INS2(L"RANGE", $2, $4);
543 | '{' '}' {
544 /* empty hash */
545 $$ = INS1(L"LITERAL", MPDM_H(0));
547 | '{' hash '}' {
548 /* non-empty hash */
549 $$ = $2;
552 | compsym '(' ')' {
553 /* function call (without args) */
554 $$ = INS1(L"EXECSYM", $1);
556 | compsym '(' list ')' {
557 /* function call (with args) */
558 $$ = INS2(L"EXECSYM", $1, $3);
560 | expr INVCALL compsym '(' ')' {
561 /* function call with only an inverse argument */
562 $$ = INS2(L"EXECSYM", $3, INS1(L"ILIST", $1));
564 | expr INVCALL compsym '(' list ')' {
565 /* function call with inverse argument and other ones */
566 $$ = INS2(L"EXECSYM", $3, INS2(L"ILIST", $1, $5));
568 | AMPERSAND compsym '(' ')' {
569 /* function call as a new thread (without args) */
570 $$ = INS1(L"THREADSYM", $2);
572 | AMPERSAND compsym '(' list ')' {
573 /* function call as a new thread (with args) */
574 $$ = INS2(L"THREADSYM", $2, $4);
576 | compsym '=' expr {
577 /* simple assignation */
578 $$ = INS2(L"ASSIGN", $1, $3);
585 void yyerror(char *s)
587 char tmp[1024];
589 snprintf(tmp, sizeof(tmp), "%s in %s, line %d",
590 s, mpsl_filename, mpsl_line + 1);
592 mpsl_error(MPDM_MBS(tmp));
596 static FILE *inc_fopen(const char *filename, mpdm_t inc)
597 /* loads filename, searching in INC if not directly accesible */
599 FILE *f = NULL;
600 char tmp[1024];
601 int n;
603 /* loop through INC, prepending each path
604 to the filename */
605 for (n = 0; n < mpdm_size(inc); n++) {
606 mpdm_t v = mpdm_aget(inc, n);
608 v = mpdm_ref(MPDM_2MBS(v->data));
609 snprintf(tmp, sizeof(tmp), "%s/%s", (char *) v->data, filename);
610 mpdm_unref(v);
612 if ((f = fopen(tmp, "r")) != NULL)
613 break;
616 return f;
620 static mpdm_t do_parse(const char *filename, wchar_t * code, FILE * file)
621 /* calls yyparse() after doing some initialisations, and returns
622 the compiled code as an executable value */
624 mpdm_t v;
625 mpdm_t x = NULL;
627 /* first line */
628 mpsl_line = 0;
630 /* reset last bytecode */
631 mpsl_bytecode = NULL;
633 /* set globals */
634 mpsl_next_char = code;
635 mpsl_file = file;
637 if (mpsl_filename != NULL)
638 free(mpsl_filename);
640 mpsl_filename = strdup(filename);
642 /* cache some values */
643 v = mpdm_hget_s(mpdm_root(), L"MPSL");
644 mpsl_opcodes = mpdm_hget_s(v, L"OPCODE");
645 mpsl_lc = mpdm_hget_s(v, L"LC");
647 /* compile! */
648 if (yyparse() == 0 && mpsl_bytecode != NULL)
649 x = mpsl_x(mpsl_bytecode, NULL, 1);
651 /* clean back cached values */
652 mpsl_opcodes = NULL;
653 mpsl_lc = NULL;
655 return x;
660 * mpsl_compile - Compiles a string of MPSL code.
661 * @code: A value containing a string of MPSL code
663 * Compiles a string of MPSL code and returns an mpdm value executable
664 * by mpdm_exec(). If there is a syntax (or other type) error, NULL
665 * is returned instead.
667 mpdm_t mpsl_compile(mpdm_t code)
669 mpdm_t x = NULL;
671 mpdm_ref(code);
672 x = do_parse("<INLINE>", (wchar_t *) code->data, NULL);
673 mpdm_unref(code);
675 return x;
680 * mpsl_compile_file - Compiles a file of MPSL code.
681 * @file: File stream or file name.
682 * @inc: search path for source files.
684 * Compiles a source file of MPSL code and returns an mpdm value
685 * executable by mpdm_exec(). If @file is an MPSL file descriptor,
686 * it's read as is and compiled; otherwise, it's assumed to be a
687 * file name, that will be searched for in any of the paths defined
688 * in the @inc array. If the file cannot be found
689 * or there is any other error, NULL is returned instead.
691 mpdm_t mpsl_compile_file(mpdm_t file, mpdm_t inc)
693 mpdm_t w;
694 mpdm_t x = NULL;
695 FILE *f = NULL;
696 const char *filename = NULL;
698 mpdm_ref(file);
699 mpdm_ref(inc);
701 if ((f = mpdm_get_filehandle(file)) != NULL) {
702 filename = "<FILE>";
703 w = file;
705 else {
706 mpdm_t v;
708 /* it's a filename; open it */
709 v = mpdm_ref(MPDM_2MBS(file->data));
711 filename = v->data;
713 if ((f = inc_fopen(filename, inc)) == NULL) {
714 char tmp[128];
716 snprintf(tmp, sizeof(tmp) - 1,
717 "File '%s' not found in INC", filename);
718 mpsl_error(MPDM_MBS(tmp));
721 mpdm_unref(v);
723 w = MPDM_F(f);
726 if (w != NULL) {
727 x = do_parse(filename, NULL, f);
728 mpdm_close(w);
731 mpdm_unref(inc);
732 mpdm_unref(file);
734 return x;
739 * mpsl_eval - Evaluates MSPL code.
740 * @code: A value containing a string of MPSL code, or executable code
741 * @args: optional arguments for @code
742 * @ctxt: context for @code
744 * Evaluates a piece of code. The @code can be a string containing MPSL source
745 * code (that will be compiled) or a direct executable value. If the compilation
746 * or the execution gives an error, the ERROR variable will be set to a printable
747 * value and NULL returned. Otherwise, the exit value from the code is returned
748 * and ERROR set to NULL. The abort flag is reset on exit.
750 mpdm_t mpsl_eval(mpdm_t code, mpdm_t args, mpdm_t ctxt)
752 mpdm_t cs, r;
754 /* reset error */
755 mpsl_error(NULL);
756 mpsl_abort = 0;
758 mpdm_ref(code);
760 /* if code is not executable, try to compile */
761 if (!MPDM_IS_EXEC(code)) {
762 mpdm_t c;
764 /* get the eval cache */
765 if ((c = mpdm_hget_s(mpdm_root(), L"__EVAL__")) == NULL)
766 c = mpdm_hset_s(mpdm_root(), L"__EVAL__", MPDM_H(0));
768 /* this code still not compiled? do it */
769 if ((cs = mpdm_hget(c, code)) == NULL)
770 cs = mpdm_hset(c, code, mpsl_compile(code));
772 else
773 cs = code;
775 /* execute, if possible */
776 if (MPDM_IS_EXEC(cs))
777 r = mpdm_exec(cs, args, ctxt);
778 else
779 r = NULL;
781 /* reset the abort flag */
782 mpsl_abort = 0;
784 mpdm_unref(code);
786 return r;