Blender V2.61 - r43446

InputParser.cpp

Go to the documentation of this file.
00001 
00004 // Parser.cpp: implementation of the CParser class.
00005 /*
00006  * Copyright (c) 1996-2000 Erwin Coumans <coockie@acm.org>
00007  *
00008  * Permission to use, copy, modify, distribute and sell this software
00009  * and its documentation for any purpose is hereby granted without fee,
00010  * provided that the above copyright notice appear in all copies and
00011  * that both that copyright notice and this permission notice appear
00012  * in supporting documentation.  Erwin Coumans makes no
00013  * representations about the suitability of this software for any
00014  * purpose.  It is provided "as is" without express or implied warranty.
00015  *
00016  */
00017 
00018 #include <stdlib.h>
00019 
00020 #include "MT_assert.h"
00021 
00022 #include "Value.h"
00023 #include "InputParser.h"
00024 #include "ErrorValue.h"
00025 #include "IntValue.h"
00026 #include "StringValue.h"
00027 #include "FloatValue.h"
00028 #include "BoolValue.h"
00029 #include "EmptyValue.h"
00030 #include "ConstExpr.h"
00031 #include "Operator2Expr.h"
00032 #include "Operator1Expr.h"
00033 #include "IdentifierExpr.h"
00034 
00035 // this is disable at the moment, I expected a memleak from it, but the error-cleanup was the reason
00036 // well, looks we don't need it anyway, until maybe the Curved Surfaces are integrated into CSG 
00037 // cool things like (IF(LOD==1,CCurvedValue,IF(LOD==2,CCurvedValue2)) etc...
00038 #include "IfExpr.h" 
00039 
00040 #if (defined(WIN32) || defined(WIN64)) && !defined(FREE_WINDOWS)
00041 #define strcasecmp  _stricmp
00042 
00043 #ifndef strtoll
00044 #define strtoll     _strtoi64
00045 #endif
00046 
00047 #endif /* Def WIN32 or Def WIN64 */
00048 
00049 #define NUM_PRIORITY 6
00050 
00051 // Construction/Destruction
00053 
00054 CParser::CParser() : m_identifierContext(NULL)
00055 {
00056 }
00057 
00058 
00059 
00060 CParser::~CParser()
00061 {
00062     if (m_identifierContext)
00063         m_identifierContext->Release();
00064 }
00065 
00066 
00067 
00068 void CParser::ScanError(const char *str)
00069 {
00070     // sets the global variable errmsg to an errormessage with
00071     // contents str, appending if it already exists
00072     //  AfxMessageBox("Parse Error:"+str,MB_ICONERROR);
00073     if (errmsg)
00074         errmsg = new COperator2Expr(VALUE_ADD_OPERATOR, errmsg, Error(str));
00075     else
00076         errmsg = Error(str);
00077 
00078     sym = errorsym;
00079 }
00080 
00081 
00082 
00083 CExpression* CParser::Error(const char *str)
00084 {
00085     // makes and returns a new CConstExpr filled with an CErrorValue
00086     // with string str
00087     //  AfxMessageBox("Error:"+str,MB_ICONERROR);
00088     return new CConstExpr(new CErrorValue(str));
00089 }
00090 
00091 
00092 
00093 void CParser::NextCh()
00094 {
00095     // sets the global variable ch to the next character, if it exists
00096     // and increases the global variable chcount
00097     chcount++;
00098 
00099     if (chcount < text.Length())
00100         ch = text[chcount];
00101     else
00102         ch = 0x00;
00103 }
00104 
00105 
00106 
00107 void CParser::TermChar(char c)
00108 {
00109     // generates an error if the next char isn't the specified char c,
00110     // otherwise, skip the char
00111     if(ch == c)
00112     {
00113         NextCh();
00114     }
00115     else
00116     {
00117         STR_String str;
00118         str.Format("Warning: %c expected\ncontinuing without it", c);
00119         trace(str);
00120     }
00121 }
00122 
00123 
00124 
00125 void CParser::DigRep()
00126 {
00127     // changes the current character to the first character that
00128     // isn't a decimal
00129     while ((ch >= '0') && (ch <= '9'))
00130         NextCh();
00131 }
00132 
00133 
00134 
00135 void CParser::CharRep()
00136 {
00137     // changes the current character to the first character that
00138     // isn't an alphanumeric character
00139     while (((ch >= '0') && (ch <= '9'))
00140         || ((ch >= 'a') && (ch <= 'z'))
00141         || ((ch >= 'A') && (ch <= 'Z'))
00142         || (ch == '.') || (ch == '_'))
00143         NextCh();
00144 }
00145 
00146 
00147 
00148 void CParser::GrabString(int start)
00149 {
00150     // puts part of the input string into the global variable
00151     // const_as_string, from position start, to position chchount
00152     const_as_string = text.Mid(start, chcount-start);
00153 }
00154 
00155 
00156 
00157 void CParser::GrabRealString(int start)
00158 {
00159     // works like GrabString but converting \\n to \n
00160     // puts part of the input string into the global variable
00161     // const_as_string, from position start, to position chchount
00162 
00163     int i;
00164     char tmpch;
00165 
00166     const_as_string = STR_String();
00167     for (i=start;i<chcount;i++) {
00168         tmpch= text[i];
00169         if ((tmpch =='\\') && (text[i+1] == 'n')){
00170             tmpch = '\n';
00171             i++;
00172         }
00173         const_as_string += tmpch;
00174     }
00175 }
00176 
00177 
00178 
00179 void CParser::NextSym()
00180 {
00181     // sets the global variable sym to the next symbol, and
00182     // if it is an operator
00183     //   sets the global variable opkind to the kind of operator
00184     // if it is a constant
00185     //   sets the global variable constkind to the kind of operator
00186     // if it is a reference to a cell
00187     //   sets the global variable cellcoord to the kind of operator
00188     
00189     errmsg = NULL;
00190     while(ch == ' ' || ch == 0x9)
00191         NextCh();
00192 
00193     switch(ch)
00194     {
00195         case '(':
00196             sym = lbracksym; NextCh();
00197             break;
00198         case ')':
00199             sym = rbracksym; NextCh();
00200             break;
00201         case ',':
00202             sym = commasym; NextCh();
00203             break;
00204         case '%' :
00205             sym = opsym; opkind = OPmodulus; NextCh();
00206             break;
00207         case '+' :
00208             sym = opsym; opkind = OPplus; NextCh();
00209             break;
00210         case '-' :
00211             sym = opsym; opkind = OPminus; NextCh();
00212             break;
00213         case '*' :
00214             sym = opsym; opkind = OPtimes; NextCh();
00215             break;
00216         case '/' :
00217             sym = opsym; opkind = OPdivide; NextCh();
00218             break;
00219         case '&' :
00220             sym = opsym; opkind = OPand; NextCh(); TermChar('&');
00221             break;
00222         case '|' :
00223             sym = opsym; opkind = OPor; NextCh(); TermChar('|');
00224             break;
00225         case '=' :
00226             sym = opsym; opkind = OPequal; NextCh(); TermChar('=');
00227             break;
00228         case '!' :
00229             sym = opsym;
00230             NextCh();
00231             if (ch == '=')
00232             {
00233                 opkind = OPunequal;
00234                 NextCh();
00235             }
00236             else
00237             {
00238                 opkind = OPnot;
00239             }
00240             break;
00241         case '>':
00242             sym = opsym;
00243             NextCh();
00244             if (ch == '=')
00245             {
00246                 opkind = OPgreaterequal;
00247                 NextCh();
00248             }
00249             else
00250             {
00251                 opkind = OPgreater;
00252             }
00253             break;
00254         case '<':
00255             sym = opsym;
00256             NextCh();
00257             if (ch == '=') {
00258                 opkind = OPlessequal;
00259                 NextCh();
00260             } else {
00261                 opkind = OPless;
00262             }
00263             break;
00264         case '\"' : {
00265             int start;
00266             sym = constsym;
00267             constkind = stringtype;
00268             NextCh();
00269             start = chcount;
00270             while ((ch != '\"') && (ch != 0x0))
00271                 NextCh();
00272             GrabRealString(start);
00273             TermChar('\"'); // check for eol before '\"'
00274             break;
00275         }
00276         case 0x0: sym = eolsym; break;
00277         default:
00278         {
00279             int start;
00280             start = chcount;
00281             DigRep();
00282             if ((start != chcount) || (ch == '.')) { // number
00283                 sym = constsym;
00284                 if (ch == '.') {
00285                     constkind = floattype;
00286                     NextCh();
00287                     DigRep();
00288                 }
00289                 else constkind = inttype;
00290                 if ((ch == 'e') || (ch == 'E')) {
00291                     int mark;
00292                     constkind = floattype;
00293                     NextCh();
00294                     if ((ch == '+') || (ch == '-')) NextCh();
00295                     mark = chcount;
00296                     DigRep();
00297                     if (mark == chcount) {
00298                         ScanError("Number expected after 'E'");
00299                         return;
00300                     }
00301                 }
00302                 GrabString(start);
00303             } else if (((ch >= 'a') && (ch <= 'z'))
00304                        || ((ch >= 'A') && (ch <= 'Z')))
00305             { // reserved word?
00306                 
00307                 start = chcount;
00308                 CharRep();
00309                 GrabString(start);
00310                 if (!strcasecmp(const_as_string, "SUM")) {
00311                     sym = sumsym;
00312                 }
00313                 else if (!strcasecmp(const_as_string, "NOT")) {
00314                     sym = opsym;
00315                     opkind = OPnot;
00316                 }
00317                 else if (!strcasecmp(const_as_string, "AND")) {
00318                     sym = opsym; opkind = OPand;
00319                 }
00320                 else if (!strcasecmp(const_as_string, "OR")) {
00321                     sym = opsym; opkind = OPor;
00322                 }
00323                 else if (!strcasecmp(const_as_string, "IF"))
00324                     sym = ifsym;
00325                 else if (!strcasecmp(const_as_string, "WHOMADE"))
00326                     sym = whocodedsym;
00327                 else if (!strcasecmp(const_as_string, "FALSE")) {
00328                     sym = constsym; constkind = booltype; boolvalue = false;
00329                 } else if (!strcasecmp(const_as_string, "TRUE")) {
00330                     sym = constsym; constkind = booltype; boolvalue = true;
00331                 } else {
00332                     sym = idsym;
00333                     //STR_String str;
00334                     //str.Format("'%s' makes no sense here", (const char*)funstr);
00335                     //ScanError(str);
00336                 }
00337             } else { // unknown symbol
00338                 STR_String str;
00339                 str.Format("Unexpected character '%c'", ch);
00340                 NextCh();
00341                 ScanError(str);
00342                 return;
00343             }
00344         }
00345     }
00346 }
00347 
00348 #if 0
00349 int CParser::MakeInt()
00350 {
00351     // returns the integer representation of the value in the global
00352     // variable const_as_string
00353     // pre: const_as_string contains only numercal chars
00354     return atoi(const_as_string);
00355 }
00356 #endif
00357 
00358 STR_String CParser::Symbol2Str(int s)
00359 {
00360     // returns a string representation of of symbol s,
00361     // for use in Term when generating an error
00362     switch(s) {
00363         case errorsym: return "error";
00364         case lbracksym: return "(";
00365         case rbracksym: return ")";
00366         case commasym: return ",";
00367         case opsym: return "operator";
00368         case constsym: return "constant";
00369         case sumsym: return "SUM";
00370         case ifsym: return "IF";
00371         case whocodedsym: return "WHOMADE";
00372         case eolsym: return "end of line";
00373         case idsym: return "identifier";
00374         default: return "unknown";  // should not happen
00375     }
00376 }
00377 
00378 void CParser::Term(int s)
00379 {
00380     // generates an error if the next symbol isn't the specified symbol s
00381     // otherwise, skip the symbol
00382     if(s == sym) NextSym();
00383     else {
00384         STR_String msg;
00385         msg.Format("Warning: " + Symbol2Str(s) + " expected\ncontinuing without it");
00386 
00387 //      AfxMessageBox(msg,MB_ICONERROR);
00388 
00389         trace(msg);
00390     }
00391 }
00392 
00393 int CParser::Priority(int optorkind)
00394 {
00395     // returns the priority of an operator
00396     // higher number means higher priority
00397     switch(optorkind) {
00398         case OPor: return 1;
00399         case OPand: return 2;
00400         case OPgreater:
00401         case OPless:
00402         case OPgreaterequal:
00403         case OPlessequal:
00404         case OPequal:
00405         case OPunequal: return 3;
00406         case OPplus:
00407         case OPminus: return 4;
00408         case OPmodulus:
00409         case OPtimes:
00410         case OPdivide: return 5;
00411     }
00412     MT_assert(false);
00413     return 0;      // should not happen
00414 }
00415 
00416 CExpression *CParser::Ex(int i)
00417 {
00418     // parses an expression in the imput, starting at priority i, and
00419     // returns an CExpression, containing the parsed input
00420     CExpression *e1 = NULL, *e2 = NULL;
00421     int opkind2;
00422     
00423     if (i < NUM_PRIORITY) {
00424         e1 = Ex(i + 1);
00425         while ((sym == opsym) && (Priority(opkind) == i)) {
00426             opkind2 = opkind;
00427             NextSym();
00428             e2 = Ex(i + 1);
00429             switch(opkind2) {
00430             case OPmodulus: e1 = new COperator2Expr(VALUE_MOD_OPERATOR,e1, e2); break;
00431             case OPplus: e1 = new COperator2Expr(VALUE_ADD_OPERATOR,e1, e2); break;
00432             case OPminus: e1 = new COperator2Expr(VALUE_SUB_OPERATOR,e1, e2); break;
00433             case OPtimes:   e1 = new COperator2Expr(VALUE_MUL_OPERATOR,e1, e2); break;
00434             case OPdivide: e1 = new COperator2Expr(VALUE_DIV_OPERATOR,e1, e2); break;
00435             case OPand: e1 = new COperator2Expr(VALUE_AND_OPERATOR,e1, e2); break;
00436             case OPor: e1 = new COperator2Expr(VALUE_OR_OPERATOR,e1, e2); break;
00437             case OPequal: e1 = new COperator2Expr(VALUE_EQL_OPERATOR,e1, e2); break;
00438             case OPunequal: e1 = new COperator2Expr(VALUE_NEQ_OPERATOR,e1, e2); break;
00439             case OPgreater: e1 = new COperator2Expr(VALUE_GRE_OPERATOR,e1, e2); break;
00440             case OPless: e1 = new COperator2Expr(VALUE_LES_OPERATOR,e1, e2); break;
00441             case OPgreaterequal: e1 = new COperator2Expr(VALUE_GEQ_OPERATOR,e1, e2); break;
00442             case OPlessequal: e1 = new COperator2Expr(VALUE_LEQ_OPERATOR,e1, e2); break;
00443             default: MT_assert(false);  break; // should not happen
00444             }
00445         }
00446     } else if (i == NUM_PRIORITY) {
00447         if ((sym == opsym) 
00448             && ( (opkind == OPminus) || (opkind == OPnot) || (opkind == OPplus) ) 
00449             )
00450         {
00451             NextSym();
00452             switch(opkind) {
00453             /* +1 is also a valid number! */
00454             case OPplus: e1 = new COperator1Expr(VALUE_POS_OPERATOR, Ex(NUM_PRIORITY)); break;
00455             case OPminus: e1 = new COperator1Expr(VALUE_NEG_OPERATOR, Ex(NUM_PRIORITY)); break;
00456             case OPnot: e1 = new COperator1Expr(VALUE_NOT_OPERATOR, Ex(NUM_PRIORITY)); break;
00457             default: {
00458                         // should not happen
00459                         e1 = Error("operator +, - or ! expected");
00460                      }
00461             }
00462         }
00463         else {
00464             switch(sym) {
00465             case constsym: {
00466                 switch(constkind) {
00467                 case booltype:
00468                     e1 = new CConstExpr(new CBoolValue(boolvalue));
00469                     break;
00470                 case inttype:
00471                     {
00472                         cInt temp;
00473                         temp = strtoll(const_as_string, NULL, 10); /* atoi is for int only */
00474                         e1 = new CConstExpr(new CIntValue(temp));
00475                         break;
00476                     }
00477                 case floattype:
00478                     {
00479                         double temp;
00480                         temp = atof(const_as_string);
00481                         e1 = new CConstExpr(new CFloatValue(temp));
00482                         break;
00483                     }
00484                 case stringtype:
00485                     e1 = new CConstExpr(new CStringValue(const_as_string,""));
00486                     break;
00487                 default :
00488                     MT_assert(false);
00489                     break;
00490                 }
00491                 NextSym();
00492                 break;
00493                            }
00494             case lbracksym:
00495                 NextSym();
00496                 e1 = Ex(1);
00497                 Term(rbracksym);
00498                 break;
00499             case ifsym:
00500             {
00501                 CExpression *e3;
00502                 NextSym();
00503                 Term(lbracksym);
00504                 e1 = Ex(1);
00505                 Term(commasym);
00506                 e2 = Ex(1);
00507                 if (sym == commasym) {
00508                     NextSym();
00509                     e3 = Ex(1);
00510                 } else {
00511                     e3 = new CConstExpr(new CEmptyValue());
00512                 }
00513                 Term(rbracksym);
00514                 e1 = new CIfExpr(e1, e2, e3);
00515                 break;
00516             }
00517             case idsym:
00518                 {
00519                     e1 = new CIdentifierExpr(const_as_string,m_identifierContext);
00520                     NextSym();
00521                     
00522                     break;
00523                 }
00524             case errorsym:
00525                 {
00526                     MT_assert(!e1);
00527                     STR_String errtext="[no info]";
00528                     if (errmsg)
00529                     {
00530                         CValue* errmsgval = errmsg->Calculate();
00531                         errtext=errmsgval->GetText();
00532                         errmsgval->Release();
00533                     
00534                         //e1 = Error(errmsg->Calculate()->GetText());//new CConstExpr(errmsg->Calculate());
00535                         
00536                         if ( !(errmsg->Release()) )
00537                         {
00538                             errmsg=NULL;
00539                         } else {
00540                             // does this happen ?
00541                             MT_assert ("does this happen");
00542                         }
00543                     }
00544                     e1 = Error(errtext);
00545 
00546                     break;              
00547                 }
00548             default:
00549                 NextSym();
00550                 //return Error("Expression expected");
00551                 MT_assert(!e1);
00552                 e1 = Error("Expression expected");
00553             }
00554         }
00555     }
00556     return e1;
00557 }
00558 
00559 CExpression *CParser::Expr()
00560 {
00561     // parses an expression in the imput, and
00562     // returns an CExpression, containing the parsed input
00563     return Ex(1);
00564 }
00565 
00566 CExpression* CParser::ProcessText
00567 (const char *intext) {
00568     
00569     // and parses the string in intext and returns it.
00570     
00571     
00572     CExpression* expr;
00573     text = intext;
00574     
00575     
00576     chcount = 0;    
00577     if (text.Length() == 0) {
00578         return NULL;
00579     }
00580     
00581     ch = text[0];
00582     /*if (ch != '=') {
00583     expr = new CConstExpr(new CStringValue(text));
00584     *dependant = deplist;
00585     return expr;
00586     } else 
00587     */
00588     //  NextCh();
00589     NextSym();
00590     expr = Expr();
00591     if (sym != eolsym) {
00592         CExpression* oldexpr = expr;
00593         expr = new COperator2Expr(VALUE_ADD_OPERATOR,
00594             oldexpr, Error(STR_String("Extra characters after expression")));//new CConstExpr(new CErrorValue("Extra characters after expression")));
00595     }
00596     if (errmsg)
00597         errmsg->Release();
00598     
00599     return expr;
00600 }
00601 
00602 
00603 
00604 float CParser::GetFloat(STR_String& txt)
00605 {
00606     // returns parsed text into a float
00607     // empty string returns -1
00608     
00609 //  AfxMessageBox("parsed string="+txt);
00610     CValue* val=NULL;
00611     float result=-1;
00612 //  String tmpstr;
00613 
00614     CExpression* expr = ProcessText(txt);
00615     if (expr) {
00616         val = expr->Calculate();
00617         result=(float)val->GetNumber();
00618         
00619         
00620     
00621         val->Release();
00622         expr->Release();
00623     }
00624 //  tmpstr.Format("parseresult=%g",result);
00625 //      AfxMessageBox(tmpstr);
00626     return result;
00627 }
00628 
00629 CValue* CParser::GetValue(STR_String& txt, bool bFallbackToText)
00630 {
00631     // returns parsed text into a value, 
00632     // empty string returns NULL value !
00633     // if bFallbackToText then unparsed stuff is put into text
00634     
00635     CValue* result=NULL;
00636     CExpression* expr = ProcessText(txt);
00637     if (expr) {
00638         result = expr->Calculate();
00639         expr->Release();
00640     }
00641     if (result)
00642     {
00643         // if the parsed stuff lead to an errorvalue, don't return errors, just NULL
00644         if (result->IsError()) {
00645             result->Release();
00646             result=NULL;
00647             if (bFallbackToText) {
00648                 if (txt.Length()>0)
00649                 {
00650                     result = new CStringValue(txt,"");
00651                 }
00652             }
00653         }
00654     }
00655     return result;
00656 }
00657 
00658 void CParser::SetContext(CValue* context)
00659 {
00660     if (m_identifierContext)
00661     {
00662         m_identifierContext->Release();
00663     }
00664     m_identifierContext = context;
00665 }