updatified makefile. to infinity and maybe a little further? can't hardly be verbose...
[tinyjs-rewrite.git] / include / tinyjs.h
blob77f5b3b854bedf78f3f3d2065e274bf720124cdb
1 /*
2 * TinyJS
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
26 * SOFTWARE.
29 #pragma once
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
34 #ifdef _WIN32
35 #ifdef _DEBUG
36 #define _CRTDBG_MAP_ALLOC
37 #include <stdlib.h>
38 #include <crtdbg.h>
39 #endif
40 #endif
41 #include <string>
42 #include <vector>
44 #ifndef TRACE
45 #define TRACE printf
46 #endif // TRACE
49 namespace TinyJS
52 const int TINYJS_LOOP_MAX_ITERATIONS = 8192;
54 enum LEX_TYPES
56 LEX_EOF = 0,
57 LEX_ID = 256,
58 LEX_INT,
59 LEX_FLOAT,
60 LEX_STR,
62 LEX_EQUAL,
63 LEX_TYPEEQUAL,
64 LEX_NEQUAL,
65 LEX_NTYPEEQUAL,
66 LEX_LEQUAL,
67 LEX_LSHIFT,
68 LEX_LSHIFTEQUAL,
69 LEX_GEQUAL,
70 LEX_RSHIFT,
71 LEX_RSHIFTUNSIGNED,
72 LEX_RSHIFTEQUAL,
73 LEX_PLUSEQUAL,
74 LEX_MINUSEQUAL,
75 LEX_PLUSPLUS,
76 LEX_MINUSMINUS,
77 LEX_ANDEQUAL,
78 LEX_ANDAND,
79 LEX_OREQUAL,
80 LEX_OROR,
81 LEX_XOREQUAL,
82 // reserved words
83 #define LEX_R_LIST_START LEX_R_IF
84 LEX_R_IF,
85 LEX_R_ELSE,
86 LEX_R_DO,
87 LEX_R_WHILE,
88 LEX_R_FOR,
89 LEX_R_BREAK,
90 LEX_R_CONTINUE,
91 LEX_R_FUNCTION,
92 LEX_R_RETURN,
93 LEX_R_VAR,
94 LEX_R_TRUE,
95 LEX_R_FALSE,
96 LEX_R_NULL,
97 LEX_R_UNDEFINED,
98 LEX_R_NEW,
99 LEX_R_LIST_END /* always the last entry */
102 enum SCRIPTVAR_FLAGS
104 SCRIPTVAR_UNDEFINED = 0,
105 SCRIPTVAR_FUNCTION = 1,
106 SCRIPTVAR_OBJECT = 2,
107 SCRIPTVAR_ARRAY = 4,
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 =
114 SCRIPTVAR_NULL |
115 SCRIPTVAR_DOUBLE |
116 SCRIPTVAR_INTEGER,
117 SCRIPTVAR_VARTYPEMASK =
118 SCRIPTVAR_DOUBLE |
119 SCRIPTVAR_INTEGER |
120 SCRIPTVAR_STRING |
121 SCRIPTVAR_FUNCTION |
122 SCRIPTVAR_OBJECT |
123 SCRIPTVAR_ARRAY |
124 SCRIPTVAR_NULL,
128 #define TINYJS_RETURN_VAR "return"
129 #define TINYJS_PROTOTYPE_CLASS "prototype"
130 #define TINYJS_TEMP_NAME ""
131 #define TINYJS_BLANK_DATA ""
133 class RuntimeError
135 public:
136 std::string text;
137 RuntimeError(const std::string &exceptionText);
140 class Lexer
142 protected:
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
149 char* m_data;
151 /// Start and end position in data string
152 int m_dataStart;
153 int m_dataEnd;
155 /// Do we own this data string?
156 bool m_dataOwned;
158 /// Position in data (we CAN go past the end of the string here)
159 int m_dataPos;
162 public:
163 char m_currCh;
164 char m_nextCh;
165 /// The type of the token that we have
166 int m_tk;
167 /// Position in the data at the beginning of the token we have here
168 int m_tokenStart;
169 /// Position in the data at the last character of the token we have here
170 int m_tokenEnd;
171 /// Position in the data at the last character of the last token
172 int m_tokenLastEnd;
173 /// Data contained in the token we have here
174 std::string m_tkStr;
176 protected:
177 void getNextCh();
178 /// Get the text token from our text string
179 void getNextToken();
181 public:
182 Lexer(const std::string &input);
183 Lexer(Lexer *owner, int startChar, int endChar);
184 ~Lexer(void);
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
194 void reset();
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);
206 class Variable;
208 typedef void (*JSCallback)(Variable *var, void *userdata);
210 class VarLink
212 public:
213 std::string name;
214 VarLink *nextSibling;
215 VarLink *prevSibling;
216 Variable *var;
217 bool owned;
219 public:
220 VarLink(Variable *var, const std::string &name = TINYJS_TEMP_NAME);
221 /// Copy constructor
222 VarLink(const VarLink &link);
223 ~VarLink();
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)
229 int getIntName();
230 /// Set the name as an integer (for arrays)
231 void setIntName(int n);
234 /// Variable class (containing a doubly-linked list of children)
235 class Variable
237 public:
238 VarLink *firstChild;
239 VarLink *lastChild;
241 public:
242 /// Create undefined
243 Variable();
245 /// User defined
246 Variable(const std::string &varData, int varFlags);
248 /// Create a string
249 Variable(const std::string &str);
250 Variable(double varData);
251 Variable(int val);
252 ~Variable(void);
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
291 int getChildren();
293 /// get data from variable
294 int getInt();
295 bool getBool()
297 return getInt() != 0;
299 double getDouble();
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);
309 void setUndefined();
310 void setArray();
313 bool equals(Variable *v);
315 bool isInt();
316 bool isDouble();
317 bool isString();
318 bool isNumeric();
319 bool isFunction();
320 bool isObject();
321 bool isArray();
322 bool isNative();
323 bool isUndefined();
324 bool isNull();
326 /// Is this *not* an array/object/etc
327 bool isBasic();
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
355 protected:
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;
374 class Interpreter
376 private:
377 /// current lexer
378 Lexer* m_lexer;
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;
386 #endif
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;
401 private:
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);
424 public:
425 Interpreter();
426 ~Interpreter();
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
440 * 'undefined'
442 std::string evaluate(const std::string &code);
444 /// add a native function to be called from TinyJS
445 /** example:
446 \code
447 void scRandInt(Variable *c, void *userdata) { ... }
448 tinyJS->addNative("function randInt(min, max)", scRandInt, 0);
449 \endcode
453 \code
454 void scSubstring(Variable *c, void *userdata) { ... }
455 tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0);
456 \endcode
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
468 void trace();
470 /// get roottable
471 Variable* getRoot();
474 /// Register useful functions with the TinyJS interpreter
475 extern void registerFunctions(Interpreter *tinyJS);
476 void registerMathFunctions(Interpreter *tinyJS);