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