Takehisa Oneta
/
TinyJS
TinyJS on mbed. TinyJS is very simple JavaScript engine.
Embed:
(wiki syntax)
Show/hide line numbers
TinyJS.h
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 * TinyJS for mbed. 00030 * 00031 * Authored by Takehisa Oneta (ohneta@gmail.com) 00032 * 10th Jan. 2013 00033 */ 00034 00035 #ifndef TINYJS_H 00036 #define TINYJS_H 00037 00038 // If defined, this keeps a note of all calls and where from in memory. This is slower, but good for debugging 00039 #define TINYJS_CALL_STACK 00040 00041 #ifdef _WIN32 00042 #ifdef _DEBUG 00043 #define _CRTDBG_MAP_ALLOC 00044 #include <stdlib.h> 00045 #include <crtdbg.h> 00046 #endif 00047 #endif 00048 #include <string> 00049 #include <vector> 00050 00051 #ifndef TRACE 00052 #define TRACE printf 00053 #endif // TRACE 00054 00055 #ifndef MBED 00056 #define MBED 1 00057 #endif // MBED 00058 00059 #define DEBUG_MEMORY 0 00060 00061 00062 const int TINYJS_LOOP_MAX_ITERATIONS = 8192; 00063 00064 enum LEX_TYPES { 00065 LEX_EOF = 0, 00066 LEX_ID = 256, 00067 LEX_INT, 00068 LEX_FLOAT, 00069 LEX_STR, 00070 00071 LEX_EQUAL, 00072 LEX_TYPEEQUAL, 00073 LEX_NEQUAL, 00074 LEX_NTYPEEQUAL, 00075 LEX_LEQUAL, 00076 LEX_LSHIFT, 00077 LEX_LSHIFTEQUAL, 00078 LEX_GEQUAL, 00079 LEX_RSHIFT, 00080 LEX_RSHIFTUNSIGNED, 00081 LEX_RSHIFTEQUAL, 00082 LEX_PLUSEQUAL, 00083 LEX_MINUSEQUAL, 00084 LEX_PLUSPLUS, 00085 LEX_MINUSMINUS, 00086 LEX_ANDEQUAL, 00087 LEX_ANDAND, 00088 LEX_OREQUAL, 00089 LEX_OROR, 00090 LEX_XOREQUAL, 00091 // reserved words 00092 #define LEX_R_LIST_START LEX_R_IF 00093 LEX_R_IF, 00094 LEX_R_ELSE, 00095 LEX_R_DO, 00096 LEX_R_WHILE, 00097 LEX_R_FOR, 00098 LEX_R_BREAK, 00099 LEX_R_CONTINUE, 00100 LEX_R_FUNCTION, 00101 LEX_R_RETURN, 00102 LEX_R_VAR, 00103 LEX_R_TRUE, 00104 LEX_R_FALSE, 00105 LEX_R_NULL, 00106 LEX_R_UNDEFINED, 00107 LEX_R_NEW, 00108 00109 LEX_R_LIST_END /* always the last entry */ 00110 }; 00111 00112 enum SCRIPTVAR_FLAGS { 00113 SCRIPTVAR_UNDEFINED = 0, 00114 SCRIPTVAR_FUNCTION = 1, 00115 SCRIPTVAR_OBJECT = 2, 00116 SCRIPTVAR_ARRAY = 4, 00117 SCRIPTVAR_DOUBLE = 8, // floating point double 00118 SCRIPTVAR_INTEGER = 16, // integer number 00119 SCRIPTVAR_STRING = 32, // string 00120 SCRIPTVAR_NULL = 64, // it seems null is its own data type 00121 00122 SCRIPTVAR_NATIVE = 128, // to specify this is a native function 00123 SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NULL | 00124 SCRIPTVAR_DOUBLE | 00125 SCRIPTVAR_INTEGER, 00126 SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_DOUBLE | 00127 SCRIPTVAR_INTEGER | 00128 SCRIPTVAR_STRING | 00129 SCRIPTVAR_FUNCTION | 00130 SCRIPTVAR_OBJECT | 00131 SCRIPTVAR_ARRAY | 00132 SCRIPTVAR_NULL, 00133 00134 }; 00135 00136 #define TINYJS_RETURN_VAR "return" 00137 #define TINYJS_PROTOTYPE_CLASS "prototype" 00138 #define TINYJS_TEMP_NAME "" 00139 #define TINYJS_BLANK_DATA "" 00140 00141 /// convert the given string into a quoted string suitable for javascript 00142 std::string getJSString(const std::string &str); 00143 00144 class CScriptException { 00145 public: 00146 std::string text; 00147 CScriptException(const std::string &exceptionText); 00148 }; 00149 00150 class CScriptLex 00151 { 00152 public: 00153 CScriptLex(const std::string &input); 00154 CScriptLex(CScriptLex *owner, int startChar, int endChar); 00155 ~CScriptLex(void); 00156 00157 char currCh, nextCh; 00158 int tk; ///< The type of the token that we have 00159 int tokenStart; ///< Position in the data at the beginning of the token we have here 00160 int tokenEnd; ///< Position in the data at the last character of the token we have here 00161 int tokenLastEnd; ///< Position in the data at the last character of the last token 00162 std::string tkStr; ///< Data contained in the token we have here 00163 00164 void match(int expected_tk); ///< Lexical match wotsit 00165 static std::string getTokenStr(int token); ///< Get the string representation of the given token 00166 void reset(); ///< Reset this lex so we can start again 00167 00168 std::string getSubString(int pos); ///< Return a sub-string from the given position up until right now 00169 CScriptLex *getSubLex(int lastPosition); ///< Return a sub-lexer from the given position up until right now 00170 00171 std::string getPosition(int pos=-1); ///< Return a string representing the position in lines and columns of the character pos given 00172 00173 protected: 00174 /* When we go into a loop, we use getSubLex to get a lexer for just the sub-part of the 00175 relevant string. This doesn't re-allocate and copy the string, but instead copies 00176 the data pointer and sets dataOwned to false, and dataStart/dataEnd to the relevant things. */ 00177 char *data; ///< Data string to get tokens from 00178 int dataStart, dataEnd; ///< Start and end position in data string 00179 bool dataOwned; ///< Do we own this data string? 00180 00181 int dataPos; ///< Position in data (we CAN go past the end of the string here) 00182 00183 void getNextCh(); 00184 void getNextToken(); ///< Get the text token from our text string 00185 }; 00186 00187 class CScriptVar; 00188 00189 typedef void (*JSCallback)(CScriptVar *var, void *userdata); 00190 00191 class CScriptVarLink 00192 { 00193 public: 00194 std::string name; 00195 CScriptVarLink *nextSibling; 00196 CScriptVarLink *prevSibling; 00197 CScriptVar *var; 00198 bool owned; 00199 00200 CScriptVarLink(CScriptVar *var, const std::string &name = TINYJS_TEMP_NAME); 00201 CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor 00202 ~CScriptVarLink(); 00203 void replaceWith(CScriptVar *newVar); ///< Replace the Variable pointed to 00204 void replaceWith(CScriptVarLink *newVar); ///< Replace the Variable pointed to (just dereferences) 00205 int getIntName(); ///< Get the name as an integer (for arrays) 00206 void setIntName(int n); ///< Set the name as an integer (for arrays) 00207 }; 00208 00209 /// Variable class (containing a doubly-linked list of children) 00210 class CScriptVar 00211 { 00212 public: 00213 CScriptVar(); ///< Create undefined 00214 CScriptVar(const std::string &varData, int varFlags); ///< User defined 00215 CScriptVar(const std::string &str); ///< Create a string 00216 CScriptVar(double varData); 00217 CScriptVar(int val); 00218 ~CScriptVar(void); 00219 00220 CScriptVar *getReturnVar(); ///< If this is a function, get the result value (for use by native functions) 00221 void setReturnVar(CScriptVar *var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() 00222 CScriptVar *getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions) 00223 00224 CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 00225 CScriptVarLink *findChildOrCreate(const std::string &childName, int varFlags=SCRIPTVAR_UNDEFINED); ///< Tries to find a child with the given name, or will create it with the given flags 00226 CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) 00227 CScriptVarLink *addChild(const std::string &childName, CScriptVar *child=NULL); 00228 CScriptVarLink *addChildNoDup(const std::string &childName, CScriptVar *child=NULL); ///< add a child overwriting any with the same name 00229 void removeChild(CScriptVar *child); 00230 void removeLink(CScriptVarLink *link); ///< Remove a specific link (this is faster than finding via a child) 00231 void removeAllChildren(); 00232 CScriptVar *getArrayIndex(int idx); ///< The the value at an array index 00233 void setArrayIndex(int idx, CScriptVar *value); ///< Set the value at an array index 00234 int getArrayLength(); ///< If this is an array, return the number of items in it (else 0) 00235 int getChildren(); ///< Get the number of children 00236 00237 int getInt(); 00238 bool getBool() { return getInt() != 0; } 00239 double getDouble(); 00240 const std::string &getString(); 00241 std::string getParsableString(); ///< get Data as a parsable javascript string 00242 void setInt(int num); 00243 void setDouble(double val); 00244 void setString(const std::string &str); 00245 void setUndefined(); 00246 void setArray(); 00247 bool equals(CScriptVar *v); 00248 00249 bool isInt() { return (flags&SCRIPTVAR_INTEGER)!=0; } 00250 bool isDouble() { return (flags&SCRIPTVAR_DOUBLE)!=0; } 00251 bool isString() { return (flags&SCRIPTVAR_STRING)!=0; } 00252 bool isNumeric() { return (flags&SCRIPTVAR_NUMERICMASK)!=0; } 00253 bool isFunction() { return (flags&SCRIPTVAR_FUNCTION)!=0; } 00254 bool isObject() { return (flags&SCRIPTVAR_OBJECT)!=0; } 00255 bool isArray() { return (flags&SCRIPTVAR_ARRAY)!=0; } 00256 bool isNative() { return (flags&SCRIPTVAR_NATIVE)!=0; } 00257 bool isUndefined() { return (flags & SCRIPTVAR_VARTYPEMASK) == SCRIPTVAR_UNDEFINED; } 00258 bool isNull() { return (flags & SCRIPTVAR_NULL)!=0; } 00259 bool isBasic() { return firstChild==0; } ///< Is this *not* an array/object/etc 00260 00261 CScriptVar *mathsOp(CScriptVar *b, int op); ///< do a maths op with another script variable 00262 void copyValue(CScriptVar *val); ///< copy the value from the value given 00263 CScriptVar *deepCopy(); ///< deep copy this node and return the result 00264 00265 void trace(std::string indentStr = "", const std::string &name = ""); ///< Dump out the contents of this using trace 00266 std::string getFlagsAsString(); ///< For debugging - just dump a string version of the flags 00267 #ifndef MBED 00268 void getJSON(std::ostringstream &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON) 00269 #else 00270 void getJSON(std::string &destination, const std::string linePrefix=""); ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON) 00271 #endif 00272 void setCallback(JSCallback callback, void *userdata); ///< Set the callback for native functions 00273 00274 CScriptVarLink *firstChild; 00275 CScriptVarLink *lastChild; 00276 00277 /// For memory management/garbage collection 00278 CScriptVar *ref(); ///< Add reference to this variable 00279 void unref(); ///< Remove a reference, and delete this variable if required 00280 int getRefs(); ///< Get the number of references to this script variable 00281 protected: 00282 int refs; ///< The number of references held to this - used for garbage collection 00283 00284 std::string data; ///< The contents of this variable if it is a string 00285 long intData; ///< The contents of this variable if it is an int 00286 double doubleData; ///< The contents of this variable if it is a double 00287 int flags; ///< the flags determine the type of the variable - int/double/string/etc 00288 JSCallback jsCallback; ///< Callback for native functions 00289 void *jsCallbackUserData; ///< user data passed as second argument to native functions 00290 00291 void init(); ///< initialisation of data members 00292 00293 /** Copy the basic data and flags from the variable given, with no 00294 * children. Should be used internally only - by copyValue and deepCopy */ 00295 void copySimpleData(CScriptVar *val); 00296 00297 friend class CTinyJS; 00298 }; 00299 00300 class CTinyJS { 00301 public: 00302 CTinyJS(); 00303 ~CTinyJS(); 00304 00305 void execute(const std::string &code); 00306 /** Evaluate the given code and return a link to a javascript object, 00307 * useful for (dangerous) JSON parsing. If nothing to return, will return 00308 * 'undefined' variable type. CScriptVarLink is returned as this will 00309 * automatically unref the result as it goes out of scope. If you want to 00310 * keep it, you must use ref() and unref() */ 00311 CScriptVarLink evaluateComplex(const std::string &code); 00312 /** Evaluate the given code and return a string. If nothing to return, will return 00313 * 'undefined' */ 00314 std::string evaluate(const std::string &code); 00315 00316 /// add a native function to be called from TinyJS 00317 /** example: 00318 \code 00319 void scRandInt(CScriptVar *c, void *userdata) { ... } 00320 tinyJS->addNative("function randInt(min, max)", scRandInt, 0); 00321 \endcode 00322 00323 or 00324 00325 \code 00326 void scSubstring(CScriptVar *c, void *userdata) { ... } 00327 tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0); 00328 \endcode 00329 */ 00330 void addNative(const std::string &funcDesc, JSCallback ptr, void *userdata); 00331 00332 /// Get the given variable specified by a path (var1.var2.etc), or return 0 00333 CScriptVar *getScriptVariable(const std::string &path); 00334 /// Get the value of the given variable, or return 0 00335 const std::string *getVariable(const std::string &path); 00336 /// set the value of the given variable, return trur if it exists and gets set 00337 bool setVariable(const std::string &path, const std::string &varData); 00338 00339 /// Send all variables to stdout 00340 void trace(); 00341 00342 CScriptVar *root; /// root of symbol table 00343 private: 00344 CScriptLex *l; /// current lexer 00345 std::vector<CScriptVar*> scopes; /// stack of scopes when parsing 00346 #ifdef TINYJS_CALL_STACK 00347 std::vector<std::string> call_stack; /// Names of places called so we can show when erroring 00348 #endif 00349 00350 CScriptVar *stringClass; /// Built in string class 00351 CScriptVar *objectClass; /// Built in object class 00352 CScriptVar *arrayClass; /// Built in array class 00353 00354 // parsing - in order of precedence 00355 CScriptVarLink *functionCall(bool &execute, CScriptVarLink *function, CScriptVar *parent); 00356 CScriptVarLink *factor(bool &execute); 00357 CScriptVarLink *unary(bool &execute); 00358 CScriptVarLink *term(bool &execute); 00359 CScriptVarLink *expression(bool &execute); 00360 CScriptVarLink *shift(bool &execute); 00361 CScriptVarLink *condition(bool &execute); 00362 CScriptVarLink *logic(bool &execute); 00363 CScriptVarLink *ternary(bool &execute); 00364 CScriptVarLink *base(bool &execute); 00365 void block(bool &execute); 00366 void statement(bool &execute); 00367 // parsing utility functions 00368 CScriptVarLink *parseFunctionDefinition(); 00369 void parseFunctionArguments(CScriptVar *funcVar); 00370 00371 CScriptVarLink *findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes 00372 /// Look up in any parent classes of the given object 00373 CScriptVarLink *findInParentClasses(CScriptVar *object, const std::string &name); 00374 }; 00375 00376 #endif 00377
Generated on Tue Jul 12 2022 21:01:11 by 1.7.2