Blender  V2.59
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         // returns the integer representation of the value in the global
00351         // variable const_as_string
00352         // pre: const_as_string contains only numercal chars
00353         return atoi(const_as_string);
00354 }
00355 #endif
00356 
00357 STR_String CParser::Symbol2Str(int s) {
00358         // returns a string representation of of symbol s,
00359         // for use in Term when generating an error
00360         switch(s) {
00361     case errorsym: return "error";
00362     case lbracksym: return "(";
00363     case rbracksym: return ")";
00364     case commasym: return ",";
00365     case opsym: return "operator";
00366     case constsym: return "constant";
00367         case sumsym: return "SUM";
00368         case ifsym: return "IF";
00369         case whocodedsym: return "WHOMADE";
00370     case eolsym: return "end of line";
00371         case idsym: return "identifier";
00372     default: return "unknown";  // should not happen
00373         }
00374 }
00375 
00376 void CParser::Term(int s) {
00377         // generates an error if the next symbol isn't the specified symbol s
00378         // otherwise, skip the symbol
00379         if(s == sym) NextSym();
00380         else {
00381                 STR_String msg;
00382                 msg.Format("Warning: " + Symbol2Str(s) + " expected\ncontinuing without it");
00383 
00384 //              AfxMessageBox(msg,MB_ICONERROR);
00385 
00386                 trace(msg);
00387         }
00388 }
00389 
00390 int CParser::Priority(int optorkind) {
00391         // returns the priority of an operator
00392         // higher number means higher priority
00393         switch(optorkind) {
00394         case OPor: return 1;
00395         case OPand: return 2;
00396         case OPgreater:
00397         case OPless:
00398         case OPgreaterequal:
00399         case OPlessequal:
00400         case OPequal:
00401         case OPunequal: return 3;
00402     case OPplus:
00403     case OPminus: return 4;
00404         case OPmodulus:
00405     case OPtimes:
00406     case OPdivide: return 5;
00407         }
00408         MT_assert(false);
00409         return 0;      // should not happen
00410 }
00411 
00412 CExpression *CParser::Ex(int i) {
00413         // parses an expression in the imput, starting at priority i, and
00414         // returns an CExpression, containing the parsed input
00415         CExpression *e1 = NULL, *e2 = NULL;
00416         int opkind2;
00417         
00418         if (i < NUM_PRIORITY) {
00419                 e1 = Ex(i + 1);
00420                 while ((sym == opsym) && (Priority(opkind) == i)) {
00421                         opkind2 = opkind;
00422                         NextSym();
00423                         e2 = Ex(i + 1);
00424                         switch(opkind2) {
00425                         case OPmodulus: e1 = new COperator2Expr(VALUE_MOD_OPERATOR,e1, e2); break;
00426                         case OPplus: e1 = new COperator2Expr(VALUE_ADD_OPERATOR,e1, e2); break;
00427                         case OPminus: e1 = new COperator2Expr(VALUE_SUB_OPERATOR,e1, e2); break;
00428                         case OPtimes:   e1 = new COperator2Expr(VALUE_MUL_OPERATOR,e1, e2); break;
00429                         case OPdivide: e1 = new COperator2Expr(VALUE_DIV_OPERATOR,e1, e2); break;
00430                         case OPand: e1 = new COperator2Expr(VALUE_AND_OPERATOR,e1, e2); break;
00431                         case OPor: e1 = new COperator2Expr(VALUE_OR_OPERATOR,e1, e2); break;
00432                         case OPequal: e1 = new COperator2Expr(VALUE_EQL_OPERATOR,e1, e2); break;
00433                         case OPunequal: e1 = new COperator2Expr(VALUE_NEQ_OPERATOR,e1, e2); break;
00434                         case OPgreater: e1 = new COperator2Expr(VALUE_GRE_OPERATOR,e1, e2); break;
00435                         case OPless: e1 = new COperator2Expr(VALUE_LES_OPERATOR,e1, e2); break;
00436                         case OPgreaterequal: e1 = new COperator2Expr(VALUE_GEQ_OPERATOR,e1, e2); break;
00437                         case OPlessequal: e1 = new COperator2Expr(VALUE_LEQ_OPERATOR,e1, e2); break;
00438                         default: MT_assert(false);      break; // should not happen
00439                         }
00440                 }
00441         } else if (i == NUM_PRIORITY) {
00442                 if ((sym == opsym) 
00443                         && ( (opkind == OPminus) || (opkind == OPnot) || (opkind == OPplus) ) 
00444                         )
00445                 {
00446                         NextSym();
00447                         switch(opkind) {
00448                         /* +1 is also a valid number! */
00449                         case OPplus: e1 = new COperator1Expr(VALUE_POS_OPERATOR, Ex(NUM_PRIORITY)); break;
00450                         case OPminus: e1 = new COperator1Expr(VALUE_NEG_OPERATOR, Ex(NUM_PRIORITY)); break;
00451                         case OPnot: e1 = new COperator1Expr(VALUE_NOT_OPERATOR, Ex(NUM_PRIORITY)); break;
00452                         default: {
00453                                                 // should not happen
00454                                                 e1 = Error("operator +, - or ! expected");
00455                                          }
00456                         }
00457                 }
00458                 else {
00459                         switch(sym) {
00460                         case constsym: {
00461                                 switch(constkind) {
00462                                 case booltype:
00463                                         e1 = new CConstExpr(new CBoolValue(boolvalue));
00464                                         break;
00465                                 case inttype:
00466                                         {
00467                                                 cInt temp;
00468                                                 temp = strtoll(const_as_string, NULL, 10); /* atoi is for int only */
00469                                                 e1 = new CConstExpr(new CIntValue(temp));
00470                                                 break;
00471                                         }
00472                                 case floattype:
00473                                         {
00474                                                 double temp;
00475                                                 temp = atof(const_as_string);
00476                                                 e1 = new CConstExpr(new CFloatValue(temp));
00477                                                 break;
00478                                         }
00479                                 case stringtype:
00480                                         e1 = new CConstExpr(new CStringValue(const_as_string,""));
00481                                         break;
00482                                 default :
00483                                         MT_assert(false);
00484                                         break;
00485                                 }
00486                                 NextSym();
00487                                 break;
00488                                                    }
00489                         case lbracksym:
00490                                 NextSym();
00491                                 e1 = Ex(1);
00492                                 Term(rbracksym);
00493                                 break;
00494                         case ifsym:
00495                         {
00496                                 CExpression *e3;
00497                                 NextSym();
00498                                 Term(lbracksym);
00499                                 e1 = Ex(1);
00500                                 Term(commasym);
00501                                 e2 = Ex(1);
00502                                 if (sym == commasym) {
00503                                         NextSym();
00504                                         e3 = Ex(1);
00505                                 } else {
00506                                         e3 = new CConstExpr(new CEmptyValue());
00507                                 }
00508                                 Term(rbracksym);
00509                                 e1 = new CIfExpr(e1, e2, e3);
00510                                 break;
00511                         }
00512                         case idsym:
00513                                 {
00514                                         e1 = new CIdentifierExpr(const_as_string,m_identifierContext);
00515                                         NextSym();
00516                                         
00517                                         break;
00518                                 }
00519                         case errorsym:
00520                                 {
00521                                         MT_assert(!e1);
00522                                         STR_String errtext="[no info]";
00523                                         if (errmsg)
00524                                         {
00525                                                 CValue* errmsgval = errmsg->Calculate();
00526                                                 errtext=errmsgval->GetText();
00527                                                 errmsgval->Release();
00528                                         
00529                                                 //e1 = Error(errmsg->Calculate()->GetText());//new CConstExpr(errmsg->Calculate());
00530                                                 
00531                                                 if ( !(errmsg->Release()) )
00532                                                 {
00533                                                         errmsg=NULL;
00534                                                 } else {
00535                                                         // does this happen ?
00536                                                         MT_assert ("does this happen");
00537                                                 }
00538                                         }
00539                                         e1 = Error(errtext);
00540 
00541                                         break;                          
00542                                 }
00543                         default:
00544                                 NextSym();
00545                                 //return Error("Expression expected");
00546                                 MT_assert(!e1);
00547                                 e1 = Error("Expression expected");
00548                         }
00549                 }
00550         }
00551         return e1;
00552 }
00553 
00554 CExpression *CParser::Expr() {
00555         // parses an expression in the imput, and
00556         // returns an CExpression, containing the parsed input
00557         return Ex(1);
00558 }
00559 
00560 CExpression* CParser::ProcessText
00561 (const char *intext) {
00562         
00563         // and parses the string in intext and returns it.
00564         
00565         
00566         CExpression* expr;
00567         text = intext;
00568         
00569         
00570         chcount = 0;    
00571         if (text.Length() == 0) {
00572                 return NULL;
00573         }
00574         
00575         ch = text[0];
00576         /*if (ch != '=') {
00577         expr = new CConstExpr(new CStringValue(text));
00578         *dependant = deplist;
00579         return expr;
00580         } else 
00581         */
00582         //      NextCh();
00583         NextSym();
00584         expr = Expr();
00585         if (sym != eolsym) {
00586                 CExpression* oldexpr = expr;
00587                 expr = new COperator2Expr(VALUE_ADD_OPERATOR,
00588                         oldexpr, Error(STR_String("Extra characters after expression")));//new CConstExpr(new CErrorValue("Extra characters after expression")));
00589         }
00590         if (errmsg)
00591                 errmsg->Release();
00592         
00593         return expr;
00594 }
00595 
00596 
00597 
00598 float CParser::GetFloat(STR_String& txt)
00599 {
00600         // returns parsed text into a float
00601         // empty string returns -1
00602         
00603 //      AfxMessageBox("parsed string="+txt);
00604         CValue* val=NULL;
00605         float result=-1;
00606 //      String tmpstr;
00607 
00608         CExpression* expr = ProcessText(txt);
00609         if (expr) {
00610                 val = expr->Calculate();
00611                 result=(float)val->GetNumber();
00612                 
00613                 
00614         
00615                 val->Release();
00616                 expr->Release();
00617         }
00618 //      tmpstr.Format("parseresult=%g",result);
00619 //              AfxMessageBox(tmpstr);
00620         return result;
00621 }
00622 
00623 CValue* CParser::GetValue(STR_String& txt, bool bFallbackToText)
00624 {
00625         // returns parsed text into a value, 
00626         // empty string returns NULL value !
00627         // if bFallbackToText then unparsed stuff is put into text
00628         
00629         CValue* result=NULL;
00630         CExpression* expr = ProcessText(txt);
00631         if (expr) {
00632                 result = expr->Calculate();
00633                 expr->Release();
00634         }
00635         if (result)
00636         {
00637                 // if the parsed stuff lead to an errorvalue, don't return errors, just NULL
00638                 if (result->IsError()) {
00639                         result->Release();
00640                         result=NULL;
00641                         if (bFallbackToText) {
00642                                 if (txt.Length()>0)
00643                                 {
00644                                         result = new CStringValue(txt,"");
00645                                 }
00646                         }
00647                 }
00648         }
00649         return result;
00650 }
00651 
00652 void CParser::SetContext(CValue* context)
00653 {
00654         if (m_identifierContext)
00655         {
00656                 m_identifierContext->Release();
00657         }
00658         m_identifierContext = context;
00659 }