1 /*
2  *  cParser.cc
3  *  Avida
4  *
5  *  Created by David on 1/16/06.
6  *  Copyright 1999-2011 Michigan State University. All rights reserved.
7  *
8  *
9  *  This file is part of Avida.
10  *
11  *  Avida is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License
12  *  as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
13  *
14  *  Avida is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License along with Avida.
18  *  If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include "cParser.h"
23 
24 #include "AvidaScript.h"
25 #include "cFile.h"
26 #include "tAutoRelease.h"
27 
28 using namespace AvidaScript;
29 
30 /*
31  The following represents the grammar for AvidaScript in BNF, adjusted so that it is compatible with recursive descent
32  parsing (to be) implemented by cParser.
33 
34 
35  script: statement_list
36 
37  statement_list: statement statement_list
38                |
39 
40  statement: assign_expr lineterm
41           | var_declare lineterm
42           | loose_block
43           | if_block lineterm
44           | while_block lineterm
45           | foreach_block lineterm
46           | declare_function lineterm
47           | define_function lineterm
48           | call_expr lineterm
49           | return_stmt lineterm
50           | lineterm
51 
52  lineterm: SUPPRESS | ENDL
53 
54  type_def: TYPE_ARRAY | TYPE_BOOL | TYPE_CHAR | TYPE_FLOAT | TYPE_INT | TYPE_MATRIX | TYPE_STRING | ID REF
55  type_any: type_def | TYPE_VOID
56 
57  assign_expr: assign_dest ASSIGN expr
58 
59  assign_dest: ID
60             | REF ARR_OPEN id_list ARR_CLOSE
61 
62  id_list: ID id_list_1
63 
64  id_list_1: COMMA ID id_list_1
65           | COMMA ID ARR_WILD
66           | COMMA ARR_WILD
67           |
68 
69 
70  expr: p0_expr
71 
72  p0_expr: p1_expr p0_expr_1
73 
74  p0_expr_1: ARR_RANGE p1_expr p0_expr_1
75           | ARR_EXPAN p1_expr p0_expr_1
76           |
77 
78  p1_expr: p2_expr p1_expr_1
79 
80  p1_expr_1: OP_LOGIC_AND p2_expr p1_expr_1
81           | OP_LOGIC_OR p2_expr p1_expr_1
82           |
83 
84  p2_expr: p3_expr p2_expr_1
85 
86  p2_expr_1: OP_BIT_AND p3_expr p2_expr_1
87           | OP_BIT_OR p3_expr p2_expr_1
88           |
89 
90 
91  p3_expr: p4_expr p3_expr_1
92 
93  p3_expr_1: OP_EQ p4_expr p3_expr_1
94           | OP_LE p4_expr p3_expr_1
95           | OP_GE p4_expr p3_expr_1
96           | OP_LT p4_expr p3_expr_1
97           | OP_GT p4_expr p3_expr_1
98           | OP_NEQ p4_expr p3_expr_1
99           |
100 
101 
102  p4_expr: p5_expr p4_expr_1
103 
104  p4_expr_1: OP_ADD p5_expr p4_expr_1
105           | OP_SUB p5_expr p4_expr_1
106           |
107 
108 
109  p5_expr: p6_expr p5_expr_1
110 
111  p5_expr_1: OP_MUL p6_expr p5_expr_1
112           | OP_DIV p6_expr p5_expr_1
113           | OP_MOD p6_expr p5_expr_1
114           |
115 
116  p6_expr: value p6_expr_1
117         | OP_BIT_NOT expr
118         | OP_LOGIC_NOT expr
119         | OP_SUB expr
120 
121  p6_expr_1: DOT ID id_expr p6_expr_1
122           | IDX_OPEN expr IDX_CLOSE
123           |
124 
125  value: FLOAT
126       | INT
127       | STRING
128       | CHAR
129       | ID id_expr
130       | PREC_OPEN expr PREC_CLOSE
131       | ARR_OPEN argument_list ARR_CLOSE
132       | MAT_MODIFY ARR_OPEN argument_list ARR_CLOSE
133 
134  id_expr: PREC_OPEN argument_list PREC_CLOSE
135         |
136 
137 
138  call_expr: call_trgt PREC_OPEN argument_list PREC_CLOSE
139 
140  call_trgt: ID call_value
141 
142  call_value: DOT ID call_value
143            | PREC_OPEN argument_list PREC_CLOSE call_sub_idx DOT ID call_value
144            | IDX_OPEN expr IDX_CLOSE call_sub_idx DOT ID call_value
145            |
146 
147  call_sub_idx: IDX_OPEN expr IDX_CLOSE call_sub_idx
148              |
149 
150 
151  argument_list: argument_list_1
152               |
153 
154  argument_list_1: expr argument_list_2
155 
156  argument_list_2: COMMA expr argument_list_2
157                 |
158 
159  loose_block: ARR_OPEN statement_list ARR_CLOSE
160 
161  if_block: CMD_IF PREC_OPEN expr PREC_CLOSE loose_block
162          | CMD_IF PREC_OPEN expr PREC_CLOSE loose_block CMD_ELSE loose_block
163 
164  while_block: CMD_WHILE PREC_OPEN expr PREC_CLOSE loose_block
165 
166  foreach_block: CMD_FOREACH type_def ID PREC_OPEN expr PREC_CLOSE loose_block
167 
168  var_declare: type_def ID
169             | type_def ID ASSIGN expr
170             | type_def ID PREC_OPEN argument_list PREC_CLOSE
171 
172  var_declare_list: var_declare_list_1
173                  |
174 
175  var_declare_list_1: var_declare var_declare_list_2
176 
177  var_declare_list_2: COMMA var_declare var_declare_list_2
178                    |
179 
180  declare_function: REF CMD_FUNCTION type_any ID PREC_OPEN var_declare_list PREC_CLOSE
181  define_function: CMD_FUNCTION type_any ID PREC_OPEN var_declare_list PREC_CLOSE loose_block
182 
183  return_stmt: CMD_RETURN expr
184 
185  */
186 
187 #ifndef DEBUG_AS_PARSER
188 #define DEBUG_AS_PARSER 0
189 #endif
190 
191 #if DEBUG_AS_PARSER
192 # define PARSE_DEBUG(x) { std::cerr << x << std::endl; }
193 # define PARSE_TRACE(x) { std::cerr << "trace: " << x << std::endl; }
194 #else
195 # define PARSE_DEBUG(x)
196 # define PARSE_TRACE(x)
197 #endif
198 
199 #define PARSE_ERROR(x) reportError(AS_PARSE_ERR_ ## x, __LINE__)
200 #define PARSE_UNEXPECT() { if (currentToken()) { PARSE_ERROR(UNEXPECTED_TOKEN); } else { PARSE_ERROR(EOF); } return NULL; }
201 
202 #define FILEPOS cASFilePosition(m_filename, m_lexer ? m_lexer->lineno() : 0)
203 
204 #define TOKEN(x) AS_TOKEN_ ## x
205 
206 
cParser()207 cParser::cParser()
208 : m_filename("(unknown)")
209 , m_eof(false)
210 , m_success(true)
211 , m_cur_tok(TOKEN(INVALID))
212 , m_next_tok(TOKEN(INVALID))
213 , m_cur_text(NULL)
214 , m_err_eof(false)
215 {
216 }
217 
Parse(cFile & input)218 bool cParser::Parse(cFile& input)
219 {
220   m_filename = input.GetFilename();
221   m_lexer = new cLexer(input.GetFileStream());
222 
223   m_tree = parseStatementList();
224 
225   if (!m_eof && m_success) PARSE_ERROR(UNEXPECTED_TOKEN);
226   if (!m_tree) PARSE_ERROR(EMPTY);
227 
228   delete m_lexer;
229   m_lexer = NULL;
230 
231   return m_success;
232 }
233 
~cParser()234 cParser::~cParser()
235 {
236   delete m_tree;
237   delete m_lexer;
238 }
239 
240 
241 
nextToken()242 ASToken_t cParser::nextToken()
243 {
244   if (m_next_tok != TOKEN(INVALID)) {
245     m_cur_tok = m_next_tok;
246     m_next_tok = TOKEN(INVALID);
247   } else {
248     m_cur_tok = (ASToken_t)m_lexer->yylex();
249   }
250   delete m_cur_text;
251   m_cur_text = NULL;
252   PARSE_DEBUG("nextToken: " << m_cur_tok);
253   return m_cur_tok;
254 }
255 
peekToken()256 ASToken_t cParser::peekToken()
257 {
258   if (m_next_tok == TOKEN(INVALID)) {
259     delete m_cur_text;
260     m_cur_text = new cString(m_lexer->YYText());
261     m_next_tok = (ASToken_t)m_lexer->yylex();
262   }
263   return m_next_tok;
264 }
265 
currentText()266 const cString& cParser::currentText()
267 {
268   if (!m_cur_text) m_cur_text = new cString(m_lexer->YYText());
269   return *m_cur_text;
270 }
271 
272 
parseArrayUnpack()273 cASTNode* cParser::parseArrayUnpack()
274 {
275   PARSE_TRACE("parseArrayUnpack");
276 
277   if (nextToken() != TOKEN(ID)) PARSE_UNEXPECT();
278 
279   tAutoRelease<cASTUnpackTarget> ut(new cASTUnpackTarget(FILEPOS));
280   (*ut).AddVar(currentText());
281 
282   while (nextToken()) {
283     if (currentToken() == TOKEN(COMMA)) {
284       nextToken();
285       if (currentToken() == TOKEN(ID)) {
286         (*ut).AddVar(currentText());
287         continue;
288       } else if (currentToken() == TOKEN(ARR_WILD)) {
289         (*ut).SetLastWild();
290         break;
291       } else {
292         PARSE_ERROR(UNEXPECTED_TOKEN);
293         break;
294       }
295     } else if (currentToken() == TOKEN(ARR_WILD)) {
296       (*ut).SetLastNamed();
297       break;
298     } else {
299       PARSE_UNEXPECT();
300     }
301   }
302 
303   if (nextToken() != TOKEN(ARR_CLOSE)) PARSE_UNEXPECT();
304   if (nextToken() != TOKEN(ASSIGN)) PARSE_UNEXPECT();
305   nextToken(); // consume '='
306 
307   (*ut).SetExpression(parseExpression());
308 
309   return ut.Release();
310 }
311 
parseArgumentList()312 cASTArgumentList* cParser::parseArgumentList()
313 {
314   PARSE_TRACE("parseArgumentList");
315   cASTArgumentList* al = new cASTArgumentList(FILEPOS);
316 
317   al->AddNode(parseExpression());
318   while (currentToken() == TOKEN(COMMA)) {
319     nextToken(); // consume ','
320     al->AddNode(parseExpression());
321   }
322 
323   return al;
324 }
325 
parseAssignment()326 cASTNode* cParser::parseAssignment()
327 {
328   PARSE_TRACE("parseAssignment");
329   cASTAssignment* an = new cASTAssignment(FILEPOS, currentText());
330 
331   nextToken(); // consume id
332 
333   nextToken(); // consume '='
334   cASTNode* expr = parseExpression();
335   an->SetExpression(expr);
336 
337   return an;
338 }
339 
parseCallExpression(cASTNode * target,bool required)340 cASTNode* cParser::parseCallExpression(cASTNode* target, bool required)
341 {
342   PARSE_TRACE("parseCallExpression");
343   tAutoRelease<cASTNode> ce(target);
344 
345   if (currentToken() == TOKEN(DOT) && peekToken() == TOKEN(BUILTIN_METHOD)) {
346     nextToken(); // consume '.'
347 
348     cASTBuiltInCall* bi = new cASTBuiltInCall(FILEPOS, currentText(), ce.Release());
349     ce.Set(bi);
350 
351     if (nextToken() != TOKEN(PREC_OPEN)) PARSE_UNEXPECT();
352     if (nextToken() != TOKEN(PREC_CLOSE)) bi->SetArguments(parseArgumentList());
353     if (currentToken() != TOKEN(PREC_CLOSE)) PARSE_UNEXPECT();
354     nextToken(); // consume ')'
355 
356     if (currentToken() != TOKEN(DOT) || currentToken() != TOKEN(IDX_OPEN)) return ce.Release();
357   }
358 
359 
360   bool eoe = false;
361   while (!eoe) {
362     if (currentToken() == TOKEN(DOT)) {
363       if (nextToken() != TOKEN(ID)) PARSE_UNEXPECT();
364       cString name(currentText());
365       nextToken(); // consume id
366 
367       if (currentToken() == TOKEN(PREC_OPEN)) {
368         cASTObjectCall* oc = new cASTObjectCall(FILEPOS, ce.Release(), name);
369         ce.Set(oc);
370         if (nextToken() != TOKEN(PREC_CLOSE)) oc->SetArguments(parseArgumentList());
371         if (currentToken() != TOKEN(PREC_CLOSE)) PARSE_UNEXPECT();
372         nextToken(); // consume ')'
373 
374         // If the next token is not a continued call expression, then set the end-of-expression flag
375         if (currentToken() != TOKEN(IDX_OPEN) && currentToken() != TOKEN(DOT)) eoe = true;
376       } else {
377         ce.Set(new cASTObjectReference(FILEPOS, ce.Release(), name));
378 
379         if (required && currentToken() == TOKEN(ASSIGN)) {
380           cASTObjectAssignment* oa = new cASTObjectAssignment(FILEPOS, ce.Release());
381           ce.Set(oa);
382           nextToken(); // consume '='
383           oa->SetExpression(parseExpression());
384           eoe = true;
385         }
386       }
387     } else if (currentToken() == TOKEN(IDX_OPEN)) {
388       do {
389         nextToken(); // consume '['
390         ce.Set(new cASTExpressionBinary(FILEPOS, TOKEN(IDX_OPEN), ce.Release(), parseExpression()));
391         if (currentToken() != TOKEN(IDX_CLOSE)) PARSE_UNEXPECT();
392       } while (nextToken() == TOKEN(IDX_OPEN));
393 
394       if (required && currentToken() == TOKEN(ASSIGN)) {
395         cASTObjectAssignment* oa = new cASTObjectAssignment(FILEPOS, ce.Release());
396         ce.Set(oa);
397         nextToken(); // consume '='
398         oa->SetExpression(parseExpression());
399         eoe = true;
400       }
401     } else {
402       if (required) { PARSE_UNEXPECT(); }
403       else eoe = true;
404     }
405   }
406 
407   return ce.Release();
408 }
409 
parseCodeBlock()410 cASTNode* cParser::parseCodeBlock()
411 {
412   PARSE_TRACE("parseCodeBlock");
413 
414   // Swallow all newlines and suppress tokens
415   while (currentToken() == TOKEN(ENDL) || currentToken() == TOKEN(SUPPRESS)) nextToken();
416 
417   if (currentToken() == TOKEN(ARR_OPEN)) return parseLooseBlock();
418 
419   PARSE_UNEXPECT();
420 }
421 
parseExpression()422 cASTNode* cParser::parseExpression()
423 {
424   PARSE_TRACE("parseExpression");
425   cASTNode* expr = NULL;
426 
427   expr = parseExprP0();
428   if (!expr) {
429     if (currentToken())
430       PARSE_ERROR(NULL_EXPR);
431     else
432       PARSE_ERROR(EOF);
433   }
434 
435   return expr;
436 }
437 
parseExprP0()438 cASTNode* cParser::parseExprP0()
439 {
440   PARSE_TRACE("parseExprP0");
441   cASTNode* l = parseExprP1();
442   cASTNode* r = NULL;
443 
444   while(true) {
445     switch (currentToken()) {
446       case TOKEN(ARR_RANGE):
447       case TOKEN(ARR_EXPAN):
448         ASToken_t op = currentToken();
449         nextToken();
450         r = parseExprP1();
451         if (!r) PARSE_ERROR(NULL_EXPR);
452         l = new cASTExpressionBinary(FILEPOS, op, l, r);
453         break;
454 
455       default:
456         return l;
457     }
458   }
459 
460   return l;
461 }
462 
parseExprP1()463 cASTNode* cParser::parseExprP1()
464 {
465   PARSE_TRACE("parseExprP1");
466   cASTNode* l = parseExprP2();
467   cASTNode* r = NULL;
468 
469   while(true) {
470     switch (currentToken()) {
471       case TOKEN(OP_LOGIC_AND):
472       case TOKEN(OP_LOGIC_OR):
473         ASToken_t op = currentToken();
474         nextToken();
475         r = parseExprP2();
476         if (!r) PARSE_ERROR(NULL_EXPR);
477         l = new cASTExpressionBinary(FILEPOS, op, l, r);
478         break;
479 
480       default:
481         return l;
482     }
483   }
484 
485   return l;
486 }
487 
parseExprP2()488 cASTNode* cParser::parseExprP2()
489 {
490   PARSE_TRACE("parseExprP2");
491   cASTNode* l = parseExprP3();
492   cASTNode* r = NULL;
493 
494   while(true) {
495     switch (currentToken()) {
496       case TOKEN(OP_BIT_AND):
497       case TOKEN(OP_BIT_OR):
498         ASToken_t op = currentToken();
499         nextToken();
500         r = parseExprP3();
501         if (!r) PARSE_ERROR(NULL_EXPR);
502         l = new cASTExpressionBinary(FILEPOS, op, l, r);
503         break;
504 
505       default:
506         return l;
507     }
508   }
509 
510   return l;
511 }
512 
parseExprP3()513 cASTNode* cParser::parseExprP3()
514 {
515   PARSE_TRACE("parseExprP3");
516   cASTNode* l = parseExprP4();
517   cASTNode* r = NULL;
518 
519   while(true) {
520     switch (currentToken()) {
521       case TOKEN(OP_EQ):
522       case TOKEN(OP_LE):
523       case TOKEN(OP_GE):
524       case TOKEN(OP_LT):
525       case TOKEN(OP_GT):
526       case TOKEN(OP_NEQ):
527         ASToken_t op = currentToken();
528         nextToken();
529         r = parseExprP4();
530         if (!r) PARSE_ERROR(NULL_EXPR);
531         l = new cASTExpressionBinary(FILEPOS, op, l, r);
532         break;
533 
534       default:
535         return l;
536     }
537   }
538 
539   return l;
540 }
541 
parseExprP4()542 cASTNode* cParser::parseExprP4()
543 {
544   PARSE_TRACE("parseExprP4");
545   cASTNode* l = parseExprP5();
546   cASTNode* r = NULL;
547 
548   while(true) {
549     switch (currentToken()) {
550       case TOKEN(OP_ADD):
551       case TOKEN(OP_SUB):
552         ASToken_t op = currentToken();
553         nextToken();
554         r = parseExprP5();
555         if (!r) PARSE_ERROR(NULL_EXPR);
556         l = new cASTExpressionBinary(FILEPOS, op, l, r);
557         break;
558 
559       default:
560         return l;
561     }
562   }
563 
564   return l;
565 }
566 
parseExprP5()567 cASTNode* cParser::parseExprP5()
568 {
569   PARSE_TRACE("parseExprP5");
570   cASTNode* l = parseExprP6();
571   cASTNode* r = NULL;
572 
573   while(true) {
574     switch (currentToken()) {
575       case TOKEN(OP_MUL):
576       case TOKEN(OP_DIV):
577       case TOKEN(OP_MOD):
578         ASToken_t op = currentToken();
579         nextToken();
580         r = parseExprP6();
581         if (!r) PARSE_ERROR(NULL_EXPR);
582         l = new cASTExpressionBinary(FILEPOS, op, l, r);
583         break;
584 
585       default:
586         return l;
587     }
588   }
589 
590   return l;
591 }
592 
parseExprP6()593 cASTNode* cParser::parseExprP6()
594 {
595   PARSE_TRACE("parseExprP6");
596   tAutoRelease<cASTNode> expr;
597 
598   bool is_matrix = false;
599 
600   switch (currentToken()) {
601     case TOKEN(FLOAT):
602       expr.Set(new cASTLiteral(FILEPOS, AS_TYPE_FLOAT, currentText()));
603       break;
604     case TOKEN(INT):
605       expr.Set(new cASTLiteral(FILEPOS, AS_TYPE_INT, currentText()));
606       break;
607     case TOKEN(CHAR):
608       expr.Set(new cASTLiteral(FILEPOS, AS_TYPE_CHAR,
609                                currentText().Substring(1, currentText().GetSize() - 2).ParseEscapeSequences()));
610       break;
611     case TOKEN(BOOL):
612       expr.Set(new cASTLiteral(FILEPOS, AS_TYPE_BOOL, currentText()));
613       break;
614     case TOKEN(STRING):
615       expr.Set(new cASTLiteral(FILEPOS, AS_TYPE_STRING,
616                                currentText().Substring(1, currentText().GetSize() - 2).ParseEscapeSequences()));
617       break;
618     case TOKEN(ID):
619       if (peekToken() == TOKEN(PREC_OPEN)) {
620         cASTFunctionCall* fc = new cASTFunctionCall(FILEPOS, currentText());
621         expr.Set(fc);
622         nextToken(); // consume id token
623         if (nextToken() != TOKEN(PREC_CLOSE)) fc->SetArguments(parseArgumentList());
624         if (currentToken() != TOKEN(PREC_CLOSE)) PARSE_UNEXPECT();
625       } else {
626         expr = new cASTVariableReference(FILEPOS, currentText());
627       }
628       break;
629     case TOKEN(BUILTIN_CALL):
630       if (peekToken() == TOKEN(PREC_OPEN)) {
631         cASTBuiltInCall* bi = new cASTBuiltInCall(FILEPOS, currentText());
632         expr.Set(bi);
633         nextToken(); // consume builtin methon name token
634         if (nextToken() != TOKEN(PREC_CLOSE)) bi->SetArguments(parseArgumentList());
635         if (currentToken() != TOKEN(PREC_CLOSE)) PARSE_UNEXPECT();
636       } else {
637         PARSE_UNEXPECT();
638       }
639       break;
640     case TOKEN(PREC_OPEN):
641       nextToken(); // consume '('
642       expr.Set(parseExpression());
643       if (expr.IsNull()) PARSE_ERROR(NULL_EXPR);
644       if (currentToken() != TOKEN(PREC_CLOSE)) PARSE_UNEXPECT();
645       break;
646 
647     case TOKEN(LITERAL_DICT):
648       expr.Set(parseLiteralDict());
649       if (currentToken() != TOKEN(ARR_CLOSE)) PARSE_UNEXPECT();
650       break;
651 
652     case TOKEN(LITERAL_MATRIX):
653       is_matrix = true;
654     case TOKEN(ARR_OPEN):
655       {
656         tAutoRelease<cASTArgumentList> al;
657         if (nextToken() != TOKEN(ARR_CLOSE)) al.Set(parseArgumentList());
658         if (currentToken() != TOKEN(ARR_CLOSE)) PARSE_UNEXPECT();
659         expr.Set(new cASTLiteralArray(FILEPOS, al.Release(), is_matrix));
660       }
661       break;
662 
663     case TOKEN(OP_BIT_NOT):
664     case TOKEN(OP_LOGIC_NOT):
665     case TOKEN(OP_SUB):
666       ASToken_t op = currentToken();
667       nextToken(); // consume operation
668       cASTNode* r = parseExprP6();
669       if (!r) {
670         PARSE_ERROR(NULL_EXPR);
671         return NULL;
672       }
673       expr.Set(new cASTExpressionUnary(FILEPOS, op, r));
674       return expr.Release();
675 
676     default:
677       return NULL;
678   }
679 
680   nextToken();
681   if (!expr.IsNull()) return parseCallExpression(expr.Release());
682 
683   return NULL;
684 }
685 
686 
parseForeachStatement()687 cASTNode* cParser::parseForeachStatement()
688 {
689   PARSE_TRACE("parseForeachStatement");
690 
691   sASTypeInfo type(AS_TYPE_INVALID);
692   switch (nextToken()) {
693     case TOKEN(TYPE_ARRAY):  type.type = AS_TYPE_ARRAY;  break;
694     case TOKEN(TYPE_BOOL):   type.type = AS_TYPE_BOOL;   break;
695     case TOKEN(TYPE_CHAR):   type.type = AS_TYPE_CHAR;   break;
696     case TOKEN(TYPE_DICT):   type.type = AS_TYPE_DICT;   break;
697     case TOKEN(TYPE_FLOAT):  type.type = AS_TYPE_FLOAT;  break;
698     case TOKEN(TYPE_INT):    type.type = AS_TYPE_INT;    break;
699     case TOKEN(TYPE_MATRIX): type.type = AS_TYPE_MATRIX; break;
700     case TOKEN(TYPE_STRING): type.type = AS_TYPE_STRING; break;
701     case TOKEN(TYPE_VAR):    type.type = AS_TYPE_VAR;    break;
702     case TOKEN(TYPE_VOID):   type.type = AS_TYPE_VOID;   break;
703     case TOKEN(ID):
704       if (peekToken() != TOKEN(REF)) {
705         nextToken();
706         PARSE_UNEXPECT();
707       }
708 
709       type.type = AS_TYPE_OBJECT_REF;
710       type.info = currentText();
711 
712       nextToken(); // consume id
713       break;
714 
715     default:
716       PARSE_UNEXPECT();
717       return NULL;
718   }
719 
720   if (nextToken() != TOKEN(ID)) {
721     PARSE_UNEXPECT();
722     return NULL;
723   }
724 
725   tAutoRelease<cASTVariableDefinition> var(new cASTVariableDefinition(FILEPOS, type, currentText()));
726 
727   if (nextToken() != TOKEN(PREC_OPEN)) {
728     PARSE_UNEXPECT();
729     return NULL;
730   }
731   nextToken(); // consume '('
732 
733   tAutoRelease<cASTNode> expr(parseExpression());
734 
735   if (currentToken() != TOKEN(PREC_CLOSE)) {
736     PARSE_UNEXPECT();
737     return NULL;
738   }
739   nextToken(); // consume ')'
740 
741   cASTNode* code = parseCodeBlock();
742 
743   return new cASTForeachBlock(FILEPOS, var.Release(), expr.Release(), code);
744 }
745 
parseFunctionDefine()746 cASTNode* cParser::parseFunctionDefine()
747 {
748   PARSE_TRACE("parseFunctionDefine");
749   cASTFunctionDefinition* fd = parseFunctionHeader();
750 
751   // If the returned function definition is valid, parse the body
752   if (fd) fd->SetCode(parseCodeBlock());
753 
754   return fd;
755 }
756 
parseFunctionHeader()757 cASTFunctionDefinition* cParser::parseFunctionHeader()
758 {
759   PARSE_TRACE("parseFunctionHeader");
760 
761   sASTypeInfo type(AS_TYPE_INVALID);
762   switch (nextToken()) {
763     case TOKEN(TYPE_ARRAY):  type.type = AS_TYPE_ARRAY;  break;
764     case TOKEN(TYPE_BOOL):   type.type = AS_TYPE_BOOL;   break;
765     case TOKEN(TYPE_CHAR):   type.type = AS_TYPE_CHAR;   break;
766     case TOKEN(TYPE_DICT):   type.type = AS_TYPE_DICT;   break;
767     case TOKEN(TYPE_FLOAT):  type.type = AS_TYPE_FLOAT;  break;
768     case TOKEN(TYPE_INT):    type.type = AS_TYPE_INT;    break;
769     case TOKEN(TYPE_MATRIX): type.type = AS_TYPE_MATRIX; break;
770     case TOKEN(TYPE_STRING): type.type = AS_TYPE_STRING; break;
771     case TOKEN(TYPE_VAR):    type.type = AS_TYPE_VAR;    break;
772     case TOKEN(TYPE_VOID):   type.type = AS_TYPE_VOID;   break;
773     case TOKEN(ID):
774       if (peekToken() != TOKEN(REF)) {
775         nextToken();
776         PARSE_UNEXPECT();
777       }
778 
779       type.type = AS_TYPE_OBJECT_REF;
780       type.info = currentText();
781 
782       nextToken(); // consume id
783       break;
784 
785     default:
786       PARSE_UNEXPECT();
787   }
788 
789   if (nextToken() != TOKEN(ID)) {
790     PARSE_UNEXPECT();
791   }
792   cString name(currentText());
793 
794   if (nextToken() != TOKEN(PREC_OPEN)) PARSE_UNEXPECT();
795 
796   tAutoRelease<cASTVariableDefinitionList> args;
797   if (nextToken() != TOKEN(PREC_CLOSE)) args.Set(parseVariableDefinitionList());
798   if (currentToken() != TOKEN(PREC_CLOSE)) PARSE_UNEXPECT();
799   nextToken(); // consume ')'
800 
801   return new cASTFunctionDefinition(FILEPOS, type, name, args.Release());
802 }
803 
parseIDStatement()804 cASTNode* cParser::parseIDStatement()
805 {
806   PARSE_TRACE("parseIDStatement");
807 
808   switch (peekToken()) {
809     case TOKEN(ASSIGN):
810       return parseAssignment();
811       break;
812     case TOKEN(PREC_OPEN):
813       {
814         cASTFunctionCall* fc = new cASTFunctionCall(FILEPOS, currentText());
815         nextToken(); // consume id token
816         if (nextToken() != TOKEN(PREC_CLOSE)) fc->SetArguments(parseArgumentList());
817         if (currentToken() != TOKEN(PREC_CLOSE)) PARSE_UNEXPECT();
818         nextToken(); // consume ')'
819 
820         if (currentToken() == TOKEN(DOT) || currentToken() == TOKEN(IDX_OPEN)) {
821           return parseCallExpression(fc, true);
822         }
823 
824         return fc;
825       }
826       break;
827     case TOKEN(DOT):
828     case TOKEN(IDX_OPEN):
829       cASTNode* target = new cASTVariableReference(FILEPOS, currentText());
830       nextToken(); // consume id
831       return parseCallExpression(target, true);
832       break;
833     case TOKEN(REF):
834       return parseVariableDefinition();
835       break;
836 
837     default:
838       PARSE_UNEXPECT();
839   }
840 }
841 
parseIfStatement()842 cASTNode* cParser::parseIfStatement()
843 {
844   PARSE_TRACE("parseIfStatement");
845 
846   if (nextToken() != TOKEN(PREC_OPEN)) PARSE_UNEXPECT();
847 
848   nextToken();
849   tAutoRelease<cASTNode> cond(parseExpression());
850   if (currentToken() != TOKEN(PREC_CLOSE)) PARSE_UNEXPECT();
851   nextToken();
852 
853   tAutoRelease<cASTIfBlock> is(new cASTIfBlock(FILEPOS, cond.Release(), parseCodeBlock()));
854 
855   while (currentToken() == TOKEN(CMD_ELSEIF)) {
856 
857     if (nextToken() != TOKEN(PREC_OPEN)) PARSE_UNEXPECT();
858     nextToken(); // consume '('
859 
860     tAutoRelease<cASTNode> elifcond(parseExpression());
861 
862     if (currentToken() != TOKEN(PREC_CLOSE)) PARSE_UNEXPECT();
863     nextToken(); // consume ')'
864 
865     cASTNode* elifcode = parseCodeBlock();
866     (*is).AddElseIf(elifcond.Release(), elifcode);
867   }
868 
869   if (currentToken() == TOKEN(CMD_ELSE)) {
870     nextToken(); // consume 'else'
871     cASTNode* code = parseCodeBlock();
872     (*is).SetElseCode(code);
873   }
874 
875   return is.Release();
876 }
877 
878 
parseLiteralDict()879 cASTNode* cParser::parseLiteralDict()
880 {
881   PARSE_TRACE("parseLiteralDict");
882   cASTLiteralDict* ld = new cASTLiteralDict(FILEPOS);
883 
884   if (peekToken() != TOKEN(ARR_CLOSE)) {
885     do {
886       nextToken(); // consume ',' (or '@{' on first pass)
887       cASTNode* idxexpr = parseExpression();
888       if (currentToken() != TOKEN(DICT_MAPPING)) PARSE_UNEXPECT();
889       nextToken(); // consume '=>'
890       cASTNode* valexpr = parseExpression();
891 
892       ld->AddMapping(idxexpr, valexpr);
893     } while (currentToken() == TOKEN(COMMA));
894   } else {
895     nextToken();
896   }
897 
898   return ld;
899 }
900 
parseLooseBlock()901 cASTNode* cParser::parseLooseBlock()
902 {
903   PARSE_TRACE("parseLooseBlock");
904   //nextToken();
905   tAutoRelease<cASTNode> sl(parseStatementList());
906 
907   if (currentToken() != TOKEN(ARR_CLOSE)) PARSE_UNEXPECT();
908   nextToken(); // consume '}'
909 
910   return sl.Release();
911 }
912 
parseRefStatement()913 cASTNode* cParser::parseRefStatement()
914 {
915   PARSE_TRACE("parseRefStatement");
916 
917   switch (nextToken()) {
918     case TOKEN(ARR_OPEN):
919       return parseArrayUnpack();
920     case TOKEN(CMD_FUNCTION):
921       return parseFunctionHeader();
922     default:
923       PARSE_UNEXPECT();
924   }
925 }
926 
parseReturnStatement()927 cASTNode* cParser::parseReturnStatement()
928 {
929   PARSE_TRACE("parseReturnStatement");
930 
931   nextToken(); // consume 'return'
932   cASTNode* rs = new cASTReturnStatement(FILEPOS, parseExpression());
933 
934   return rs;
935 }
936 
937 
parseStatementList()938 cASTNode* cParser::parseStatementList()
939 {
940   PARSE_TRACE("parseStatementList");
941   tAutoRelease<cASTStatementList> sl(new cASTStatementList(FILEPOS));
942 
943   tAutoRelease<cASTNode> node;
944 
945   while (nextToken()) {
946     switch (currentToken()) {
947       case TOKEN(ARR_OPEN):
948         node.Set(parseLooseBlock());
949         break;
950       case TOKEN(CMD_IF):
951         node.Set(parseIfStatement());
952         break;
953       case TOKEN(CMD_FOREACH):
954         node.Set(parseForeachStatement());
955         break;
956       case TOKEN(CMD_FUNCTION):
957         node.Set(parseFunctionDefine());
958         break;
959       case TOKEN(CMD_RETURN):
960         node.Set(parseReturnStatement());
961         break;
962       case TOKEN(CMD_WHILE):
963         node.Set(parseWhileStatement());
964         break;
965       case TOKEN(ENDL):
966         continue;
967       case TOKEN(ID):
968         node.Set(parseIDStatement());
969         break;
970       case TOKEN(REF):
971         node.Set(parseRefStatement());
972         break;
973       case TOKEN(SUPPRESS):
974         continue;
975       case TOKEN(TYPE_ARRAY):
976       case TOKEN(TYPE_BOOL):
977       case TOKEN(TYPE_CHAR):
978       case TOKEN(TYPE_DICT):
979       case TOKEN(TYPE_FLOAT):
980       case TOKEN(TYPE_INT):
981       case TOKEN(TYPE_MATRIX):
982       case TOKEN(TYPE_STRING):
983       case TOKEN(TYPE_VAR):
984         node.Set(parseVariableDefinition());
985         break;
986 
987       default:
988         return sl.Release();
989     }
990 
991     if (node.IsNull() && m_success) PARSE_ERROR(INTERNAL); // Should not receive a null response without an error flag
992 
993     if (currentToken() == TOKEN(SUPPRESS)) {
994       if (!node.IsNull()) (*node).SuppressOutput();
995     } else if (currentToken() != TOKEN(ENDL)) {
996       PARSE_ERROR(UNTERMINATED_EXPR);
997     }
998 
999     (*sl).AddNode(node.Release());
1000   }
1001 
1002   if (!currentToken()) m_eof = true;
1003   return sl.Release();
1004 }
1005 
1006 
parseVariableDefinition()1007 cASTVariableDefinition* cParser::parseVariableDefinition()
1008 {
1009   PARSE_TRACE("parseVariableDefinition");
1010 
1011   sASTypeInfo vtype(AS_TYPE_INVALID);
1012   switch (currentToken()) {
1013     case TOKEN(TYPE_ARRAY):  vtype.type = AS_TYPE_ARRAY;  break;
1014     case TOKEN(TYPE_BOOL):   vtype.type = AS_TYPE_BOOL;   break;
1015     case TOKEN(TYPE_CHAR):   vtype.type = AS_TYPE_CHAR;   break;
1016     case TOKEN(TYPE_DICT):   vtype.type = AS_TYPE_DICT;   break;
1017     case TOKEN(TYPE_FLOAT):  vtype.type = AS_TYPE_FLOAT;  break;
1018     case TOKEN(TYPE_INT):    vtype.type = AS_TYPE_INT;    break;
1019     case TOKEN(TYPE_MATRIX): vtype.type = AS_TYPE_MATRIX; break;
1020     case TOKEN(TYPE_STRING): vtype.type = AS_TYPE_STRING; break;
1021     case TOKEN(TYPE_VAR):    vtype.type = AS_TYPE_VAR;    break;
1022     case TOKEN(ID):
1023       if (peekToken() != TOKEN(REF)) {
1024         nextToken();
1025         PARSE_UNEXPECT();
1026       }
1027 
1028       vtype.type = AS_TYPE_OBJECT_REF;
1029       vtype.info = currentText();
1030 
1031       nextToken(); // consume id
1032       break;
1033 
1034     default:
1035       PARSE_UNEXPECT();
1036   }
1037 
1038   if (nextToken() != TOKEN(ID)) PARSE_UNEXPECT();
1039 
1040   tAutoRelease<cASTVariableDefinition> vd(new cASTVariableDefinition(FILEPOS, vtype, currentText()));
1041 
1042   switch (nextToken()) {
1043     case TOKEN(ASSIGN):
1044       nextToken();
1045       cASTNode* expr = parseExpression();
1046       (*vd).SetAssignmentExpression(expr);
1047       break;
1048     case TOKEN(PREC_OPEN):
1049       if (nextToken() != TOKEN(PREC_CLOSE)) (*vd).SetDimensions(parseArgumentList());
1050       if (currentToken() != TOKEN(PREC_CLOSE)) PARSE_UNEXPECT();
1051       nextToken(); // consume ')'
1052       break;
1053 
1054     default:
1055       break;
1056   }
1057 
1058   return vd.Release();
1059 }
1060 
parseVariableDefinitionList()1061 cASTVariableDefinitionList* cParser::parseVariableDefinitionList()
1062 {
1063   PARSE_TRACE("parseVariableDefinitionList");
1064   tAutoRelease<cASTVariableDefinitionList> vl(new cASTVariableDefinitionList(FILEPOS));
1065 
1066   cASTVariableDefinition* vd = parseVariableDefinition();
1067   if (!vd) return NULL;
1068 
1069   (*vl).AddNode(vd);
1070   while (currentToken() == TOKEN(COMMA)) {
1071     nextToken(); // consume ','
1072     vd = parseVariableDefinition();
1073     if (!vd) return NULL;
1074     (*vl).AddNode(vd);
1075   }
1076 
1077   return vl.Release();
1078 }
1079 
parseWhileStatement()1080 cASTNode* cParser::parseWhileStatement()
1081 {
1082   PARSE_TRACE("parseWhileStatement");
1083 
1084   if (nextToken() != TOKEN(PREC_OPEN)) PARSE_UNEXPECT();
1085 
1086   nextToken();
1087   tAutoRelease<cASTNode> cond(parseExpression());
1088   if (currentToken() != TOKEN(PREC_CLOSE)) PARSE_UNEXPECT();
1089   nextToken();
1090 
1091   cASTNode* code = parseCodeBlock();
1092   return new cASTWhileBlock(FILEPOS, cond.Release(), code);
1093 }
1094 
1095 
reportError(ASParseError_t err,const int line)1096 void cParser::reportError(ASParseError_t err, const int line)
1097 {
1098 #if DEBUG_AS_PARSER
1099 # define ERR_ENDL "  (cParser.cc:" << line << ")" << std::endl
1100 #else
1101 # define ERR_ENDL std::endl
1102 #endif
1103 
1104   m_success = false;
1105 
1106   std::cerr << m_filename << ":";
1107 
1108   int lineno = m_lexer ? m_lexer->lineno() : 0;
1109   if (lineno) std::cerr << lineno;
1110   else std::cerr << "?";
1111 
1112   std::cerr << ": error: ";
1113 
1114   switch (err) {
1115     case AS_PARSE_ERR_UNEXPECTED_TOKEN:
1116       std::cerr << "unexpected token '" << currentText() << "'" << ERR_ENDL;
1117       break;
1118     case AS_PARSE_ERR_UNTERMINATED_EXPR:
1119       std::cerr << "unterminated expression" << ERR_ENDL;
1120       break;
1121     case AS_PARSE_ERR_NULL_EXPR:
1122       std::cerr << "expected expression, found '" << currentText() << "'" << ERR_ENDL;
1123       break;
1124     case AS_PARSE_ERR_EOF:
1125       if (!m_err_eof) {
1126         std::cerr << "unexpected end of file" << ERR_ENDL;
1127         m_err_eof = true;
1128       }
1129       break;
1130     case AS_PARSE_ERR_EMPTY:
1131       std::cerr << "empty script, no valid statements found" << ERR_ENDL;
1132       break;
1133     case AS_PARSE_ERR_INTERNAL:
1134       std::cerr << "internal parser error at cParser.cc:" << line << std::endl;
1135       break;
1136     case AS_PARSE_ERR_UNKNOWN:
1137     default:
1138       std::cerr << "parse error" << std::endl;
1139   }
1140 
1141 #undef ERR_ENDL
1142 }
1143 
1144 #undef PARSE_DEBUG()
1145 #undef PARSE_TRACE()
1146 
1147 #undef PARSE_ERROR()
1148 #undef PARSE_UNEXPECT()
1149 
1150 #undef FILEPOS
1151 
1152 #undef TOKEN()
1153