4 * A single-file Javascript-alike engine
6 * Authored By Gordon Williams <gw@pur3.co.uk>
8 * Copyright (C) 2009 Pur3 Ltd
10 * Permission is hereby granted, free of charge, to any person obtaining a copy of
11 * this software and associated documentation files (the "Software"), to deal in
12 * the Software without restriction, including without limitation the rights to
13 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
14 * of the Software, and to permit persons to whom the Software is furnished to do
15 * so, subject to the following conditions:
17 * The above copyright notice and this permission notice shall be included in all
18 * copies or substantial portions of the Software.
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
31 // If defined, this keeps a note of all calls and where from in memory. This is slower, but good for debugging
32 #define TINYJS_CALL_STACK
36 #define _CRTDBG_MAP_ALLOC
52 const int TINYJS_LOOP_MAX_ITERATIONS
= 8192;
83 #define LEX_R_LIST_START LEX_R_IF
99 LEX_R_LIST_END
/* always the last entry */
104 SCRIPTVAR_UNDEFINED
= 0,
105 SCRIPTVAR_FUNCTION
= 1,
106 SCRIPTVAR_OBJECT
= 2,
108 SCRIPTVAR_DOUBLE
= 8, // floating point double
109 SCRIPTVAR_INTEGER
= 16, // integer number
110 SCRIPTVAR_STRING
= 32, // string
111 SCRIPTVAR_NULL
= 64, // it seems null is its own data type
112 SCRIPTVAR_NATIVE
= 128, // to specify this is a native function
113 SCRIPTVAR_NUMERICMASK
=
117 SCRIPTVAR_VARTYPEMASK
=
128 #define TINYJS_RETURN_VAR "return"
129 #define TINYJS_PROTOTYPE_CLASS "prototype"
130 #define TINYJS_TEMP_NAME ""
131 #define TINYJS_BLANK_DATA ""
137 RuntimeError(const std::string
&exceptionText
);
144 * When we go into a loop, we use getSubLex to get a lexer for just the sub-part of the
145 * relevant string. This doesn't re-allocate and copy the string, but instead copies
146 * the data pointer and sets dataOwned to false, and dataStart/dataEnd to the relevant things.
148 /// Data string to get tokens from
151 /// Start and end position in data string
155 /// Do we own this data string?
158 /// Position in data (we CAN go past the end of the string here)
165 /// The type of the token that we have
167 /// Position in the data at the beginning of the token we have here
169 /// Position in the data at the last character of the token we have here
171 /// Position in the data at the last character of the last token
173 /// Data contained in the token we have here
178 /// Get the text token from our text string
182 Lexer(const std::string
&input
);
183 Lexer(Lexer
*owner
, int startChar
, int endChar
);
187 /// Lexical match wotsit
188 void match(int expected_tk
);
190 /// Get the string representation of the given token
191 static std::string
getTokenStr(int token
);
193 /// Reset this lex so we can start again
196 /// Return a sub-string from the given position up until right now
197 std::string
getSubString(int pos
);
199 /// Return a sub-lexer from the given position up until right now
200 Lexer
*getSubLex(int lastPosition
);
202 /// Return a string representing the position in lines and columns of the character pos given
203 std::string
getPosition(int pos
=-1);
208 typedef void (*JSCallback
)(Variable
*var
, void *userdata
);
214 VarLink
*nextSibling
;
215 VarLink
*prevSibling
;
220 VarLink(Variable
*var
, const std::string
&name
= TINYJS_TEMP_NAME
);
222 VarLink(const VarLink
&link
);
224 /// Replace the Variable pointed to
225 void replaceWith(Variable
*newVar
);
226 /// Replace the Variable pointed to (just dereferences)
227 void replaceWith(VarLink
*newVar
);
228 /// Get the name as an integer (for arrays)
230 /// Set the name as an integer (for arrays)
231 void setIntName(int n
);
234 /// Variable class (containing a doubly-linked list of children)
246 Variable(const std::string
&varData
, int varFlags
);
249 Variable(const std::string
&str
);
250 Variable(double varData
);
254 /// If this is a function, get the result value (for use by native functions)
255 Variable
*getReturnVar();
257 /// Set the result value. Use this when setting complex return data as it avoids a deepCopy()
258 void setReturnVar(Variable
*var
);
260 /// If this is a function, get the parameter with the given name (for use by native functions)
261 Variable
*getParameter(const std::string
&name
);
263 /// Tries to find a child with the given name, may return 0
264 VarLink
*findChild(const std::string
&childName
);
266 /// Tries to find a child with the given name, or will create it with the given flags
267 VarLink
*findChildOrCreate(const std::string
&childName
, int varFlags
=SCRIPTVAR_UNDEFINED
);
269 ///< Tries to find a child with the given path (separated by dots)
270 VarLink
*findChildOrCreateByPath(const std::string
&path
);
272 ///< add a child overwriting any with the same name
273 VarLink
*addChild(const std::string
&childName
, Variable
*child
=NULL
);
274 VarLink
*addChildNoDup(const std::string
&childName
, Variable
*child
=NULL
);
275 void removeChild(Variable
*child
);
277 /// Remove a specific link (this is faster than finding via a child)
278 void removeLink(VarLink
*link
);
279 void removeAllChildren();
281 ///< The the value at an array index
282 Variable
*getArrayIndex(int idx
);
284 ///< Set the value at an array index
285 void setArrayIndex(int idx
, Variable
*value
);
287 ///< If this is an array, return the number of items in it (else 0)
288 int getArrayLength();
290 /// Get the number of children
293 /// get data from variable
297 return getInt() != 0;
300 const std::string
& getString();
302 ///< get Data as a parsable javascript string
303 std::string
getParsableString();
305 /// set data for variable
306 void setInt(int num
);
307 void setDouble(double val
);
308 void setString(const std::string
&str
);
313 bool equals(Variable
*v
);
326 /// Is this *not* an array/object/etc
329 /// do a maths op with another script variable
330 Variable
*mathsOp(Variable
*b
, int op
);
332 ///< copy the value from the value given
333 void copyValue(Variable
*val
);
335 /// deep copy this node and return the result
336 Variable
*deepCopy();
338 /// Dump out the contents of this using trace
339 void trace(std::string indentStr
= "", const std::string
&name
= "");
341 /// For debugging - just dump a string version of the flags
342 std::string
getFlagsAsString();
344 ///< Write out all the JS code needed to recreate this script variable to the stream (as JSON)
345 void getJSON(std::ostringstream
&destination
, const std::string linePrefix
="");
347 /// Set the callback for native functions
348 void setCallback(JSCallback callback
, void *userdata
);
351 /// For memory management/garbage collection
352 Variable
*ref(); ///< Add reference to this variable
353 void unref(); ///< Remove a reference, and delete this variable if required
354 int getRefs(); ///< Get the number of references to this script variable
356 int refs
; ///< The number of references held to this - used for garbage collection
358 std::string data
; ///< The contents of this variable if it is a string
359 long intData
; ///< The contents of this variable if it is an int
360 double doubleData
; ///< The contents of this variable if it is a double
361 int flags
; ///< the flags determine the type of the variable - int/double/string/etc
362 JSCallback jsCallback
; ///< Callback for native functions
363 void *jsCallbackUserData
; ///< user data passed as second argument to native functions
365 void init(); ///< initialisation of data members
367 /** Copy the basic data and flags from the variable given, with no
368 * children. Should be used internally only - by copyValue and deepCopy */
369 void copySimpleData(Variable
*val
);
371 friend class Interpreter
;
380 /// stack of scopes when parsing
381 std::vector
<Variable
*> m_scopes
;
383 #ifdef TINYJS_CALL_STACK
384 /// Names of places called so we can show when erroring
385 std::vector
<std::string
> m_callstack
;
388 /// Built in string class
389 Variable
* m_stringclass
;
391 /// Built in object class
392 Variable
* m_objectclass
;
394 /// Built in array class
395 Variable
* m_arrayclass
;
397 /// root of symbol table
398 Variable
* m_roottable
;
402 // parsing - in order of precedence
403 VarLink
*functionCall(
404 bool &execute
, VarLink
*function
, Variable
*parent
);
405 VarLink
*factor(bool &execute
);
406 VarLink
*unary(bool &execute
);
407 VarLink
*term(bool &execute
);
408 VarLink
*expression(bool &execute
);
409 VarLink
*shift(bool &execute
);
410 VarLink
*condition(bool &execute
);
411 VarLink
*logic(bool &execute
);
412 VarLink
*ternary(bool &execute
);
413 VarLink
*base(bool &execute
);
414 void block(bool &execute
);
415 void statement(bool &execute
);
416 // parsing utility functions
417 VarLink
*parseFunctionDefinition();
418 void parseFunctionArguments(Variable
*funcVar
);
420 VarLink
*findInScopes(const std::string
&childName
); ///< Finds a child, looking recursively up the scopes
421 /// Look up in any parent classes of the given object
422 VarLink
*findInParentClasses(Variable
*object
, const std::string
&name
);
428 void execute(const std::string
&code
);
430 * Evaluate the given code and return a link to a javascript object,
431 * useful for (dangerous) JSON parsing. If nothing to return, will return
432 * 'undefined' variable type. VarLink is returned as this will
433 * automatically unref the result as it goes out of scope. If you want to
434 * keep it, you must use ref() and unref() */
435 VarLink
evaluateComplex(const std::string
&code
);
438 * Evaluate the given code and return a string.
439 * If nothing to return, will return
442 std::string
evaluate(const std::string
&code
);
444 /// add a native function to be called from TinyJS
447 void scRandInt(Variable *c, void *userdata) { ... }
448 tinyJS->addNative("function randInt(min, max)", scRandInt, 0);
454 void scSubstring(Variable *c, void *userdata) { ... }
455 tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0);
458 void addNative(const std::string
&funcDesc
, JSCallback ptr
, void *userdata
);
460 /// Get the given variable specified by a path (var1.var2.etc), or return 0
461 Variable
*getScriptVariable(const std::string
&path
);
462 /// Get the value of the given variable, or return 0
463 const std::string
*getVariable(const std::string
&path
);
464 /// set the value of the given variable, return trur if it exists and gets set
465 bool setVariable(const std::string
&path
, const std::string
&varData
);
467 /// Send all variables to stdout
474 /// Register useful functions with the TinyJS interpreter
475 extern void registerFunctions(Interpreter
*tinyJS
);
476 void registerMathFunctions(Interpreter
*tinyJS
);