Spelling Cleanup
[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 {
351         // returns the integer representation of the value in the global
352         // variable const_as_string
353         // pre: const_as_string contains only numercal chars
354         return atoi(const_as_string);
355 }
356 #endif
357
358 STR_String CParser::Symbol2Str(int s)
359 {
360         // returns a string representation of of symbol s,
361         // for use in Term when generating an error
362         switch(s) {
363                 case errorsym: return "error";
364                 case lbracksym: return "(";
365                 case rbracksym: return ")";
366                 case commasym: return ",";
367                 case opsym: return "operator";
368                 case constsym: return "constant";
369                 case sumsym: return "SUM";
370                 case ifsym: return "IF";
371                 case whocodedsym: return "WHOMADE";
372                 case eolsym: return "end of line";
373                 case idsym: return "identifier";
374                 default: return "unknown";  // should not happen
375         }
376 }
377
378 void CParser::Term(int s)
379 {
380         // generates an error if the next symbol isn't the specified symbol s
381         // otherwise, skip the symbol
382         if(s == sym) NextSym();
383         else {
384                 STR_String msg;
385                 msg.Format("Warning: " + Symbol2Str(s) + " expected\ncontinuing without it");
386
387 //              AfxMessageBox(msg,MB_ICONERROR);
388
389                 trace(msg);
390         }
391 }
392
393 int CParser::Priority(int optorkind)
394 {
395         // returns the priority of an operator
396         // higher number means higher priority
397         switch(optorkind) {
398                 case OPor: return 1;
399                 case OPand: return 2;
400                 case OPgreater:
401                 case OPless:
402                 case OPgreaterequal:
403                 case OPlessequal:
404                 case OPequal:
405                 case OPunequal: return 3;
406                 case OPplus:
407                 case OPminus: return 4;
408                 case OPmodulus:
409                 case OPtimes:
410                 case OPdivide: return 5;
411         }
412         MT_assert(false);
413         return 0;      // should not happen
414 }
415
416 CExpression *CParser::Ex(int i)
417 {
418         // parses an expression in the imput, starting at priority i, and
419         // returns an CExpression, containing the parsed input
420         CExpression *e1 = NULL, *e2 = NULL;
421         int opkind2;
422         
423         if (i < NUM_PRIORITY) {
424                 e1 = Ex(i + 1);
425                 while ((sym == opsym) && (Priority(opkind) == i)) {
426                         opkind2 = opkind;
427                         NextSym();
428                         e2 = Ex(i + 1);
429                         switch(opkind2) {
430                         case OPmodulus: e1 = new COperator2Expr(VALUE_MOD_OPERATOR,e1, e2); break;
431                         case OPplus: e1 = new COperator2Expr(VALUE_ADD_OPERATOR,e1, e2); break;
432                         case OPminus: e1 = new COperator2Expr(VALUE_SUB_OPERATOR,e1, e2); break;
433                         case OPtimes:   e1 = new COperator2Expr(VALUE_MUL_OPERATOR,e1, e2); break;
434                         case OPdivide: e1 = new COperator2Expr(VALUE_DIV_OPERATOR,e1, e2); break;
435                         case OPand: e1 = new COperator2Expr(VALUE_AND_OPERATOR,e1, e2); break;
436                         case OPor: e1 = new COperator2Expr(VALUE_OR_OPERATOR,e1, e2); break;
437                         case OPequal: e1 = new COperator2Expr(VALUE_EQL_OPERATOR,e1, e2); break;
438                         case OPunequal: e1 = new COperator2Expr(VALUE_NEQ_OPERATOR,e1, e2); break;
439                         case OPgreater: e1 = new COperator2Expr(VALUE_GRE_OPERATOR,e1, e2); break;
440                         case OPless: e1 = new COperator2Expr(VALUE_LES_OPERATOR,e1, e2); break;
441                         case OPgreaterequal: e1 = new COperator2Expr(VALUE_GEQ_OPERATOR,e1, e2); break;
442                         case OPlessequal: e1 = new COperator2Expr(VALUE_LEQ_OPERATOR,e1, e2); break;
443                         default: MT_assert(false);      break; // should not happen
444                         }
445                 }
446         } else if (i == NUM_PRIORITY) {
447                 if ((sym == opsym) 
448                         && ( (opkind == OPminus) || (opkind == OPnot) || (opkind == OPplus) ) 
449                         )
450                 {
451                         NextSym();
452                         switch(opkind) {
453                         /* +1 is also a valid number! */
454                         case OPplus: e1 = new COperator1Expr(VALUE_POS_OPERATOR, Ex(NUM_PRIORITY)); break;
455                         case OPminus: e1 = new COperator1Expr(VALUE_NEG_OPERATOR, Ex(NUM_PRIORITY)); break;
456                         case OPnot: e1 = new COperator1Expr(VALUE_NOT_OPERATOR, Ex(NUM_PRIORITY)); break;
457                         default: {
458                                                 // should not happen
459                                                 e1 = Error("operator +, - or ! expected");
460                                          }
461                         }
462                 }
463                 else {
464                         switch(sym) {
465                         case constsym: {
466                                 switch(constkind) {
467                                 case booltype:
468                                         e1 = new CConstExpr(new CBoolValue(boolvalue));
469                                         break;
470                                 case inttype:
471                                         {
472                                                 cInt temp;
473                                                 temp = strtoll(const_as_string, NULL, 10); /* atoi is for int only */
474                                                 e1 = new CConstExpr(new CIntValue(temp));
475                                                 break;
476                                         }
477                                 case floattype:
478                                         {
479                                                 double temp;
480                                                 temp = atof(const_as_string);
481                                                 e1 = new CConstExpr(new CFloatValue(temp));
482                                                 break;
483                                         }
484                                 case stringtype:
485                                         e1 = new CConstExpr(new CStringValue(const_as_string,""));
486                                         break;
487                                 default :
488                                         MT_assert(false);
489                                         break;
490                                 }
491                                 NextSym();
492                                 break;
493                                                    }
494                         case lbracksym:
495                                 NextSym();
496                                 e1 = Ex(1);
497                                 Term(rbracksym);
498                                 break;
499                         case ifsym:
500                         {
501                                 CExpression *e3;
502                                 NextSym();
503                                 Term(lbracksym);
504                                 e1 = Ex(1);
505                                 Term(commasym);
506                                 e2 = Ex(1);
507                                 if (sym == commasym) {
508                                         NextSym();
509                                         e3 = Ex(1);
510                                 } else {
511                                         e3 = new CConstExpr(new CEmptyValue());
512                                 }
513                                 Term(rbracksym);
514                                 e1 = new CIfExpr(e1, e2, e3);
515                                 break;
516                         }
517                         case idsym:
518                                 {
519                                         e1 = new CIdentifierExpr(const_as_string,m_identifierContext);
520                                         NextSym();
521                                         
522                                         break;
523                                 }
524                         case errorsym:
525                                 {
526                                         MT_assert(!e1);
527                                         STR_String errtext="[no info]";
528                                         if (errmsg)
529                                         {
530                                                 CValue* errmsgval = errmsg->Calculate();
531                                                 errtext=errmsgval->GetText();
532                                                 errmsgval->Release();
533                                         
534                                                 //e1 = Error(errmsg->Calculate()->GetText());//new CConstExpr(errmsg->Calculate());
535                                                 
536                                                 if ( !(errmsg->Release()) )
537                                                 {
538                                                         errmsg=NULL;
539                                                 } else {
540                                                         // does this happen ?
541                                                         MT_assert ("does this happen");
542                                                 }
543                                         }
544                                         e1 = Error(errtext);
545
546                                         break;                          
547                                 }
548                         default:
549                                 NextSym();
550                                 //return Error("Expression expected");
551                                 MT_assert(!e1);
552                                 e1 = Error("Expression expected");
553                         }
554                 }
555         }
556         return e1;
557 }
558
559 CExpression *CParser::Expr()
560 {
561         // parses an expression in the imput, and
562         // returns an CExpression, containing the parsed input
563         return Ex(1);
564 }
565
566 CExpression* CParser::ProcessText
567 (const char *intext) {
568         
569         // and parses the string in intext and returns it.
570         
571         
572         CExpression* expr;
573         text = intext;
574         
575         
576         chcount = 0;    
577         if (text.Length() == 0) {
578                 return NULL;
579         }
580         
581         ch = text[0];
582         /*if (ch != '=') {
583         expr = new CConstExpr(new CStringValue(text));
584         *dependent = deplist;
585         return expr;
586         } else 
587         */
588         //      NextCh();
589         NextSym();
590         expr = Expr();
591         if (sym != eolsym) {
592                 CExpression* oldexpr = expr;
593                 expr = new COperator2Expr(VALUE_ADD_OPERATOR,
594                         oldexpr, Error(STR_String("Extra characters after expression")));//new CConstExpr(new CErrorValue("Extra characters after expression")));
595         }
596         if (errmsg)
597                 errmsg->Release();
598         
599         return expr;
600 }
601
602
603
604 float CParser::GetFloat(STR_String& txt)
605 {
606         // returns parsed text into a float
607         // empty string returns -1
608         
609 //      AfxMessageBox("parsed string="+txt);
610         CValue* val=NULL;
611         float result=-1;
612 //      String tmpstr;
613
614         CExpression* expr = ProcessText(txt);
615         if (expr) {
616                 val = expr->Calculate();
617                 result=(float)val->GetNumber();
618                 
619                 
620         
621                 val->Release();
622                 expr->Release();
623         }
624 //      tmpstr.Format("parseresult=%g",result);
625 //              AfxMessageBox(tmpstr);
626         return result;
627 }
628
629 CValue* CParser::GetValue(STR_String& txt, bool bFallbackToText)
630 {
631         // returns parsed text into a value, 
632         // empty string returns NULL value !
633         // if bFallbackToText then unparsed stuff is put into text
634         
635         CValue* result=NULL;
636         CExpression* expr = ProcessText(txt);
637         if (expr) {
638                 result = expr->Calculate();
639                 expr->Release();
640         }
641         if (result)
642         {
643                 // if the parsed stuff lead to an errorvalue, don't return errors, just NULL
644                 if (result->IsError()) {
645                         result->Release();
646                         result=NULL;
647                         if (bFallbackToText) {
648                                 if (txt.Length()>0)
649                                 {
650                                         result = new CStringValue(txt,"");
651                                 }
652                         }
653                 }
654         }
655         return result;
656 }
657
658 void CParser::SetContext(CValue* context)
659 {
660         if (m_identifierContext)
661         {
662                 m_identifierContext->Release();
663         }
664         m_identifierContext = context;
665 }