Takehisa Oneta
/
TinyJS
TinyJS on mbed. TinyJS is very simple JavaScript engine.
Embed:
(wiki syntax)
Show/hide line numbers
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
Generated on Tue Jul 12 2022 21:01:11 by 1.7.2