fix for error when changing DISBALE_PYTHON -> WITH_PYTHON,
[blender.git] / source / gameengine / Expressions / InputParser.cpp
1 // Parser.cpp: implementation of the CParser class.
2 /*
3  * Copyright (c) 1996-2000 Erwin Coumans <coockie@acm.org>
4  *
5  * Permission to use, copy, modify, distribute and sell this software
6  * and its documentation for any purpose is hereby granted without fee,
7  * provided that the above copyright notice appear in all copies and
8  * that both that copyright notice and this permission notice appear
9  * in supporting documentation.  Erwin Coumans makes no
10  * representations about the suitability of this software for any
11  * purpose.  It is provided "as is" without express or implied warranty.
12  *
13  */
14
15 #include <stdlib.h>
16
17 #include "MT_assert.h"
18
19 #include "Value.h"
20 #include "InputParser.h"
21 #include "ErrorValue.h"
22 #include "IntValue.h"
23 #include "StringValue.h"
24 #include "FloatValue.h"
25 #include "BoolValue.h"
26 #include "EmptyValue.h"
27 #include "ConstExpr.h"
28 #include "Operator2Expr.h"
29 #include "Operator1Expr.h"
30 #include "IdentifierExpr.h"
31
32 // this is disable at the moment, I expected a memleak from it, but the error-cleanup was the reason
33 // well, looks we don't need it anyway, until maybe the Curved Surfaces are integrated into CSG 
34 // cool things like (IF(LOD==1,CCurvedValue,IF(LOD==2,CCurvedValue2)) etc...
35 #include "IfExpr.h" 
36
37 #if (defined(WIN32) || defined(WIN64)) && !defined(FREE_WINDOWS)
38 #define strcasecmp      _stricmp
39
40 #ifndef strtoll
41 #define strtoll         _strtoi64
42 #endif
43
44 #endif /* Def WIN32 or Def WIN64 */
45
46 #define NUM_PRIORITY 6
47 //////////////////////////////////////////////////////////////////////
48 // Construction/Destruction
49 //////////////////////////////////////////////////////////////////////
50
51 CParser::CParser() : m_identifierContext(NULL)
52 {
53 }
54
55
56
57 CParser::~CParser()
58 {
59         if (m_identifierContext)
60                 m_identifierContext->Release();
61 }
62
63
64
65 void CParser::ScanError(const char *str)
66 {
67         // sets the global variable errmsg to an errormessage with
68         // contents str, appending if it already exists
69         //      AfxMessageBox("Parse Error:"+str,MB_ICONERROR);
70         if (errmsg)
71                 errmsg = new COperator2Expr(VALUE_ADD_OPERATOR, errmsg, Error(str));
72         else
73                 errmsg = Error(str);
74
75         sym = errorsym;
76 }
77
78
79
80 CExpression* CParser::Error(const char *str)
81 {
82         // makes and returns a new CConstExpr filled with an CErrorValue
83         // with string str
84         //      AfxMessageBox("Error:"+str,MB_ICONERROR);
85         return new CConstExpr(new CErrorValue(str));
86 }
87
88
89
90 void CParser::NextCh()
91 {
92         // sets the global variable ch to the next character, if it exists
93         // and increases the global variable chcount
94         chcount++;
95
96         if (chcount < text.Length())
97                 ch = text[chcount];
98         else
99                 ch = 0x00;
100 }
101
102
103
104 void CParser::TermChar(char c)
105 {
106         // generates an error if the next char isn't the specified char c,
107         // otherwise, skip the char
108         if(ch == c)
109         {
110                 NextCh();
111         }
112         else
113         {
114                 STR_String str;
115                 str.Format("Warning: %c expected\ncontinuing without it", c);
116                 trace(str);
117         }
118 }
119
120
121
122 void CParser::DigRep()
123 {
124         // changes the current character to the first character that
125         // isn't a decimal
126         while ((ch >= '0') && (ch <= '9'))
127                 NextCh();
128 }
129
130
131
132 void CParser::CharRep()
133 {
134         // changes the current character to the first character that
135         // isn't an alphanumeric character
136         while (((ch >= '0') && (ch <= '9'))
137                 || ((ch >= 'a') && (ch <= 'z'))
138                 || ((ch >= 'A') && (ch <= 'Z'))
139                 || (ch == '.') || (ch == '_'))
140                 NextCh();
141 }
142
143
144
145 void CParser::GrabString(int start)
146 {
147         // puts part of the input string into the global variable
148         // const_as_string, from position start, to position chchount
149         const_as_string = text.Mid(start, chcount-start);
150 }
151
152
153
154 void CParser::NextSym()
155 {
156         // sets the global variable sym to the next symbol, and
157         // if it is an operator
158         //   sets the global variable opkind to the kind of operator
159         // if it is a constant
160         //   sets the global variable constkind to the kind of operator
161         // if it is a reference to a cell
162         //   sets the global variable cellcoord to the kind of operator
163         
164         errmsg = NULL;
165         while(ch == ' ' || ch == 0x9)
166                 NextCh();
167
168         switch(ch)
169         {
170     case '(':
171                 sym = lbracksym; NextCh();
172                 break;
173     case ')':
174                 sym = rbracksym; NextCh();
175                 break;
176     case ',':
177                 sym = commasym; NextCh();
178                 break;
179         case '%' :
180                 sym = opsym; opkind = OPmodulus; NextCh();
181                 break;
182     case '+' :
183                 sym = opsym; opkind = OPplus; NextCh();
184                 break;
185     case '-' :
186                 sym = opsym; opkind = OPminus; NextCh();
187                 break;
188     case '*' :
189                 sym = opsym; opkind = OPtimes; NextCh();
190                 break;
191     case '/' :
192                 sym = opsym; opkind = OPdivide; NextCh();
193                 break;
194         case '&' :
195                 sym = opsym; opkind = OPand; NextCh(); TermChar('&');
196                 break;
197         case '|' :
198                 sym = opsym; opkind = OPor; NextCh(); TermChar('|');
199                 break;
200         case '=' :
201                 sym = opsym; opkind = OPequal; NextCh(); TermChar('=');
202                 break;
203         case '!' :
204                 sym = opsym;
205                 NextCh();
206                 if (ch == '=')
207                 {
208                         opkind = OPunequal;
209                         NextCh();
210                 }
211                 else
212                 {
213                         opkind = OPnot;
214                 }
215                 break;
216         case '>':
217                 sym = opsym;
218                 NextCh();
219                 if (ch == '=')
220                 {
221                         opkind = OPgreaterequal;
222                         NextCh();
223                 }
224                 else
225                 {
226                         opkind = OPgreater;
227                 }
228                 break;
229         case '<':
230                 sym = opsym;
231                 NextCh();
232                 if (ch == '=') {
233                         opkind = OPlessequal;
234                         NextCh();
235                 } else {
236                         opkind = OPless;
237                 }
238                 break;
239     case '\"' : {
240                 int start;
241                 sym = constsym;
242                 constkind = stringtype;
243                 NextCh();
244                 start = chcount;
245                 while ((ch != '\"') && (ch != 0x0))
246                         NextCh();
247                 GrabString(start);
248                 TermChar('\"'); // check for eol before '\"'
249                 break;
250                                 }
251     case 0x0: sym = eolsym; break;
252     default: 
253                 {
254                         int start;
255                         start = chcount;
256                         DigRep();
257                         if ((start != chcount) || (ch == '.')) { // number
258                                 sym = constsym;
259                                 if (ch == '.') {
260                                         constkind = floattype;
261                                         NextCh();
262                                         DigRep();
263                                 }
264                                 else constkind = inttype;
265                                 if ((ch == 'e') || (ch == 'E')) {
266                                         int mark;
267                                         constkind = floattype;
268                                         NextCh();
269                                         if ((ch == '+') || (ch == '-')) NextCh();
270                                         mark = chcount;
271                                         DigRep();
272                                         if (mark == chcount) {
273                                                 ScanError("Number expected after 'E'");
274                                                 return;
275                                         }
276                                 }
277                                 GrabString(start);
278                         } else if (((ch >= 'a') && (ch <= 'z'))
279                                 || ((ch >= 'A') && (ch <= 'Z')))
280                         { // reserved word?
281                                 
282                                 start = chcount;
283                                 CharRep();
284                                 GrabString(start);
285                                 if (!strcasecmp(const_as_string, "SUM")) {
286                                         sym = sumsym;
287                                 }
288                                 else if (!strcasecmp(const_as_string, "NOT")) {
289                                         sym = opsym;
290                                         opkind = OPnot;
291                                 }
292                                 else if (!strcasecmp(const_as_string, "AND")) {
293                                         sym = opsym; opkind = OPand;
294                                 }
295                                 else if (!strcasecmp(const_as_string, "OR")) {
296                                         sym = opsym; opkind = OPor;
297                                 }
298                                 else if (!strcasecmp(const_as_string, "IF"))
299                                         sym = ifsym;
300                                 else if (!strcasecmp(const_as_string, "WHOMADE"))
301                                         sym = whocodedsym;
302                                 else if (!strcasecmp(const_as_string, "FALSE")) {
303                                         sym = constsym; constkind = booltype; boolvalue = false;
304                                 } else if (!strcasecmp(const_as_string, "TRUE")) {
305                                         sym = constsym; constkind = booltype; boolvalue = true;
306                                 } else {
307                                         sym = idsym;
308                                         //STR_String str;
309                                         //str.Format("'%s' makes no sense here", (const char*)funstr);
310                                         //ScanError(str);
311                                 }
312                         } else { // unknown symbol
313                                 STR_String str;
314                                 str.Format("Unexpected character '%c'", ch);
315                                 NextCh();
316                                 ScanError(str);
317                                 return;
318                         }
319                 }
320         }
321 }
322
323 #if 0
324 int CParser::MakeInt() {
325         // returns the integer representation of the value in the global
326         // variable const_as_string
327         // pre: const_as_string contains only numercal chars
328         return atoi(const_as_string);
329 }
330 #endif
331
332 STR_String CParser::Symbol2Str(int s) {
333         // returns a string representation of of symbol s,
334         // for use in Term when generating an error
335         switch(s) {
336     case errorsym: return "error";
337     case lbracksym: return "(";
338     case rbracksym: return ")";
339     case commasym: return ",";
340     case opsym: return "operator";
341     case constsym: return "constant";
342         case sumsym: return "SUM";
343         case ifsym: return "IF";
344         case whocodedsym: return "WHOMADE";
345     case eolsym: return "end of line";
346         case idsym: return "identifier";
347     default: return "unknown";  // should not happen
348         }
349 }
350
351 void CParser::Term(int s) {
352         // generates an error if the next symbol isn't the specified symbol s
353         // otherwise, skip the symbol
354         if(s == sym) NextSym();
355         else {
356                 STR_String msg;
357                 msg.Format("Warning: " + Symbol2Str(s) + " expected\ncontinuing without it");
358
359 //              AfxMessageBox(msg,MB_ICONERROR);
360
361                 trace(msg);
362         }
363 }
364
365 int CParser::Priority(int optorkind) {
366         // returns the priority of an operator
367         // higher number means higher priority
368         switch(optorkind) {
369         case OPor: return 1;
370         case OPand: return 2;
371         case OPgreater:
372         case OPless:
373         case OPgreaterequal:
374         case OPlessequal:
375         case OPequal:
376         case OPunequal: return 3;
377     case OPplus:
378     case OPminus: return 4;
379         case OPmodulus:
380     case OPtimes:
381     case OPdivide: return 5;
382         }
383         MT_assert(false);
384         return 0;      // should not happen
385 }
386
387 CExpression *CParser::Ex(int i) {
388         // parses an expression in the imput, starting at priority i, and
389         // returns an CExpression, containing the parsed input
390         CExpression *e1 = NULL, *e2 = NULL;
391         int opkind2;
392         
393         if (i < NUM_PRIORITY) {
394                 e1 = Ex(i + 1);
395                 while ((sym == opsym) && (Priority(opkind) == i)) {
396                         opkind2 = opkind;
397                         NextSym();
398                         e2 = Ex(i + 1);
399                         switch(opkind2) {
400                         case OPmodulus: e1 = new COperator2Expr(VALUE_MOD_OPERATOR,e1, e2); break;
401                         case OPplus: e1 = new COperator2Expr(VALUE_ADD_OPERATOR,e1, e2); break;
402                         case OPminus: e1 = new COperator2Expr(VALUE_SUB_OPERATOR,e1, e2); break;
403                         case OPtimes:   e1 = new COperator2Expr(VALUE_MUL_OPERATOR,e1, e2); break;
404                         case OPdivide: e1 = new COperator2Expr(VALUE_DIV_OPERATOR,e1, e2); break;
405                         case OPand: e1 = new COperator2Expr(VALUE_AND_OPERATOR,e1, e2); break;
406                         case OPor: e1 = new COperator2Expr(VALUE_OR_OPERATOR,e1, e2); break;
407                         case OPequal: e1 = new COperator2Expr(VALUE_EQL_OPERATOR,e1, e2); break;
408                         case OPunequal: e1 = new COperator2Expr(VALUE_NEQ_OPERATOR,e1, e2); break;
409                         case OPgreater: e1 = new COperator2Expr(VALUE_GRE_OPERATOR,e1, e2); break;
410                         case OPless: e1 = new COperator2Expr(VALUE_LES_OPERATOR,e1, e2); break;
411                         case OPgreaterequal: e1 = new COperator2Expr(VALUE_GEQ_OPERATOR,e1, e2); break;
412                         case OPlessequal: e1 = new COperator2Expr(VALUE_LEQ_OPERATOR,e1, e2); break;
413                         default: MT_assert(false);      break; // should not happen
414                         }
415                 }
416         } else if (i == NUM_PRIORITY) {
417                 if ((sym == opsym) 
418                         && ( (opkind == OPminus) || (opkind == OPnot) || (opkind == OPplus) ) 
419                         )
420                 {
421                         NextSym();
422                         switch(opkind) {
423                         /* +1 is also a valid number! */
424                         case OPplus: e1 = new COperator1Expr(VALUE_POS_OPERATOR, Ex(NUM_PRIORITY)); break;
425                         case OPminus: e1 = new COperator1Expr(VALUE_NEG_OPERATOR, Ex(NUM_PRIORITY)); break;
426                         case OPnot: e1 = new COperator1Expr(VALUE_NOT_OPERATOR, Ex(NUM_PRIORITY)); break;
427                         default: {
428                                                 // should not happen
429                                                 e1 = Error("operator +, - or ! expected");
430                                          }
431                         }
432                 }
433                 else {
434                         switch(sym) {
435                         case constsym: {
436                                 switch(constkind) {
437                                 case booltype:
438                                         e1 = new CConstExpr(new CBoolValue(boolvalue));
439                                         break;
440                                 case inttype:
441                                         {
442                                                 cInt temp;
443                                                 temp = strtoll(const_as_string, NULL, 10); /* atoi is for int only */
444                                                 e1 = new CConstExpr(new CIntValue(temp));
445                                                 break;
446                                         }
447                                 case floattype:
448                                         {
449                                                 double temp;
450                                                 temp = atof(const_as_string);
451                                                 e1 = new CConstExpr(new CFloatValue(temp));
452                                                 break;
453                                         }
454                                 case stringtype:
455                                         e1 = new CConstExpr(new CStringValue(const_as_string,""));
456                                         break;
457                                 default :
458                                         MT_assert(false);
459                                         break;
460                                 }
461                                 NextSym();
462                                 break;
463                                                    }
464                         case lbracksym:
465                                 NextSym();
466                                 e1 = Ex(1);
467                                 Term(rbracksym);
468                                 break;
469                         case ifsym:
470                         {
471                                 CExpression *e3;
472                                 NextSym();
473                                 Term(lbracksym);
474                                 e1 = Ex(1);
475                                 Term(commasym);
476                                 e2 = Ex(1);
477                                 if (sym == commasym) {
478                                         NextSym();
479                                         e3 = Ex(1);
480                                 } else {
481                                         e3 = new CConstExpr(new CEmptyValue());
482                                 }
483                                 Term(rbracksym);
484                                 e1 = new CIfExpr(e1, e2, e3);
485                                 break;
486                         }
487                         case idsym:
488                                 {
489                                         e1 = new CIdentifierExpr(const_as_string,m_identifierContext);
490                                         NextSym();
491                                         
492                                         break;
493                                 }
494                         case errorsym:
495                                 {
496                                         MT_assert(!e1);
497                                         STR_String errtext="[no info]";
498                                         if (errmsg)
499                                         {
500                                                 CValue* errmsgval = errmsg->Calculate();
501                                                 errtext=errmsgval->GetText();
502                                                 errmsgval->Release();
503                                         
504                                                 //e1 = Error(errmsg->Calculate()->GetText());//new CConstExpr(errmsg->Calculate());
505                                                 
506                                                 if ( !(errmsg->Release()) )
507                                                 {
508                                                         errmsg=NULL;
509                                                 } else {
510                                                         // does this happen ?
511                                                         MT_assert ("does this happen");
512                                                 }
513                                         }
514                                         e1 = Error(errtext);
515
516                                         break;                          
517                                 }
518                         default:
519                                 NextSym();
520                                 //return Error("Expression expected");
521                                 MT_assert(!e1);
522                                 e1 = Error("Expression expected");
523                         }
524                 }
525         }
526         return e1;
527 }
528
529 CExpression *CParser::Expr() {
530         // parses an expression in the imput, and
531         // returns an CExpression, containing the parsed input
532         return Ex(1);
533 }
534
535 CExpression* CParser::ProcessText
536 (const char *intext) {
537         
538         // and parses the string in intext and returns it.
539         
540         
541         CExpression* expr;
542         text = intext;
543         
544         
545         chcount = 0;    
546         if (text.Length() == 0) {
547                 return NULL;
548         }
549         
550         ch = text[0];
551         /*if (ch != '=') {
552         expr = new CConstExpr(new CStringValue(text));
553         *dependant = deplist;
554         return expr;
555         } else 
556         */
557         //      NextCh();
558         NextSym();
559         expr = Expr();
560         if (sym != eolsym) {
561                 CExpression* oldexpr = expr;
562                 expr = new COperator2Expr(VALUE_ADD_OPERATOR,
563                         oldexpr, Error(STR_String("Extra characters after expression")));//new CConstExpr(new CErrorValue("Extra characters after expression")));
564         }
565         if (errmsg)
566                 errmsg->Release();
567         
568         return expr;
569 }
570
571
572
573 float CParser::GetFloat(STR_String& txt)
574 {
575         // returns parsed text into a float
576         // empty string returns -1
577         
578 //      AfxMessageBox("parsed string="+txt);
579         CValue* val=NULL;
580         float result=-1;
581 //      String tmpstr;
582
583         CExpression* expr = ProcessText(txt);
584         if (expr) {
585                 val = expr->Calculate();
586                 result=(float)val->GetNumber();
587                 
588                 
589         
590                 val->Release();
591                 expr->Release();
592         }
593 //      tmpstr.Format("parseresult=%g",result);
594 //              AfxMessageBox(tmpstr);
595         return result;
596 }
597
598 CValue* CParser::GetValue(STR_String& txt, bool bFallbackToText)
599 {
600         // returns parsed text into a value, 
601         // empty string returns NULL value !
602         // if bFallbackToText then unparsed stuff is put into text
603         
604         CValue* result=NULL;
605         CExpression* expr = ProcessText(txt);
606         if (expr) {
607                 result = expr->Calculate();
608                 expr->Release();
609         }
610         if (result)
611         {
612                 // if the parsed stuff lead to an errorvalue, don't return errors, just NULL
613                 if (result->IsError()) {
614                         result->Release();
615                         result=NULL;
616                         if (bFallbackToText) {
617                                 if (txt.Length()>0)
618                                 {
619                                         result = new CStringValue(txt,"");
620                                 }
621                         }
622                 }
623         }
624         return result;
625 }
626
627 void CParser::SetContext(CValue* context)
628 {
629         if (m_identifierContext)
630         {
631                 m_identifierContext->Release();
632         }
633         m_identifierContext = context;
634 }