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