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