TinyJS on mbed. TinyJS is very simple JavaScript engine.
TinyJS on mbed
what's this ?
TinyJS is an extremely simple (under than 2000 lines) JavaScript interpreter engine.
I ported on mbed. but it restrict any features.
TinyJS project is https://github.com/gfwilliams/tiny-js
TinyJSは2000行以下で書かれた非常に小さいJavaScriptインタプリタエンジンです。
これをmbedに移植してみました。(ただし、いろいろ制限があります)
本家はこちら。 https://github.com/gfwilliams/tiny-js
how to use
You must use on serial terminal application by mbed serial USB.
baud is 57600bps
USBシリアルとして、ターミナルソフトを接続するとコンソールが表示されます。
ボーレートは57600bpsになってます。
functions
functions for mbed.
mbed用関数
- mbed.DigitalIn(pinName, mode)
- mbed.DigitalOut(pinName, val)
- mbed.AnalogIn(pinName)
- mbed.AnalogOut(pinName, val)
- mbed.InterruptIn(pinName, edge, mode, callback)
- mbed.TimerStart()
- mbed.TimerStop()
- mbed.TimerReset()
- mbed.TimerRead()
- mbed.Timeout(callback, t)
- mbed.wait(s)
- mbed.memfree()
sample JavaScript codes
DigitalOut
mbed.DigitalOut('LED1', 1); mbed.DigitalOut('LED2', 0); mbed.DigitalOut('LED3', 1); mbed.DigitalOut('LED4', 0);
LED1 = On, LED2=Off, LED3=On, ED4=Off
LED1 = 点灯、LED2=消灯、LED3=点灯、LED4=消灯
DigitalIn
print(mbed.DigitalIn('p5', 'PullUp'));
p5 is pull up, read, and print on console.
p5をPullUpして読みプリントする。
AnalogOut
mbed.AnalogOut('p18', 0.8);
p18 is analog output, value is 0.8.
p18を 値0.8でアナログ出力する。
AnalogIn
print(mbed.AnalogIn('p20'));
p20 is read analog voltage, and print on console.
p20をアナログ入力しプリントする。
InterruptIn
var led1 = 0; mbed.InterruptIn('p5', 'fall', 'PullUp', function() {led1 = !led1; mbed.DigitalOut('LED1', led1);});
Interrupt on p5, and ON/OFF does LED1.
p5で割り込んでLED1をON/OFFする。
Timeout and wait sample code
mbed.Timeout(function() {mbed.DigitalOut('LED1', 1);mbed.wait(3);mbed.DigitalOut('LED1', 0);}, 4);
LED1=on when wait for 4 seconds. and LED1=off for 3 seconds later.
LED1を4秒待って点灯して3秒後に消灯する。
memfree
print(mbed.memfree());
This prints the number of bytes of the remainder memory on mbed where TinyJS is usable.
これはTinyJSが使えるmbed上での残りメモリのバイト数をプリントアウトする。
LED Blinker by Timeout
blinker = function() {var led = 0; mbed.Timeout(function() {led = !led; mbed.DigitalOut('LED1', led);blinker();}, 0.5);}; blinker();
LED Blinker by Timeout.
Timeoutを使ったLチカ。
restrictions
- There is very little available memory. (Less than 9kbytes on LPC1768)
- Registration of InterruptIn is 4 limit.
- The loop to 8,192 times.
- The built-in functions (general JavaScript functions) that TinyJS prepares for for securing of memory is not included.
more, more, more ....
制限事項
- 利用できるメモリは非常に少ない。(LPC1768で9kbytes以下)
- InterruptInで登録できる割り込みは4つまで。4つを超えると1つめから順番に削除される。
- ループは8192回まで。
- メモリ確保のためTinyJSが用意している組み込み関数(一般的なJavaScript関数)は含まれない。
他、多数....
sample movies
http://www.youtube.com/watch?v=ARp0DK70JGM
http://www.youtube.com/watch?v=UOZQ4eEC4xA
TinyJS.h
- Committer:
- ohneta
- Date:
- 2014-01-20
- Revision:
- 8:819934a27c2d
- Parent:
- 0:aae260bdcdd9
File content as of revision 8:819934a27c2d:
/* * TinyJS * * A single-file Javascript-alike engine * * Authored By Gordon Williams <gw@pur3.co.uk> * * Copyright (C) 2009 Pur3 Ltd * * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* * TinyJS for mbed. * * Authored by Takehisa Oneta (ohneta@gmail.com) * 10th Jan. 2013 */ #ifndef TINYJS_H #define TINYJS_H // If defined, this keeps a note of all calls and where from in memory. This is slower, but good for debugging #define TINYJS_CALL_STACK #ifdef _WIN32 #ifdef _DEBUG #define _CRTDBG_MAP_ALLOC #include <stdlib.h> #include <crtdbg.h> #endif #endif #include <string> #include <vector> #ifndef TRACE #define TRACE printf #endif // TRACE #ifndef MBED #define MBED 1 #endif // MBED #define DEBUG_MEMORY 0 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_TYPEEQUAL, LEX_NEQUAL, LEX_NTYPEEQUAL, LEX_LEQUAL, LEX_LSHIFT, LEX_LSHIFTEQUAL, LEX_GEQUAL, LEX_RSHIFT, LEX_RSHIFTUNSIGNED, LEX_RSHIFTEQUAL, LEX_PLUSEQUAL, LEX_MINUSEQUAL, LEX_PLUSPLUS, LEX_MINUSMINUS, LEX_ANDEQUAL, LEX_ANDAND, LEX_OREQUAL, LEX_OROR, LEX_XOREQUAL, // reserved words #define LEX_R_LIST_START LEX_R_IF LEX_R_IF, LEX_R_ELSE, LEX_R_DO, LEX_R_WHILE, LEX_R_FOR, LEX_R_BREAK, LEX_R_CONTINUE, LEX_R_FUNCTION, LEX_R_RETURN, LEX_R_VAR, LEX_R_TRUE, LEX_R_FALSE, LEX_R_NULL, LEX_R_UNDEFINED, LEX_R_NEW, LEX_R_LIST_END /* always the last entry */ }; enum SCRIPTVAR_FLAGS { SCRIPTVAR_UNDEFINED = 0, SCRIPTVAR_FUNCTION = 1, SCRIPTVAR_OBJECT = 2, SCRIPTVAR_ARRAY = 4, SCRIPTVAR_DOUBLE = 8, // floating point double SCRIPTVAR_INTEGER = 16, // integer number SCRIPTVAR_STRING = 32, // string SCRIPTVAR_NULL = 64, // it seems null is its own data type SCRIPTVAR_NATIVE = 128, // to specify this is a native function SCRIPTVAR_NUMERICMASK = SCRIPTVAR_NULL | SCRIPTVAR_DOUBLE | SCRIPTVAR_INTEGER, SCRIPTVAR_VARTYPEMASK = SCRIPTVAR_DOUBLE | SCRIPTVAR_INTEGER | SCRIPTVAR_STRING | SCRIPTVAR_FUNCTION | SCRIPTVAR_OBJECT | SCRIPTVAR_ARRAY | SCRIPTVAR_NULL, }; #define TINYJS_RETURN_VAR "return" #define TINYJS_PROTOTYPE_CLASS "prototype" #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 tokenStart; ///< Position in the data at the beginning of the token we have here int tokenEnd; ///< Position in the data at the last character of the token we have here int tokenLastEnd; ///< Position in the data at the last character of the last token std::string tkStr; ///< Data contained in the token we have here void match(int expected_tk); ///< Lexical match wotsit static 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=-1); ///< 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 (we CAN go past the end of the string here) void getNextCh(); void getNextToken(); ///< Get the text token from our text string }; class CScriptVar; typedef void (*JSCallback)(CScriptVar *var, void *userdata); class CScriptVarLink { public: std::string name; CScriptVarLink *nextSibling; CScriptVarLink *prevSibling; CScriptVar *var; bool owned; CScriptVarLink(CScriptVar *var, const std::string &name = TINYJS_TEMP_NAME); CScriptVarLink(const CScriptVarLink &link); ///< Copy constructor ~CScriptVarLink(); void replaceWith(CScriptVar *newVar); ///< Replace the Variable pointed to void replaceWith(CScriptVarLink *newVar); ///< Replace the Variable pointed to (just dereferences) int getIntName(); ///< Get the name as an integer (for arrays) void setIntName(int n); ///< Set the name as an integer (for arrays) }; /// Variable class (containing a doubly-linked list of children) class CScriptVar { public: CScriptVar(); ///< Create undefined CScriptVar(const std::string &varData, int varFlags); ///< User defined CScriptVar(const std::string &str); ///< Create a string CScriptVar(double varData); CScriptVar(int val); ~CScriptVar(void); CScriptVar *getReturnVar(); ///< If this is a function, get the result value (for use by native functions) void setReturnVar(CScriptVar *var); ///< Set the result value. Use this when setting complex return data as it avoids a deepCopy() CScriptVar *getParameter(const std::string &name); ///< If this is a function, get the parameter with the given name (for use by native functions) CScriptVarLink *findChild(const std::string &childName); ///< Tries to find a child with the given name, may return 0 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 CScriptVarLink *findChildOrCreateByPath(const std::string &path); ///< Tries to find a child with the given path (separated by dots) CScriptVarLink *addChild(const std::string &childName, CScriptVar *child=NULL); CScriptVarLink *addChildNoDup(const std::string &childName, CScriptVar *child=NULL); ///< add a child overwriting any with the same name void removeChild(CScriptVar *child); void removeLink(CScriptVarLink *link); ///< Remove a specific link (this is faster than finding via a child) void removeAllChildren(); CScriptVar *getArrayIndex(int idx); ///< The the value at an array index void setArrayIndex(int idx, CScriptVar *value); ///< Set the value at an array index int getArrayLength(); ///< If this is an array, return the number of items in it (else 0) int getChildren(); ///< Get the number of children int getInt(); bool getBool() { return getInt() != 0; } double getDouble(); const std::string &getString(); std::string getParsableString(); ///< get Data as a parsable javascript string void setInt(int num); void setDouble(double val); void setString(const std::string &str); void setUndefined(); void setArray(); bool equals(CScriptVar *v); bool isInt() { return (flags&SCRIPTVAR_INTEGER)!=0; } bool isDouble() { return (flags&SCRIPTVAR_DOUBLE)!=0; } bool isString() { return (flags&SCRIPTVAR_STRING)!=0; } bool isNumeric() { return (flags&SCRIPTVAR_NUMERICMASK)!=0; } bool isFunction() { return (flags&SCRIPTVAR_FUNCTION)!=0; } bool isObject() { return (flags&SCRIPTVAR_OBJECT)!=0; } bool isArray() { return (flags&SCRIPTVAR_ARRAY)!=0; } bool isNative() { return (flags&SCRIPTVAR_NATIVE)!=0; } bool isUndefined() { return (flags & SCRIPTVAR_VARTYPEMASK) == SCRIPTVAR_UNDEFINED; } bool isNull() { return (flags & SCRIPTVAR_NULL)!=0; } bool isBasic() { return firstChild==0; } ///< Is this *not* an array/object/etc 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 = "", const std::string &name = ""); ///< Dump out the contents of this using trace std::string getFlagsAsString(); ///< For debugging - just dump a string version of the flags #ifndef MBED 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) #else 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) #endif void setCallback(JSCallback callback, void *userdata); ///< Set the callback for native functions CScriptVarLink *firstChild; CScriptVarLink *lastChild; /// For memory management/garbage collection CScriptVar *ref(); ///< Add reference to this variable void unref(); ///< Remove a reference, and delete this variable if required int getRefs(); ///< Get the number of references to this script variable protected: int refs; ///< The number of references held to this - used for garbage collection std::string data; ///< The contents of this variable if it is a string long intData; ///< The contents of this variable if it is an int double doubleData; ///< The contents of this variable if it is a double int flags; ///< the flags determine the type of the variable - int/double/string/etc JSCallback jsCallback; ///< Callback for native functions void *jsCallbackUserData; ///< user data passed as second argument to native functions void init(); ///< initialisation of data members /** Copy the basic data and flags from the variable given, with no * children. Should be used internally only - by copyValue and deepCopy */ void copySimpleData(CScriptVar *val); friend class CTinyJS; }; class CTinyJS { public: CTinyJS(); ~CTinyJS(); void execute(const std::string &code); /** Evaluate the given code and return a link to a javascript object, * useful for (dangerous) JSON parsing. If nothing to return, will return * 'undefined' variable type. CScriptVarLink is returned as this will * automatically unref the result as it goes out of scope. If you want to * keep it, you must use ref() and unref() */ CScriptVarLink evaluateComplex(const std::string &code); /** Evaluate the given code and return a string. If nothing to return, will return * 'undefined' */ std::string evaluate(const std::string &code); /// add a native function to be called from TinyJS /** example: \code void scRandInt(CScriptVar *c, void *userdata) { ... } tinyJS->addNative("function randInt(min, max)", scRandInt, 0); \endcode or \code void scSubstring(CScriptVar *c, void *userdata) { ... } tinyJS->addNative("function String.substring(lo, hi)", scSubstring, 0); \endcode */ void addNative(const std::string &funcDesc, JSCallback ptr, void *userdata); /// Get the given variable specified by a path (var1.var2.etc), or return 0 CScriptVar *getScriptVariable(const std::string &path); /// Get the value of the given variable, or return 0 const std::string *getVariable(const std::string &path); /// set the value of the given variable, return trur if it exists and gets set bool setVariable(const std::string &path, const std::string &varData); /// Send all variables to stdout void trace(); CScriptVar *root; /// root of symbol table private: CScriptLex *l; /// current lexer std::vector<CScriptVar*> scopes; /// stack of scopes when parsing #ifdef TINYJS_CALL_STACK std::vector<std::string> call_stack; /// Names of places called so we can show when erroring #endif CScriptVar *stringClass; /// Built in string class CScriptVar *objectClass; /// Built in object class CScriptVar *arrayClass; /// Built in array class // parsing - in order of precedence CScriptVarLink *functionCall(bool &execute, CScriptVarLink *function, CScriptVar *parent); CScriptVarLink *factor(bool &execute); CScriptVarLink *unary(bool &execute); CScriptVarLink *term(bool &execute); CScriptVarLink *expression(bool &execute); CScriptVarLink *shift(bool &execute); CScriptVarLink *condition(bool &execute); CScriptVarLink *logic(bool &execute); CScriptVarLink *ternary(bool &execute); CScriptVarLink *base(bool &execute); void block(bool &execute); void statement(bool &execute); // parsing utility functions CScriptVarLink *parseFunctionDefinition(); void parseFunctionArguments(CScriptVar *funcVar); CScriptVarLink *findInScopes(const std::string &childName); ///< Finds a child, looking recursively up the scopes /// Look up in any parent classes of the given object CScriptVarLink *findInParentClasses(CScriptVar *object, const std::string &name); }; #endif