TinyJS on mbed. TinyJS is very simple JavaScript engine.

Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers TinyJS.h Source File

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