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