Updated TODO (Closes: #1106).
[mpsl.git] / mpsl.y
blob2b3835bcf17d1e1f03b8f26a303acfa2c911d083
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
100 %left '!'
101 %left STRCAT STREQ NUMEQ STRNE NUMNE NUMGE NUMLE HASHPAIR RANGE '>''<'
102 %left BITAND
103 %left BITOR BITXOR
104 %left '+' '-'
105 %left '*' '/' MOD
106 %nonassoc UMINUS
108 %type <ins> stmt expr sym_list stmt_list list hash compsym
112 program:
113 function { ; }
116 function:
117 function stmt_list {
118 mpsl_bytecode = $2;
120 | /* NULL */
123 stmt:
124 ';' {
125 /* null instruction */
126 $$ = INS0(L"MULTI");
128 | expr ';' {
129 /* expression, as is */
130 $$ = $1;
133 | WHILE '(' expr ')' stmt
135 /* while loop */
136 $$ = INS2(L"WHILE", $3, $5);
138 | IF '(' expr ')' stmt %prec IFI
140 /* if - then construction */
141 $$ = INS2(L"IF", $3, $5);
143 | IF '(' expr ')' stmt ELSE stmt
145 /* if - then - else construction */
146 $$ = INS3(L"IF", $3, $5, $7);
149 | SUB compsym '{' stmt_list '}'
151 /* subroutine definition,
152 without arguments */
153 $$ = INS2(L"ASSIGN", $2,
154 INS1(L"LITERAL",
155 mpsl_x($4, NULL, 1)));
158 | SUB compsym '(' ')' '{' stmt_list '}'
160 /* subroutine definition,
161 without arguments (second
162 syntax, including parens) */
163 $$ = INS2(L"ASSIGN", $2,
164 INS1(L"LITERAL",
165 mpsl_x($6, NULL, 1)));
168 | SUB compsym '(' sym_list ')' '{' stmt_list '}'
170 /* subroutine definition,
171 with arguments */
172 $$ = INS2(L"ASSIGN", $2,
173 INS1(L"LITERAL",
174 mpsl_x($7, $4, 1)));
177 | FOREACH '(' compsym ',' expr ')' stmt
179 /* foreach construction */
180 /* a block frame is created, the iterator
181 created as local, and the foreach executed */
182 $$ = INS1(L"BLKFRAME",
183 INS2(L"MULTI",
184 INS1(L"LOCAL", $3),
185 INS3(L"FOREACH", $3, $5, $7)
189 | FOREACH '(' LOCAL compsym ',' expr ')' stmt
191 compiler_warning("useless use of local in foreach loop");
193 $$ = INS1(L"BLKFRAME",
194 INS2(L"MULTI",
195 INS1(L"LOCAL", $4),
196 INS3(L"FOREACH", $4, $6, $8)
201 | '{' stmt_list '}' {
202 /* block of instructions,
203 with local symbol table */
204 $$ = INS1(L"BLKFRAME", $2);
207 | LOCAL sym_list ';' {
208 /* local symbol creation */
209 $$ = INS1(L"LOCAL", $2);
211 | LOCAL SYMBOL '=' expr ';'
213 /* contraction; local symbol
214 creation and assignation */
215 $$ = INS2(L"MULTI",
216 INS1(L"LOCAL",
217 INS1(L"LITERAL", $2)),
218 INS2(L"ASSIGN",
219 INS1(L"LITERAL", $2),$4)
222 | BREAK ';' {
223 /* break (exit from loop) */
224 $$ = INS0(L"BREAK");
226 | RETURN expr ';' {
227 /* return from subroutine */
228 $$ = INS1(L"RETURN", $2);
230 | RETURN ';' {
231 /* return from subroutine (void) */
232 $$ = INS0(L"RETURN");
236 stmt_list:
237 stmt { $$ = $1; }
238 | stmt_list stmt {
239 /* sequence of instructions */
240 $$ = INS2(L"MULTI", $1, $2);
244 list:
245 expr {
246 $$ = INS1(L"LIST", $1);
248 | list ',' expr {
249 /* build list from list of
250 instructions */
251 $$ = INS2(L"LIST", $3, $1);
255 sym_list:
256 SYMBOL {
257 $$ = INS1(L"LIST",
258 INS1(L"LITERAL", $1));
260 | sym_list ',' SYMBOL {
261 /* comma-separated list of symbols */
262 $$ = INS2(L"LIST",
263 INS1(L"LITERAL", $3), $1);
267 hash:
268 expr HASHPAIR expr {
269 $$ = INS2(L"HASH", $1, $3);
271 | hash ',' expr HASHPAIR expr
273 /* build hash from list of
274 instructions */
275 $$ = INS3(L"HASH", $3, $5, $1);
279 compsym:
280 SYMBOL {
281 $$ = INS1(L"LIST",
282 INS1(L"LITERAL", $1));
284 | compsym '.' INTEGER {
285 /* a.5 compound symbol */
286 $$ = INS2(L"LIST",
287 INS1(L"LITERAL", $3), $1);
289 | compsym '.' SYMBOL {
290 /* a.b compound symbol */
291 $$ = INS2(L"LIST",
292 INS1(L"LITERAL", $3), $1);
294 | compsym '[' expr ']' {
295 /* a["b"] or a[5] compound symbol */
296 $$ = INS2(L"LIST", $3, $1);
300 expr:
301 INTEGER {
302 /* literal integer */
303 $$ = INS1(L"LITERAL", $1);
305 | STRING {
306 /* literal string */
307 $$ = INS1(L"LITERAL", $1);
309 | REAL {
310 /* literal real number */
311 $$ = INS1(L"LITERAL", $1);
313 | compsym {
314 /* compound symbol */
315 $$ = INS1(L"SYMVAL", $1);
317 | NULLV {
318 /* NULL value */
319 $$ = INS1(L"LITERAL", NULL);
322 | '-' expr %prec UMINUS {
323 /* unary minus */
324 $$ = INS1(L"UMINUS", $2);
327 /* math operations */
328 | expr '+' expr { $$ = INS2(L"ADD", $1, $3); }
329 | expr '-' expr { $$ = INS2(L"SUB", $1, $3); }
330 | expr '*' expr { $$ = INS2(L"MUL", $1, $3); }
331 | expr '/' expr { $$ = INS2(L"DIV", $1, $3); }
332 | expr MOD expr { $$ = INS2(L"MOD", $1, $3); }
334 /* bit operations */
335 | expr BITAND expr { $$ = INS2(L"BITAND", $1, $3); }
336 | expr BITOR expr { $$ = INS2(L"BITOR", $1, $3); }
337 | expr BITXOR expr { $$ = INS2(L"BITXOR", $1, $3); }
339 /* increment and decrement (prefix) */
340 | INC compsym { $$ = INS2(L"ASSIGN", $2,
341 INS2(L"ADD",
342 INS1(L"SYMVAL", $2),
343 INS1(L"LITERAL", MPDM_I(1))
347 | DEC compsym { $$ = INS2(L"ASSIGN", $2,
348 INS2(L"SUB",
349 INS1(L"SYMVAL", $2),
350 INS1(L"LITERAL", MPDM_I(1))
355 /* increment and decrement (suffix) */
356 | compsym INC { $$ = INS2(L"IMULTI",
357 INS1(L"SYMVAL", $1),
358 INS2(L"ASSIGN", $1,
359 INS2(L"ADD",
360 INS1(L"SYMVAL", $1),
361 INS1(L"LITERAL", MPDM_I(1))
366 | compsym DEC { $$ = INS2(L"IMULTI",
367 INS1(L"SYMVAL", $1),
368 INS2(L"ASSIGN", $1,
369 INS2(L"SUB",
370 INS1(L"SYMVAL", $1),
371 INS1(L"LITERAL", MPDM_I(1))
377 /* immediate math operations */
378 | compsym IADD expr { $$ = INS2(L"ASSIGN", $1,
379 INS2(L"ADD",
380 INS1(L"SYMVAL", $1),
384 | compsym ISUB expr { $$ = INS2(L"ASSIGN", $1,
385 INS2(L"SUB",
386 INS1(L"SYMVAL", $1),
390 | compsym IMUL expr { $$ = INS2(L"ASSIGN", $1,
391 INS2(L"MUL",
392 INS1(L"SYMVAL", $1),
396 | compsym IDIV expr { $$ = INS2(L"ASSIGN", $1,
397 INS2(L"DIV",
398 INS1(L"SYMVAL", $1),
402 | compsym IMOD expr { $$ = INS2(L"ASSIGN", $1,
403 INS2(L"MOD",
404 INS1(L"SYMVAL", $1),
409 | '!' expr {
410 /* boolean not */
411 $$ = INS1(L"NOT", $2);
413 | expr '<' expr {
414 /* bool less than */
415 $$ = INS2(L"NUMLT", $1, $3);
417 | expr '>' expr {
418 /* bool greater than */
419 $$ = INS2(L"NUMGT", $1, $3);
421 | expr NUMLE expr {
422 /* bool less or equal than */
423 $$ = INS2(L"NUMLE", $1, $3);
425 | expr NUMGE expr {
426 /* bool greater or equal than */
427 $$ = INS2(L"NUMGE", $1, $3);
429 | expr NUMEQ expr {
430 /* bool numeric equal */
431 $$ = INS2(L"NUMEQ", $1, $3);
433 | expr NUMNE expr {
434 /* bool numeric non-equal */
435 $$ = INS1(L"NOT",
436 INS2(L"NUMEQ", $1, $3));
439 | expr STRCAT expr {
440 /* string concatenation */
441 $$ = INS2(L"STRCAT", $1, $3);
443 | expr STREQ expr {
444 /* bool string equal */
445 $$ = INS2(L"STREQ", $1, $3);
447 | expr STRNE expr {
448 /* bool string non-equal */
449 $$ = INS1(L"NOT",
450 INS2(L"STREQ", $1, $3));
453 | expr BOOLAND expr {
454 /* boolean and */
455 $$ = INS2(L"AND", $1, $3);
457 | expr BOOLOR expr {
458 /* boolean or */
459 $$ = INS2(L"OR", $1, $3);
462 | SUB '{' stmt_list '}' {
463 /* anonymous subroutine (without args) */
464 $$ = INS1(L"LITERAL", mpsl_x($3, NULL, 0));
467 | SUB '(' sym_list ')' '{' stmt_list '}'
469 /* anonymous subroutine (with args) */
470 $$ = INS1(L"LITERAL", mpsl_x($6, $3, 0));
473 | '(' expr ')' {
474 /* parenthesized expression */
475 $$ = $2;
478 | '[' ']' {
479 /* empty list */
480 $$ = INS1(L"LITERAL", MPDM_A(0));
482 | '[' list ']' {
483 /* non-empty list */
484 $$ = $2;
486 | '[' expr RANGE expr ']'
488 /* build range from expressions */
489 $$ = INS2(L"RANGE", $2, $4);
492 | '{' '}' {
493 /* empty hash */
494 $$ = INS1(L"LITERAL", MPDM_H(0));
496 | '{' hash '}' {
497 /* non-empty hash */
498 $$ = $2;
501 | compsym '(' ')' {
502 /* function call (without args) */
503 $$ = INS1(L"EXECSYM", $1);
505 | compsym '(' list ')' {
506 /* function call (with args) */
507 $$ = INS2(L"EXECSYM", $1, $3);
509 | compsym '=' expr {
510 /* simple assignation */
511 $$ = INS2(L"ASSIGN", $1, $3);
518 void yyerror(char * s)
520 char tmp[1024];
522 snprintf(tmp, sizeof(tmp), "%s in %s, line %d",
523 s, mpsl_filename, mpsl_line + 1);
525 mpsl_error(MPDM_MBS(tmp));
529 static FILE * inc_fopen(const char * filename, mpdm_t inc)
530 /* loads filename, searching in INC if not directly accesible */
532 FILE * f = NULL;
533 char tmp[1024];
534 int n;
536 /* loop through INC, prepending each path
537 to the filename */
538 for (n = 0; n < mpdm_size(inc); n++) {
539 mpdm_t v = mpdm_aget(inc, n);
541 v = MPDM_2MBS(v->data);
542 snprintf(tmp, sizeof(tmp), "%s/%s",
543 (char *)v->data, filename);
545 if ((f = fopen(tmp, "r")) != NULL)
546 break;
549 return f;
553 static mpdm_t do_parse(const char * filename, wchar_t * code, FILE * file)
554 /* calls yyparse() after doing some initialisations, and returns
555 the compiled code as an executable value */
557 mpdm_t v;
558 mpdm_t x = NULL;
560 /* first line */
561 mpsl_line = 0;
563 /* reset last bytecode */
564 mpsl_bytecode = NULL;
566 /* set globals */
567 mpsl_next_char = code;
568 mpsl_file = file;
570 if (mpsl_filename != NULL)
571 free(mpsl_filename);
573 mpsl_filename = strdup(filename);
575 /* cache some values */
576 v = mpdm_hget_s(mpdm_root(), L"MPSL");
577 mpsl_opcodes = mpdm_hget_s(v, L"OPCODE");
578 mpsl_lc = mpdm_hget_s(v, L"LC");
580 /* compile! */
581 if (yyparse() == 0 && mpsl_bytecode != NULL)
582 x = mpsl_x(mpsl_bytecode, NULL, 1);
584 /* clean back cached values */
585 mpsl_opcodes = NULL;
586 mpsl_lc = NULL;
588 return x;
593 * mpsl_compile - Compiles a string of MPSL code.
594 * @code: A value containing a string of MPSL code
596 * Compiles a string of MPSL code and returns an mpdm value executable
597 * by mpdm_exec(). If there is a syntax (or other type) error, NULL
598 * is returned instead.
600 mpdm_t mpsl_compile(mpdm_t code)
602 mpdm_t x = NULL;
604 mpdm_ref(code);
605 x = do_parse("<INLINE>", (wchar_t *) code->data, NULL);
606 mpdm_unref(code);
608 return x;
613 * mpsl_compile_file - Compiles a file of MPSL code.
614 * @file: File stream or file name.
616 * Compiles a source file of MPSL code and returns an mpdm value
617 * executable by mpdm_exec(). If @file is an MPSL file descriptor,
618 * it's read as is and compiled; otherwise, it's assumed to be a
619 * file name, that will be searched for in any of the paths defined
620 * in the INC MPSL global array (take note that the current
621 * directory is NOT searched by default). If the file cannot be found
622 * or there is any other error, NULL is returned instead.
624 mpdm_t mpsl_compile_file(mpdm_t file)
626 mpdm_t x = NULL;
627 FILE * f = NULL;
628 const char * filename = NULL;
630 if ((f = mpdm_get_filehandle(file)) != NULL) {
631 filename = "<FILE>";
633 else {
634 mpdm_t inc = mpsl_get_symbol(MPDM_LS(L"INC"));
636 /* it's a filename; open it */
637 file = MPDM_2MBS(file->data);
639 filename = file->data;
641 if ((f = inc_fopen(filename, inc)) == NULL) {
642 char tmp[128];
644 snprintf(tmp, sizeof(tmp) - 1,
645 "File '%s' not found in INC",
646 filename);
647 mpsl_error(MPDM_MBS(tmp));
649 return (NULL);
652 file = MPDM_F(f);
655 x = do_parse(filename, NULL, f);
657 mpdm_close(file);
659 return x;
664 * mpsl_eval - Evaluates MSPL code.
665 * @code: A value containing a string of MPSL code, or executable code
666 * @args: optional arguments for @code
668 * Evaluates a piece of code. The @code can be a string containing MPSL source
669 * code (that will be compiled) or a direct executable value. If the compilation
670 * or the execution gives an error, the ERROR variable will be set to a printable
671 * value and NULL returned. Otherwise, the exit value from the code is returned
672 * and ERROR set to NULL. The abort flag is reset on exit.
674 mpdm_t mpsl_eval(mpdm_t code, mpdm_t args)
676 mpdm_t v = NULL;
678 /* reset error */
679 mpsl_error(NULL);
680 mpsl_abort = 0;
682 /* if code is not executable, try to compile */
683 if (!MPDM_IS_EXEC(code))
684 code = mpsl_compile(code);
686 /* execute, if possible */
687 if (MPDM_IS_EXEC(code))
688 v = mpdm_exec(code, args);
690 /* reset the abort flag */
691 mpsl_abort = 0;
693 return v;