Blender V2.61 - r43446
|
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 }