1 /** \file gameengine/Expressions/InputParser.cpp
4 // Parser.cpp: implementation of the CParser class.
6 * Copyright (c) 1996-2000 Erwin Coumans <coockie@acm.org>
8 * Permission to use, copy, modify, distribute and sell this software
9 * and its documentation for any purpose is hereby granted without fee,
10 * provided that the above copyright notice appear in all copies and
11 * that both that copyright notice and this permission notice appear
12 * in supporting documentation. Erwin Coumans makes no
13 * representations about the suitability of this software for any
14 * purpose. It is provided "as is" without express or implied warranty.
20 #include "MT_assert.h"
23 #include "InputParser.h"
24 #include "ErrorValue.h"
26 #include "StringValue.h"
27 #include "FloatValue.h"
28 #include "BoolValue.h"
29 #include "EmptyValue.h"
30 #include "ConstExpr.h"
31 #include "Operator2Expr.h"
32 #include "Operator1Expr.h"
33 #include "IdentifierExpr.h"
35 // this is disable at the moment, I expected a memleak from it, but the error-cleanup was the reason
36 // well, looks we don't need it anyway, until maybe the Curved Surfaces are integrated into CSG
37 // cool things like (IF(LOD==1,CCurvedValue,IF(LOD==2,CCurvedValue2)) etc...
40 #if (defined(WIN32) || defined(WIN64)) && !defined(FREE_WINDOWS)
41 #define strcasecmp _stricmp
44 #define strtoll _strtoi64
47 #endif /* Def WIN32 or Def WIN64 */
49 #define NUM_PRIORITY 6
50 //////////////////////////////////////////////////////////////////////
51 // Construction/Destruction
52 //////////////////////////////////////////////////////////////////////
54 CParser::CParser() : m_identifierContext(NULL)
62 if (m_identifierContext)
63 m_identifierContext->Release();
68 void CParser::ScanError(const char *str)
70 // sets the global variable errmsg to an errormessage with
71 // contents str, appending if it already exists
72 // AfxMessageBox("Parse Error:"+str,MB_ICONERROR);
74 errmsg = new COperator2Expr(VALUE_ADD_OPERATOR, errmsg, Error(str));
83 CExpression* CParser::Error(const char *str)
85 // makes and returns a new CConstExpr filled with an CErrorValue
87 // AfxMessageBox("Error:"+str,MB_ICONERROR);
88 return new CConstExpr(new CErrorValue(str));
93 void CParser::NextCh()
95 // sets the global variable ch to the next character, if it exists
96 // and increases the global variable chcount
99 if (chcount < text.Length())
107 void CParser::TermChar(char c)
109 // generates an error if the next char isn't the specified char c,
110 // otherwise, skip the char
118 str.Format("Warning: %c expected\ncontinuing without it", c);
125 void CParser::DigRep()
127 // changes the current character to the first character that
129 while ((ch >= '0') && (ch <= '9'))
135 void CParser::CharRep()
137 // changes the current character to the first character that
138 // isn't an alphanumeric character
139 while (((ch >= '0') && (ch <= '9'))
140 || ((ch >= 'a') && (ch <= 'z'))
141 || ((ch >= 'A') && (ch <= 'Z'))
142 || (ch == '.') || (ch == '_'))
148 void CParser::GrabString(int start)
150 // puts part of the input string into the global variable
151 // const_as_string, from position start, to position chchount
152 const_as_string = text.Mid(start, chcount-start);
157 void CParser::GrabRealString(int start)
159 // works like GrabString but converting \\n to \n
160 // puts part of the input string into the global variable
161 // const_as_string, from position start, to position chchount
166 const_as_string = STR_String();
167 for (i=start;i<chcount;i++) {
169 if ((tmpch =='\\') && (text[i+1] == 'n')){
173 const_as_string += tmpch;
179 void CParser::NextSym()
181 // sets the global variable sym to the next symbol, and
182 // if it is an operator
183 // sets the global variable opkind to the kind of operator
184 // if it is a constant
185 // sets the global variable constkind to the kind of operator
186 // if it is a reference to a cell
187 // sets the global variable cellcoord to the kind of operator
190 while(ch == ' ' || ch == 0x9)
196 sym = lbracksym; NextCh();
199 sym = rbracksym; NextCh();
202 sym = commasym; NextCh();
205 sym = opsym; opkind = OPmodulus; NextCh();
208 sym = opsym; opkind = OPplus; NextCh();
211 sym = opsym; opkind = OPminus; NextCh();
214 sym = opsym; opkind = OPtimes; NextCh();
217 sym = opsym; opkind = OPdivide; NextCh();
220 sym = opsym; opkind = OPand; NextCh(); TermChar('&');
223 sym = opsym; opkind = OPor; NextCh(); TermChar('|');
226 sym = opsym; opkind = OPequal; NextCh(); TermChar('=');
246 opkind = OPgreaterequal;
258 opkind = OPlessequal;
267 constkind = stringtype;
270 while ((ch != '\"') && (ch != 0x0))
272 GrabRealString(start);
273 TermChar('\"'); // check for eol before '\"'
276 case 0x0: sym = eolsym; break;
282 if ((start != chcount) || (ch == '.')) { // number
285 constkind = floattype;
289 else constkind = inttype;
290 if ((ch == 'e') || (ch == 'E')) {
292 constkind = floattype;
294 if ((ch == '+') || (ch == '-')) NextCh();
297 if (mark == chcount) {
298 ScanError("Number expected after 'E'");
303 } else if (((ch >= 'a') && (ch <= 'z'))
304 || ((ch >= 'A') && (ch <= 'Z')))
310 if (!strcasecmp(const_as_string, "SUM")) {
313 else if (!strcasecmp(const_as_string, "NOT")) {
317 else if (!strcasecmp(const_as_string, "AND")) {
318 sym = opsym; opkind = OPand;
320 else if (!strcasecmp(const_as_string, "OR")) {
321 sym = opsym; opkind = OPor;
323 else if (!strcasecmp(const_as_string, "IF"))
325 else if (!strcasecmp(const_as_string, "WHOMADE"))
327 else if (!strcasecmp(const_as_string, "FALSE")) {
328 sym = constsym; constkind = booltype; boolvalue = false;
329 } else if (!strcasecmp(const_as_string, "TRUE")) {
330 sym = constsym; constkind = booltype; boolvalue = true;
334 //str.Format("'%s' makes no sense here", (const char*)funstr);
337 } else { // unknown symbol
339 str.Format("Unexpected character '%c'", ch);
349 int CParser::MakeInt() {
350 // returns the integer representation of the value in the global
351 // variable const_as_string
352 // pre: const_as_string contains only numercal chars
353 return atoi(const_as_string);
357 STR_String CParser::Symbol2Str(int s) {
358 // returns a string representation of of symbol s,
359 // for use in Term when generating an error
361 case errorsym: return "error";
362 case lbracksym: return "(";
363 case rbracksym: return ")";
364 case commasym: return ",";
365 case opsym: return "operator";
366 case constsym: return "constant";
367 case sumsym: return "SUM";
368 case ifsym: return "IF";
369 case whocodedsym: return "WHOMADE";
370 case eolsym: return "end of line";
371 case idsym: return "identifier";
372 default: return "unknown"; // should not happen
376 void CParser::Term(int s) {
377 // generates an error if the next symbol isn't the specified symbol s
378 // otherwise, skip the symbol
379 if(s == sym) NextSym();
382 msg.Format("Warning: " + Symbol2Str(s) + " expected\ncontinuing without it");
384 // AfxMessageBox(msg,MB_ICONERROR);
390 int CParser::Priority(int optorkind) {
391 // returns the priority of an operator
392 // higher number means higher priority
395 case OPand: return 2;
401 case OPunequal: return 3;
403 case OPminus: return 4;
406 case OPdivide: return 5;
409 return 0; // should not happen
412 CExpression *CParser::Ex(int i) {
413 // parses an expression in the imput, starting at priority i, and
414 // returns an CExpression, containing the parsed input
415 CExpression *e1 = NULL, *e2 = NULL;
418 if (i < NUM_PRIORITY) {
420 while ((sym == opsym) && (Priority(opkind) == i)) {
425 case OPmodulus: e1 = new COperator2Expr(VALUE_MOD_OPERATOR,e1, e2); break;
426 case OPplus: e1 = new COperator2Expr(VALUE_ADD_OPERATOR,e1, e2); break;
427 case OPminus: e1 = new COperator2Expr(VALUE_SUB_OPERATOR,e1, e2); break;
428 case OPtimes: e1 = new COperator2Expr(VALUE_MUL_OPERATOR,e1, e2); break;
429 case OPdivide: e1 = new COperator2Expr(VALUE_DIV_OPERATOR,e1, e2); break;
430 case OPand: e1 = new COperator2Expr(VALUE_AND_OPERATOR,e1, e2); break;
431 case OPor: e1 = new COperator2Expr(VALUE_OR_OPERATOR,e1, e2); break;
432 case OPequal: e1 = new COperator2Expr(VALUE_EQL_OPERATOR,e1, e2); break;
433 case OPunequal: e1 = new COperator2Expr(VALUE_NEQ_OPERATOR,e1, e2); break;
434 case OPgreater: e1 = new COperator2Expr(VALUE_GRE_OPERATOR,e1, e2); break;
435 case OPless: e1 = new COperator2Expr(VALUE_LES_OPERATOR,e1, e2); break;
436 case OPgreaterequal: e1 = new COperator2Expr(VALUE_GEQ_OPERATOR,e1, e2); break;
437 case OPlessequal: e1 = new COperator2Expr(VALUE_LEQ_OPERATOR,e1, e2); break;
438 default: MT_assert(false); break; // should not happen
441 } else if (i == NUM_PRIORITY) {
443 && ( (opkind == OPminus) || (opkind == OPnot) || (opkind == OPplus) )
448 /* +1 is also a valid number! */
449 case OPplus: e1 = new COperator1Expr(VALUE_POS_OPERATOR, Ex(NUM_PRIORITY)); break;
450 case OPminus: e1 = new COperator1Expr(VALUE_NEG_OPERATOR, Ex(NUM_PRIORITY)); break;
451 case OPnot: e1 = new COperator1Expr(VALUE_NOT_OPERATOR, Ex(NUM_PRIORITY)); break;
454 e1 = Error("operator +, - or ! expected");
463 e1 = new CConstExpr(new CBoolValue(boolvalue));
468 temp = strtoll(const_as_string, NULL, 10); /* atoi is for int only */
469 e1 = new CConstExpr(new CIntValue(temp));
475 temp = atof(const_as_string);
476 e1 = new CConstExpr(new CFloatValue(temp));
480 e1 = new CConstExpr(new CStringValue(const_as_string,""));
502 if (sym == commasym) {
506 e3 = new CConstExpr(new CEmptyValue());
509 e1 = new CIfExpr(e1, e2, e3);
514 e1 = new CIdentifierExpr(const_as_string,m_identifierContext);
522 STR_String errtext="[no info]";
525 CValue* errmsgval = errmsg->Calculate();
526 errtext=errmsgval->GetText();
527 errmsgval->Release();
529 //e1 = Error(errmsg->Calculate()->GetText());//new CConstExpr(errmsg->Calculate());
531 if ( !(errmsg->Release()) )
535 // does this happen ?
536 MT_assert ("does this happen");
545 //return Error("Expression expected");
547 e1 = Error("Expression expected");
554 CExpression *CParser::Expr() {
555 // parses an expression in the imput, and
556 // returns an CExpression, containing the parsed input
560 CExpression* CParser::ProcessText
561 (const char *intext) {
563 // and parses the string in intext and returns it.
571 if (text.Length() == 0) {
577 expr = new CConstExpr(new CStringValue(text));
578 *dependant = deplist;
586 CExpression* oldexpr = expr;
587 expr = new COperator2Expr(VALUE_ADD_OPERATOR,
588 oldexpr, Error(STR_String("Extra characters after expression")));//new CConstExpr(new CErrorValue("Extra characters after expression")));
598 float CParser::GetFloat(STR_String& txt)
600 // returns parsed text into a float
601 // empty string returns -1
603 // AfxMessageBox("parsed string="+txt);
608 CExpression* expr = ProcessText(txt);
610 val = expr->Calculate();
611 result=(float)val->GetNumber();
618 // tmpstr.Format("parseresult=%g",result);
619 // AfxMessageBox(tmpstr);
623 CValue* CParser::GetValue(STR_String& txt, bool bFallbackToText)
625 // returns parsed text into a value,
626 // empty string returns NULL value !
627 // if bFallbackToText then unparsed stuff is put into text
630 CExpression* expr = ProcessText(txt);
632 result = expr->Calculate();
637 // if the parsed stuff lead to an errorvalue, don't return errors, just NULL
638 if (result->IsError()) {
641 if (bFallbackToText) {
644 result = new CStringValue(txt,"");
652 void CParser::SetContext(CValue* context)
654 if (m_identifierContext)
656 m_identifierContext->Release();
658 m_identifierContext = context;