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