TinyJS on mbed. TinyJS is very simple JavaScript engine.

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers TinyJS.cpp Source File

TinyJS.cpp

00001 /*
00002  * TinyJS
00003  *
00004  * A single-file Javascript-alike engine
00005  *
00006  * Authored By Gordon Williams <gw@pur3.co.uk>
00007  *
00008  * Copyright (C) 2009 Pur3 Ltd
00009  *
00010  * Permission is hereby granted, free of charge, to any person obtaining a copy of
00011  * this software and associated documentation files (the "Software"), to deal in
00012  * the Software without restriction, including without limitation the rights to
00013  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
00014  * of the Software, and to permit persons to whom the Software is furnished to do
00015  * so, subject to the following conditions:
00016 
00017  * The above copyright notice and this permission notice shall be included in all
00018  * copies or substantial portions of the Software.
00019 
00020  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00021  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00022  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00023  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00024  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00025  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00026  * SOFTWARE.
00027  */
00028 
00029 /* Version 0.1  :  (gw) First published on Google Code
00030    Version 0.11 :  Making sure the 'root' variable never changes
00031                    'symbol_base' added for the current base of the sybmbol table
00032    Version 0.12 :  Added findChildOrCreate, changed string passing to use references
00033                    Fixed broken string encoding in getJSString()
00034                    Removed getInitCode and added getJSON instead
00035                    Added nil
00036                    Added rough JSON parsing
00037                    Improved example app
00038    Version 0.13 :  Added tokenEnd/tokenLastEnd to lexer to avoid parsing whitespace
00039                    Ability to define functions without names
00040                    Can now do "var mine = function(a,b) { ... };"
00041                    Slightly better 'trace' function
00042                    Added findChildOrCreateByPath function
00043                    Added simple test suite
00044                    Added skipping of blocks when not executing
00045    Version 0.14 :  Added parsing of more number types
00046                    Added parsing of string defined with '
00047                    Changed nil to null as per spec, added 'undefined'
00048                    Now set variables with the correct scope, and treat unknown
00049                               as 'undefined' rather than failing
00050                    Added proper (I hope) handling of null and undefined
00051                    Added === check
00052    Version 0.15 :  Fix for possible memory leaks
00053    Version 0.16 :  Removal of un-needed findRecursive calls
00054                    symbol_base removed and replaced with 'scopes' stack
00055                    Added reference counting a proper tree structure
00056                        (Allowing pass by reference)
00057                    Allowed JSON output to output IDs, not strings
00058                    Added get/set for array indices
00059                    Changed Callbacks to include user data pointer
00060                    Added some support for objects
00061                    Added more Java-esque builtin functions
00062    Version 0.17 :  Now we don't deepCopy the parent object of the class
00063                    Added JSON.stringify and eval()
00064                    Nicer JSON indenting
00065                    Fixed function output in JSON
00066                    Added evaluateComplex
00067                    Fixed some reentrancy issues with evaluate/execute
00068    Version 0.18 :  Fixed some issues with code being executed when it shouldn't
00069    Version 0.19 :  Added array.length
00070                    Changed '__parent' to 'prototype' to bring it more in line with javascript
00071    Version 0.20 :  Added '%' operator
00072    Version 0.21 :  Added array type
00073                    String.length() no more - now String.length
00074                    Added extra constructors to reduce confusion
00075                    Fixed checks against undefined
00076    Version 0.22 :  First part of ardi's changes:
00077                        sprintf -> sprintf_s
00078                        extra tokens parsed
00079                        array memory leak fixed
00080                    Fixed memory leak in evaluateComplex
00081                    Fixed memory leak in FOR loops
00082                    Fixed memory leak for unary minus
00083    Version 0.23 :  Allowed evaluate[Complex] to take in semi-colon separated
00084                      statements and then only return the value from the last one.
00085                      Also checks to make sure *everything* was parsed.
00086                    Ints + doubles are now stored in binary form (faster + more precise)
00087    Version 0.24 :  More useful error for maths ops
00088                    Don't dump everything on a match error.
00089    Version 0.25 :  Better string escaping
00090    Version 0.26 :  Add CScriptVar::equals
00091                    Add built-in array functions
00092    Version 0.27 :  Added OZLB's TinyJS.setVariable (with some tweaks)
00093                    Added OZLB's Maths Functions
00094    Version 0.28 :  Ternary operator
00095                    Rudimentary call stack on error
00096                    Added String Character functions
00097                    Added shift operators
00098    Version 0.29 :  Added new object via functions
00099                    Fixed getString() for double on some platforms
00100    Version 0.30 :  Rlyeh Mario's patch for Math Functions on VC++
00101    Version 0.31 :  Add exec() to TinyJS functions
00102                    Now print quoted JSON that can be read by PHP/Python parsers
00103                    Fixed postfix increment operator
00104    Version 0.32 :  Fixed Math.randInt on 32 bit PCs, where it was broken
00105    Version 0.33 :  Fixed Memory leak + brokenness on === comparison
00106 
00107     NOTE:
00108           Constructing an array with an initial length 'Array(5)' doesn't work
00109           Recursive loops of data such as a.foo = a; fail to be garbage collected
00110           length variable cannot be set
00111           The postfix increment operator returns the current value, not the previous as it should.
00112           There is no prefix increment operator
00113           Arrays are implemented as a linked list - hence a lookup time is O(n)
00114 
00115     TODO:
00116           Utility va-args style function in TinyJS for executing a function directly
00117           Merge the parsing of expressions/statements so eval("statement") works like we'd expect.
00118           Move 'shift' implementation into mathsOp
00119 
00120  */
00121 /*
00122  * TinyJS for mbed.
00123  *
00124  * Authored by Takehisa Oneta (ohneta@gmail.com)
00125  * 10th Jan. 2013
00126  */
00127 
00128 #include "TinyJS.h"
00129 #include <assert.h>
00130 
00131 #define ASSERT(X) assert(X)
00132 /* Frees the given link IF it isn't owned by anything else */
00133 #define CLEAN(x) { CScriptVarLink *__v = x; if (__v && !__v->owned) { delete __v; } }
00134 /* Create a LINK to point to VAR and free the old link.
00135  * BUT this is more clever - it tries to keep the old link if it's not owned to save allocations */
00136 #define CREATE_LINK(LINK, VAR) { if (!LINK || LINK->owned) LINK = new CScriptVarLink(VAR); else LINK->replaceWith(VAR); }
00137 
00138 #include <string>
00139 #include <string.h>
00140 #include <sstream>
00141 #include <cstdlib>
00142 #include <stdio.h>
00143 
00144 using namespace std;
00145 
00146 #ifdef _WIN32
00147 #ifdef _DEBUG
00148    #ifndef DBG_NEW
00149       #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
00150       #define new DBG_NEW
00151    #endif
00152 #endif
00153 #endif
00154 
00155 #ifdef __GNUC__
00156 #define vsprintf_s vsnprintf
00157 #define sprintf_s snprintf
00158 #ifndef MBED
00159 #define _strdup strdup
00160 #else
00161 char *_strdup(const char *str)
00162 {
00163     size_t siz;
00164     char *copy;
00165 
00166     siz = strlen(str) + 1;
00167     if ((copy = (char *)malloc(siz)) == NULL)
00168         return(NULL);
00169     (void)memcpy(copy, str, siz);
00170     return(copy);
00171 }
00172 #endif
00173 #endif
00174 
00175 #ifdef MBED
00176 extern int mbedErrorFlag;
00177 extern std::string mbedErrorMessage;  
00178 
00179 #define LMATCH_VOID(c)  {mbedErrorFlag = 0;l->match(c);if (mbedErrorFlag != 0) return;}
00180 #define LMATCH(c)  {mbedErrorFlag = 0;l->match(c);if (mbedErrorFlag != 0) return 0;}
00181 //#define LMATCH_VOID(c)  {l->match(c);}
00182 //#define LMATCH(c)  {l->match(c);}
00183 #endif
00184 
00185 
00186 // ----------------------------------------------------------------------------------- Memory Debug
00187 
00188 #if DEBUG_MEMORY
00189 
00190 vector<CScriptVar*> allocatedVars;
00191 vector<CScriptVarLink*> allocatedLinks;
00192 
00193 void mark_allocated(CScriptVar *v) {
00194     allocatedVars.push_back(v);
00195 }
00196 
00197 void mark_deallocated(CScriptVar *v) {
00198     for (size_t i=0;i<allocatedVars.size();i++) {
00199       if (allocatedVars[i] == v) {
00200         allocatedVars.erase(allocatedVars.begin()+i);
00201         break;
00202       }
00203     }
00204 }
00205 
00206 void mark_allocated(CScriptVarLink *v) {
00207     allocatedLinks.push_back(v);
00208 }
00209 
00210 void mark_deallocated(CScriptVarLink *v) {
00211     for (size_t i=0;i<allocatedLinks.size();i++) {
00212       if (allocatedLinks[i] == v) {
00213         allocatedLinks.erase(allocatedLinks.begin()+i);
00214         break;
00215       }
00216     }
00217 }
00218 
00219 void show_allocated() {
00220     for (size_t i=0;i<allocatedVars.size();i++) {
00221       printf("ALLOCATED, %d refs\n", allocatedVars[i]->getRefs());
00222       allocatedVars[i]->trace("  ");
00223     }
00224     for (size_t i=0;i<allocatedLinks.size();i++) {
00225       printf("ALLOCATED LINK %s, allocated[%d] to \n", allocatedLinks[i]->name.c_str(), allocatedLinks[i]->var->getRefs());
00226       allocatedLinks[i]->var->trace("  ");
00227     }
00228     allocatedVars.clear();
00229     allocatedLinks.clear();
00230 }
00231 #endif
00232 
00233 // ----------------------------------------------------------------------------------- Utils
00234 bool isWhitespace(char ch) {
00235     return (ch==' ') || (ch=='\t') || (ch=='\n') || (ch=='\r');
00236 }
00237 
00238 bool isNumeric(char ch) {
00239     return (ch>='0') && (ch<='9');
00240 }
00241 bool isNumber(const string &str) {
00242     for (size_t i=0;i<str.size();i++)
00243       if (!isNumeric(str[i])) return false;
00244     return true;
00245 }
00246 bool isHexadecimal(char ch) {
00247     return ((ch>='0') && (ch<='9')) ||
00248            ((ch>='a') && (ch<='f')) ||
00249            ((ch>='A') && (ch<='F'));
00250 }
00251 bool isAlpha(char ch) {
00252     return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_';
00253 }
00254 
00255 bool isIDString(const char *s) {
00256     if (!isAlpha(*s))
00257         return false;
00258     while (*s) {
00259         if (!(isAlpha(*s) || isNumeric(*s)))
00260             return false;
00261         s++;
00262     }
00263     return true;
00264 }
00265 
00266 void replace(string &str, char textFrom, const char *textTo) {
00267     int sLen = strlen(textTo);
00268     size_t p = str.find(textFrom);
00269     while (p != string::npos) {
00270         str = str.substr(0, p) + textTo + str.substr(p+1);
00271         p = str.find(textFrom, p+sLen);
00272     }
00273 }
00274 
00275 /// convert the given string into a quoted string suitable for javascript
00276 std::string getJSString(const std::string &str) {
00277     std::string nStr = str;
00278     for (size_t i=0;i<nStr.size();i++) {
00279       const char *replaceWith = "";
00280       bool replace = true;
00281 
00282       switch (nStr[i]) {
00283         case '\\': replaceWith = "\\\\"; break;
00284         case '\n': replaceWith = "\\n"; break;
00285         case '\r': replaceWith = "\\r"; break;
00286         case '\a': replaceWith = "\\a"; break;
00287         case '"': replaceWith = "\\\""; break;
00288         default: {
00289           int nCh = ((int)nStr[i]) &0xFF;
00290           if (nCh<32 || nCh>127) {
00291             char buffer[5];
00292             sprintf_s(buffer, 5, "\\x%02X", nCh);
00293             replaceWith = buffer;
00294           } else replace=false;
00295         }
00296       }
00297 
00298       if (replace) {
00299         nStr = nStr.substr(0, i) + replaceWith + nStr.substr(i+1);
00300         i += strlen(replaceWith)-1;
00301       }
00302     }
00303     return "\"" + nStr + "\"";
00304 }
00305 
00306 /** Is the string alphanumeric */
00307 bool isAlphaNum(const std::string &str) {
00308     if (str.size()==0) return true;
00309     if (!isAlpha(str[0])) return false;
00310     for (size_t i=0;i<str.size();i++)
00311       if (!(isAlpha(str[i]) || isNumeric(str[i])))
00312         return false;
00313     return true;
00314 }
00315 
00316 // ----------------------------------------------------------------------------------- CSCRIPTEXCEPTION
00317 
00318 CScriptException::CScriptException(const string &exceptionText) {
00319     text = exceptionText;
00320 }
00321 
00322 // ----------------------------------------------------------------------------------- CSCRIPTLEX
00323 
00324 CScriptLex::CScriptLex(const string &input) {
00325     data = _strdup(input.c_str());
00326     dataOwned = true;
00327     dataStart = 0;
00328     dataEnd = strlen(data);
00329     reset();
00330 }
00331 
00332 CScriptLex::CScriptLex(CScriptLex *owner, int startChar, int endChar) {
00333     data = owner->data;
00334     dataOwned = false;
00335     dataStart = startChar;
00336     dataEnd = endChar;
00337     reset();
00338 }
00339 
00340 CScriptLex::~CScriptLex(void)
00341 {
00342     if (dataOwned)
00343         free((void*)data);
00344 }
00345 
00346 void CScriptLex::reset() {
00347     dataPos = dataStart;
00348     tokenStart = 0;
00349     tokenEnd = 0;
00350     tokenLastEnd = 0;
00351     tk = 0;
00352     tkStr = "";
00353     getNextCh();
00354     getNextCh();
00355     getNextToken();
00356 }
00357 
00358 void CScriptLex::match(int expected_tk) {
00359     if (tk!=expected_tk) {
00360 #ifndef MBED
00361         ostringstream errorString;
00362         errorString << "Got " << getTokenStr(tk) << " expected " << getTokenStr(expected_tk)
00363          << " at " << getPosition(tokenStart);
00364         throw new CScriptException(errorString.str());
00365 #else
00366         mbedErrorFlag = 1;
00367         mbedErrorMessage = "Got ";
00368         mbedErrorMessage += getTokenStr(tk);
00369         mbedErrorMessage += " expected ";
00370         mbedErrorMessage += getTokenStr(expected_tk);
00371         mbedErrorMessage += " at ";
00372         mbedErrorMessage += getPosition(tokenStart);
00373         return;
00374 #endif
00375     }
00376     getNextToken();
00377 }
00378 
00379 string CScriptLex::getTokenStr(int token) {
00380     if (token>32 && token<128) {
00381         char buf[4] = "' '";
00382         buf[1] = (char)token;
00383         return buf;
00384     }
00385     switch (token) {
00386         case LEX_EOF : return "EOF";
00387         case LEX_ID : return "ID";
00388         case LEX_INT : return "INT";
00389         case LEX_FLOAT : return "FLOAT";
00390         case LEX_STR : return "STRING";
00391         case LEX_EQUAL : return "==";
00392         case LEX_TYPEEQUAL : return "===";
00393         case LEX_NEQUAL : return "!=";
00394         case LEX_NTYPEEQUAL : return "!==";
00395         case LEX_LEQUAL : return "<=";
00396         case LEX_LSHIFT : return "<<";
00397         case LEX_LSHIFTEQUAL : return "<<=";
00398         case LEX_GEQUAL : return ">=";
00399         case LEX_RSHIFT : return ">>";
00400         case LEX_RSHIFTUNSIGNED : return ">>";
00401         case LEX_RSHIFTEQUAL : return ">>=";
00402         case LEX_PLUSEQUAL : return "+=";
00403         case LEX_MINUSEQUAL : return "-=";
00404         case LEX_PLUSPLUS : return "++";
00405         case LEX_MINUSMINUS : return "--";
00406         case LEX_ANDEQUAL : return "&=";
00407         case LEX_ANDAND : return "&&";
00408         case LEX_OREQUAL : return "|=";
00409         case LEX_OROR : return "||";
00410         case LEX_XOREQUAL : return "^=";
00411                 // reserved words
00412         case LEX_R_IF : return "if";
00413         case LEX_R_ELSE : return "else";
00414         case LEX_R_DO : return "do";
00415         case LEX_R_WHILE : return "while";
00416         case LEX_R_FOR : return "for";
00417         case LEX_R_BREAK : return "break";
00418         case LEX_R_CONTINUE : return "continue";
00419         case LEX_R_FUNCTION : return "function";
00420         case LEX_R_RETURN : return "return";
00421         case LEX_R_VAR : return "var";
00422         case LEX_R_TRUE : return "true";
00423         case LEX_R_FALSE : return "false";
00424         case LEX_R_NULL : return "null";
00425         case LEX_R_UNDEFINED : return "undefined";
00426         case LEX_R_NEW : return "new";
00427     }
00428 
00429 #ifndef MBED
00430     ostringstream msg;
00431     msg << "?[" << token << "]";
00432     return msg.str();
00433 #else
00434     string msg;
00435     msg = "?[";
00436     msg += token;
00437     msg += "]";
00438     return msg;
00439 #endif
00440 }
00441 
00442 void CScriptLex::getNextCh() {
00443     currCh = nextCh;
00444     if (dataPos < dataEnd)
00445         nextCh = data[dataPos];
00446     else
00447         nextCh = 0;
00448     dataPos++;
00449 }
00450 
00451 void CScriptLex::getNextToken() {
00452     tk = LEX_EOF;
00453     tkStr.clear();
00454     while (currCh && isWhitespace(currCh)) getNextCh();
00455     // newline comments
00456     if (currCh=='/' && nextCh=='/') {
00457         while (currCh && currCh!='\n') getNextCh();
00458         getNextCh();
00459         getNextToken();
00460         return;
00461     }
00462     // block comments
00463     if (currCh=='/' && nextCh=='*') {
00464         while (currCh && (currCh!='*' || nextCh!='/')) getNextCh();
00465         getNextCh();
00466         getNextCh();
00467         getNextToken();
00468         return;
00469     }
00470     // record beginning of this token
00471     tokenStart = dataPos-2;
00472     // tokens
00473     if (isAlpha(currCh)) { //  IDs
00474         while (isAlpha(currCh) || isNumeric(currCh)) {
00475             tkStr += currCh;
00476             getNextCh();
00477         }
00478         tk = LEX_ID;
00479              if (tkStr=="if") tk = LEX_R_IF;
00480         else if (tkStr=="else") tk = LEX_R_ELSE;
00481         else if (tkStr=="do") tk = LEX_R_DO;
00482         else if (tkStr=="while") tk = LEX_R_WHILE;
00483         else if (tkStr=="for") tk = LEX_R_FOR;
00484         else if (tkStr=="break") tk = LEX_R_BREAK;
00485         else if (tkStr=="continue") tk = LEX_R_CONTINUE;
00486         else if (tkStr=="function") tk = LEX_R_FUNCTION;
00487         else if (tkStr=="return") tk = LEX_R_RETURN;
00488         else if (tkStr=="var") tk = LEX_R_VAR;
00489         else if (tkStr=="true") tk = LEX_R_TRUE;
00490         else if (tkStr=="false") tk = LEX_R_FALSE;
00491         else if (tkStr=="null") tk = LEX_R_NULL;
00492         else if (tkStr=="undefined") tk = LEX_R_UNDEFINED;
00493         else if (tkStr=="new") tk = LEX_R_NEW;
00494     } else if (isNumeric(currCh)) { // Numbers
00495         bool isHex = false;
00496         if (currCh=='0') { tkStr += currCh; getNextCh(); }
00497         if (currCh=='x') {
00498           isHex = true;
00499           tkStr += currCh; getNextCh();
00500         }
00501         tk = LEX_INT;
00502         while (isNumeric(currCh) || (isHex && isHexadecimal(currCh))) {
00503             tkStr += currCh;
00504             getNextCh();
00505         }
00506         if (!isHex && currCh=='.') {
00507             tk = LEX_FLOAT;
00508             tkStr += '.';
00509             getNextCh();
00510             while (isNumeric(currCh)) {
00511                 tkStr += currCh;
00512                 getNextCh();
00513             }
00514         }
00515         // do fancy e-style floating point
00516         if (!isHex && (currCh=='e'||currCh=='E')) {
00517           tk = LEX_FLOAT;
00518           tkStr += currCh; getNextCh();
00519           if (currCh=='-') { tkStr += currCh; getNextCh(); }
00520           while (isNumeric(currCh)) {
00521              tkStr += currCh; getNextCh();
00522           }
00523         }
00524     } else if (currCh=='"') {
00525         // strings...
00526         getNextCh();
00527         while (currCh && currCh!='"') {
00528             if (currCh == '\\') {
00529                 getNextCh();
00530                 switch (currCh) {
00531                 case 'n' : tkStr += '\n'; break;
00532                 case '"' : tkStr += '"'; break;
00533                 case '\\' : tkStr += '\\'; break;
00534                 default: tkStr += currCh;
00535                 }
00536             } else {
00537                 tkStr += currCh;
00538             }
00539             getNextCh();
00540         }
00541         getNextCh();
00542         tk = LEX_STR;
00543     } else if (currCh=='\'') {
00544         // strings again...
00545         getNextCh();
00546         while (currCh && currCh!='\'') {
00547             if (currCh == '\\') {
00548                 getNextCh();
00549                 switch (currCh) {
00550                 case 'n' : tkStr += '\n'; break;
00551                 case 'a' : tkStr += '\a'; break;
00552                 case 'r' : tkStr += '\r'; break;
00553                 case 't' : tkStr += '\t'; break;
00554                 case '\'' : tkStr += '\''; break;
00555                 case '\\' : tkStr += '\\'; break;
00556                 case 'x' : { // hex digits
00557                               char buf[3] = "??";
00558                               getNextCh(); buf[0] = currCh;
00559                               getNextCh(); buf[1] = currCh;
00560                               tkStr += (char)strtol(buf,0,16);
00561                            } break;
00562                 default: if (currCh>='0' && currCh<='7') {
00563                            // octal digits
00564                            char buf[4] = "???";
00565                            buf[0] = currCh;
00566                            getNextCh(); buf[1] = currCh;
00567                            getNextCh(); buf[2] = currCh;
00568                            tkStr += (char)strtol(buf,0,8);
00569                          } else
00570                            tkStr += currCh;
00571                 }
00572             } else {
00573                 tkStr += currCh;
00574             }
00575             getNextCh();
00576         }
00577         getNextCh();
00578         tk = LEX_STR;
00579     } else {
00580         // single chars
00581         tk = currCh;
00582         if (currCh) getNextCh();
00583         if (tk=='=' && currCh=='=') { // ==
00584             tk = LEX_EQUAL;
00585             getNextCh();
00586             if (currCh=='=') { // ===
00587               tk = LEX_TYPEEQUAL;
00588               getNextCh();
00589             }
00590         } else if (tk=='!' && currCh=='=') { // !=
00591             tk = LEX_NEQUAL;
00592             getNextCh();
00593             if (currCh=='=') { // !==
00594               tk = LEX_NTYPEEQUAL;
00595               getNextCh();
00596             }
00597         } else if (tk=='<' && currCh=='=') {
00598             tk = LEX_LEQUAL;
00599             getNextCh();
00600         } else if (tk=='<' && currCh=='<') {
00601             tk = LEX_LSHIFT;
00602             getNextCh();
00603             if (currCh=='=') { // <<=
00604               tk = LEX_LSHIFTEQUAL;
00605               getNextCh();
00606             }
00607         } else if (tk=='>' && currCh=='=') {
00608             tk = LEX_GEQUAL;
00609             getNextCh();
00610         } else if (tk=='>' && currCh=='>') {
00611             tk = LEX_RSHIFT;
00612             getNextCh();
00613             if (currCh=='=') { // >>=
00614               tk = LEX_RSHIFTEQUAL;
00615               getNextCh();
00616             } else if (currCh=='>') { // >>>
00617               tk = LEX_RSHIFTUNSIGNED;
00618               getNextCh();
00619             }
00620         }  else if (tk=='+' && currCh=='=') {
00621             tk = LEX_PLUSEQUAL;
00622             getNextCh();
00623         }  else if (tk=='-' && currCh=='=') {
00624             tk = LEX_MINUSEQUAL;
00625             getNextCh();
00626         }  else if (tk=='+' && currCh=='+') {
00627             tk = LEX_PLUSPLUS;
00628             getNextCh();
00629         }  else if (tk=='-' && currCh=='-') {
00630             tk = LEX_MINUSMINUS;
00631             getNextCh();
00632         } else if (tk=='&' && currCh=='=') {
00633             tk = LEX_ANDEQUAL;
00634             getNextCh();
00635         } else if (tk=='&' && currCh=='&') {
00636             tk = LEX_ANDAND;
00637             getNextCh();
00638         } else if (tk=='|' && currCh=='=') {
00639             tk = LEX_OREQUAL;
00640             getNextCh();
00641         } else if (tk=='|' && currCh=='|') {
00642             tk = LEX_OROR;
00643             getNextCh();
00644         } else if (tk=='^' && currCh=='=') {
00645             tk = LEX_XOREQUAL;
00646             getNextCh();
00647         }
00648     }
00649     /* This isn't quite right yet */
00650     tokenLastEnd = tokenEnd;
00651     tokenEnd = dataPos-3;
00652 }
00653 
00654 string CScriptLex::getSubString(int lastPosition) {
00655     int lastCharIdx = tokenLastEnd+1;
00656     if (lastCharIdx < dataEnd) {
00657         /* save a memory alloc by using our data array to create the
00658            substring */
00659         char old = data[lastCharIdx];
00660         data[lastCharIdx] = 0;
00661         std::string value = &data[lastPosition];
00662         data[lastCharIdx] = old;
00663         return value;
00664     } else {
00665         return std::string(&data[lastPosition]);
00666     }
00667 }
00668 
00669 
00670 CScriptLex *CScriptLex::getSubLex(int lastPosition) {
00671     int lastCharIdx = tokenLastEnd+1;
00672     if (lastCharIdx < dataEnd)
00673         return new CScriptLex(this, lastPosition, lastCharIdx);
00674     else
00675         return new CScriptLex(this, lastPosition, dataEnd );
00676 }
00677 
00678 string CScriptLex::getPosition(int pos) {
00679     if (pos<0) pos=tokenLastEnd;
00680     int line = 1,col = 1;
00681     for (int i=0;i<pos;i++) {
00682         char ch;
00683         if (i < dataEnd)
00684             ch = data[i];
00685         else
00686             ch = 0;
00687         col++;
00688         if (ch=='\n') {
00689             line++;
00690             col = 0;
00691         }
00692     }
00693     char buf[256];
00694     sprintf_s(buf, 256, "(line: %d, col: %d)", line, col);
00695     return buf;
00696 }
00697 
00698 // ----------------------------------------------------------------------------------- CSCRIPTVARLINK
00699 
00700 CScriptVarLink::CScriptVarLink(CScriptVar *var, const std::string &name) {
00701 #if DEBUG_MEMORY
00702     mark_allocated(this);
00703 #endif
00704     this->name = name;
00705     this->nextSibling = 0;
00706     this->prevSibling = 0;
00707     this->var = var->ref();
00708     this->owned = false;
00709 }
00710 
00711 CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) {
00712     // Copy constructor
00713 #if DEBUG_MEMORY
00714     mark_allocated(this);
00715 #endif
00716     this->name = link.name;
00717     this->nextSibling = 0;
00718     this->prevSibling = 0;
00719     this->var = link.var->ref();
00720     this->owned = false;
00721 }
00722 
00723 CScriptVarLink::~CScriptVarLink() {
00724 #if DEBUG_MEMORY
00725     mark_deallocated(this);
00726 #endif
00727     var->unref();
00728 }
00729 
00730 void CScriptVarLink::replaceWith(CScriptVar *newVar) {
00731     CScriptVar *oldVar = var;
00732     var = newVar->ref();
00733     oldVar->unref();
00734 }
00735 
00736 void CScriptVarLink::replaceWith(CScriptVarLink *newVar) {
00737     if (newVar)
00738       replaceWith(newVar->var);
00739     else
00740       replaceWith(new CScriptVar());
00741 }
00742 
00743 int CScriptVarLink::getIntName() {
00744     return atoi(name.c_str());
00745 }
00746 void CScriptVarLink::setIntName(int n) {
00747     char sIdx[64];
00748     sprintf_s(sIdx, sizeof(sIdx), "%d", n);
00749     name = sIdx;
00750 }
00751 
00752 // ----------------------------------------------------------------------------------- CSCRIPTVAR
00753 
00754 CScriptVar::CScriptVar() {
00755     refs = 0;
00756 #if DEBUG_MEMORY
00757     mark_allocated(this);
00758 #endif
00759     init();
00760     flags = SCRIPTVAR_UNDEFINED;
00761 }
00762 
00763 CScriptVar::CScriptVar(const string &str) {
00764     refs = 0;
00765 #if DEBUG_MEMORY
00766     mark_allocated(this);
00767 #endif
00768     init();
00769     flags = SCRIPTVAR_STRING;
00770     data = str;
00771 }
00772 
00773 
00774 CScriptVar::CScriptVar(const string &varData, int varFlags) {
00775     refs = 0;
00776 #if DEBUG_MEMORY
00777     mark_allocated(this);
00778 #endif
00779     init();
00780     flags = varFlags;
00781     if (varFlags & SCRIPTVAR_INTEGER) {
00782       intData = strtol(varData.c_str(),0,0);
00783     } else if (varFlags & SCRIPTVAR_DOUBLE) {
00784       doubleData = strtod(varData.c_str(),0);
00785     } else
00786       data = varData;
00787 }
00788 
00789 CScriptVar::CScriptVar(double val) {
00790     refs = 0;
00791 #if DEBUG_MEMORY
00792     mark_allocated(this);
00793 #endif
00794     init();
00795     setDouble(val);
00796 }
00797 
00798 CScriptVar::CScriptVar(int val) {
00799     refs = 0;
00800 #if DEBUG_MEMORY
00801     mark_allocated(this);
00802 #endif
00803     init();
00804     setInt(val);
00805 }
00806 
00807 CScriptVar::~CScriptVar(void) {
00808 #if DEBUG_MEMORY
00809     mark_deallocated(this);
00810 #endif
00811     removeAllChildren();
00812 }
00813 
00814 void CScriptVar::init() {
00815     firstChild = 0;
00816     lastChild = 0;
00817     flags = 0;
00818     jsCallback = 0;
00819     jsCallbackUserData = 0;
00820     data = TINYJS_BLANK_DATA;
00821     intData = 0;
00822     doubleData = 0;
00823 }
00824 
00825 CScriptVar *CScriptVar::getReturnVar() {
00826     return getParameter(TINYJS_RETURN_VAR);
00827 }
00828 
00829 void CScriptVar::setReturnVar(CScriptVar *var) {
00830     findChildOrCreate(TINYJS_RETURN_VAR)->replaceWith(var);
00831 }
00832 
00833 
00834 CScriptVar *CScriptVar::getParameter(const std::string &name) {
00835     return findChildOrCreate(name)->var;
00836 }
00837 
00838 CScriptVarLink *CScriptVar::findChild(const string &childName) {
00839     CScriptVarLink *v = firstChild;
00840     while (v) {
00841         if (v->name.compare(childName)==0)
00842             return v;
00843         v = v->nextSibling;
00844     }
00845     return 0;
00846 }
00847 
00848 CScriptVarLink *CScriptVar::findChildOrCreate(const string &childName, int varFlags) {
00849     CScriptVarLink *l = findChild(childName);
00850     if (l) return l;
00851 
00852     return addChild(childName, new CScriptVar(TINYJS_BLANK_DATA, varFlags));
00853 }
00854 
00855 CScriptVarLink *CScriptVar::findChildOrCreateByPath(const std::string &path) {
00856   size_t p = path.find('.');
00857   if (p == string::npos)
00858     return findChildOrCreate(path);
00859 
00860   return findChildOrCreate(path.substr(0,p), SCRIPTVAR_OBJECT)->var->
00861             findChildOrCreateByPath(path.substr(p+1));
00862 }
00863 
00864 CScriptVarLink *CScriptVar::addChild(const std::string &childName, CScriptVar *child) {
00865   if (isUndefined()) {
00866     flags = SCRIPTVAR_OBJECT;
00867   }
00868     // if no child supplied, create one
00869     if (!child)
00870       child = new CScriptVar();
00871 
00872     CScriptVarLink *link = new CScriptVarLink(child, childName);
00873     link->owned = true;
00874     if (lastChild) {
00875         lastChild->nextSibling = link;
00876         link->prevSibling = lastChild;
00877         lastChild = link;
00878     } else {
00879         firstChild = link;
00880         lastChild = link;
00881     }
00882     return link;
00883 }
00884 
00885 CScriptVarLink *CScriptVar::addChildNoDup(const std::string &childName, CScriptVar *child) {
00886     // if no child supplied, create one
00887     if (!child)
00888       child = new CScriptVar();
00889 
00890     CScriptVarLink *v = findChild(childName);
00891     if (v) {
00892         v->replaceWith(child);
00893     } else {
00894         v = addChild(childName, child);
00895     }
00896 
00897     return v;
00898 }
00899 
00900 void CScriptVar::removeChild(CScriptVar *child) {
00901     CScriptVarLink *link = firstChild;
00902     while (link) {
00903         if (link->var == child)
00904             break;
00905         link = link->nextSibling;
00906     }
00907     ASSERT(link);
00908     removeLink(link);
00909 }
00910 
00911 void CScriptVar::removeLink(CScriptVarLink *link) {
00912     if (!link) return;
00913     if (link->nextSibling)
00914       link->nextSibling->prevSibling = link->prevSibling;
00915     if (link->prevSibling)
00916       link->prevSibling->nextSibling = link->nextSibling;
00917     if (lastChild == link)
00918         lastChild = link->prevSibling;
00919     if (firstChild == link)
00920         firstChild = link->nextSibling;
00921     delete link;
00922 }
00923 
00924 void CScriptVar::removeAllChildren() {
00925     CScriptVarLink *c = firstChild;
00926     while (c) {
00927         CScriptVarLink *t = c->nextSibling;
00928         delete c;
00929         c = t;
00930     }
00931     firstChild = 0;
00932     lastChild = 0;
00933 }
00934 
00935 CScriptVar *CScriptVar::getArrayIndex(int idx) {
00936     char sIdx[64];
00937     sprintf_s(sIdx, sizeof(sIdx), "%d", idx);
00938     CScriptVarLink *link = findChild(sIdx);
00939     if (link) return link->var;
00940     else return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NULL); // undefined
00941 }
00942 
00943 void CScriptVar::setArrayIndex(int idx, CScriptVar *value) {
00944     char sIdx[64];
00945     sprintf_s(sIdx, sizeof(sIdx), "%d", idx);
00946     CScriptVarLink *link = findChild(sIdx);
00947 
00948     if (link) {
00949       if (value->isUndefined())
00950         removeLink(link);
00951       else
00952         link->replaceWith(value);
00953     } else {
00954       if (!value->isUndefined())
00955         addChild(sIdx, value);
00956     }
00957 }
00958 
00959 int CScriptVar::getArrayLength() {
00960     int highest = -1;
00961     if (!isArray()) return 0;
00962 
00963     CScriptVarLink *link = firstChild;
00964     while (link) {
00965       if (isNumber(link->name)) {
00966         int val = atoi(link->name.c_str());
00967         if (val > highest) highest = val;
00968       }
00969       link = link->nextSibling;
00970     }
00971     return highest+1;
00972 }
00973 
00974 int CScriptVar::getChildren() {
00975     int n = 0;
00976     CScriptVarLink *link = firstChild;
00977     while (link) {
00978       n++;
00979       link = link->nextSibling;
00980     }
00981     return n;
00982 }
00983 
00984 int CScriptVar::getInt() {
00985     /* strtol understands about hex and octal */
00986     if (isInt()) return intData;
00987     if (isNull()) return 0;
00988     if (isUndefined()) return 0;
00989     if (isDouble()) return (int)doubleData;
00990     return 0;
00991 }
00992 
00993 double CScriptVar::getDouble() {
00994     if (isDouble()) return doubleData;
00995     if (isInt()) return intData;
00996     if (isNull()) return 0;
00997     if (isUndefined()) return 0;
00998     return 0; /* or NaN? */
00999 }
01000 
01001 const string &CScriptVar::getString() {
01002     /* Because we can't return a string that is generated on demand.
01003      * I should really just use char* :) */
01004     static string s_null = "null";
01005     static string s_undefined = "undefined";
01006     if (isInt()) {
01007       char buffer[32];
01008       sprintf_s(buffer, sizeof(buffer), "%ld", intData);
01009       data = buffer;
01010       return data;
01011     }
01012     if (isDouble()) {
01013       char buffer[32];
01014       sprintf_s(buffer, sizeof(buffer), "%f", doubleData);
01015       data = buffer;
01016       return data;
01017     }
01018     if (isNull()) return s_null;
01019     if (isUndefined()) return s_undefined;
01020     // are we just a string here?
01021     return data;
01022 }
01023 
01024 void CScriptVar::setInt(int val) {
01025     flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_INTEGER;
01026     intData = val;
01027     doubleData = 0;
01028     data = TINYJS_BLANK_DATA;
01029 }
01030 
01031 void CScriptVar::setDouble(double val) {
01032     flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_DOUBLE;
01033     doubleData = val;
01034     intData = 0;
01035     data = TINYJS_BLANK_DATA;
01036 }
01037 
01038 void CScriptVar::setString(const string &str) {
01039     // name sure it's not still a number or integer
01040     flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_STRING;
01041     data = str;
01042     intData = 0;
01043     doubleData = 0;
01044 }
01045 
01046 void CScriptVar::setUndefined() {
01047     // name sure it's not still a number or integer
01048     flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_UNDEFINED;
01049     data = TINYJS_BLANK_DATA;
01050     intData = 0;
01051     doubleData = 0;
01052     removeAllChildren();
01053 }
01054 
01055 void CScriptVar::setArray() {
01056     // name sure it's not still a number or integer
01057     flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_ARRAY;
01058     data = TINYJS_BLANK_DATA;
01059     intData = 0;
01060     doubleData = 0;
01061     removeAllChildren();
01062 }
01063 
01064 bool CScriptVar::equals(CScriptVar *v) {
01065     CScriptVar *resV = mathsOp(v, LEX_EQUAL);
01066     bool res = resV->getBool();
01067     delete resV;
01068     return res;
01069 }
01070 
01071 CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) {
01072     CScriptVar *a = this;
01073     // Type equality check
01074     if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) {
01075       // check type first, then call again to check data
01076       bool eql = ((a->flags & SCRIPTVAR_VARTYPEMASK) ==
01077                   (b->flags & SCRIPTVAR_VARTYPEMASK));
01078       if (eql) {
01079         CScriptVar *contents = a->mathsOp(b, LEX_EQUAL);
01080         if (!contents->getBool()) eql = false;
01081         if (!contents->refs) delete contents;
01082       }
01083                  ;
01084       if (op == LEX_TYPEEQUAL)
01085         return new CScriptVar(eql);
01086       else
01087         return new CScriptVar(!eql);
01088     }
01089     // do maths...
01090     if (a->isUndefined() && b->isUndefined()) {
01091       if (op == LEX_EQUAL) return new CScriptVar(true);
01092       else if (op == LEX_NEQUAL) return new CScriptVar(false);
01093       else return new CScriptVar(); // undefined
01094     } else if ((a->isNumeric() || a->isUndefined()) &&
01095                (b->isNumeric() || b->isUndefined())) {
01096         if (!a->isDouble() && !b->isDouble()) {
01097             // use ints
01098             int da = a->getInt();
01099             int db = b->getInt();
01100             switch (op) {
01101                 case '+': return new CScriptVar(da+db);
01102                 case '-': return new CScriptVar(da-db);
01103                 case '*': return new CScriptVar(da*db);
01104                 case '/': return new CScriptVar(da/db);
01105                 case '&': return new CScriptVar(da&db);
01106                 case '|': return new CScriptVar(da|db);
01107                 case '^': return new CScriptVar(da^db);
01108                 case '%': return new CScriptVar(da%db);
01109                 case LEX_EQUAL:     return new CScriptVar(da==db);
01110                 case LEX_NEQUAL:    return new CScriptVar(da!=db);
01111                 case '<':     return new CScriptVar(da<db);
01112                 case LEX_LEQUAL:    return new CScriptVar(da<=db);
01113                 case '>':     return new CScriptVar(da>db);
01114                 case LEX_GEQUAL:    return new CScriptVar(da>=db);
01115 #ifndef MBED
01116                 default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the Int datatype");
01117 #else
01118                 default:
01119                     mbedErrorFlag = 1;
01120                     mbedErrorMessage = "Operation ";
01121                     mbedErrorMessage += CScriptLex::getTokenStr(op);
01122                     mbedErrorMessage += " not supported on the Int datatype";
01123                     return 0;
01124 #endif
01125             }
01126         } else {
01127             // use doubles
01128             double da = a->getDouble();
01129             double db = b->getDouble();
01130             switch (op) {
01131                 case '+': return new CScriptVar(da+db);
01132                 case '-': return new CScriptVar(da-db);
01133                 case '*': return new CScriptVar(da*db);
01134                 case '/': return new CScriptVar(da/db);
01135                 case LEX_EQUAL:     return new CScriptVar(da==db);
01136                 case LEX_NEQUAL:    return new CScriptVar(da!=db);
01137                 case '<':     return new CScriptVar(da<db);
01138                 case LEX_LEQUAL:    return new CScriptVar(da<=db);
01139                 case '>':     return new CScriptVar(da>db);
01140                 case LEX_GEQUAL:    return new CScriptVar(da>=db);
01141 #ifndef MBED
01142                 default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the Double datatype");
01143 #else
01144                 default:
01145                     mbedErrorFlag = 1;
01146                     mbedErrorMessage = "Operation ";
01147                     mbedErrorMessage += CScriptLex::getTokenStr(op);
01148                     mbedErrorMessage += " not supported on the Double datatype";
01149                     return 0;
01150 #endif
01151             }
01152         }
01153     } else if (a->isArray()) {
01154       /* Just check pointers */
01155       switch (op) {
01156            case LEX_EQUAL: return new CScriptVar(a==b);
01157            case LEX_NEQUAL: return new CScriptVar(a!=b);
01158 #ifndef MBED
01159            default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the Array datatype");
01160 #else
01161            default:
01162              mbedErrorFlag = 1;;
01163              mbedErrorMessage = "Operation ";
01164              mbedErrorMessage += CScriptLex::getTokenStr(op);
01165              mbedErrorMessage += " not supported on the Array datatype";
01166              return 0;
01167 
01168 #endif
01169       }
01170     } else if (a->isObject()) {
01171           /* Just check pointers */
01172           switch (op) {
01173                case LEX_EQUAL: return new CScriptVar(a==b);
01174                case LEX_NEQUAL: return new CScriptVar(a!=b);
01175 #ifndef MBED
01176                default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the Object datatype");
01177 #else
01178                default:
01179                  mbedErrorFlag = 1;
01180                  mbedErrorMessage = "Operation ";
01181                  mbedErrorMessage += CScriptLex::getTokenStr(op);
01182                  mbedErrorMessage += " not supported on the Object datatype";
01183                  return 0;
01184 #endif
01185           }
01186     } else {
01187        string da = a->getString();
01188        string db = b->getString();
01189        // use strings
01190        switch (op) {
01191            case '+':           return new CScriptVar(da+db, SCRIPTVAR_STRING);
01192            case LEX_EQUAL:     return new CScriptVar(da==db);
01193            case LEX_NEQUAL:    return new CScriptVar(da!=db);
01194            case '<':     return new CScriptVar(da<db);
01195            case LEX_LEQUAL:    return new CScriptVar(da<=db);
01196            case '>':     return new CScriptVar(da>db);
01197            case LEX_GEQUAL:    return new CScriptVar(da>=db);
01198 #ifndef MBED
01199            default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the string datatype");
01200 #else
01201            default:
01202              mbedErrorFlag = 1;
01203              mbedErrorMessage = "Operation ";
01204              mbedErrorMessage += CScriptLex::getTokenStr(op);
01205              mbedErrorMessage += " not supported on the string datatype";
01206              return 0;
01207 #endif
01208        }
01209     }
01210     ASSERT(0);
01211     return 0;
01212 }
01213 
01214 void CScriptVar::copySimpleData(CScriptVar *val) {
01215     data = val->data;
01216     intData = val->intData;
01217     doubleData = val->doubleData;
01218     flags = (flags & ~SCRIPTVAR_VARTYPEMASK) | (val->flags & SCRIPTVAR_VARTYPEMASK);
01219 }
01220 
01221 void CScriptVar::copyValue(CScriptVar *val) {
01222     if (val) {
01223       copySimpleData(val);
01224       // remove all current children
01225       removeAllChildren();
01226       // copy children of 'val'
01227       CScriptVarLink *child = val->firstChild;
01228       while (child) {
01229         CScriptVar *copied;
01230         // don't copy the 'parent' object...
01231         if (child->name != TINYJS_PROTOTYPE_CLASS)
01232           copied = child->var->deepCopy();
01233         else
01234           copied = child->var;
01235 
01236         addChild(child->name, copied);
01237 
01238         child = child->nextSibling;
01239       }
01240     } else {
01241       setUndefined();
01242     }
01243 }
01244 
01245 CScriptVar *CScriptVar::deepCopy() {
01246     CScriptVar *newVar = new CScriptVar();
01247     newVar->copySimpleData(this);
01248     // copy children
01249     CScriptVarLink *child = firstChild;
01250     while (child) {
01251         CScriptVar *copied;
01252         // don't copy the 'parent' object...
01253         if (child->name != TINYJS_PROTOTYPE_CLASS)
01254           copied = child->var->deepCopy();
01255         else
01256           copied = child->var;
01257 
01258         newVar->addChild(child->name, copied);
01259         child = child->nextSibling;
01260     }
01261     return newVar;
01262 }
01263 
01264 void CScriptVar::trace(string indentStr, const string &name) {
01265     TRACE("%s'%s' = '%s' %s\n",
01266         indentStr.c_str(),
01267         name.c_str(),
01268         getString().c_str(),
01269         getFlagsAsString().c_str());
01270     string indent = indentStr+" ";
01271     CScriptVarLink *link = firstChild;
01272     while (link) {
01273       link->var->trace(indent, link->name);
01274       link = link->nextSibling;
01275     }
01276 }
01277 
01278 string CScriptVar::getFlagsAsString() {
01279   string flagstr = "";
01280   if (flags&SCRIPTVAR_FUNCTION) flagstr = flagstr + "FUNCTION ";
01281   if (flags&SCRIPTVAR_OBJECT) flagstr = flagstr + "OBJECT ";
01282   if (flags&SCRIPTVAR_ARRAY) flagstr = flagstr + "ARRAY ";
01283   if (flags&SCRIPTVAR_NATIVE) flagstr = flagstr + "NATIVE ";
01284   if (flags&SCRIPTVAR_DOUBLE) flagstr = flagstr + "DOUBLE ";
01285   if (flags&SCRIPTVAR_INTEGER) flagstr = flagstr + "INTEGER ";
01286   if (flags&SCRIPTVAR_STRING) flagstr = flagstr + "STRING ";
01287   return flagstr;
01288 }
01289 
01290 string CScriptVar::getParsableString() {
01291   // Numbers can just be put in directly
01292   if (isNumeric())
01293     return getString();
01294   if (isFunction()) {
01295 #ifndef MBED
01296     ostringstream funcStr;
01297     funcStr << "function (";
01298     // get list of parameters
01299     CScriptVarLink *link = firstChild;
01300     while (link) {
01301       funcStr << link->name;
01302       if (link->nextSibling) funcStr << ",";
01303       link = link->nextSibling;
01304     }
01305     // add function body
01306     funcStr << ") " << getString();
01307     return funcStr.str();
01308 #else
01309     string funcStr;
01310     funcStr = "function (";
01311     CScriptVarLink *link = firstChild;
01312     while (link) {
01313       funcStr += link->name;
01314       if (link->nextSibling)  funcStr += ",";
01315       link = link->nextSibling;
01316     }
01317     // add function body
01318     funcStr += ") ";
01319     funcStr += getString();
01320     return funcStr;
01321 #endif
01322   }
01323   // if it is a string then we quote it
01324   if (isString())
01325     return getJSString(getString());
01326   if (isNull())
01327       return "null";
01328   return "undefined";
01329 }
01330 
01331 #ifndef MBED
01332 void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) {
01333    if (isObject()) {
01334       string indentedLinePrefix = linePrefix+"  ";
01335       // children - handle with bracketed list
01336       destination << "{ \n";
01337       CScriptVarLink *link = firstChild;
01338       while (link) {
01339         destination << indentedLinePrefix;
01340         destination  << getJSString(link->name);
01341         destination  << " : ";
01342         link->var->getJSON(destination, indentedLinePrefix);
01343         link = link->nextSibling;
01344         if (link) {
01345           destination  << ",\n";
01346         }
01347       }
01348       destination << "\n" << linePrefix << "}";
01349     } else if (isArray()) {
01350       string indentedLinePrefix = linePrefix+"  ";
01351       destination << "[\n";
01352       int len = getArrayLength();
01353       if (len>10000) len=10000; // we don't want to get stuck here!
01354 
01355       for (int i=0;i<len;i++) {
01356         getArrayIndex(i)->getJSON(destination, indentedLinePrefix);
01357         if (i<len-1) destination  << ",\n";
01358       }
01359 
01360       destination << "\n" << linePrefix << "]";
01361     } else {
01362       // no children or a function... just write value directly
01363       destination << getParsableString();
01364     }
01365 }
01366 #else
01367 void CScriptVar::getJSON(string &destination, const string linePrefix) {
01368    if (isObject()) {
01369       string indentedLinePrefix = linePrefix+"  ";
01370       // children - handle with bracketed list
01371       destination += "{ \n";
01372       CScriptVarLink *link = firstChild;
01373       while (link) {
01374         destination += indentedLinePrefix;
01375         destination += getJSString(link->name);
01376         destination += " : ";
01377         link->var->getJSON(destination, indentedLinePrefix);
01378         link = link->nextSibling;
01379         if (link) {
01380           destination += ",\n";
01381         }
01382       }
01383       destination += "\n";
01384       destination += linePrefix;
01385       destination += "}";
01386     } else if (isArray()) {
01387       string indentedLinePrefix = linePrefix+"  ";
01388       destination + "[\n";
01389       int len = getArrayLength();
01390       if (len>10000) len=10000; // we don't want to get stuck here!
01391 
01392       for (int i=0;i<len;i++) {
01393         getArrayIndex(i)->getJSON(destination, indentedLinePrefix);
01394         if (i<len-1) {
01395             destination += ",\n";
01396         }
01397       }
01398 
01399       destination += "\n";
01400       destination += linePrefix;
01401       destination += "]";
01402     } else {
01403       // no children or a function... just write value directly
01404       destination += getParsableString();
01405     }
01406 }
01407 #endif
01408 
01409 void CScriptVar::setCallback(JSCallback callback, void *userdata) {
01410     jsCallback = callback;
01411     jsCallbackUserData = userdata;
01412 }
01413 
01414 CScriptVar *CScriptVar::ref() {
01415     refs++;
01416     return this;
01417 }
01418 
01419 void CScriptVar::unref() {
01420     if (refs<=0) printf("OMFG, we have unreffed too far!\n");
01421     if ((--refs)==0) {
01422       delete this;
01423     }
01424 }
01425 
01426 int CScriptVar::getRefs() {
01427     return refs;
01428 }
01429 
01430 
01431 // ----------------------------------------------------------------------------------- CSCRIPT
01432 
01433 CTinyJS::CTinyJS() {
01434     l = 0;
01435     root = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref();
01436     // Add built-in classes
01437     stringClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref();
01438     arrayClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref();
01439     objectClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref();
01440     root->addChild("String", stringClass);
01441     root->addChild("Array", arrayClass);
01442     root->addChild("Object", objectClass);
01443 }
01444 
01445 CTinyJS::~CTinyJS() {
01446     ASSERT(!l);
01447     scopes.clear();
01448     stringClass->unref();
01449     arrayClass->unref();
01450     objectClass->unref();
01451     root->unref();
01452 
01453 #if DEBUG_MEMORY
01454     show_allocated();
01455 #endif
01456 }
01457 
01458 void CTinyJS::trace() {
01459     root->trace();
01460 }
01461 
01462 void CTinyJS::execute(const string &code) {
01463     CScriptLex *oldLex = l;
01464     vector<CScriptVar*> oldScopes = scopes;
01465     l = new CScriptLex(code);
01466 #ifdef TINYJS_CALL_STACK
01467     call_stack.clear();
01468 #endif
01469     scopes.clear();
01470     scopes.push_back(root);
01471 
01472 #ifndef MBED
01473     try {
01474         bool execute = true;
01475         while (l->tk) statement(execute);
01476     } catch (CScriptException *e) {
01477         ostringstream msg;
01478         msg << "Error " << e->text;
01479 #ifdef TINYJS_CALL_STACK
01480         for (int i=(int)call_stack.size()-1;i>=0;i--)
01481           msg << "\n" << i << ": " << call_stack.at(i);
01482 #endif
01483         msg << " at " << l->getPosition();
01484         delete l;
01485         l = oldLex;
01486 
01487         throw new CScriptException(msg.str());
01488     }
01489 #else
01490     {
01491       bool execute = true;
01492       while (l->tk) {
01493         mbedErrorFlag = 0;
01494         statement(execute);
01495         if (mbedErrorFlag != 0) {
01496           string msg;
01497           msg = "Error ";
01498           msg += mbedErrorMessage;
01499 #ifdef TINYJS_CALL_STACK
01500           for (int i=(int)call_stack.size()-1;i>=0;i--) {
01501             msg += "\n";
01502             msg += i;
01503             msg += ": ";
01504             msg += call_stack.at(i);
01505           }
01506 #endif
01507           msg += " at ";
01508           msg += l->getPosition();
01509 
01510           delete l;
01511           l = oldLex;
01512 
01513           mbedErrorFlag = 1;
01514           mbedErrorMessage = msg;
01515           return;
01516         }
01517       }
01518     }
01519 #endif
01520 
01521 
01522     delete l;
01523     l = oldLex;
01524     scopes = oldScopes;
01525 }
01526 
01527 CScriptVarLink CTinyJS::evaluateComplex(const string &code) {
01528     CScriptLex *oldLex = l;
01529     vector<CScriptVar*> oldScopes = scopes;
01530 
01531     l = new CScriptLex(code);
01532 #ifdef TINYJS_CALL_STACK
01533     call_stack.clear();
01534 #endif
01535     scopes.clear();
01536     scopes.push_back(root);
01537     CScriptVarLink *v = 0;
01538 #ifndef MBED
01539     try {
01540         bool execute = true;
01541         do {
01542           CLEAN(v);
01543           v = base(execute);
01544           if (l->tk!=LEX_EOF) l->match(';');
01545         } while (l->tk!=LEX_EOF);
01546     } catch (CScriptException *e) {
01547       ostringstream msg;
01548       msg << "Error " << e->text;
01549 #ifdef TINYJS_CALL_STACK
01550       for (int i=(int)call_stack.size()-1;i>=0;i--)
01551         msg << "\n" << i << ": " << call_stack.at(i);
01552 #endif
01553       msg << " at " << l->getPosition();
01554       delete l;
01555       l = oldLex;
01556 
01557         throw new CScriptException(msg.str());
01558     }
01559 #else
01560     {
01561       bool execute = true;
01562       do {
01563         CLEAN(v);
01564         mbedErrorFlag = 0;
01565         v = base(execute);
01566         if (mbedErrorFlag != 0) {
01567           string msg;
01568           msg = "Error ";
01569           msg += mbedErrorMessage;
01570 #ifdef TINYJS_CALL_STACK
01571           for (int i=(int)call_stack.size()-1;i>=0;i--) {
01572             msg += "\n";
01573             msg += i;
01574             msg += ": ";
01575             msg += call_stack.at(i);
01576           }
01577 #endif
01578           msg += " at ";
01579           msg += l->getPosition();
01580           delete l;
01581           l = oldLex;
01582 
01583           mbedErrorFlag = 1;
01584           mbedErrorMessage = msg;
01585           return 0;
01586         }
01587 
01588         if (l->tk!=LEX_EOF) {
01589             mbedErrorFlag = 0;
01590             l->match(';');
01591             if (mbedErrorFlag != 0)  return 0;
01592         }
01593       } while (l->tk!=LEX_EOF);
01594     }
01595 #endif
01596     delete l;
01597     l = oldLex;
01598     scopes = oldScopes;
01599 
01600     if (v) {
01601         CScriptVarLink r = *v;
01602         CLEAN(v);
01603         return r;
01604     }
01605     // return undefined...
01606     return CScriptVarLink(new CScriptVar());
01607 }
01608 
01609 string CTinyJS::evaluate(const string &code) {
01610     return evaluateComplex(code).var->getString();
01611 }
01612 
01613 void CTinyJS::parseFunctionArguments(CScriptVar *funcVar) {
01614 #ifndef MBED
01615   l->match('(');
01616   while (l->tk!=')') {
01617       funcVar->addChildNoDup(l->tkStr);
01618       l->match(LEX_ID);
01619       if (l->tk!=')') l->match(',');
01620   }
01621   l->match(')');
01622 #else
01623   LMATCH_VOID('(');
01624   while (l->tk!=')') {
01625       funcVar->addChildNoDup(l->tkStr);
01626       LMATCH_VOID(LEX_ID);
01627       if (l->tk!=')') {
01628         LMATCH_VOID(',');
01629       }
01630   }
01631   LMATCH_VOID(')');
01632 #endif
01633 }
01634 
01635 void CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata) {
01636     CScriptLex *oldLex = l;
01637     l = new CScriptLex(funcDesc);
01638 
01639     CScriptVar *base = root;
01640 #ifndef MBED
01641     l->match(LEX_R_FUNCTION);
01642 #else
01643     LMATCH_VOID(LEX_R_FUNCTION);
01644 #endif
01645     string funcName = l->tkStr;
01646 #ifndef MBED
01647     l->match(LEX_ID);
01648 #else
01649     LMATCH_VOID(LEX_ID);
01650 #endif
01651     /* Check for dots, we might want to do something like function String.substring ... */
01652     while (l->tk == '.') {
01653 #ifndef MBED
01654       l->match('.');
01655 #else
01656       LMATCH_VOID('.');
01657 #endif
01658       CScriptVarLink *link = base->findChild(funcName);
01659       // if it doesn't exist, make an object class
01660       if (!link) link = base->addChild(funcName, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT));
01661       base = link->var;
01662       funcName = l->tkStr;
01663 #ifndef MBED
01664       l->match(LEX_ID);
01665 #else
01666       LMATCH_VOID(LEX_ID);
01667 #endif
01668     }
01669 
01670     CScriptVar *funcVar = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION | SCRIPTVAR_NATIVE);
01671     funcVar->setCallback(ptr, userdata);
01672     parseFunctionArguments(funcVar);
01673     delete l;
01674     l = oldLex;
01675 
01676     base->addChild(funcName, funcVar);
01677 }
01678 
01679 CScriptVarLink *CTinyJS::parseFunctionDefinition() {
01680   // actually parse a function...
01681 #ifndef MBED
01682   l->match(LEX_R_FUNCTION);
01683 #else
01684   LMATCH(LEX_R_FUNCTION);
01685 #endif
01686   string funcName = TINYJS_TEMP_NAME;
01687   /* we can have functions without names */
01688   if (l->tk==LEX_ID) {
01689     funcName = l->tkStr;
01690 #ifndef MBED
01691     l->match(LEX_ID);
01692 #else
01693     LMATCH(LEX_ID);
01694 #endif
01695   }
01696   CScriptVarLink *funcVar = new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION), funcName);
01697   parseFunctionArguments(funcVar->var);
01698   int funcBegin = l->tokenStart;
01699   bool noexecute = false;
01700   block(noexecute);
01701   funcVar->var->data = l->getSubString(funcBegin);
01702   return funcVar;
01703 }
01704 
01705 /** Handle a function call (assumes we've parsed the function name and we're
01706  * on the start bracket). 'parent' is the object that contains this method,
01707  * if there was one (otherwise it's just a normnal function).
01708  */
01709 CScriptVarLink *CTinyJS::functionCall(bool &execute, CScriptVarLink *function, CScriptVar *parent) {
01710   if (execute) {
01711     if (!function->var->isFunction()) {
01712         string errorMsg = "Expecting '";
01713         errorMsg = errorMsg + function->name + "' to be a function";
01714 #ifndef MBED
01715         throw new CScriptException(errorMsg.c_str());
01716 #else
01717         mbedErrorFlag = 1;
01718         mbedErrorMessage = errorMsg;
01719         return 0;
01720 #endif
01721     }
01722 #ifndef MBED
01723     l->match('(');
01724 #else
01725     LMATCH('(');
01726 #endif
01727     // create a new symbol table entry for execution of this function
01728     CScriptVar *functionRoot = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION);
01729     if (parent)
01730       functionRoot->addChildNoDup("this", parent);
01731     // grab in all parameters
01732     CScriptVarLink *v = function->var->firstChild;
01733     while (v) {
01734         CScriptVarLink *value = base(execute);
01735         if (execute) {
01736             if (value->var->isBasic()) {
01737               // pass by value
01738               functionRoot->addChild(v->name, value->var->deepCopy());
01739             } else {
01740               // pass by reference
01741               functionRoot->addChild(v->name, value->var);
01742             }
01743         }
01744         CLEAN(value);
01745 #ifndef MBED
01746         if (l->tk!=')') l->match(',');
01747 #else
01748         if (l->tk!=')')  LMATCH(',');
01749 #endif
01750         v = v->nextSibling;
01751     }
01752 #ifndef MBED
01753     l->match(')');
01754 #else
01755     LMATCH(')');
01756 #endif
01757     // setup a return variable
01758     CScriptVarLink *returnVar = NULL;
01759     // execute function!
01760     // add the function's execute space to the symbol table so we can recurse
01761     CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR);
01762     scopes.push_back(functionRoot);
01763 #ifdef TINYJS_CALL_STACK
01764     call_stack.push_back(function->name + " from " + l->getPosition());
01765 #endif
01766 
01767     if (function->var->isNative()) {
01768         ASSERT(function->var->jsCallback);
01769         function->var->jsCallback(functionRoot, function->var->jsCallbackUserData);
01770     } else {
01771         /* we just want to execute the block, but something could
01772          * have messed up and left us with the wrong ScriptLex, so
01773          * we want to be careful here... */
01774 #ifndef MBED
01775         CScriptException *exception = 0;
01776         CScriptLex *oldLex = l;
01777         CScriptLex *newLex = new CScriptLex(function->var->getString());
01778         l = newLex;
01779 
01780         try {
01781           block(execute);
01782           // because return will probably have called this, and set execute to false
01783           execute = true;
01784         } catch (CScriptException *e) {
01785           exception = e;
01786         }
01787         delete newLex;
01788         l = oldLex;
01789 
01790         if (exception)
01791           throw exception;
01792 #else
01793         CScriptLex *oldLex = l;
01794         CScriptLex *newLex = new CScriptLex(function->var->getString());
01795         l = newLex;
01796 
01797         {
01798           mbedErrorFlag = 0;
01799           block(execute);
01800           if (mbedErrorFlag != 0) {
01801             delete newLex;
01802             l = oldLex;
01803             return 0;
01804           } else {
01805             execute = true;
01806 
01807             delete newLex;
01808             l = oldLex;
01809           }
01810         }
01811 #endif
01812 
01813     }
01814 #ifdef TINYJS_CALL_STACK
01815     if (!call_stack.empty()) call_stack.pop_back();
01816 #endif
01817     scopes.pop_back();
01818     /* get the real return var before we remove it from our function */
01819     returnVar = new CScriptVarLink(returnVarLink->var);
01820     functionRoot->removeLink(returnVarLink);
01821     delete functionRoot;
01822     if (returnVar)
01823       return returnVar;
01824     else
01825       return new CScriptVarLink(new CScriptVar());
01826   } else {
01827     // function, but not executing - just parse args and be done
01828 #ifndef MBED
01829     l->match('(');
01830     while (l->tk != ')') {
01831       CScriptVarLink *value = base(execute);
01832       CLEAN(value);
01833       if (l->tk!=')') l->match(',');
01834     }
01835     l->match(')');
01836 #else
01837     LMATCH('(');
01838     while (l->tk != ')') {
01839       CScriptVarLink *value = base(execute);
01840       CLEAN(value);
01841       if (l->tk!=')') LMATCH(',');
01842     }
01843     LMATCH(')');
01844 #endif
01845     if (l->tk == '{') { // TODO: why is this here?
01846       block(execute);
01847     }
01848     /* function will be a blank scriptvarlink if we're not executing,
01849      * so just return it rather than an alloc/free */
01850     return function;
01851   }
01852 }
01853 
01854 #ifndef MBED
01855 CScriptVarLink *CTinyJS::factor(bool &execute) {
01856     if (l->tk=='(') {
01857         l->match('(');
01858         CScriptVarLink *a = base(execute);
01859         l->match(')');
01860         return a;
01861     }
01862     if (l->tk==LEX_R_TRUE) {
01863         l->match(LEX_R_TRUE);
01864         return new CScriptVarLink(new CScriptVar(1));
01865     }
01866     if (l->tk==LEX_R_FALSE) {
01867         l->match(LEX_R_FALSE);
01868         return new CScriptVarLink(new CScriptVar(0));
01869     }
01870     if (l->tk==LEX_R_NULL) {
01871         l->match(LEX_R_NULL);
01872         return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NULL));
01873     }
01874     if (l->tk==LEX_R_UNDEFINED) {
01875         l->match(LEX_R_UNDEFINED);
01876         return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED));
01877     }
01878     if (l->tk==LEX_ID) {
01879         CScriptVarLink *a = execute ? findInScopes(l->tkStr) : new CScriptVarLink(new CScriptVar());
01880         //printf("0x%08X for %s at %s\n", (unsigned int)a, l->tkStr.c_str(), l->getPosition().c_str());
01881         /* The parent if we're executing a method call */
01882         CScriptVar *parent = 0;
01883 
01884         if (execute && !a) {
01885           /* Variable doesn't exist! JavaScript says we should create it
01886            * (we won't add it here. This is done in the assignment operator)*/
01887           a = new CScriptVarLink(new CScriptVar(), l->tkStr);
01888         }
01889         l->match(LEX_ID);
01890         while (l->tk=='(' || l->tk=='.' || l->tk=='[') {
01891             if (l->tk=='(') { // ------------------------------------- Function Call
01892                 a = functionCall(execute, a, parent);
01893             } else if (l->tk == '.') { // ------------------------------------- Record Access
01894                 l->match('.');
01895                 if (execute) {
01896                   const string &name = l->tkStr;
01897                   CScriptVarLink *child = a->var->findChild(name);
01898                   if (!child) child = findInParentClasses(a->var, name);
01899                   if (!child) {
01900                     /* if we haven't found this defined yet, use the built-in
01901                        'length' properly */
01902                     if (a->var->isArray() && name == "length") {
01903                       int l = a->var->getArrayLength();
01904                       child = new CScriptVarLink(new CScriptVar(l));
01905                     } else if (a->var->isString() && name == "length") {
01906                       int l = a->var->getString().size();
01907                       child = new CScriptVarLink(new CScriptVar(l));
01908                     } else {
01909                       child = a->var->addChild(name);
01910                     }
01911                   }
01912                   parent = a->var;
01913                   a = child;
01914                 }
01915                 l->match(LEX_ID);
01916             } else if (l->tk == '[') { // ------------------------------------- Array Access
01917                 l->match('[');
01918                 CScriptVarLink *index = base(execute);
01919                 l->match(']');
01920                 if (execute) {
01921                   CScriptVarLink *child = a->var->findChildOrCreate(index->var->getString());
01922                   parent = a->var;
01923                   a = child;
01924                 }
01925                 CLEAN(index);
01926             } else ASSERT(0);
01927         }
01928         return a;
01929     }
01930     if (l->tk==LEX_INT || l->tk==LEX_FLOAT) {
01931         CScriptVar *a = new CScriptVar(l->tkStr,
01932             ((l->tk==LEX_INT)?SCRIPTVAR_INTEGER:SCRIPTVAR_DOUBLE));
01933         l->match(l->tk);
01934         return new CScriptVarLink(a);
01935     }
01936     if (l->tk==LEX_STR) {
01937         CScriptVar *a = new CScriptVar(l->tkStr, SCRIPTVAR_STRING);
01938         l->match(LEX_STR);
01939         return new CScriptVarLink(a);
01940     }
01941     if (l->tk=='{') {
01942         CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT);
01943         /* JSON-style object definition */
01944         l->match('{');
01945         while (l->tk != '}') {
01946           string id = l->tkStr;
01947           // we only allow strings or IDs on the left hand side of an initialisation
01948           if (l->tk==LEX_STR) l->match(LEX_STR);
01949           else l->match(LEX_ID);
01950           l->match(':');
01951           if (execute) {
01952             CScriptVarLink *a = base(execute);
01953             contents->addChild(id, a->var);
01954             CLEAN(a);
01955           }
01956           // no need to clean here, as it will definitely be used
01957           if (l->tk != '}') l->match(',');
01958         }
01959 
01960         l->match('}');
01961         return new CScriptVarLink(contents);
01962     }
01963     if (l->tk=='[') {
01964         CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_ARRAY);
01965         /* JSON-style array */
01966         l->match('[');
01967         int idx = 0;
01968         while (l->tk != ']') {
01969           if (execute) {
01970             char idx_str[16]; // big enough for 2^32
01971             sprintf_s(idx_str, sizeof(idx_str), "%d",idx);
01972 
01973             CScriptVarLink *a = base(execute);
01974             contents->addChild(idx_str, a->var);
01975             CLEAN(a);
01976           }
01977           // no need to clean here, as it will definitely be used
01978           if (l->tk != ']') l->match(',');
01979           idx++;
01980         }
01981         l->match(']');
01982         return new CScriptVarLink(contents);
01983     }
01984     if (l->tk==LEX_R_FUNCTION) {
01985       CScriptVarLink *funcVar = parseFunctionDefinition();
01986         if (funcVar->name != TINYJS_TEMP_NAME)
01987           TRACE("Functions not defined at statement-level are not meant to have a name");
01988         return funcVar;
01989     }
01990     if (l->tk==LEX_R_NEW) {
01991       // new -> create a new object
01992       l->match(LEX_R_NEW);
01993       const string &className = l->tkStr;
01994       if (execute) {
01995         CScriptVarLink *objClassOrFunc = findInScopes(className);
01996         if (!objClassOrFunc) {
01997           TRACE("%s is not a valid class name", className.c_str());
01998           return new CScriptVarLink(new CScriptVar());
01999         }
02000         l->match(LEX_ID);
02001         CScriptVar *obj = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT);
02002         CScriptVarLink *objLink = new CScriptVarLink(obj);
02003         if (objClassOrFunc->var->isFunction()) {
02004           CLEAN(functionCall(execute, objClassOrFunc, obj));
02005         } else {
02006           obj->addChild(TINYJS_PROTOTYPE_CLASS, objClassOrFunc->var);
02007           if (l->tk == '(') {
02008             l->match('(');
02009             l->match(')');
02010           }
02011         }
02012         return objLink;
02013       } else {
02014         l->match(LEX_ID);
02015         if (l->tk == '(') {
02016           l->match('(');
02017           l->match(')');
02018         }
02019       }
02020     }
02021     // Nothing we can do here... just hope it's the end...
02022     l->match(LEX_EOF);
02023     return 0;
02024 }
02025 #else
02026 CScriptVarLink *CTinyJS::factor(bool &execute) {
02027     if (l->tk=='(') {
02028         LMATCH('(');
02029         CScriptVarLink *a = base(execute);
02030         LMATCH(')');
02031         return a;
02032     }
02033     if (l->tk==LEX_R_TRUE) {
02034         LMATCH(LEX_R_TRUE);
02035         return new CScriptVarLink(new CScriptVar(1));
02036     }
02037     if (l->tk==LEX_R_FALSE) {
02038         LMATCH(LEX_R_FALSE);
02039         return new CScriptVarLink(new CScriptVar(0));
02040     }
02041     if (l->tk==LEX_R_NULL) {
02042         LMATCH(LEX_R_NULL);
02043         return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NULL));
02044     }
02045     if (l->tk==LEX_R_UNDEFINED) {
02046         LMATCH(LEX_R_UNDEFINED);
02047         return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED));
02048     }
02049     if (l->tk==LEX_ID) {
02050         CScriptVarLink *a = execute ? findInScopes(l->tkStr) : new CScriptVarLink(new CScriptVar());
02051         //printf("0x%08X for %s at %s\n", (unsigned int)a, l->tkStr.c_str(), l->getPosition().c_str());
02052         /* The parent if we're executing a method call */
02053         CScriptVar *parent = 0;
02054 
02055         if (execute && !a) {
02056           /* Variable doesn't exist! JavaScript says we should create it
02057            * (we won't add it here. This is done in the assignment operator)*/
02058           a = new CScriptVarLink(new CScriptVar(), l->tkStr);
02059         }
02060         LMATCH(LEX_ID);
02061         while (l->tk=='(' || l->tk=='.' || l->tk=='[') {
02062             if (l->tk=='(') { // ------------------------------------- Function Call
02063                 a = functionCall(execute, a, parent);
02064             } else if (l->tk == '.') { // ------------------------------------- Record Access
02065                 LMATCH('.');
02066                 if (execute) {
02067                   const string &name = l->tkStr;
02068                   CScriptVarLink *child = a->var->findChild(name);
02069                   if (!child) child = findInParentClasses(a->var, name);
02070                   if (!child) {
02071                     /* if we haven't found this defined yet, use the built-in
02072                        'length' properly */
02073                     if (a->var->isArray() && name == "length") {
02074                       int l = a->var->getArrayLength();
02075                       child = new CScriptVarLink(new CScriptVar(l));
02076                     } else if (a->var->isString() && name == "length") {
02077                       int l = a->var->getString().size();
02078                       child = new CScriptVarLink(new CScriptVar(l));
02079                     } else {
02080                       child = a->var->addChild(name);
02081                     }
02082                   }
02083                   parent = a->var;
02084                   a = child;
02085                 }
02086                 LMATCH(LEX_ID);
02087             } else if (l->tk == '[') { // ------------------------------------- Array Access
02088                 LMATCH('[');
02089                 CScriptVarLink *index = base(execute);
02090                 LMATCH(']');
02091                 if (execute) {
02092                   CScriptVarLink *child = a->var->findChildOrCreate(index->var->getString());
02093                   parent = a->var;
02094                   a = child;
02095                 }
02096                 CLEAN(index);
02097             } else ASSERT(0);
02098         }
02099         return a;
02100     }
02101     if (l->tk==LEX_INT || l->tk==LEX_FLOAT) {
02102         CScriptVar *a = new CScriptVar(l->tkStr,
02103             ((l->tk==LEX_INT)?SCRIPTVAR_INTEGER:SCRIPTVAR_DOUBLE));
02104         LMATCH(l->tk);
02105         return new CScriptVarLink(a);
02106     }
02107     if (l->tk==LEX_STR) {
02108         CScriptVar *a = new CScriptVar(l->tkStr, SCRIPTVAR_STRING);
02109         LMATCH(LEX_STR);
02110         return new CScriptVarLink(a);
02111     }
02112     if (l->tk=='{') {
02113         CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT);
02114         /* JSON-style object definition */
02115         LMATCH('{');
02116         while (l->tk != '}') {
02117           string id = l->tkStr;
02118           // we only allow strings or IDs on the left hand side of an initialisation
02119           if (l->tk==LEX_STR) {
02120               LMATCH(LEX_STR);
02121           } else {
02122               LMATCH(LEX_ID);
02123           }
02124           LMATCH(':');
02125           if (execute) {
02126             CScriptVarLink *a = base(execute);
02127             contents->addChild(id, a->var);
02128             CLEAN(a);
02129           }
02130           // no need to clean here, as it will definitely be used
02131           if (l->tk != '}') LMATCH(',');
02132         }
02133 
02134         LMATCH('}');
02135         return new CScriptVarLink(contents);
02136     }
02137     if (l->tk=='[') {
02138         CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_ARRAY);
02139         /* JSON-style array */
02140         LMATCH('[');
02141         int idx = 0;
02142         while (l->tk != ']') {
02143           if (execute) {
02144             char idx_str[16]; // big enough for 2^32
02145             sprintf_s(idx_str, sizeof(idx_str), "%d",idx);
02146 
02147             CScriptVarLink *a = base(execute);
02148             contents->addChild(idx_str, a->var);
02149             CLEAN(a);
02150           }
02151           // no need to clean here, as it will definitely be used
02152           if (l->tk != ']') LMATCH(',');
02153           idx++;
02154         }
02155         LMATCH(']');
02156         return new CScriptVarLink(contents);
02157     }
02158     if (l->tk==LEX_R_FUNCTION) {
02159       CScriptVarLink *funcVar = parseFunctionDefinition();
02160         if (funcVar->name != TINYJS_TEMP_NAME)
02161           TRACE("Functions not defined at statement-level are not meant to have a name");
02162         return funcVar;
02163     }
02164     if (l->tk==LEX_R_NEW) {
02165       // new -> create a new object
02166       LMATCH(LEX_R_NEW);
02167       const string &className = l->tkStr;
02168       if (execute) {
02169         CScriptVarLink *objClassOrFunc = findInScopes(className);
02170         if (!objClassOrFunc) {
02171           TRACE("%s is not a valid class name", className.c_str());
02172           return new CScriptVarLink(new CScriptVar());
02173         }
02174         LMATCH(LEX_ID);
02175         CScriptVar *obj = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT);
02176         CScriptVarLink *objLink = new CScriptVarLink(obj);
02177         if (objClassOrFunc->var->isFunction()) {
02178           CLEAN(functionCall(execute, objClassOrFunc, obj));
02179         } else {
02180           obj->addChild(TINYJS_PROTOTYPE_CLASS, objClassOrFunc->var);
02181           if (l->tk == '(') {
02182             LMATCH('(');
02183             LMATCH(')');
02184           }
02185         }
02186         return objLink;
02187       } else {
02188         LMATCH(LEX_ID);
02189         if (l->tk == '(') {
02190           LMATCH('(');
02191           LMATCH(')');
02192         }
02193       }
02194     }
02195     // Nothing we can do here... just hope it's the end...
02196     LMATCH(LEX_EOF);
02197     return 0;
02198 }
02199 #endif
02200 
02201 CScriptVarLink *CTinyJS::unary(bool &execute) {
02202     CScriptVarLink *a;
02203     if (l->tk=='!') {
02204 #ifndef MBED
02205         l->match('!'); // binary not
02206 #else
02207         LMATCH('!'); // binary not
02208 #endif
02209         a = factor(execute);
02210         if (execute) {
02211             CScriptVar zero(0);
02212             CScriptVar *res = a->var->mathsOp(&zero, LEX_EQUAL);
02213             CREATE_LINK(a, res);
02214         }
02215     } else
02216         a = factor(execute);
02217     return a;
02218 }
02219 
02220 CScriptVarLink *CTinyJS::term(bool &execute) {
02221     CScriptVarLink *a = unary(execute);
02222     while (l->tk=='*' || l->tk=='/' || l->tk=='%') {
02223         int op = l->tk;
02224 #ifndef MBED
02225         l->match(l->tk);
02226 #else
02227         LMATCH(l->tk);
02228 #endif
02229         CScriptVarLink *b = unary(execute);
02230         if (execute) {
02231             CScriptVar *res = a->var->mathsOp(b->var, op);
02232             CREATE_LINK(a, res);
02233         }
02234         CLEAN(b);
02235     }
02236     return a;
02237 }
02238 
02239 CScriptVarLink *CTinyJS::expression(bool &execute) {
02240     bool negate = false;
02241     if (l->tk=='-') {
02242 #ifndef MBED
02243         l->match('-');
02244 #else
02245         LMATCH('-');
02246 #endif
02247         negate = true;
02248     }
02249     CScriptVarLink *a = term(execute);
02250     if (negate) {
02251         CScriptVar zero(0);
02252         CScriptVar *res = zero.mathsOp(a->var, '-');
02253         CREATE_LINK(a, res);
02254     }
02255 
02256     while (l->tk=='+' || l->tk=='-' ||
02257         l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) {
02258         int op = l->tk;
02259 #ifndef MBED
02260         l->match(l->tk);
02261 #else
02262         LMATCH(l->tk);
02263 #endif
02264         if (op==LEX_PLUSPLUS || op==LEX_MINUSMINUS) {
02265             if (execute) {
02266                 CScriptVar one(1);
02267                 CScriptVar *res = a->var->mathsOp(&one, op==LEX_PLUSPLUS ? '+' : '-');
02268                 CScriptVarLink *oldValue = new CScriptVarLink(a->var);
02269                 // in-place add/subtract
02270                 a->replaceWith(res);
02271                 CLEAN(a);
02272                 a = oldValue;
02273             }
02274         } else {
02275             CScriptVarLink *b = term(execute);
02276             if (execute) {
02277                 // not in-place, so just replace
02278                 CScriptVar *res = a->var->mathsOp(b->var, op);
02279                 CREATE_LINK(a, res);
02280             }
02281             CLEAN(b);
02282         }
02283     }
02284     return a;
02285 }
02286 
02287 CScriptVarLink *CTinyJS::shift(bool &execute) {
02288   CScriptVarLink *a = expression(execute);
02289   if (l->tk==LEX_LSHIFT || l->tk==LEX_RSHIFT || l->tk==LEX_RSHIFTUNSIGNED) {
02290     int op = l->tk;
02291 #ifndef MBED
02292     l->match(op);
02293 #else
02294     LMATCH(op);
02295 #endif
02296     CScriptVarLink *b = base(execute);
02297     int shift = execute ? b->var->getInt() : 0;
02298     CLEAN(b);
02299     if (execute) {
02300       if (op==LEX_LSHIFT) a->var->setInt(a->var->getInt() << shift);
02301       if (op==LEX_RSHIFT) a->var->setInt(a->var->getInt() >> shift);
02302       if (op==LEX_RSHIFTUNSIGNED) a->var->setInt(((unsigned int)a->var->getInt()) >> shift);
02303     }
02304   }
02305   return a;
02306 }
02307 
02308 CScriptVarLink *CTinyJS::condition(bool &execute) {
02309     CScriptVarLink *a = shift(execute);
02310     CScriptVarLink *b;
02311     while (l->tk==LEX_EQUAL || l->tk==LEX_NEQUAL ||
02312            l->tk==LEX_TYPEEQUAL || l->tk==LEX_NTYPEEQUAL ||
02313            l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL ||
02314            l->tk=='<' || l->tk=='>') {
02315         int op = l->tk;
02316 #ifndef MBED
02317         l->match(l->tk);
02318 #else
02319         LMATCH(l->tk);
02320 #endif
02321         b = shift(execute);
02322         if (execute) {
02323             CScriptVar *res = a->var->mathsOp(b->var, op);
02324             CREATE_LINK(a,res);
02325         }
02326         CLEAN(b);
02327     }
02328     return a;
02329 }
02330 
02331 CScriptVarLink *CTinyJS::logic(bool &execute) {
02332     CScriptVarLink *a = condition(execute);
02333     CScriptVarLink *b;
02334     while (l->tk=='&' || l->tk=='|' || l->tk=='^' || l->tk==LEX_ANDAND || l->tk==LEX_OROR) {
02335         bool noexecute = false;
02336         int op = l->tk;
02337 #ifndef MBED
02338         l->match(l->tk);
02339 #else
02340         LMATCH(l->tk);
02341 #endif
02342         bool shortCircuit = false;
02343         bool boolean = false;
02344         // if we have short-circuit ops, then if we know the outcome
02345         // we don't bother to execute the other op. Even if not
02346         // we need to tell mathsOp it's an & or |
02347         if (op==LEX_ANDAND) {
02348             op = '&';
02349             shortCircuit = !a->var->getBool();
02350             boolean = true;
02351         } else if (op==LEX_OROR) {
02352             op = '|';
02353             shortCircuit = a->var->getBool();
02354             boolean = true;
02355         }
02356         b = condition(shortCircuit ? noexecute : execute);
02357         if (execute && !shortCircuit) {
02358             if (boolean) {
02359               CScriptVar *newa = new CScriptVar(a->var->getBool());
02360               CScriptVar *newb = new CScriptVar(b->var->getBool());
02361               CREATE_LINK(a, newa);
02362               CREATE_LINK(b, newb);
02363             }
02364             CScriptVar *res = a->var->mathsOp(b->var, op);
02365             CREATE_LINK(a, res);
02366         }
02367         CLEAN(b);
02368     }
02369     return a;
02370 }
02371 
02372 CScriptVarLink *CTinyJS::ternary(bool &execute) {
02373   CScriptVarLink *lhs = logic(execute);
02374   bool noexec = false;
02375   if (l->tk=='?') {
02376 #ifndef MBED
02377     l->match('?');
02378 #else
02379     LMATCH('?');
02380 #endif
02381     if (!execute) {
02382       CLEAN(lhs);
02383       CLEAN(base(noexec));
02384 #ifndef MBED
02385       l->match(':');
02386 #else
02387       LMATCH(':');
02388 #endif
02389       CLEAN(base(noexec));
02390     } else {
02391       bool first = lhs->var->getBool();
02392       CLEAN(lhs);
02393       if (first) {
02394         lhs = base(execute);
02395 #ifndef MBED
02396         l->match(':');
02397 #else
02398         LMATCH(':');
02399 #endif
02400         CLEAN(base(noexec));
02401       } else {
02402         CLEAN(base(noexec));
02403 #ifndef MBED
02404         l->match(':');
02405 #else
02406         LMATCH(':');
02407 #endif
02408         lhs = base(execute);
02409       }
02410     }
02411   }
02412 
02413   return lhs;
02414 }
02415 
02416 CScriptVarLink *CTinyJS::base(bool &execute) {
02417     CScriptVarLink *lhs = ternary(execute);
02418     if (l->tk=='=' || l->tk==LEX_PLUSEQUAL || l->tk==LEX_MINUSEQUAL) {
02419         /* If we're assigning to this and we don't have a parent,
02420          * add it to the symbol table root as per JavaScript. */
02421         if (execute && !lhs->owned) {
02422           if (lhs->name.length()>0) {
02423             CScriptVarLink *realLhs = root->addChildNoDup(lhs->name, lhs->var);
02424             CLEAN(lhs);
02425             lhs = realLhs;
02426           } else
02427             TRACE("Trying to assign to an un-named type\n");
02428         }
02429 
02430         int op = l->tk;
02431 #ifndef MBED
02432         l->match(l->tk);
02433 #else
02434         LMATCH(l->tk);
02435 #endif
02436         CScriptVarLink *rhs = base(execute);
02437         if (execute) {
02438             if (op=='=') {
02439                 lhs->replaceWith(rhs);
02440             } else if (op==LEX_PLUSEQUAL) {
02441                 CScriptVar *res = lhs->var->mathsOp(rhs->var, '+');
02442                 lhs->replaceWith(res);
02443             } else if (op==LEX_MINUSEQUAL) {
02444                 CScriptVar *res = lhs->var->mathsOp(rhs->var, '-');
02445                 lhs->replaceWith(res);
02446             } else ASSERT(0);
02447         }
02448         CLEAN(rhs);
02449     }
02450     return lhs;
02451 }
02452 
02453 #ifndef MBED
02454 void CTinyJS::block(bool &execute) {
02455     l->match('{');
02456     if (execute) {
02457       while (l->tk && l->tk!='}')
02458         statement(execute);
02459       l->match('}');
02460     } else {
02461       // fast skip of blocks
02462       int brackets = 1;
02463       while (l->tk && brackets) {
02464         if (l->tk == '{') brackets++;
02465         if (l->tk == '}') brackets--;
02466         l->match(l->tk);
02467       }
02468     }
02469 }
02470 #else
02471 void CTinyJS::block(bool &execute) {
02472     LMATCH_VOID('{');
02473     if (execute) {
02474       while (l->tk && l->tk!='}')
02475         statement(execute);
02476       LMATCH_VOID('}');
02477     } else {
02478       // fast skip of blocks
02479       int brackets = 1;
02480       while (l->tk && brackets) {
02481         if (l->tk == '{') brackets++;
02482         if (l->tk == '}') brackets--;
02483         LMATCH_VOID(l->tk);
02484       }
02485     }
02486 }
02487 #endif
02488 
02489 #ifndef MBED
02490 void CTinyJS::statement(bool &execute) {
02491     if (l->tk==LEX_ID ||
02492         l->tk==LEX_INT ||
02493         l->tk==LEX_FLOAT ||
02494         l->tk==LEX_STR ||
02495         l->tk=='-') {
02496         /* Execute a simple statement that only contains basic arithmetic... */
02497         CLEAN(base(execute));
02498         l->match(';');
02499     } else if (l->tk=='{') {
02500         /* A block of code */
02501         block(execute);
02502     } else if (l->tk==';') {
02503         /* Empty statement - to allow things like ;;; */
02504         l->match(';');
02505     } else if (l->tk==LEX_R_VAR) {
02506         /* variable creation. TODO - we need a better way of parsing the left
02507          * hand side. Maybe just have a flag called can_create_var that we
02508          * set and then we parse as if we're doing a normal equals.*/
02509         l->match(LEX_R_VAR);
02510         while (l->tk != ';') {
02511           CScriptVarLink *a = 0;
02512           if (execute)
02513             a = scopes.back()->findChildOrCreate(l->tkStr);
02514           l->match(LEX_ID);
02515           // now do stuff defined with dots
02516           while (l->tk == '.') {
02517               l->match('.');
02518               if (execute) {
02519                   CScriptVarLink *lastA = a;
02520                   a = lastA->var->findChildOrCreate(l->tkStr);
02521               }
02522               l->match(LEX_ID);
02523           }
02524           // sort out initialiser
02525           if (l->tk == '=') {
02526               l->match('=');
02527               CScriptVarLink *var = base(execute);
02528               if (execute)
02529                   a->replaceWith(var);
02530               CLEAN(var);
02531           }
02532           if (l->tk != ';')
02533             l->match(',');
02534         }       
02535         l->match(';');
02536     } else if (l->tk==LEX_R_IF) {
02537         l->match(LEX_R_IF);
02538         l->match('(');
02539         CScriptVarLink *var = base(execute);
02540         l->match(')');
02541         bool cond = execute && var->var->getBool();
02542         CLEAN(var);
02543         bool noexecute = false; // because we need to be abl;e to write to it
02544         statement(cond ? execute : noexecute);
02545         if (l->tk==LEX_R_ELSE) {
02546             l->match(LEX_R_ELSE);
02547             statement(cond ? noexecute : execute);
02548         }
02549     } else if (l->tk==LEX_R_WHILE) {
02550         // We do repetition by pulling out the string representing our statement
02551         // there's definitely some opportunity for optimisation here
02552         l->match(LEX_R_WHILE);
02553         l->match('(');
02554         int whileCondStart = l->tokenStart;
02555         bool noexecute = false;
02556         CScriptVarLink *cond = base(execute);
02557         bool loopCond = execute && cond->var->getBool();
02558         CLEAN(cond);
02559         CScriptLex *whileCond = l->getSubLex(whileCondStart);
02560         l->match(')');
02561         int whileBodyStart = l->tokenStart;
02562         statement(loopCond ? execute : noexecute);
02563         CScriptLex *whileBody = l->getSubLex(whileBodyStart);
02564         CScriptLex *oldLex = l;
02565         int loopCount = TINYJS_LOOP_MAX_ITERATIONS;
02566         while (loopCond && loopCount-->0) {
02567             whileCond->reset();
02568             l = whileCond;
02569             cond = base(execute);
02570             loopCond = execute && cond->var->getBool();
02571             CLEAN(cond);
02572             if (loopCond) {
02573                 whileBody->reset();
02574                 l = whileBody;
02575                 statement(execute);
02576             }
02577         }
02578         l = oldLex;
02579         delete whileCond;
02580         delete whileBody;
02581 
02582         if (loopCount<=0) {
02583             root->trace();
02584             TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition().c_str());
02585             throw new CScriptException("LOOP_ERROR");
02586         }
02587     } else if (l->tk==LEX_R_FOR) {
02588         l->match(LEX_R_FOR);
02589         l->match('(');
02590         statement(execute); // initialisation
02591         //l->match(';');
02592         int forCondStart = l->tokenStart;
02593         bool noexecute = false;
02594         CScriptVarLink *cond = base(execute); // condition
02595         bool loopCond = execute && cond->var->getBool();
02596         CLEAN(cond);
02597         CScriptLex *forCond = l->getSubLex(forCondStart);
02598         l->match(';');
02599         int forIterStart = l->tokenStart;
02600         CLEAN(base(noexecute)); // iterator
02601         CScriptLex *forIter = l->getSubLex(forIterStart);
02602         l->match(')');
02603         int forBodyStart = l->tokenStart;
02604         statement(loopCond ? execute : noexecute);
02605         CScriptLex *forBody = l->getSubLex(forBodyStart);
02606         CScriptLex *oldLex = l;
02607         if (loopCond) {
02608             forIter->reset();
02609             l = forIter;
02610             CLEAN(base(execute));
02611         }
02612         int loopCount = TINYJS_LOOP_MAX_ITERATIONS;
02613         while (execute && loopCond && loopCount-->0) {
02614             forCond->reset();
02615             l = forCond;
02616             cond = base(execute);
02617             loopCond = cond->var->getBool();
02618             CLEAN(cond);
02619             if (execute && loopCond) {
02620                 forBody->reset();
02621                 l = forBody;
02622                 statement(execute);
02623             }
02624             if (execute && loopCond) {
02625                 forIter->reset();
02626                 l = forIter;
02627                 CLEAN(base(execute));
02628             }
02629         }
02630         l = oldLex;
02631         delete forCond;
02632         delete forIter;
02633         delete forBody;
02634         if (loopCount<=0) {
02635             root->trace();
02636             TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition().c_str());
02637             throw new CScriptException("LOOP_ERROR");
02638         }
02639     } else if (l->tk==LEX_R_RETURN) {
02640         l->match(LEX_R_RETURN);
02641         CScriptVarLink *result = 0;
02642         if (l->tk != ';')
02643           result = base(execute);
02644         if (execute) {
02645           CScriptVarLink *resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR);
02646           if (resultVar)
02647             resultVar->replaceWith(result);
02648           else
02649             TRACE("RETURN statement, but not in a function.\n");
02650           execute = false;
02651         }
02652         CLEAN(result);
02653         l->match(';');
02654     } else if (l->tk==LEX_R_FUNCTION) {
02655         CScriptVarLink *funcVar = parseFunctionDefinition();
02656         if (execute) {
02657           if (funcVar->name == TINYJS_TEMP_NAME)
02658             TRACE("Functions defined at statement-level are meant to have a name\n");
02659           else
02660             scopes.back()->addChildNoDup(funcVar->name, funcVar->var);
02661         }
02662         CLEAN(funcVar);
02663     } else l->match(LEX_EOF);
02664 }
02665 
02666 #else
02667 
02668 void CTinyJS::statement(bool &execute) {
02669     if (l->tk==LEX_ID ||
02670         l->tk==LEX_INT ||
02671         l->tk==LEX_FLOAT ||
02672         l->tk==LEX_STR ||
02673         l->tk=='-') {
02674         /* Execute a simple statement that only contains basic arithmetic... */
02675         CLEAN(base(execute));
02676         LMATCH_VOID(';');
02677     } else if (l->tk=='{') {
02678         /* A block of code */
02679         block(execute);
02680     } else if (l->tk==';') {
02681         /* Empty statement - to allow things like ;;; */
02682         LMATCH_VOID(';');
02683     } else if (l->tk==LEX_R_VAR) {
02684         /* variable creation. TODO - we need a better way of parsing the left
02685          * hand side. Maybe just have a flag called can_create_var that we
02686          * set and then we parse as if we're doing a normal equals.*/
02687         LMATCH_VOID(LEX_R_VAR);
02688         while (l->tk != ';') {
02689           CScriptVarLink *a = 0;
02690           if (execute)
02691             a = scopes.back()->findChildOrCreate(l->tkStr);
02692           LMATCH_VOID(LEX_ID);
02693           // now do stuff defined with dots
02694           while (l->tk == '.') {
02695               LMATCH_VOID('.');
02696               if (execute) {
02697                   CScriptVarLink *lastA = a;
02698                   a = lastA->var->findChildOrCreate(l->tkStr);
02699               }
02700               LMATCH_VOID(LEX_ID);
02701           }
02702           // sort out initialiser
02703           if (l->tk == '=') {
02704               LMATCH_VOID('=');
02705               CScriptVarLink *var = base(execute);
02706               if (execute)
02707                   a->replaceWith(var);
02708               CLEAN(var);
02709           }
02710           if (l->tk != ';')
02711             LMATCH_VOID(',');
02712         }       
02713         LMATCH_VOID(';');
02714     } else if (l->tk==LEX_R_IF) {
02715         LMATCH_VOID(LEX_R_IF);
02716         LMATCH_VOID('(');
02717         CScriptVarLink *var = base(execute);
02718         LMATCH_VOID(')');
02719         bool cond = execute && var->var->getBool();
02720         CLEAN(var);
02721         bool noexecute = false; // because we need to be abl;e to write to it
02722         statement(cond ? execute : noexecute);
02723         if (l->tk==LEX_R_ELSE) {
02724             LMATCH_VOID(LEX_R_ELSE);
02725             statement(cond ? noexecute : execute);
02726         }
02727     } else if (l->tk==LEX_R_WHILE) {
02728         // We do repetition by pulling out the string representing our statement
02729         // there's definitely some opportunity for optimisation here
02730         LMATCH_VOID(LEX_R_WHILE);
02731         LMATCH_VOID('(');
02732         int whileCondStart = l->tokenStart;
02733         bool noexecute = false;
02734         CScriptVarLink *cond = base(execute);
02735         bool loopCond = execute && cond->var->getBool();
02736         CLEAN(cond);
02737         CScriptLex *whileCond = l->getSubLex(whileCondStart);
02738         LMATCH_VOID(')');
02739         int whileBodyStart = l->tokenStart;
02740         statement(loopCond ? execute : noexecute);
02741         CScriptLex *whileBody = l->getSubLex(whileBodyStart);
02742         CScriptLex *oldLex = l;
02743         int loopCount = TINYJS_LOOP_MAX_ITERATIONS;
02744         while (loopCond && loopCount-->0) {
02745             whileCond->reset();
02746             l = whileCond;
02747             cond = base(execute);
02748             loopCond = execute && cond->var->getBool();
02749             CLEAN(cond);
02750             if (loopCond) {
02751                 whileBody->reset();
02752                 l = whileBody;
02753                 statement(execute);
02754             }
02755         }
02756         l = oldLex;
02757         delete whileCond;
02758         delete whileBody;
02759 
02760         if (loopCount<=0) {
02761             root->trace();
02762             TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition().c_str());
02763             mbedErrorFlag = 1;
02764             mbedErrorMessage = "LOOP_ERROR";
02765             return;
02766         }
02767     } else if (l->tk==LEX_R_FOR) {
02768         LMATCH_VOID(LEX_R_FOR);
02769         LMATCH_VOID('(');
02770         statement(execute); // initialisation
02771         //LMATCH(';');
02772         int forCondStart = l->tokenStart;
02773         bool noexecute = false;
02774         CScriptVarLink *cond = base(execute); // condition
02775         bool loopCond = execute && cond->var->getBool();
02776         CLEAN(cond);
02777         CScriptLex *forCond = l->getSubLex(forCondStart);
02778         LMATCH_VOID(';');
02779         int forIterStart = l->tokenStart;
02780         CLEAN(base(noexecute)); // iterator
02781         CScriptLex *forIter = l->getSubLex(forIterStart);
02782         LMATCH_VOID(')');
02783         int forBodyStart = l->tokenStart;
02784         statement(loopCond ? execute : noexecute);
02785         CScriptLex *forBody = l->getSubLex(forBodyStart);
02786         CScriptLex *oldLex = l;
02787         if (loopCond) {
02788             forIter->reset();
02789             l = forIter;
02790             CLEAN(base(execute));
02791         }
02792         int loopCount = TINYJS_LOOP_MAX_ITERATIONS;
02793         while (execute && loopCond && loopCount-->0) {
02794             forCond->reset();
02795             l = forCond;
02796             cond = base(execute);
02797             loopCond = cond->var->getBool();
02798             CLEAN(cond);
02799             if (execute && loopCond) {
02800                 forBody->reset();
02801                 l = forBody;
02802                 statement(execute);
02803             }
02804             if (execute && loopCond) {
02805                 forIter->reset();
02806                 l = forIter;
02807                 CLEAN(base(execute));
02808             }
02809         }
02810         l = oldLex;
02811         delete forCond;
02812         delete forIter;
02813         delete forBody;
02814         if (loopCount<=0) {
02815             root->trace();
02816             TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition().c_str());
02817             mbedErrorFlag = 1;
02818             mbedErrorMessage = "LOOP_ERROR";
02819             return;
02820         }
02821     } else if (l->tk==LEX_R_RETURN) {
02822         LMATCH_VOID(LEX_R_RETURN);
02823         CScriptVarLink *result = 0;
02824         if (l->tk != ';')
02825           result = base(execute);
02826         if (execute) {
02827           CScriptVarLink *resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR);
02828           if (resultVar)
02829             resultVar->replaceWith(result);
02830           else
02831             TRACE("RETURN statement, but not in a function.\n");
02832           execute = false;
02833         }
02834         CLEAN(result);
02835         LMATCH_VOID(';');
02836     } else if (l->tk==LEX_R_FUNCTION) {
02837         CScriptVarLink *funcVar = parseFunctionDefinition();
02838         if (execute) {
02839           if (funcVar->name == TINYJS_TEMP_NAME)
02840             TRACE("Functions defined at statement-level are meant to have a name\n");
02841           else
02842             scopes.back()->addChildNoDup(funcVar->name, funcVar->var);
02843         }
02844         CLEAN(funcVar);
02845     } else LMATCH_VOID(LEX_EOF);
02846 }
02847 #endif
02848 
02849 /// Get the given variable specified by a path (var1.var2.etc), or return 0
02850 CScriptVar *CTinyJS::getScriptVariable(const string &path) {
02851     // traverse path
02852     size_t prevIdx = 0;
02853     size_t thisIdx = path.find('.');
02854     if (thisIdx == string::npos) thisIdx = path.length();
02855     CScriptVar *var = root;
02856     while (var && prevIdx<path.length()) {
02857         string el = path.substr(prevIdx, thisIdx-prevIdx);
02858         CScriptVarLink *varl = var->findChild(el);
02859         var = varl?varl->var:0;
02860         prevIdx = thisIdx+1;
02861         thisIdx = path.find('.', prevIdx);
02862         if (thisIdx == string::npos) thisIdx = path.length();
02863     }
02864     return var;
02865 }
02866 
02867 /// Get the value of the given variable, or return 0
02868 const string *CTinyJS::getVariable(const string &path) {
02869     CScriptVar *var = getScriptVariable(path);
02870     // return result
02871     if (var)
02872         return &var->getString();
02873     else
02874         return 0;
02875 }
02876 
02877 /// set the value of the given variable, return trur if it exists and gets set
02878 bool CTinyJS::setVariable(const std::string &path, const std::string &varData) {
02879     CScriptVar *var = getScriptVariable(path);
02880     // return result
02881     if (var) {
02882         if (var->isInt())
02883             var->setInt((int)strtol(varData.c_str(),0,0));
02884         else if (var->isDouble())
02885             var->setDouble(strtod(varData.c_str(),0));
02886         else
02887             var->setString(varData.c_str());
02888         return true;
02889     }    
02890     else
02891         return false;
02892 }
02893 
02894 /// Finds a child, looking recursively up the scopes
02895 CScriptVarLink *CTinyJS::findInScopes(const std::string &childName) {
02896     for (int s=scopes.size()-1;s>=0;s--) {
02897       CScriptVarLink *v = scopes[s]->findChild(childName);
02898       if (v) return v;
02899     }
02900     return NULL;
02901 
02902 }
02903 
02904 /// Look up in any parent classes of the given object
02905 CScriptVarLink *CTinyJS::findInParentClasses(CScriptVar *object, const std::string &name) {
02906     // Look for links to actual parent classes
02907     CScriptVarLink *parentClass = object->findChild(TINYJS_PROTOTYPE_CLASS);
02908     while (parentClass) {
02909       CScriptVarLink *implementation = parentClass->var->findChild(name);
02910       if (implementation) return implementation;
02911       parentClass = parentClass->var->findChild(TINYJS_PROTOTYPE_CLASS);
02912     }
02913     // else fake it for strings and finally objects
02914     if (object->isString()) {
02915       CScriptVarLink *implementation = stringClass->findChild(name);
02916       if (implementation) return implementation;
02917     }
02918     if (object->isArray()) {
02919       CScriptVarLink *implementation = arrayClass->findChild(name);
02920       if (implementation) return implementation;
02921     }
02922     CScriptVarLink *implementation = objectClass->findChild(name);
02923     if (implementation) return implementation;
02924 
02925     return 0;
02926 }
02927