Updated RELEASE_NOTES (Closes: #1122).
[mpsl.git] / mpsl.y
blob6a17db628a1b8429ba0110cac85528457609032e
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)
63 #define INS1(o,a1) mpsl_mkins(o, 1, a1, NULL, NULL)
64 #define INS2(o,a1,a2) mpsl_mkins(o, 2, a1, a2, NULL)
65 #define INS3(o,a1,a2,a3) mpsl_mkins(o, 3, a1, a2, a3)
67 static mpdm_t mpsl_x(mpdm_t a1, mpdm_t a2, int sf)
68 /* creates an executable value with the MPSL executor as the first
69 argument and a compiled stream as the second */
71 return MPDM_X2(mpsl_exec_p,
72 mpsl_mkins(sf ? L"SUBFRAME" : L"BLKFRAME",
73 a2 == NULL ? 1 : 2, a1, a2, NULL));
77 static void compiler_warning(char *str)
79 fprintf(stderr, "WARNING: %s.\n", str);
85 %union {
86 mpdm_t v; /* a simple value */
87 mpdm_t ins; /* an 'instruction': [ opcode, args ] */
90 %token <v> NULLV INTEGER REAL STRING SYMBOL LITERAL
91 %token WHILE IF SUB FOREACH LOCAL BREAK RETURN
92 %nonassoc IFI
93 %nonassoc ELSE
95 %left BOOLAND BOOLOR
96 %left INC DEC IADD ISUB IMUL IDIV IMOD IBITAND IBITOR IBITXOR ISHR ISHL
97 %left '!'
98 %left STRCAT STREQ NUMEQ STRNE NUMNE NUMGE NUMLE HASHPAIR RANGE '>''<' INVCALL
99 %left AMPERSAND
100 %left BITOR BITXOR
101 %left SHL SHR
102 %left '+' '-'
103 %left '*' '/' MOD POW
104 %nonassoc UMINUS
106 %type <ins> stmt expr sym_list stmt_list list hash compsym
110 program:
111 function { ; }
114 function:
115 function stmt_list {
116 mpsl_bytecode = $2;
118 | /* NULL */
121 stmt:
122 ';' {
123 /* null instruction */
124 $$ = INS0(L"MULTI");
126 | expr ';' {
127 /* expression, as is */
128 $$ = $1;
131 | WHILE '(' expr ')' stmt
133 /* while loop */
134 $$ = INS2(L"WHILE", $3, $5);
136 | IF '(' expr ')' stmt %prec IFI
138 /* if - then construction */
139 $$ = INS2(L"IF", $3, $5);
141 | IF '(' expr ')' stmt ELSE stmt
143 /* if - then - else construction */
144 $$ = INS3(L"IF", $3, $5, $7);
147 | SUB compsym '{' stmt_list '}'
149 /* subroutine definition,
150 without arguments */
151 $$ = INS2(L"ASSIGN", $2,
152 INS1(L"LITERAL",
153 mpsl_x($4, NULL, 1)));
156 | SUB compsym '(' ')' '{' stmt_list '}'
158 /* subroutine definition,
159 without arguments (second
160 syntax, including parens) */
161 $$ = INS2(L"ASSIGN", $2,
162 INS1(L"LITERAL",
163 mpsl_x($6, NULL, 1)));
166 | SUB compsym '(' sym_list ')' '{' stmt_list '}'
168 /* subroutine definition,
169 with arguments */
170 $$ = INS2(L"ASSIGN", $2,
171 INS1(L"LITERAL",
172 mpsl_x($7, $4, 1)));
175 | FOREACH '(' compsym ',' expr ')' stmt
177 /* foreach construction */
178 /* a block frame is created, the iterator
179 created as local, and the foreach executed */
180 $$ = INS1(L"BLKFRAME",
181 INS2(L"MULTI",
182 INS1(L"LOCAL", $3),
183 INS3(L"FOREACH", $3, $5, $7)
187 | FOREACH '(' LOCAL compsym ',' expr ')' stmt
189 compiler_warning("useless use of local in foreach loop");
191 $$ = INS1(L"BLKFRAME",
192 INS2(L"MULTI",
193 INS1(L"LOCAL", $4),
194 INS3(L"FOREACH", $4, $6, $8)
199 | '{' stmt_list '}' {
200 /* block of instructions,
201 with local symbol table */
202 $$ = INS1(L"BLKFRAME", $2);
205 | LOCAL sym_list ';' {
206 /* local symbol creation */
207 $$ = INS1(L"LOCAL", $2);
209 | LOCAL SYMBOL '=' expr ';'
211 /* contraction; local symbol
212 creation and assignation */
213 $$ = INS2(L"MULTI",
214 INS1(L"LOCAL",
215 INS1(L"LITERAL", $2)),
216 INS2(L"ASSIGN",
217 INS1(L"LITERAL", $2),$4)
220 | BREAK ';' {
221 /* break (exit from loop) */
222 $$ = INS0(L"BREAK");
224 | RETURN expr ';' {
225 /* return from subroutine */
226 $$ = INS1(L"RETURN", $2);
228 | RETURN ';' {
229 /* return from subroutine (void) */
230 $$ = INS0(L"RETURN");
234 stmt_list:
235 stmt { $$ = $1; }
236 | stmt_list stmt {
237 /* sequence of instructions */
238 $$ = INS2(L"MULTI", $1, $2);
242 list:
243 expr {
244 $$ = INS1(L"LIST", $1);
246 | list ',' expr {
247 /* build list from list of
248 instructions */
249 $$ = INS2(L"LIST", $3, $1);
253 sym_list:
254 SYMBOL {
255 $$ = INS1(L"LIST",
256 INS1(L"LITERAL", $1));
258 | sym_list ',' SYMBOL {
259 /* comma-separated list of symbols */
260 $$ = INS2(L"LIST",
261 INS1(L"LITERAL", $3), $1);
265 hash:
266 expr HASHPAIR expr {
267 $$ = INS2(L"HASH", $1, $3);
269 | hash ',' expr HASHPAIR expr
271 /* build hash from list of
272 instructions */
273 $$ = INS3(L"HASH", $3, $5, $1);
277 compsym:
278 SYMBOL {
279 $$ = INS1(L"LIST",
280 INS1(L"LITERAL", $1));
282 | compsym '.' INTEGER {
283 /* a.5 compound symbol */
284 $$ = INS2(L"LIST",
285 INS1(L"LITERAL", $3), $1);
287 | compsym '.' SYMBOL {
288 /* a.b compound symbol */
289 $$ = INS2(L"LIST",
290 INS1(L"LITERAL", $3), $1);
292 | compsym '[' expr ']' {
293 /* a["b"] or a[5] compound symbol */
294 $$ = INS2(L"LIST", $3, $1);
298 expr:
299 INTEGER {
300 /* literal integer */
301 $$ = INS1(L"LITERAL", $1);
303 | STRING {
304 /* literal string */
305 $$ = INS1(L"LITERAL", $1);
307 | REAL {
308 /* literal real number */
309 $$ = INS1(L"LITERAL", $1);
311 | compsym {
312 /* compound symbol */
313 $$ = INS1(L"SYMVAL", $1);
315 | NULLV {
316 /* NULL value */
317 $$ = INS1(L"LITERAL", NULL);
320 | '-' expr %prec UMINUS {
321 /* unary minus */
322 $$ = INS1(L"UMINUS", $2);
325 /* math operations */
326 | expr '+' expr { $$ = INS2(L"ADD", $1, $3); }
327 | expr '-' expr { $$ = INS2(L"SUB", $1, $3); }
328 | expr '*' expr { $$ = INS2(L"MUL", $1, $3); }
329 | expr '/' expr { $$ = INS2(L"DIV", $1, $3); }
330 | expr MOD expr { $$ = INS2(L"MOD", $1, $3); }
331 | expr POW expr { $$ = INS2(L"POW", $1, $3); }
333 /* bit operations */
334 | expr AMPERSAND expr { $$ = INS2(L"BITAND", $1, $3); }
335 | expr BITOR expr { $$ = INS2(L"BITOR", $1, $3); }
336 | expr BITXOR expr { $$ = INS2(L"BITXOR", $1, $3); }
337 | expr SHL expr { $$ = INS2(L"SHL", $1, $3); }
338 | expr SHR expr { $$ = INS2(L"SHR", $1, $3); }
340 /* increment and decrement (prefix) */
341 | INC compsym { $$ = INS2(L"ASSIGN", $2,
342 INS2(L"ADD",
343 INS1(L"SYMVAL", $2),
344 INS1(L"LITERAL", MPDM_I(1))
348 | DEC compsym { $$ = INS2(L"ASSIGN", $2,
349 INS2(L"SUB",
350 INS1(L"SYMVAL", $2),
351 INS1(L"LITERAL", MPDM_I(1))
356 /* increment and decrement (suffix) */
357 | compsym INC { $$ = INS2(L"IMULTI",
358 INS1(L"SYMVAL", $1),
359 INS2(L"ASSIGN", $1,
360 INS2(L"ADD",
361 INS1(L"SYMVAL", $1),
362 INS1(L"LITERAL", MPDM_I(1))
367 | compsym DEC { $$ = INS2(L"IMULTI",
368 INS1(L"SYMVAL", $1),
369 INS2(L"ASSIGN", $1,
370 INS2(L"SUB",
371 INS1(L"SYMVAL", $1),
372 INS1(L"LITERAL", MPDM_I(1))
378 /* immediate math operations */
379 | compsym IADD expr { $$ = INS2(L"ASSIGN", $1,
380 INS2(L"ADD",
381 INS1(L"SYMVAL", $1),
385 | compsym ISUB expr { $$ = INS2(L"ASSIGN", $1,
386 INS2(L"SUB",
387 INS1(L"SYMVAL", $1),
391 | compsym IMUL expr { $$ = INS2(L"ASSIGN", $1,
392 INS2(L"MUL",
393 INS1(L"SYMVAL", $1),
397 | compsym IDIV expr { $$ = INS2(L"ASSIGN", $1,
398 INS2(L"DIV",
399 INS1(L"SYMVAL", $1),
403 | compsym IMOD expr { $$ = INS2(L"ASSIGN", $1,
404 INS2(L"MOD",
405 INS1(L"SYMVAL", $1),
409 | compsym IBITAND expr { $$ = INS2(L"ASSIGN", $1,
410 INS2(L"BITAND",
411 INS1(L"SYMVAL", $1),
415 | compsym IBITOR expr { $$ = INS2(L"ASSIGN", $1,
416 INS2(L"BITOR",
417 INS1(L"SYMVAL", $1),
421 | compsym IBITXOR expr { $$ = INS2(L"ASSIGN", $1,
422 INS2(L"BITXOR",
423 INS1(L"SYMVAL", $1),
427 | compsym ISHL expr { $$ = INS2(L"ASSIGN", $1,
428 INS2(L"SHL",
429 INS1(L"SYMVAL", $1),
433 | compsym ISHR expr { $$ = INS2(L"ASSIGN", $1,
434 INS2(L"SHR",
435 INS1(L"SYMVAL", $1),
440 | '!' expr {
441 /* boolean not */
442 $$ = INS1(L"NOT", $2);
444 | expr '<' expr {
445 /* bool less than */
446 $$ = INS2(L"NUMLT", $1, $3);
448 | expr '>' expr {
449 /* bool greater than */
450 $$ = INS2(L"NUMGT", $1, $3);
452 | expr NUMLE expr {
453 /* bool less or equal than */
454 $$ = INS2(L"NUMLE", $1, $3);
456 | expr NUMGE expr {
457 /* bool greater or equal than */
458 $$ = INS2(L"NUMGE", $1, $3);
460 | expr NUMEQ expr {
461 /* bool numeric equal */
462 $$ = INS2(L"NUMEQ", $1, $3);
464 | expr NUMNE expr {
465 /* bool numeric non-equal */
466 $$ = INS1(L"NOT",
467 INS2(L"NUMEQ", $1, $3));
470 | expr STRCAT expr {
471 /* string concatenation */
472 $$ = INS2(L"STRCAT", $1, $3);
474 | expr STREQ expr {
475 /* bool string equal */
476 $$ = INS2(L"STREQ", $1, $3);
478 | expr STRNE expr {
479 /* bool string non-equal */
480 $$ = INS1(L"NOT",
481 INS2(L"STREQ", $1, $3));
484 | expr BOOLAND expr {
485 /* boolean and */
486 $$ = INS2(L"AND", $1, $3);
488 | expr BOOLOR expr {
489 /* boolean or */
490 $$ = INS2(L"OR", $1, $3);
493 | SUB '{' stmt_list '}' {
494 /* anonymous subroutine (without args) */
495 $$ = INS1(L"LITERAL", mpsl_x($3, NULL, 0));
498 | SUB '(' sym_list ')' '{' stmt_list '}'
500 /* anonymous subroutine (with args) */
501 $$ = INS1(L"LITERAL", mpsl_x($6, $3, 0));
504 | '(' expr ')' {
505 /* parenthesized expression */
506 $$ = $2;
509 | '[' ']' {
510 /* empty list */
511 $$ = INS1(L"LITERAL", MPDM_A(0));
513 | '[' list ']' {
514 /* non-empty list */
515 $$ = $2;
517 | '[' expr RANGE expr ']'
519 /* build range from expressions */
520 $$ = INS2(L"RANGE", $2, $4);
523 | '{' '}' {
524 /* empty hash */
525 $$ = INS1(L"LITERAL", MPDM_H(0));
527 | '{' hash '}' {
528 /* non-empty hash */
529 $$ = $2;
532 | compsym '(' ')' {
533 /* function call (without args) */
534 $$ = INS1(L"EXECSYM", $1);
536 | compsym '(' list ')' {
537 /* function call (with args) */
538 $$ = INS2(L"EXECSYM", $1, $3);
540 | expr INVCALL compsym '(' ')' {
541 /* function call with only an inverse argument */
542 $$ = INS2(L"EXECSYM", $3, INS1(L"ILIST", $1));
544 | expr INVCALL compsym '(' list ')' {
545 /* function call with inverse argument and other ones */
546 $$ = INS2(L"EXECSYM", $3, INS2(L"ILIST", $1, $5));
548 | AMPERSAND compsym '(' ')' {
549 /* function call as a new thread (without args) */
550 $$ = INS1(L"THREADSYM", $2);
552 | AMPERSAND compsym '(' list ')' {
553 /* function call as a new thread (with args) */
554 $$ = INS2(L"THREADSYM", $2, $4);
556 | compsym '=' expr {
557 /* simple assignation */
558 $$ = INS2(L"ASSIGN", $1, $3);
565 void yyerror(char *s)
567 char tmp[1024];
569 snprintf(tmp, sizeof(tmp), "%s in %s, line %d",
570 s, mpsl_filename, mpsl_line + 1);
572 mpsl_error(MPDM_MBS(tmp));
576 static FILE *inc_fopen(const char *filename, mpdm_t inc)
577 /* loads filename, searching in INC if not directly accesible */
579 FILE *f = NULL;
580 char tmp[1024];
581 int n;
583 /* loop through INC, prepending each path
584 to the filename */
585 for (n = 0; n < mpdm_size(inc); n++) {
586 mpdm_t v = mpdm_aget(inc, n);
588 v = mpdm_ref(MPDM_2MBS(v->data));
589 snprintf(tmp, sizeof(tmp), "%s/%s", (char *) v->data, filename);
590 mpdm_unref(v);
592 if ((f = fopen(tmp, "r")) != NULL)
593 break;
596 return f;
600 static mpdm_t do_parse(const char *filename, wchar_t * code, FILE * file)
601 /* calls yyparse() after doing some initialisations, and returns
602 the compiled code as an executable value */
604 mpdm_t v;
605 mpdm_t x = NULL;
607 /* first line */
608 mpsl_line = 0;
610 /* reset last bytecode */
611 mpsl_bytecode = NULL;
613 /* set globals */
614 mpsl_next_char = code;
615 mpsl_file = file;
617 if (mpsl_filename != NULL)
618 free(mpsl_filename);
620 mpsl_filename = strdup(filename);
622 /* cache some values */
623 v = mpdm_hget_s(mpdm_root(), L"MPSL");
624 mpsl_opcodes = mpdm_hget_s(v, L"OPCODE");
625 mpsl_lc = mpdm_hget_s(v, L"LC");
627 /* compile! */
628 if (yyparse() == 0 && mpsl_bytecode != NULL)
629 x = mpsl_x(mpsl_bytecode, NULL, 1);
631 /* clean back cached values */
632 mpsl_opcodes = NULL;
633 mpsl_lc = NULL;
635 return x;
640 * mpsl_compile - Compiles a string of MPSL code.
641 * @code: A value containing a string of MPSL code
643 * Compiles a string of MPSL code and returns an mpdm value executable
644 * by mpdm_exec(). If there is a syntax (or other type) error, NULL
645 * is returned instead.
647 mpdm_t mpsl_compile(mpdm_t code)
649 mpdm_t x = NULL;
651 mpdm_ref(code);
652 x = do_parse("<INLINE>", (wchar_t *) code->data, NULL);
653 mpdm_unref(code);
655 return x;
660 * mpsl_compile_file - Compiles a file of MPSL code.
661 * @file: File stream or file name.
662 * @inc: search path for source files.
664 * Compiles a source file of MPSL code and returns an mpdm value
665 * executable by mpdm_exec(). If @file is an MPSL file descriptor,
666 * it's read as is and compiled; otherwise, it's assumed to be a
667 * file name, that will be searched for in any of the paths defined
668 * in the @inc array. If the file cannot be found
669 * or there is any other error, NULL is returned instead.
671 mpdm_t mpsl_compile_file(mpdm_t file, mpdm_t inc)
673 mpdm_t w;
674 mpdm_t x = NULL;
675 FILE *f = NULL;
676 const char *filename = NULL;
678 mpdm_ref(file);
679 mpdm_ref(inc);
681 if ((f = mpdm_get_filehandle(file)) != NULL) {
682 filename = "<FILE>";
683 w = file;
685 else {
686 mpdm_t v;
688 /* it's a filename; open it */
689 v = mpdm_ref(MPDM_2MBS(file->data));
691 filename = v->data;
693 if ((f = inc_fopen(filename, inc)) == NULL) {
694 char tmp[128];
696 snprintf(tmp, sizeof(tmp) - 1,
697 "File '%s' not found in INC", filename);
698 mpsl_error(MPDM_MBS(tmp));
701 mpdm_unref(v);
703 w = MPDM_F(f);
706 if (w != NULL) {
707 x = do_parse(filename, NULL, f);
708 mpdm_close(w);
711 mpdm_unref(inc);
712 mpdm_unref(file);
714 return x;
719 * mpsl_eval - Evaluates MSPL code.
720 * @code: A value containing a string of MPSL code, or executable code
721 * @args: optional arguments for @code
722 * @ctxt: context for @code
724 * Evaluates a piece of code. The @code can be a string containing MPSL source
725 * code (that will be compiled) or a direct executable value. If the compilation
726 * or the execution gives an error, the ERROR variable will be set to a printable
727 * value and NULL returned. Otherwise, the exit value from the code is returned
728 * and ERROR set to NULL. The abort flag is reset on exit.
730 mpdm_t mpsl_eval(mpdm_t code, mpdm_t args, mpdm_t ctxt)
732 mpdm_t cs, r;
734 /* reset error */
735 mpsl_error(NULL);
736 mpsl_abort = 0;
738 mpdm_ref(code);
740 /* if code is not executable, try to compile */
741 if (!MPDM_IS_EXEC(code)) {
742 mpdm_t c;
744 /* get the eval cache */
745 if ((c = mpdm_hget_s(mpdm_root(), L"__EVAL__")) == NULL)
746 c = mpdm_hset_s(mpdm_root(), L"__EVAL__", MPDM_H(0));
748 /* this code still not compiled? do it */
749 if ((cs = mpdm_hget(c, code)) == NULL)
750 cs = mpdm_hset(c, code, mpsl_compile(code));
752 else
753 cs = code;
755 /* execute, if possible */
756 if (MPDM_IS_EXEC(cs))
757 r = mpdm_exec(cs, args, ctxt);
758 else
759 r = NULL;
761 /* reset the abort flag */
762 mpsl_abort = 0;
764 mpdm_unref(code);
766 return r;