// Parser.cpp: implementation of the CParser class. /* * Copyright (c) 1996-2000 Erwin Coumans * * Permission to use, copy, modify, distribute and sell this software * and its documentation for any purpose is hereby granted without fee, * provided that the above copyright notice appear in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation. Erwin Coumans makes no * representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * */ #include #include "Value.h" #include "InputParser.h" #include "ErrorValue.h" #include "IntValue.h" #include "StringValue.h" #include "FloatValue.h" #include "BoolValue.h" #include "EmptyValue.h" #include "ConstExpr.h" #include "Operator2Expr.h" #include "Operator1Expr.h" #include "IdentifierExpr.h" // this is disable at the moment, I expected a memleak from it, but the error-cleanup was the reason // well, looks we don't need it anyway, until maybe the Curved Surfaces are integrated into CSG // cool things like (IF(LOD==1,CCurvedValue,IF(LOD==2,CCurvedValue2)) etc... #include "IfExpr.h" #define NUM_PRIORITY 6 ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CParser::CParser() : m_identifierContext(NULL) { } CParser::~CParser() { if (m_identifierContext) m_identifierContext->Release(); } void CParser::ScanError(STR_String str) { // sets the global variable errmsg to an errormessage with // contents str, appending if it already exists // AfxMessageBox("Parse Error:"+str,MB_ICONERROR); if (errmsg) errmsg = new COperator2Expr(VALUE_ADD_OPERATOR, errmsg, Error(str)); else errmsg = Error(str); sym = errorsym; } CExpression* CParser::Error(STR_String str) { // makes and returns a new CConstExpr filled with an CErrorValue // with string str // AfxMessageBox("Error:"+str,MB_ICONERROR); return new CConstExpr(new CErrorValue(str)); } void CParser::NextCh() { // sets the global variable ch to the next character, if it exists // and increases the global variable chcount chcount++; if (chcount < text.Length()) ch = text[chcount]; else ch = 0x00; } void CParser::TermChar(char c) { // generates an error if the next char isn't the specified char c, // otherwise, skip the char if(ch == c) { NextCh(); } else { STR_String str; str.Format("Warning: %c expected\ncontinuing without it", c); trace(str); } } void CParser::DigRep() { // changes the current character to the first character that // isn't a decimal while ((ch >= '0') && (ch <= '9')) NextCh(); } void CParser::CharRep() { // changes the current character to the first character that // isn't an alphanumeric character while (((ch >= '0') && (ch <= '9')) || ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) || (ch == '.') || (ch == '_')) NextCh(); } void CParser::GrabString(int start) { // puts part of the input string into the global variable // const_as_string, from position start, to position chchount const_as_string = text.Mid(start, chcount-start); } void CParser::NextSym() { // sets the global variable sym to the next symbol, and // if it is an operator // sets the global variable opkind to the kind of operator // if it is a constant // sets the global variable constkind to the kind of operator // if it is a reference to a cell // sets the global variable cellcoord to the kind of operator errmsg = NULL; while(ch == ' ' || ch == 0x9) NextCh(); switch(ch) { case '(': sym = lbracksym; NextCh(); break; case ')': sym = rbracksym; NextCh(); break; case ',': sym = commasym; NextCh(); break; case '+' : sym = opsym; opkind = OPplus; NextCh(); break; case '-' : sym = opsym; opkind = OPminus; NextCh(); break; case '*' : sym = opsym; opkind = OPtimes; NextCh(); break; case '/' : sym = opsym; opkind = OPdivide; NextCh(); break; case '&' : sym = opsym; opkind = OPand; NextCh(); TermChar('&'); break; case '|' : sym = opsym; opkind = OPor; NextCh(); TermChar('|'); break; case '=' : sym = opsym; opkind = OPequal; NextCh(); TermChar('='); break; case '!' : sym = opsym; NextCh(); if (ch == '=') { opkind = OPunequal; NextCh(); } else { opkind = OPnot; } break; case '>': sym = opsym; NextCh(); if (ch == '=') { opkind = OPgreaterequal; NextCh(); } else { opkind = OPgreater; } break; case '<': sym = opsym; NextCh(); if (ch == '=') { opkind = OPlessequal; NextCh(); } else { opkind = OPless; } break; case '\"' : { int start; sym = constsym; constkind = stringtype; NextCh(); start = chcount; while ((ch != '\"') && (ch != 0x0)) NextCh(); GrabString(start); TermChar('\"'); // check for eol before '\"' break; } case 0x0: sym = eolsym; break; default: { int start; start = chcount; DigRep(); if ((start != chcount) || (ch == '.')) { // number sym = constsym; if (ch == '.') { constkind = floattype; NextCh(); DigRep(); } else constkind = inttype; if ((ch == 'e') || (ch == 'E')) { int mark; constkind = floattype; NextCh(); if ((ch == '+') || (ch == '-')) NextCh(); mark = chcount; DigRep(); if (mark == chcount) { ScanError("Number expected after 'E'"); return; } } GrabString(start); } else if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))) { // reserved word? int start; STR_String funstr; start = chcount; CharRep(); GrabString(start); funstr = const_as_string; funstr.Upper(); if (funstr == STR_String("SUM")) { sym = sumsym; } else if (funstr == STR_String("NOT")) { sym = opsym; opkind = OPnot; } else if (funstr == STR_String("AND")) { sym = opsym; opkind = OPand; } else if (funstr == STR_String("OR")) { sym = opsym; opkind = OPor; } else if (funstr == STR_String("IF")) { sym = ifsym; } else if (funstr == STR_String("WHOMADE")) { sym = whocodedsym; } else if (funstr == STR_String("FALSE")) { sym = constsym; constkind = booltype; boolvalue = false; } else if (funstr == STR_String("TRUE")) { sym = constsym; constkind = booltype; boolvalue = true; } else { sym = idsym; //STR_String str; //str.Format("'%s' makes no sense here", (const char*)funstr); //ScanError(str); } } else { // unknown symbol STR_String str; str.Format("Unexpected character '%c'", ch); NextCh(); ScanError(str); return; } } } } int CParser::MakeInt() { // returns the integer representation of the value in the global // variable const_as_string // pre: const_as_string contains only numercal chars return atoi(const_as_string); } STR_String CParser::Symbol2Str(int s) { // returns a string representation of of symbol s, // for use in Term when generating an error switch(s) { case errorsym: return "error"; case lbracksym: return "("; case rbracksym: return ")"; case commasym: return ","; case opsym: return "operator"; case constsym: return "constant"; case sumsym: return "SUM"; case ifsym: return "IF"; case whocodedsym: return "WHOMADE"; case eolsym: return "end of line"; case idsym: return "identifier"; default: return "unknown"; // should not happen } } void CParser::Term(int s) { // generates an error if the next symbol isn't the specified symbol s // otherwise, skip the symbol if(s == sym) NextSym(); else { STR_String msg; msg.Format("Warning: " + Symbol2Str(s) + " expected\ncontinuing without it"); // AfxMessageBox(msg,MB_ICONERROR); trace(msg); } } int CParser::Priority(int optorkind) { // returns the priority of an operator // higher number means higher priority switch(optorkind) { case OPor: return 1; case OPand: return 2; case OPgreater: case OPless: case OPgreaterequal: case OPlessequal: case OPequal: case OPunequal: return 3; case OPplus: case OPminus: return 4; case OPtimes: case OPdivide: return 5; } assert(false); return 0; // should not happen } CExpression *CParser::Ex(int i) { // parses an expression in the imput, starting at priority i, and // returns an CExpression, containing the parsed input CExpression *e1 = NULL, *e2 = NULL; int opkind2; if (i < NUM_PRIORITY) { e1 = Ex(i + 1); while ((sym == opsym) && (Priority(opkind) == i)) { opkind2 = opkind; NextSym(); e2 = Ex(i + 1); switch(opkind2) { case OPplus: e1 = new COperator2Expr(VALUE_ADD_OPERATOR,e1, e2); break; case OPminus: e1 = new COperator2Expr(VALUE_SUB_OPERATOR,e1, e2); break; case OPtimes: e1 = new COperator2Expr(VALUE_MUL_OPERATOR,e1, e2); break; case OPdivide: e1 = new COperator2Expr(VALUE_DIV_OPERATOR,e1, e2); break; case OPand: e1 = new COperator2Expr(VALUE_AND_OPERATOR,e1, e2); break; case OPor: e1 = new COperator2Expr(VALUE_OR_OPERATOR,e1, e2); break; case OPequal: e1 = new COperator2Expr(VALUE_EQL_OPERATOR,e1, e2); break; case OPunequal: e1 = new COperator2Expr(VALUE_NEQ_OPERATOR,e1, e2); break; case OPgreater: e1 = new COperator2Expr(VALUE_GRE_OPERATOR,e1, e2); break; case OPless: e1 = new COperator2Expr(VALUE_LES_OPERATOR,e1, e2); break; case OPgreaterequal: e1 = new COperator2Expr(VALUE_GEQ_OPERATOR,e1, e2); break; case OPlessequal: e1 = new COperator2Expr(VALUE_LEQ_OPERATOR,e1, e2); break; default: assert(false); break; // should not happen } } } else if (i == NUM_PRIORITY) { if ((sym == opsym) && ( (opkind == OPminus) || (opkind == OPnot) || (opkind == OPplus) ) ) { NextSym(); switch(opkind) { /* +1 is also a valid number! */ case OPplus: e1 = new COperator1Expr(VALUE_POS_OPERATOR, Ex(NUM_PRIORITY)); break; case OPminus: e1 = new COperator1Expr(VALUE_NEG_OPERATOR, Ex(NUM_PRIORITY)); break; case OPnot: e1 = new COperator1Expr(VALUE_NOT_OPERATOR, Ex(NUM_PRIORITY)); break; default: { // should not happen e1 = Error("operator +, - or ! expected"); } } } else { switch(sym) { case constsym: { switch(constkind) { case booltype: e1 = new CConstExpr(new CBoolValue(boolvalue)); break; case inttype: { int temp; temp = atoi(const_as_string); e1 = new CConstExpr(new CIntValue(temp)); break; } case floattype: { double temp; temp = atof(const_as_string); e1 = new CConstExpr(new CFloatValue(temp)); break; } case stringtype: e1 = new CConstExpr(new CStringValue(const_as_string,"")); break; default : assert(false); break; } NextSym(); break; } case lbracksym: NextSym(); e1 = Ex(1); Term(rbracksym); break; case ifsym: { CExpression *e3; NextSym(); Term(lbracksym); e1 = Ex(1); Term(commasym); e2 = Ex(1); if (sym == commasym) { NextSym(); e3 = Ex(1); } else { e3 = new CConstExpr(new CEmptyValue()); } Term(rbracksym); e1 = new CIfExpr(e1, e2, e3); break; } case idsym: { e1 = new CIdentifierExpr(const_as_string,m_identifierContext); NextSym(); break; } case errorsym: { assert(!e1); STR_String errtext="[no info]"; if (errmsg) { CValue* errmsgval = errmsg->Calculate(); errtext=errmsgval->GetText(); errmsgval->Release(); //e1 = Error(errmsg->Calculate()->GetText());//new CConstExpr(errmsg->Calculate()); if ( !(errmsg->Release()) ) { errmsg=NULL; } else { // does this happen ? assert ("does this happen"); } } e1 = Error(errtext); break; } default: NextSym(); //return Error("Expression expected"); assert(!e1); e1 = Error("Expression expected"); } } } return e1; } CExpression *CParser::Expr() { // parses an expression in the imput, and // returns an CExpression, containing the parsed input return Ex(1); } CExpression* CParser::ProcessText (STR_String intext) { // and parses the string in intext and returns it. CExpression* expr; text = intext; chcount = 0; if (text.Length() == 0) { return NULL; } ch = text[0]; /*if (ch != '=') { expr = new CConstExpr(new CStringValue(text)); *dependant = deplist; return expr; } else */ // NextCh(); NextSym(); expr = Expr(); if (sym != eolsym) { CExpression* oldexpr = expr; expr = new COperator2Expr(VALUE_ADD_OPERATOR, oldexpr, Error(STR_String("Extra characters after expression")));//new CConstExpr(new CErrorValue("Extra characters after expression"))); } if (errmsg) errmsg->Release(); return expr; } float CParser::GetFloat(STR_String txt) { // returns parsed text into a float // empty string returns -1 // AfxMessageBox("parsed string="+txt); CValue* val=NULL; float result=-1; // String tmpstr; CExpression* expr = ProcessText(txt); if (expr) { val = expr->Calculate(); result=val->GetNumber(); val->Release(); expr->Release(); } // tmpstr.Format("parseresult=%g",result); // AfxMessageBox(tmpstr); return result; } CValue* CParser::GetValue(STR_String txt, bool bFallbackToText) { // returns parsed text into a value, // empty string returns NULL value ! // if bFallbackToText then unparsed stuff is put into text CValue* result=NULL; CExpression* expr = ProcessText(txt); if (expr) { result = expr->Calculate(); expr->Release(); } if (result) { // if the parsed stuff lead to an errorvalue, don't return errors, just NULL if (result->IsError()) { result->Release(); result=NULL; if (bFallbackToText) { if (txt.Length()>0) { result = new CStringValue(txt,""); } } } } return result; } void CParser::SetContext(CValue* context) { if (m_identifierContext) { m_identifierContext->Release(); } m_identifierContext = context; } PyObject* CParserPyMake(PyObject* ignored,PyObject* args) { char* txt; Py_Try(PyArg_ParseTuple(args,"s",&txt)); CParser parser; CExpression* expr = parser.ProcessText(txt); CValue* val = expr->Calculate(); expr->Release(); return val; } static PyMethodDef CParserMethods[] = { { "calc", CParserPyMake , Py_NEWARGS}, { NULL,NULL} // Sentinel }; extern "C" { void initExpressionModule(void) { Py_InitModule("Expression",CParserMethods); } }