1 // Parser.cpp: implementation of the CParser class.
3 * Copyright (c) 1996-2000 Erwin Coumans <coockie@acm.org>
5 * Permission to use, copy, modify, distribute and sell this software
6 * and its documentation for any purpose is hereby granted without fee,
7 * provided that the above copyright notice appear in all copies and
8 * that both that copyright notice and this permission notice appear
9 * in supporting documentation. Erwin Coumans makes no
10 * representations about the suitability of this software for any
11 * purpose. It is provided "as is" without express or implied warranty.
17 #include "MT_assert.h"
20 #include "InputParser.h"
21 #include "ErrorValue.h"
23 #include "StringValue.h"
24 #include "FloatValue.h"
25 #include "BoolValue.h"
26 #include "EmptyValue.h"
27 #include "ConstExpr.h"
28 #include "Operator2Expr.h"
29 #include "Operator1Expr.h"
30 #include "IdentifierExpr.h"
36 // this is disable at the moment, I expected a memleak from it, but the error-cleanup was the reason
37 // well, looks we don't need it anyway, until maybe the Curved Surfaces are integrated into CSG
38 // cool things like (IF(LOD==1,CCurvedValue,IF(LOD==2,CCurvedValue2)) etc...
41 #if defined(WIN32) || defined(WIN64)
42 #define strcasecmp _stricmp
43 #endif /* Def WIN32 or Def WIN64 */
45 #define NUM_PRIORITY 6
46 //////////////////////////////////////////////////////////////////////
47 // Construction/Destruction
48 //////////////////////////////////////////////////////////////////////
50 CParser::CParser() : m_identifierContext(NULL)
58 if (m_identifierContext)
59 m_identifierContext->Release();
64 void CParser::ScanError(STR_String str)
66 // sets the global variable errmsg to an errormessage with
67 // contents str, appending if it already exists
68 // AfxMessageBox("Parse Error:"+str,MB_ICONERROR);
70 errmsg = new COperator2Expr(VALUE_ADD_OPERATOR, errmsg, Error(str));
79 CExpression* CParser::Error(STR_String str)
81 // makes and returns a new CConstExpr filled with an CErrorValue
83 // AfxMessageBox("Error:"+str,MB_ICONERROR);
84 return new CConstExpr(new CErrorValue(str));
89 void CParser::NextCh()
91 // sets the global variable ch to the next character, if it exists
92 // and increases the global variable chcount
95 if (chcount < text.Length())
103 void CParser::TermChar(char c)
105 // generates an error if the next char isn't the specified char c,
106 // otherwise, skip the char
114 str.Format("Warning: %c expected\ncontinuing without it", c);
121 void CParser::DigRep()
123 // changes the current character to the first character that
125 while ((ch >= '0') && (ch <= '9'))
131 void CParser::CharRep()
133 // changes the current character to the first character that
134 // isn't an alphanumeric character
135 while (((ch >= '0') && (ch <= '9'))
136 || ((ch >= 'a') && (ch <= 'z'))
137 || ((ch >= 'A') && (ch <= 'Z'))
138 || (ch == '.') || (ch == '_'))
144 void CParser::GrabString(int start)
146 // puts part of the input string into the global variable
147 // const_as_string, from position start, to position chchount
148 const_as_string = text.Mid(start, chcount-start);
153 void CParser::NextSym()
155 // sets the global variable sym to the next symbol, and
156 // if it is an operator
157 // sets the global variable opkind to the kind of operator
158 // if it is a constant
159 // sets the global variable constkind to the kind of operator
160 // if it is a reference to a cell
161 // sets the global variable cellcoord to the kind of operator
164 while(ch == ' ' || ch == 0x9)
170 sym = lbracksym; NextCh();
173 sym = rbracksym; NextCh();
176 sym = commasym; NextCh();
179 sym = opsym; opkind = OPmodulus; NextCh();
182 sym = opsym; opkind = OPplus; NextCh();
185 sym = opsym; opkind = OPminus; NextCh();
188 sym = opsym; opkind = OPtimes; NextCh();
191 sym = opsym; opkind = OPdivide; NextCh();
194 sym = opsym; opkind = OPand; NextCh(); TermChar('&');
197 sym = opsym; opkind = OPor; NextCh(); TermChar('|');
200 sym = opsym; opkind = OPequal; NextCh(); TermChar('=');
220 opkind = OPgreaterequal;
232 opkind = OPlessequal;
241 constkind = stringtype;
244 while ((ch != '\"') && (ch != 0x0))
247 TermChar('\"'); // check for eol before '\"'
250 case 0x0: sym = eolsym; break;
256 if ((start != chcount) || (ch == '.')) { // number
259 constkind = floattype;
263 else constkind = inttype;
264 if ((ch == 'e') || (ch == 'E')) {
266 constkind = floattype;
268 if ((ch == '+') || (ch == '-')) NextCh();
271 if (mark == chcount) {
272 ScanError("Number expected after 'E'");
277 } else if (((ch >= 'a') && (ch <= 'z'))
278 || ((ch >= 'A') && (ch <= 'Z')))
284 if (!strcasecmp(const_as_string, "SUM")) {
287 else if (!strcasecmp(const_as_string, "NOT")) {
291 else if (!strcasecmp(const_as_string, "AND")) {
292 sym = opsym; opkind = OPand;
294 else if (!strcasecmp(const_as_string, "OR")) {
295 sym = opsym; opkind = OPor;
297 else if (!strcasecmp(const_as_string, "IF"))
299 else if (!strcasecmp(const_as_string, "WHOMADE"))
301 else if (!strcasecmp(const_as_string, "FALSE")) {
302 sym = constsym; constkind = booltype; boolvalue = false;
303 } else if (!strcasecmp(const_as_string, "TRUE")) {
304 sym = constsym; constkind = booltype; boolvalue = true;
308 //str.Format("'%s' makes no sense here", (const char*)funstr);
311 } else { // unknown symbol
313 str.Format("Unexpected character '%c'", ch);
323 int CParser::MakeInt() {
324 // returns the integer representation of the value in the global
325 // variable const_as_string
326 // pre: const_as_string contains only numercal chars
327 return atoi(const_as_string);
331 STR_String CParser::Symbol2Str(int s) {
332 // returns a string representation of of symbol s,
333 // for use in Term when generating an error
335 case errorsym: return "error";
336 case lbracksym: return "(";
337 case rbracksym: return ")";
338 case commasym: return ",";
339 case opsym: return "operator";
340 case constsym: return "constant";
341 case sumsym: return "SUM";
342 case ifsym: return "IF";
343 case whocodedsym: return "WHOMADE";
344 case eolsym: return "end of line";
345 case idsym: return "identifier";
346 default: return "unknown"; // should not happen
350 void CParser::Term(int s) {
351 // generates an error if the next symbol isn't the specified symbol s
352 // otherwise, skip the symbol
353 if(s == sym) NextSym();
356 msg.Format("Warning: " + Symbol2Str(s) + " expected\ncontinuing without it");
358 // AfxMessageBox(msg,MB_ICONERROR);
364 int CParser::Priority(int optorkind) {
365 // returns the priority of an operator
366 // higher number means higher priority
369 case OPand: return 2;
375 case OPunequal: return 3;
377 case OPminus: return 4;
380 case OPdivide: return 5;
383 return 0; // should not happen
386 CExpression *CParser::Ex(int i) {
387 // parses an expression in the imput, starting at priority i, and
388 // returns an CExpression, containing the parsed input
389 CExpression *e1 = NULL, *e2 = NULL;
392 if (i < NUM_PRIORITY) {
394 while ((sym == opsym) && (Priority(opkind) == i)) {
399 case OPmodulus: e1 = new COperator2Expr(VALUE_MOD_OPERATOR,e1, e2); break;
400 case OPplus: e1 = new COperator2Expr(VALUE_ADD_OPERATOR,e1, e2); break;
401 case OPminus: e1 = new COperator2Expr(VALUE_SUB_OPERATOR,e1, e2); break;
402 case OPtimes: e1 = new COperator2Expr(VALUE_MUL_OPERATOR,e1, e2); break;
403 case OPdivide: e1 = new COperator2Expr(VALUE_DIV_OPERATOR,e1, e2); break;
404 case OPand: e1 = new COperator2Expr(VALUE_AND_OPERATOR,e1, e2); break;
405 case OPor: e1 = new COperator2Expr(VALUE_OR_OPERATOR,e1, e2); break;
406 case OPequal: e1 = new COperator2Expr(VALUE_EQL_OPERATOR,e1, e2); break;
407 case OPunequal: e1 = new COperator2Expr(VALUE_NEQ_OPERATOR,e1, e2); break;
408 case OPgreater: e1 = new COperator2Expr(VALUE_GRE_OPERATOR,e1, e2); break;
409 case OPless: e1 = new COperator2Expr(VALUE_LES_OPERATOR,e1, e2); break;
410 case OPgreaterequal: e1 = new COperator2Expr(VALUE_GEQ_OPERATOR,e1, e2); break;
411 case OPlessequal: e1 = new COperator2Expr(VALUE_LEQ_OPERATOR,e1, e2); break;
412 default: MT_assert(false); break; // should not happen
415 } else if (i == NUM_PRIORITY) {
417 && ( (opkind == OPminus) || (opkind == OPnot) || (opkind == OPplus) )
422 /* +1 is also a valid number! */
423 case OPplus: e1 = new COperator1Expr(VALUE_POS_OPERATOR, Ex(NUM_PRIORITY)); break;
424 case OPminus: e1 = new COperator1Expr(VALUE_NEG_OPERATOR, Ex(NUM_PRIORITY)); break;
425 case OPnot: e1 = new COperator1Expr(VALUE_NOT_OPERATOR, Ex(NUM_PRIORITY)); break;
428 e1 = Error("operator +, - or ! expected");
437 e1 = new CConstExpr(new CBoolValue(boolvalue));
442 temp = strtoll(const_as_string, NULL, 10); /* atoi is for int only */
443 e1 = new CConstExpr(new CIntValue(temp));
449 temp = atof(const_as_string);
450 e1 = new CConstExpr(new CFloatValue(temp));
454 e1 = new CConstExpr(new CStringValue(const_as_string,""));
476 if (sym == commasym) {
480 e3 = new CConstExpr(new CEmptyValue());
483 e1 = new CIfExpr(e1, e2, e3);
488 e1 = new CIdentifierExpr(const_as_string,m_identifierContext);
496 STR_String errtext="[no info]";
499 CValue* errmsgval = errmsg->Calculate();
500 errtext=errmsgval->GetText();
501 errmsgval->Release();
503 //e1 = Error(errmsg->Calculate()->GetText());//new CConstExpr(errmsg->Calculate());
505 if ( !(errmsg->Release()) )
509 // does this happen ?
510 MT_assert ("does this happen");
519 //return Error("Expression expected");
521 e1 = Error("Expression expected");
528 CExpression *CParser::Expr() {
529 // parses an expression in the imput, and
530 // returns an CExpression, containing the parsed input
534 CExpression* CParser::ProcessText
535 (STR_String intext) {
537 // and parses the string in intext and returns it.
545 if (text.Length() == 0) {
551 expr = new CConstExpr(new CStringValue(text));
552 *dependant = deplist;
560 CExpression* oldexpr = expr;
561 expr = new COperator2Expr(VALUE_ADD_OPERATOR,
562 oldexpr, Error(STR_String("Extra characters after expression")));//new CConstExpr(new CErrorValue("Extra characters after expression")));
572 float CParser::GetFloat(STR_String txt)
574 // returns parsed text into a float
575 // empty string returns -1
577 // AfxMessageBox("parsed string="+txt);
582 CExpression* expr = ProcessText(txt);
584 val = expr->Calculate();
585 result=(float)val->GetNumber();
592 // tmpstr.Format("parseresult=%g",result);
593 // AfxMessageBox(tmpstr);
597 CValue* CParser::GetValue(STR_String txt, bool bFallbackToText)
599 // returns parsed text into a value,
600 // empty string returns NULL value !
601 // if bFallbackToText then unparsed stuff is put into text
604 CExpression* expr = ProcessText(txt);
606 result = expr->Calculate();
611 // if the parsed stuff lead to an errorvalue, don't return errors, just NULL
612 if (result->IsError()) {
615 if (bFallbackToText) {
618 result = new CStringValue(txt,"");
626 void CParser::SetContext(CValue* context)
628 if (m_identifierContext)
630 m_identifierContext->Release();
632 m_identifierContext = context;
638 PyObject* CParserPyMake(PyObject* ignored,PyObject* args)
641 if (!PyArg_ParseTuple(args,"s",&txt))
644 CExpression* expr = parser.ProcessText(txt);
645 CValue* val = expr->Calculate();
650 static PyMethodDef CParserMethods[] =
652 { "calc", CParserPyMake , METH_VARARGS},
653 { NULL,NULL} // Sentinel
657 void initExpressionModule(void)
659 Py_InitModule("Expression",CParserMethods);