You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

229 lines
7.1 KiB

/*
* TinyJS
*
* A single-file Javascript-alike engine
*
* Authored By Gordon Williams <gw@pur3.co.uk>
*
* Copyright (C) 2009 Pur3 Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef TINYJS_H
#define TINYJS_H
#include <string>
#ifndef TRACE
#define TRACE printf
#endif // TRACE
const int TINYJS_LOOP_MAX_ITERATIONS = 8192;
enum LEX_TYPES {
LEX_EOF = 0,
LEX_ID = 256,
LEX_INT,
LEX_FLOAT,
LEX_STR,
LEX_EQUAL,
LEX_NEQUAL,
LEX_LEQUAL,
LEX_GEQUAL,
LEX_PLUSEQUAL,
LEX_MINUSEQUAL,
LEX_PLUSPLUS,
LEX_MINUSMINUS,
LEX_ANDAND,
LEX_OROR,
// reserved words
LEX_R_IF,
LEX_R_ELSE,
LEX_R_WHILE,
LEX_R_FOR,
LEX_R_FUNCTION,
LEX_R_RETURN,
LEX_R_VAR,
LEX_R_TRUE,
LEX_R_FALSE,
};
enum SCRIPTVAR_FLAGS {
SCRIPTVAR_FUNCTION = 1,
SCRIPTVAR_NATIVE = 2,
SCRIPTVAR_NUMERIC = 4,
SCRIPTVAR_INTEGER = 8, // eg. not floating point
SCRIPTVAR_VARTYPEMASK = 12,
SCRIPTVAR_PARAMETER = 16
};
#define TINYJS_RETURN_VAR "return"
#define TINYJS_TEMP_NAME ""
#define TINYJS_BLANK_DATA ""
/// convert the given string into a quoted string suitable for javascript
std::string getJSString(const std::string &str);
class CScriptException {
public:
std::string text;
CScriptException(const std::string &exceptionText);
};
class CScriptLex
{
public:
CScriptLex(const std::string &input);
CScriptLex(CScriptLex *owner, int startChar, int endChar);
~CScriptLex(void);
char currCh, nextCh;
int tk; ///< The type of the token that we have
int tokenPosition; ///< Position in the data at the beginning of the token we have here
std::string tkStr; ///< Data contained in the token we have here
void match(int expected_tk); ///< Lexical match wotsit
std::string getTokenStr(int token); ///< Get the string representation of the given token
void reset(); ///< Reset this lex so we can start again
std::string getSubString(int pos); ///< Return a sub-string from the given position up until right now
CScriptLex *getSubLex(int lastPosition); ///< Return a sub-lexer from the given position up until right now
std::string getPosition(int pos); ///< Return a string representing the position in lines and columns of the character pos given
protected:
/* When we go into a loop, we use getSubLex to get a lexer for just the sub-part of the
relevant string. This doesn't re-allocate and copy the string, but instead copies
the data pointer and sets dataOwned to false, and dataStart/dataEnd to the relevant things. */
char *data; ///< Data string to get tokens from
int dataStart, dataEnd; ///< Start and end position in data string
bool dataOwned; ///< Do we own this data string?
int dataPos; ///< Position in data
void getNextCh();
void getNextToken(); ///< Get the text token from our text string
};
class CScriptVar;
typedef void (*JSCallback)(CScriptVar *var);
/// Variable class (containing a doubly-linked list of children)
class CScriptVar
{
public:
CScriptVar(void);
CScriptVar(std::string varName, std::string varData=TINYJS_BLANK_DATA, int varFlags = 0);
CScriptVar(double varData);
CScriptVar(int val);
~CScriptVar(void);
CScriptVar *findChild(std::string childName); ///< Tries to find a child with the given name, may return 0
CScriptVar *getChild(std::string childName); ///< Gets a child with the given name, produces error on fail
CScriptVar *findRecursive(std::string childName); ///< Finds a child, looking recursively up the tree
void addChild(CScriptVar *child);
CScriptVar *addChildNoDup(CScriptVar *child); ///< add a child overwriting any with the same name
void removeChild(CScriptVar *child);
void removeAllChildren();
CScriptVar *getRoot(); ///< Get the absolute root of the tree
std::string getName();
int getInt();
bool getBool() { return getInt() != 0; }
double getDouble();
std::string &getString();
void setInt(int num);
void setDouble(double val);
void setString(std::string str);
void setVoid();
bool isInt() { return (flags&SCRIPTVAR_NUMERIC)!=0 && (flags&SCRIPTVAR_INTEGER)!=0; }
bool isDouble() { return (flags&SCRIPTVAR_NUMERIC)!=0 && (flags&SCRIPTVAR_INTEGER)==0; }
bool isNumeric() { return (flags&SCRIPTVAR_NUMERIC)!=0; }
bool isFunction() { return (flags&SCRIPTVAR_FUNCTION)!=0; }
bool isParameter() { return (flags&SCRIPTVAR_PARAMETER)!=0; }
bool isNative() { return (flags&SCRIPTVAR_NATIVE)!=0; }
CScriptVar *mathsOp(CScriptVar *b, int op); ///< do a maths op with another script variable
void copyValue(CScriptVar *val); ///< copy the value from the value given
CScriptVar *deepCopy(); ///< deep copy this node and return the result
void trace(std::string indentStr = ""); ///< Dump out the contents of this using trace
void getInitialiseCode(std::ostringstream &destination, const std::string &prefix = ""); ///< Write out all the JS code needed to recreate this script variable to the stream
void setCallback(JSCallback callback);
CScriptVar *firstChild;
CScriptVar *lastChild;
CScriptVar *nextSibling;
CScriptVar *prevSibling;
CScriptVar *parent;
protected:
std::string name;
std::string data;
int flags;
JSCallback jsCallback;
void init(); // initilisation of data members
friend class CTinyJS;
};
class CTinyJS {
public:
CTinyJS();
~CTinyJS();
void execute(const std::string &code);
std::string evaluate(const std::string &code);
/// add a function to the root scope
/** example:
\code
void scRandInt(CScriptVar *c) { ... }
tinyJS->addNative("function randInt(min, max)", scRandInt);
\endcode
*/
void addNative(const std::string &funcDesc, JSCallback ptr);
/// Get the value of the given variable, or return 0
std::string *getVariable(const std::string &path);
/// Send all variables to stdout
void trace();
CScriptVar *root; /// root of symbol table
private:
CScriptLex *l; /// current lexer
CScriptVar *symbol_base; /// current symbol table base
// parsing - in order of precedence
CScriptVar *factor(bool &execute);
CScriptVar *unary(bool &execute);
CScriptVar *term(bool &execute);
CScriptVar *expression(bool &execute);
CScriptVar *condition(bool &execute);
CScriptVar *logic(bool &execute);
CScriptVar *base(bool &execute);
void block(bool &execute);
void statement(bool &execute);
};
#endif