TinyJS on mbed. TinyJS is very simple JavaScript engine.

Dependencies:   mbed

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

Revision:
0:aae260bdcdd9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TinyJS.cpp	Sat Jan 11 20:19:11 2014 +0000
@@ -0,0 +1,2927 @@
+/*
+ * 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.
+ */
+
+/* Version 0.1  :  (gw) First published on Google Code
+   Version 0.11 :  Making sure the 'root' variable never changes
+                   'symbol_base' added for the current base of the sybmbol table
+   Version 0.12 :  Added findChildOrCreate, changed string passing to use references
+                   Fixed broken string encoding in getJSString()
+                   Removed getInitCode and added getJSON instead
+                   Added nil
+                   Added rough JSON parsing
+                   Improved example app
+   Version 0.13 :  Added tokenEnd/tokenLastEnd to lexer to avoid parsing whitespace
+                   Ability to define functions without names
+                   Can now do "var mine = function(a,b) { ... };"
+                   Slightly better 'trace' function
+                   Added findChildOrCreateByPath function
+                   Added simple test suite
+                   Added skipping of blocks when not executing
+   Version 0.14 :  Added parsing of more number types
+                   Added parsing of string defined with '
+                   Changed nil to null as per spec, added 'undefined'
+                   Now set variables with the correct scope, and treat unknown
+                              as 'undefined' rather than failing
+                   Added proper (I hope) handling of null and undefined
+                   Added === check
+   Version 0.15 :  Fix for possible memory leaks
+   Version 0.16 :  Removal of un-needed findRecursive calls
+                   symbol_base removed and replaced with 'scopes' stack
+                   Added reference counting a proper tree structure
+                       (Allowing pass by reference)
+                   Allowed JSON output to output IDs, not strings
+                   Added get/set for array indices
+                   Changed Callbacks to include user data pointer
+                   Added some support for objects
+                   Added more Java-esque builtin functions
+   Version 0.17 :  Now we don't deepCopy the parent object of the class
+                   Added JSON.stringify and eval()
+                   Nicer JSON indenting
+                   Fixed function output in JSON
+                   Added evaluateComplex
+                   Fixed some reentrancy issues with evaluate/execute
+   Version 0.18 :  Fixed some issues with code being executed when it shouldn't
+   Version 0.19 :  Added array.length
+                   Changed '__parent' to 'prototype' to bring it more in line with javascript
+   Version 0.20 :  Added '%' operator
+   Version 0.21 :  Added array type
+                   String.length() no more - now String.length
+                   Added extra constructors to reduce confusion
+                   Fixed checks against undefined
+   Version 0.22 :  First part of ardi's changes:
+                       sprintf -> sprintf_s
+                       extra tokens parsed
+                       array memory leak fixed
+                   Fixed memory leak in evaluateComplex
+                   Fixed memory leak in FOR loops
+                   Fixed memory leak for unary minus
+   Version 0.23 :  Allowed evaluate[Complex] to take in semi-colon separated
+                     statements and then only return the value from the last one.
+                     Also checks to make sure *everything* was parsed.
+                   Ints + doubles are now stored in binary form (faster + more precise)
+   Version 0.24 :  More useful error for maths ops
+                   Don't dump everything on a match error.
+   Version 0.25 :  Better string escaping
+   Version 0.26 :  Add CScriptVar::equals
+                   Add built-in array functions
+   Version 0.27 :  Added OZLB's TinyJS.setVariable (with some tweaks)
+                   Added OZLB's Maths Functions
+   Version 0.28 :  Ternary operator
+                   Rudimentary call stack on error
+                   Added String Character functions
+                   Added shift operators
+   Version 0.29 :  Added new object via functions
+                   Fixed getString() for double on some platforms
+   Version 0.30 :  Rlyeh Mario's patch for Math Functions on VC++
+   Version 0.31 :  Add exec() to TinyJS functions
+                   Now print quoted JSON that can be read by PHP/Python parsers
+                   Fixed postfix increment operator
+   Version 0.32 :  Fixed Math.randInt on 32 bit PCs, where it was broken
+   Version 0.33 :  Fixed Memory leak + brokenness on === comparison
+
+    NOTE:
+          Constructing an array with an initial length 'Array(5)' doesn't work
+          Recursive loops of data such as a.foo = a; fail to be garbage collected
+          length variable cannot be set
+          The postfix increment operator returns the current value, not the previous as it should.
+          There is no prefix increment operator
+          Arrays are implemented as a linked list - hence a lookup time is O(n)
+
+    TODO:
+          Utility va-args style function in TinyJS for executing a function directly
+          Merge the parsing of expressions/statements so eval("statement") works like we'd expect.
+          Move 'shift' implementation into mathsOp
+
+ */
+/*
+ * TinyJS for mbed.
+ *
+ * Authored by Takehisa Oneta (ohneta@gmail.com)
+ * 10th Jan. 2013
+ */
+
+#include "TinyJS.h"
+#include <assert.h>
+
+#define ASSERT(X) assert(X)
+/* Frees the given link IF it isn't owned by anything else */
+#define CLEAN(x) { CScriptVarLink *__v = x; if (__v && !__v->owned) { delete __v; } }
+/* Create a LINK to point to VAR and free the old link.
+ * BUT this is more clever - it tries to keep the old link if it's not owned to save allocations */
+#define CREATE_LINK(LINK, VAR) { if (!LINK || LINK->owned) LINK = new CScriptVarLink(VAR); else LINK->replaceWith(VAR); }
+
+#include <string>
+#include <string.h>
+#include <sstream>
+#include <cstdlib>
+#include <stdio.h>
+
+using namespace std;
+
+#ifdef _WIN32
+#ifdef _DEBUG
+   #ifndef DBG_NEW
+      #define DBG_NEW new ( _NORMAL_BLOCK , __FILE__ , __LINE__ )
+      #define new DBG_NEW
+   #endif
+#endif
+#endif
+
+#ifdef __GNUC__
+#define vsprintf_s vsnprintf
+#define sprintf_s snprintf
+#ifndef MBED
+#define _strdup strdup
+#else
+char *_strdup(const char *str)
+{
+    size_t siz;
+    char *copy;
+
+    siz = strlen(str) + 1;
+    if ((copy = (char *)malloc(siz)) == NULL)
+        return(NULL);
+    (void)memcpy(copy, str, siz);
+    return(copy);
+}
+#endif
+#endif
+
+#ifdef MBED
+extern int mbedErrorFlag;
+extern std::string mbedErrorMessage;  
+
+#define LMATCH_VOID(c)  {mbedErrorFlag = 0;l->match(c);if (mbedErrorFlag != 0) return;}
+#define LMATCH(c)  {mbedErrorFlag = 0;l->match(c);if (mbedErrorFlag != 0) return 0;}
+//#define LMATCH_VOID(c)  {l->match(c);}
+//#define LMATCH(c)  {l->match(c);}
+#endif
+
+
+// ----------------------------------------------------------------------------------- Memory Debug
+
+#if DEBUG_MEMORY
+
+vector<CScriptVar*> allocatedVars;
+vector<CScriptVarLink*> allocatedLinks;
+
+void mark_allocated(CScriptVar *v) {
+    allocatedVars.push_back(v);
+}
+
+void mark_deallocated(CScriptVar *v) {
+    for (size_t i=0;i<allocatedVars.size();i++) {
+      if (allocatedVars[i] == v) {
+        allocatedVars.erase(allocatedVars.begin()+i);
+        break;
+      }
+    }
+}
+
+void mark_allocated(CScriptVarLink *v) {
+    allocatedLinks.push_back(v);
+}
+
+void mark_deallocated(CScriptVarLink *v) {
+    for (size_t i=0;i<allocatedLinks.size();i++) {
+      if (allocatedLinks[i] == v) {
+        allocatedLinks.erase(allocatedLinks.begin()+i);
+        break;
+      }
+    }
+}
+
+void show_allocated() {
+    for (size_t i=0;i<allocatedVars.size();i++) {
+      printf("ALLOCATED, %d refs\n", allocatedVars[i]->getRefs());
+      allocatedVars[i]->trace("  ");
+    }
+    for (size_t i=0;i<allocatedLinks.size();i++) {
+      printf("ALLOCATED LINK %s, allocated[%d] to \n", allocatedLinks[i]->name.c_str(), allocatedLinks[i]->var->getRefs());
+      allocatedLinks[i]->var->trace("  ");
+    }
+    allocatedVars.clear();
+    allocatedLinks.clear();
+}
+#endif
+
+// ----------------------------------------------------------------------------------- Utils
+bool isWhitespace(char ch) {
+    return (ch==' ') || (ch=='\t') || (ch=='\n') || (ch=='\r');
+}
+
+bool isNumeric(char ch) {
+    return (ch>='0') && (ch<='9');
+}
+bool isNumber(const string &str) {
+    for (size_t i=0;i<str.size();i++)
+      if (!isNumeric(str[i])) return false;
+    return true;
+}
+bool isHexadecimal(char ch) {
+    return ((ch>='0') && (ch<='9')) ||
+           ((ch>='a') && (ch<='f')) ||
+           ((ch>='A') && (ch<='F'));
+}
+bool isAlpha(char ch) {
+    return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_';
+}
+
+bool isIDString(const char *s) {
+    if (!isAlpha(*s))
+        return false;
+    while (*s) {
+        if (!(isAlpha(*s) || isNumeric(*s)))
+            return false;
+        s++;
+    }
+    return true;
+}
+
+void replace(string &str, char textFrom, const char *textTo) {
+    int sLen = strlen(textTo);
+    size_t p = str.find(textFrom);
+    while (p != string::npos) {
+        str = str.substr(0, p) + textTo + str.substr(p+1);
+        p = str.find(textFrom, p+sLen);
+    }
+}
+
+/// convert the given string into a quoted string suitable for javascript
+std::string getJSString(const std::string &str) {
+    std::string nStr = str;
+    for (size_t i=0;i<nStr.size();i++) {
+      const char *replaceWith = "";
+      bool replace = true;
+
+      switch (nStr[i]) {
+        case '\\': replaceWith = "\\\\"; break;
+        case '\n': replaceWith = "\\n"; break;
+        case '\r': replaceWith = "\\r"; break;
+        case '\a': replaceWith = "\\a"; break;
+        case '"': replaceWith = "\\\""; break;
+        default: {
+          int nCh = ((int)nStr[i]) &0xFF;
+          if (nCh<32 || nCh>127) {
+            char buffer[5];
+            sprintf_s(buffer, 5, "\\x%02X", nCh);
+            replaceWith = buffer;
+          } else replace=false;
+        }
+      }
+
+      if (replace) {
+        nStr = nStr.substr(0, i) + replaceWith + nStr.substr(i+1);
+        i += strlen(replaceWith)-1;
+      }
+    }
+    return "\"" + nStr + "\"";
+}
+
+/** Is the string alphanumeric */
+bool isAlphaNum(const std::string &str) {
+    if (str.size()==0) return true;
+    if (!isAlpha(str[0])) return false;
+    for (size_t i=0;i<str.size();i++)
+      if (!(isAlpha(str[i]) || isNumeric(str[i])))
+        return false;
+    return true;
+}
+
+// ----------------------------------------------------------------------------------- CSCRIPTEXCEPTION
+
+CScriptException::CScriptException(const string &exceptionText) {
+    text = exceptionText;
+}
+
+// ----------------------------------------------------------------------------------- CSCRIPTLEX
+
+CScriptLex::CScriptLex(const string &input) {
+    data = _strdup(input.c_str());
+    dataOwned = true;
+    dataStart = 0;
+    dataEnd = strlen(data);
+    reset();
+}
+
+CScriptLex::CScriptLex(CScriptLex *owner, int startChar, int endChar) {
+    data = owner->data;
+    dataOwned = false;
+    dataStart = startChar;
+    dataEnd = endChar;
+    reset();
+}
+
+CScriptLex::~CScriptLex(void)
+{
+    if (dataOwned)
+        free((void*)data);
+}
+
+void CScriptLex::reset() {
+    dataPos = dataStart;
+    tokenStart = 0;
+    tokenEnd = 0;
+    tokenLastEnd = 0;
+    tk = 0;
+    tkStr = "";
+    getNextCh();
+    getNextCh();
+    getNextToken();
+}
+
+void CScriptLex::match(int expected_tk) {
+    if (tk!=expected_tk) {
+#ifndef MBED
+        ostringstream errorString;
+        errorString << "Got " << getTokenStr(tk) << " expected " << getTokenStr(expected_tk)
+         << " at " << getPosition(tokenStart);
+        throw new CScriptException(errorString.str());
+#else
+        mbedErrorFlag = 1;
+        mbedErrorMessage = "Got ";
+        mbedErrorMessage += getTokenStr(tk);
+        mbedErrorMessage += " expected ";
+        mbedErrorMessage += getTokenStr(expected_tk);
+        mbedErrorMessage += " at ";
+        mbedErrorMessage += getPosition(tokenStart);
+        return;
+#endif
+    }
+    getNextToken();
+}
+
+string CScriptLex::getTokenStr(int token) {
+    if (token>32 && token<128) {
+        char buf[4] = "' '";
+        buf[1] = (char)token;
+        return buf;
+    }
+    switch (token) {
+        case LEX_EOF : return "EOF";
+        case LEX_ID : return "ID";
+        case LEX_INT : return "INT";
+        case LEX_FLOAT : return "FLOAT";
+        case LEX_STR : return "STRING";
+        case LEX_EQUAL : return "==";
+        case LEX_TYPEEQUAL : return "===";
+        case LEX_NEQUAL : return "!=";
+        case LEX_NTYPEEQUAL : return "!==";
+        case LEX_LEQUAL : return "<=";
+        case LEX_LSHIFT : return "<<";
+        case LEX_LSHIFTEQUAL : return "<<=";
+        case LEX_GEQUAL : return ">=";
+        case LEX_RSHIFT : return ">>";
+        case LEX_RSHIFTUNSIGNED : return ">>";
+        case LEX_RSHIFTEQUAL : return ">>=";
+        case LEX_PLUSEQUAL : return "+=";
+        case LEX_MINUSEQUAL : return "-=";
+        case LEX_PLUSPLUS : return "++";
+        case LEX_MINUSMINUS : return "--";
+        case LEX_ANDEQUAL : return "&=";
+        case LEX_ANDAND : return "&&";
+        case LEX_OREQUAL : return "|=";
+        case LEX_OROR : return "||";
+        case LEX_XOREQUAL : return "^=";
+                // reserved words
+        case LEX_R_IF : return "if";
+        case LEX_R_ELSE : return "else";
+        case LEX_R_DO : return "do";
+        case LEX_R_WHILE : return "while";
+        case LEX_R_FOR : return "for";
+        case LEX_R_BREAK : return "break";
+        case LEX_R_CONTINUE : return "continue";
+        case LEX_R_FUNCTION : return "function";
+        case LEX_R_RETURN : return "return";
+        case LEX_R_VAR : return "var";
+        case LEX_R_TRUE : return "true";
+        case LEX_R_FALSE : return "false";
+        case LEX_R_NULL : return "null";
+        case LEX_R_UNDEFINED : return "undefined";
+        case LEX_R_NEW : return "new";
+    }
+
+#ifndef MBED
+    ostringstream msg;
+    msg << "?[" << token << "]";
+    return msg.str();
+#else
+    string msg;
+    msg = "?[";
+    msg += token;
+    msg += "]";
+    return msg;
+#endif
+}
+
+void CScriptLex::getNextCh() {
+    currCh = nextCh;
+    if (dataPos < dataEnd)
+        nextCh = data[dataPos];
+    else
+        nextCh = 0;
+    dataPos++;
+}
+
+void CScriptLex::getNextToken() {
+    tk = LEX_EOF;
+    tkStr.clear();
+    while (currCh && isWhitespace(currCh)) getNextCh();
+    // newline comments
+    if (currCh=='/' && nextCh=='/') {
+        while (currCh && currCh!='\n') getNextCh();
+        getNextCh();
+        getNextToken();
+        return;
+    }
+    // block comments
+    if (currCh=='/' && nextCh=='*') {
+        while (currCh && (currCh!='*' || nextCh!='/')) getNextCh();
+        getNextCh();
+        getNextCh();
+        getNextToken();
+        return;
+    }
+    // record beginning of this token
+    tokenStart = dataPos-2;
+    // tokens
+    if (isAlpha(currCh)) { //  IDs
+        while (isAlpha(currCh) || isNumeric(currCh)) {
+            tkStr += currCh;
+            getNextCh();
+        }
+        tk = LEX_ID;
+             if (tkStr=="if") tk = LEX_R_IF;
+        else if (tkStr=="else") tk = LEX_R_ELSE;
+        else if (tkStr=="do") tk = LEX_R_DO;
+        else if (tkStr=="while") tk = LEX_R_WHILE;
+        else if (tkStr=="for") tk = LEX_R_FOR;
+        else if (tkStr=="break") tk = LEX_R_BREAK;
+        else if (tkStr=="continue") tk = LEX_R_CONTINUE;
+        else if (tkStr=="function") tk = LEX_R_FUNCTION;
+        else if (tkStr=="return") tk = LEX_R_RETURN;
+        else if (tkStr=="var") tk = LEX_R_VAR;
+        else if (tkStr=="true") tk = LEX_R_TRUE;
+        else if (tkStr=="false") tk = LEX_R_FALSE;
+        else if (tkStr=="null") tk = LEX_R_NULL;
+        else if (tkStr=="undefined") tk = LEX_R_UNDEFINED;
+        else if (tkStr=="new") tk = LEX_R_NEW;
+    } else if (isNumeric(currCh)) { // Numbers
+        bool isHex = false;
+        if (currCh=='0') { tkStr += currCh; getNextCh(); }
+        if (currCh=='x') {
+          isHex = true;
+          tkStr += currCh; getNextCh();
+        }
+        tk = LEX_INT;
+        while (isNumeric(currCh) || (isHex && isHexadecimal(currCh))) {
+            tkStr += currCh;
+            getNextCh();
+        }
+        if (!isHex && currCh=='.') {
+            tk = LEX_FLOAT;
+            tkStr += '.';
+            getNextCh();
+            while (isNumeric(currCh)) {
+                tkStr += currCh;
+                getNextCh();
+            }
+        }
+        // do fancy e-style floating point
+        if (!isHex && (currCh=='e'||currCh=='E')) {
+          tk = LEX_FLOAT;
+          tkStr += currCh; getNextCh();
+          if (currCh=='-') { tkStr += currCh; getNextCh(); }
+          while (isNumeric(currCh)) {
+             tkStr += currCh; getNextCh();
+          }
+        }
+    } else if (currCh=='"') {
+        // strings...
+        getNextCh();
+        while (currCh && currCh!='"') {
+            if (currCh == '\\') {
+                getNextCh();
+                switch (currCh) {
+                case 'n' : tkStr += '\n'; break;
+                case '"' : tkStr += '"'; break;
+                case '\\' : tkStr += '\\'; break;
+                default: tkStr += currCh;
+                }
+            } else {
+                tkStr += currCh;
+            }
+            getNextCh();
+        }
+        getNextCh();
+        tk = LEX_STR;
+    } else if (currCh=='\'') {
+        // strings again...
+        getNextCh();
+        while (currCh && currCh!='\'') {
+            if (currCh == '\\') {
+                getNextCh();
+                switch (currCh) {
+                case 'n' : tkStr += '\n'; break;
+                case 'a' : tkStr += '\a'; break;
+                case 'r' : tkStr += '\r'; break;
+                case 't' : tkStr += '\t'; break;
+                case '\'' : tkStr += '\''; break;
+                case '\\' : tkStr += '\\'; break;
+                case 'x' : { // hex digits
+                              char buf[3] = "??";
+                              getNextCh(); buf[0] = currCh;
+                              getNextCh(); buf[1] = currCh;
+                              tkStr += (char)strtol(buf,0,16);
+                           } break;
+                default: if (currCh>='0' && currCh<='7') {
+                           // octal digits
+                           char buf[4] = "???";
+                           buf[0] = currCh;
+                           getNextCh(); buf[1] = currCh;
+                           getNextCh(); buf[2] = currCh;
+                           tkStr += (char)strtol(buf,0,8);
+                         } else
+                           tkStr += currCh;
+                }
+            } else {
+                tkStr += currCh;
+            }
+            getNextCh();
+        }
+        getNextCh();
+        tk = LEX_STR;
+    } else {
+        // single chars
+        tk = currCh;
+        if (currCh) getNextCh();
+        if (tk=='=' && currCh=='=') { // ==
+            tk = LEX_EQUAL;
+            getNextCh();
+            if (currCh=='=') { // ===
+              tk = LEX_TYPEEQUAL;
+              getNextCh();
+            }
+        } else if (tk=='!' && currCh=='=') { // !=
+            tk = LEX_NEQUAL;
+            getNextCh();
+            if (currCh=='=') { // !==
+              tk = LEX_NTYPEEQUAL;
+              getNextCh();
+            }
+        } else if (tk=='<' && currCh=='=') {
+            tk = LEX_LEQUAL;
+            getNextCh();
+        } else if (tk=='<' && currCh=='<') {
+            tk = LEX_LSHIFT;
+            getNextCh();
+            if (currCh=='=') { // <<=
+              tk = LEX_LSHIFTEQUAL;
+              getNextCh();
+            }
+        } else if (tk=='>' && currCh=='=') {
+            tk = LEX_GEQUAL;
+            getNextCh();
+        } else if (tk=='>' && currCh=='>') {
+            tk = LEX_RSHIFT;
+            getNextCh();
+            if (currCh=='=') { // >>=
+              tk = LEX_RSHIFTEQUAL;
+              getNextCh();
+            } else if (currCh=='>') { // >>>
+              tk = LEX_RSHIFTUNSIGNED;
+              getNextCh();
+            }
+        }  else if (tk=='+' && currCh=='=') {
+            tk = LEX_PLUSEQUAL;
+            getNextCh();
+        }  else if (tk=='-' && currCh=='=') {
+            tk = LEX_MINUSEQUAL;
+            getNextCh();
+        }  else if (tk=='+' && currCh=='+') {
+            tk = LEX_PLUSPLUS;
+            getNextCh();
+        }  else if (tk=='-' && currCh=='-') {
+            tk = LEX_MINUSMINUS;
+            getNextCh();
+        } else if (tk=='&' && currCh=='=') {
+            tk = LEX_ANDEQUAL;
+            getNextCh();
+        } else if (tk=='&' && currCh=='&') {
+            tk = LEX_ANDAND;
+            getNextCh();
+        } else if (tk=='|' && currCh=='=') {
+            tk = LEX_OREQUAL;
+            getNextCh();
+        } else if (tk=='|' && currCh=='|') {
+            tk = LEX_OROR;
+            getNextCh();
+        } else if (tk=='^' && currCh=='=') {
+            tk = LEX_XOREQUAL;
+            getNextCh();
+        }
+    }
+    /* This isn't quite right yet */
+    tokenLastEnd = tokenEnd;
+    tokenEnd = dataPos-3;
+}
+
+string CScriptLex::getSubString(int lastPosition) {
+    int lastCharIdx = tokenLastEnd+1;
+    if (lastCharIdx < dataEnd) {
+        /* save a memory alloc by using our data array to create the
+           substring */
+        char old = data[lastCharIdx];
+        data[lastCharIdx] = 0;
+        std::string value = &data[lastPosition];
+        data[lastCharIdx] = old;
+        return value;
+    } else {
+        return std::string(&data[lastPosition]);
+    }
+}
+
+
+CScriptLex *CScriptLex::getSubLex(int lastPosition) {
+    int lastCharIdx = tokenLastEnd+1;
+    if (lastCharIdx < dataEnd)
+        return new CScriptLex(this, lastPosition, lastCharIdx);
+    else
+        return new CScriptLex(this, lastPosition, dataEnd );
+}
+
+string CScriptLex::getPosition(int pos) {
+    if (pos<0) pos=tokenLastEnd;
+    int line = 1,col = 1;
+    for (int i=0;i<pos;i++) {
+        char ch;
+        if (i < dataEnd)
+            ch = data[i];
+        else
+            ch = 0;
+        col++;
+        if (ch=='\n') {
+            line++;
+            col = 0;
+        }
+    }
+    char buf[256];
+    sprintf_s(buf, 256, "(line: %d, col: %d)", line, col);
+    return buf;
+}
+
+// ----------------------------------------------------------------------------------- CSCRIPTVARLINK
+
+CScriptVarLink::CScriptVarLink(CScriptVar *var, const std::string &name) {
+#if DEBUG_MEMORY
+    mark_allocated(this);
+#endif
+    this->name = name;
+    this->nextSibling = 0;
+    this->prevSibling = 0;
+    this->var = var->ref();
+    this->owned = false;
+}
+
+CScriptVarLink::CScriptVarLink(const CScriptVarLink &link) {
+    // Copy constructor
+#if DEBUG_MEMORY
+    mark_allocated(this);
+#endif
+    this->name = link.name;
+    this->nextSibling = 0;
+    this->prevSibling = 0;
+    this->var = link.var->ref();
+    this->owned = false;
+}
+
+CScriptVarLink::~CScriptVarLink() {
+#if DEBUG_MEMORY
+    mark_deallocated(this);
+#endif
+    var->unref();
+}
+
+void CScriptVarLink::replaceWith(CScriptVar *newVar) {
+    CScriptVar *oldVar = var;
+    var = newVar->ref();
+    oldVar->unref();
+}
+
+void CScriptVarLink::replaceWith(CScriptVarLink *newVar) {
+    if (newVar)
+      replaceWith(newVar->var);
+    else
+      replaceWith(new CScriptVar());
+}
+
+int CScriptVarLink::getIntName() {
+    return atoi(name.c_str());
+}
+void CScriptVarLink::setIntName(int n) {
+    char sIdx[64];
+    sprintf_s(sIdx, sizeof(sIdx), "%d", n);
+    name = sIdx;
+}
+
+// ----------------------------------------------------------------------------------- CSCRIPTVAR
+
+CScriptVar::CScriptVar() {
+    refs = 0;
+#if DEBUG_MEMORY
+    mark_allocated(this);
+#endif
+    init();
+    flags = SCRIPTVAR_UNDEFINED;
+}
+
+CScriptVar::CScriptVar(const string &str) {
+    refs = 0;
+#if DEBUG_MEMORY
+    mark_allocated(this);
+#endif
+    init();
+    flags = SCRIPTVAR_STRING;
+    data = str;
+}
+
+
+CScriptVar::CScriptVar(const string &varData, int varFlags) {
+    refs = 0;
+#if DEBUG_MEMORY
+    mark_allocated(this);
+#endif
+    init();
+    flags = varFlags;
+    if (varFlags & SCRIPTVAR_INTEGER) {
+      intData = strtol(varData.c_str(),0,0);
+    } else if (varFlags & SCRIPTVAR_DOUBLE) {
+      doubleData = strtod(varData.c_str(),0);
+    } else
+      data = varData;
+}
+
+CScriptVar::CScriptVar(double val) {
+    refs = 0;
+#if DEBUG_MEMORY
+    mark_allocated(this);
+#endif
+    init();
+    setDouble(val);
+}
+
+CScriptVar::CScriptVar(int val) {
+    refs = 0;
+#if DEBUG_MEMORY
+    mark_allocated(this);
+#endif
+    init();
+    setInt(val);
+}
+
+CScriptVar::~CScriptVar(void) {
+#if DEBUG_MEMORY
+    mark_deallocated(this);
+#endif
+    removeAllChildren();
+}
+
+void CScriptVar::init() {
+    firstChild = 0;
+    lastChild = 0;
+    flags = 0;
+    jsCallback = 0;
+    jsCallbackUserData = 0;
+    data = TINYJS_BLANK_DATA;
+    intData = 0;
+    doubleData = 0;
+}
+
+CScriptVar *CScriptVar::getReturnVar() {
+    return getParameter(TINYJS_RETURN_VAR);
+}
+
+void CScriptVar::setReturnVar(CScriptVar *var) {
+    findChildOrCreate(TINYJS_RETURN_VAR)->replaceWith(var);
+}
+
+
+CScriptVar *CScriptVar::getParameter(const std::string &name) {
+    return findChildOrCreate(name)->var;
+}
+
+CScriptVarLink *CScriptVar::findChild(const string &childName) {
+    CScriptVarLink *v = firstChild;
+    while (v) {
+        if (v->name.compare(childName)==0)
+            return v;
+        v = v->nextSibling;
+    }
+    return 0;
+}
+
+CScriptVarLink *CScriptVar::findChildOrCreate(const string &childName, int varFlags) {
+    CScriptVarLink *l = findChild(childName);
+    if (l) return l;
+
+    return addChild(childName, new CScriptVar(TINYJS_BLANK_DATA, varFlags));
+}
+
+CScriptVarLink *CScriptVar::findChildOrCreateByPath(const std::string &path) {
+  size_t p = path.find('.');
+  if (p == string::npos)
+    return findChildOrCreate(path);
+
+  return findChildOrCreate(path.substr(0,p), SCRIPTVAR_OBJECT)->var->
+            findChildOrCreateByPath(path.substr(p+1));
+}
+
+CScriptVarLink *CScriptVar::addChild(const std::string &childName, CScriptVar *child) {
+  if (isUndefined()) {
+    flags = SCRIPTVAR_OBJECT;
+  }
+    // if no child supplied, create one
+    if (!child)
+      child = new CScriptVar();
+
+    CScriptVarLink *link = new CScriptVarLink(child, childName);
+    link->owned = true;
+    if (lastChild) {
+        lastChild->nextSibling = link;
+        link->prevSibling = lastChild;
+        lastChild = link;
+    } else {
+        firstChild = link;
+        lastChild = link;
+    }
+    return link;
+}
+
+CScriptVarLink *CScriptVar::addChildNoDup(const std::string &childName, CScriptVar *child) {
+    // if no child supplied, create one
+    if (!child)
+      child = new CScriptVar();
+
+    CScriptVarLink *v = findChild(childName);
+    if (v) {
+        v->replaceWith(child);
+    } else {
+        v = addChild(childName, child);
+    }
+
+    return v;
+}
+
+void CScriptVar::removeChild(CScriptVar *child) {
+    CScriptVarLink *link = firstChild;
+    while (link) {
+        if (link->var == child)
+            break;
+        link = link->nextSibling;
+    }
+    ASSERT(link);
+    removeLink(link);
+}
+
+void CScriptVar::removeLink(CScriptVarLink *link) {
+    if (!link) return;
+    if (link->nextSibling)
+      link->nextSibling->prevSibling = link->prevSibling;
+    if (link->prevSibling)
+      link->prevSibling->nextSibling = link->nextSibling;
+    if (lastChild == link)
+        lastChild = link->prevSibling;
+    if (firstChild == link)
+        firstChild = link->nextSibling;
+    delete link;
+}
+
+void CScriptVar::removeAllChildren() {
+    CScriptVarLink *c = firstChild;
+    while (c) {
+        CScriptVarLink *t = c->nextSibling;
+        delete c;
+        c = t;
+    }
+    firstChild = 0;
+    lastChild = 0;
+}
+
+CScriptVar *CScriptVar::getArrayIndex(int idx) {
+    char sIdx[64];
+    sprintf_s(sIdx, sizeof(sIdx), "%d", idx);
+    CScriptVarLink *link = findChild(sIdx);
+    if (link) return link->var;
+    else return new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_NULL); // undefined
+}
+
+void CScriptVar::setArrayIndex(int idx, CScriptVar *value) {
+    char sIdx[64];
+    sprintf_s(sIdx, sizeof(sIdx), "%d", idx);
+    CScriptVarLink *link = findChild(sIdx);
+
+    if (link) {
+      if (value->isUndefined())
+        removeLink(link);
+      else
+        link->replaceWith(value);
+    } else {
+      if (!value->isUndefined())
+        addChild(sIdx, value);
+    }
+}
+
+int CScriptVar::getArrayLength() {
+    int highest = -1;
+    if (!isArray()) return 0;
+
+    CScriptVarLink *link = firstChild;
+    while (link) {
+      if (isNumber(link->name)) {
+        int val = atoi(link->name.c_str());
+        if (val > highest) highest = val;
+      }
+      link = link->nextSibling;
+    }
+    return highest+1;
+}
+
+int CScriptVar::getChildren() {
+    int n = 0;
+    CScriptVarLink *link = firstChild;
+    while (link) {
+      n++;
+      link = link->nextSibling;
+    }
+    return n;
+}
+
+int CScriptVar::getInt() {
+    /* strtol understands about hex and octal */
+    if (isInt()) return intData;
+    if (isNull()) return 0;
+    if (isUndefined()) return 0;
+    if (isDouble()) return (int)doubleData;
+    return 0;
+}
+
+double CScriptVar::getDouble() {
+    if (isDouble()) return doubleData;
+    if (isInt()) return intData;
+    if (isNull()) return 0;
+    if (isUndefined()) return 0;
+    return 0; /* or NaN? */
+}
+
+const string &CScriptVar::getString() {
+    /* Because we can't return a string that is generated on demand.
+     * I should really just use char* :) */
+    static string s_null = "null";
+    static string s_undefined = "undefined";
+    if (isInt()) {
+      char buffer[32];
+      sprintf_s(buffer, sizeof(buffer), "%ld", intData);
+      data = buffer;
+      return data;
+    }
+    if (isDouble()) {
+      char buffer[32];
+      sprintf_s(buffer, sizeof(buffer), "%f", doubleData);
+      data = buffer;
+      return data;
+    }
+    if (isNull()) return s_null;
+    if (isUndefined()) return s_undefined;
+    // are we just a string here?
+    return data;
+}
+
+void CScriptVar::setInt(int val) {
+    flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_INTEGER;
+    intData = val;
+    doubleData = 0;
+    data = TINYJS_BLANK_DATA;
+}
+
+void CScriptVar::setDouble(double val) {
+    flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_DOUBLE;
+    doubleData = val;
+    intData = 0;
+    data = TINYJS_BLANK_DATA;
+}
+
+void CScriptVar::setString(const string &str) {
+    // name sure it's not still a number or integer
+    flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_STRING;
+    data = str;
+    intData = 0;
+    doubleData = 0;
+}
+
+void CScriptVar::setUndefined() {
+    // name sure it's not still a number or integer
+    flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_UNDEFINED;
+    data = TINYJS_BLANK_DATA;
+    intData = 0;
+    doubleData = 0;
+    removeAllChildren();
+}
+
+void CScriptVar::setArray() {
+    // name sure it's not still a number or integer
+    flags = (flags&~SCRIPTVAR_VARTYPEMASK) | SCRIPTVAR_ARRAY;
+    data = TINYJS_BLANK_DATA;
+    intData = 0;
+    doubleData = 0;
+    removeAllChildren();
+}
+
+bool CScriptVar::equals(CScriptVar *v) {
+    CScriptVar *resV = mathsOp(v, LEX_EQUAL);
+    bool res = resV->getBool();
+    delete resV;
+    return res;
+}
+
+CScriptVar *CScriptVar::mathsOp(CScriptVar *b, int op) {
+    CScriptVar *a = this;
+    // Type equality check
+    if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) {
+      // check type first, then call again to check data
+      bool eql = ((a->flags & SCRIPTVAR_VARTYPEMASK) ==
+                  (b->flags & SCRIPTVAR_VARTYPEMASK));
+      if (eql) {
+        CScriptVar *contents = a->mathsOp(b, LEX_EQUAL);
+        if (!contents->getBool()) eql = false;
+        if (!contents->refs) delete contents;
+      }
+                 ;
+      if (op == LEX_TYPEEQUAL)
+        return new CScriptVar(eql);
+      else
+        return new CScriptVar(!eql);
+    }
+    // do maths...
+    if (a->isUndefined() && b->isUndefined()) {
+      if (op == LEX_EQUAL) return new CScriptVar(true);
+      else if (op == LEX_NEQUAL) return new CScriptVar(false);
+      else return new CScriptVar(); // undefined
+    } else if ((a->isNumeric() || a->isUndefined()) &&
+               (b->isNumeric() || b->isUndefined())) {
+        if (!a->isDouble() && !b->isDouble()) {
+            // use ints
+            int da = a->getInt();
+            int db = b->getInt();
+            switch (op) {
+                case '+': return new CScriptVar(da+db);
+                case '-': return new CScriptVar(da-db);
+                case '*': return new CScriptVar(da*db);
+                case '/': return new CScriptVar(da/db);
+                case '&': return new CScriptVar(da&db);
+                case '|': return new CScriptVar(da|db);
+                case '^': return new CScriptVar(da^db);
+                case '%': return new CScriptVar(da%db);
+                case LEX_EQUAL:     return new CScriptVar(da==db);
+                case LEX_NEQUAL:    return new CScriptVar(da!=db);
+                case '<':     return new CScriptVar(da<db);
+                case LEX_LEQUAL:    return new CScriptVar(da<=db);
+                case '>':     return new CScriptVar(da>db);
+                case LEX_GEQUAL:    return new CScriptVar(da>=db);
+#ifndef MBED
+                default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the Int datatype");
+#else
+                default:
+                    mbedErrorFlag = 1;
+                    mbedErrorMessage = "Operation ";
+                    mbedErrorMessage += CScriptLex::getTokenStr(op);
+                    mbedErrorMessage += " not supported on the Int datatype";
+                    return 0;
+#endif
+            }
+        } else {
+            // use doubles
+            double da = a->getDouble();
+            double db = b->getDouble();
+            switch (op) {
+                case '+': return new CScriptVar(da+db);
+                case '-': return new CScriptVar(da-db);
+                case '*': return new CScriptVar(da*db);
+                case '/': return new CScriptVar(da/db);
+                case LEX_EQUAL:     return new CScriptVar(da==db);
+                case LEX_NEQUAL:    return new CScriptVar(da!=db);
+                case '<':     return new CScriptVar(da<db);
+                case LEX_LEQUAL:    return new CScriptVar(da<=db);
+                case '>':     return new CScriptVar(da>db);
+                case LEX_GEQUAL:    return new CScriptVar(da>=db);
+#ifndef MBED
+                default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the Double datatype");
+#else
+                default:
+                    mbedErrorFlag = 1;
+                    mbedErrorMessage = "Operation ";
+                    mbedErrorMessage += CScriptLex::getTokenStr(op);
+                    mbedErrorMessage += " not supported on the Double datatype";
+                    return 0;
+#endif
+            }
+        }
+    } else if (a->isArray()) {
+      /* Just check pointers */
+      switch (op) {
+           case LEX_EQUAL: return new CScriptVar(a==b);
+           case LEX_NEQUAL: return new CScriptVar(a!=b);
+#ifndef MBED
+           default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the Array datatype");
+#else
+           default:
+             mbedErrorFlag = 1;;
+             mbedErrorMessage = "Operation ";
+             mbedErrorMessage += CScriptLex::getTokenStr(op);
+             mbedErrorMessage += " not supported on the Array datatype";
+             return 0;
+
+#endif
+      }
+    } else if (a->isObject()) {
+          /* Just check pointers */
+          switch (op) {
+               case LEX_EQUAL: return new CScriptVar(a==b);
+               case LEX_NEQUAL: return new CScriptVar(a!=b);
+#ifndef MBED
+               default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the Object datatype");
+#else
+               default:
+                 mbedErrorFlag = 1;
+                 mbedErrorMessage = "Operation ";
+                 mbedErrorMessage += CScriptLex::getTokenStr(op);
+                 mbedErrorMessage += " not supported on the Object datatype";
+                 return 0;
+#endif
+          }
+    } else {
+       string da = a->getString();
+       string db = b->getString();
+       // use strings
+       switch (op) {
+           case '+':           return new CScriptVar(da+db, SCRIPTVAR_STRING);
+           case LEX_EQUAL:     return new CScriptVar(da==db);
+           case LEX_NEQUAL:    return new CScriptVar(da!=db);
+           case '<':     return new CScriptVar(da<db);
+           case LEX_LEQUAL:    return new CScriptVar(da<=db);
+           case '>':     return new CScriptVar(da>db);
+           case LEX_GEQUAL:    return new CScriptVar(da>=db);
+#ifndef MBED
+           default: throw new CScriptException("Operation "+CScriptLex::getTokenStr(op)+" not supported on the string datatype");
+#else
+           default:
+             mbedErrorFlag = 1;
+             mbedErrorMessage = "Operation ";
+             mbedErrorMessage += CScriptLex::getTokenStr(op);
+             mbedErrorMessage += " not supported on the string datatype";
+             return 0;
+#endif
+       }
+    }
+    ASSERT(0);
+    return 0;
+}
+
+void CScriptVar::copySimpleData(CScriptVar *val) {
+    data = val->data;
+    intData = val->intData;
+    doubleData = val->doubleData;
+    flags = (flags & ~SCRIPTVAR_VARTYPEMASK) | (val->flags & SCRIPTVAR_VARTYPEMASK);
+}
+
+void CScriptVar::copyValue(CScriptVar *val) {
+    if (val) {
+      copySimpleData(val);
+      // remove all current children
+      removeAllChildren();
+      // copy children of 'val'
+      CScriptVarLink *child = val->firstChild;
+      while (child) {
+        CScriptVar *copied;
+        // don't copy the 'parent' object...
+        if (child->name != TINYJS_PROTOTYPE_CLASS)
+          copied = child->var->deepCopy();
+        else
+          copied = child->var;
+
+        addChild(child->name, copied);
+
+        child = child->nextSibling;
+      }
+    } else {
+      setUndefined();
+    }
+}
+
+CScriptVar *CScriptVar::deepCopy() {
+    CScriptVar *newVar = new CScriptVar();
+    newVar->copySimpleData(this);
+    // copy children
+    CScriptVarLink *child = firstChild;
+    while (child) {
+        CScriptVar *copied;
+        // don't copy the 'parent' object...
+        if (child->name != TINYJS_PROTOTYPE_CLASS)
+          copied = child->var->deepCopy();
+        else
+          copied = child->var;
+
+        newVar->addChild(child->name, copied);
+        child = child->nextSibling;
+    }
+    return newVar;
+}
+
+void CScriptVar::trace(string indentStr, const string &name) {
+    TRACE("%s'%s' = '%s' %s\n",
+        indentStr.c_str(),
+        name.c_str(),
+        getString().c_str(),
+        getFlagsAsString().c_str());
+    string indent = indentStr+" ";
+    CScriptVarLink *link = firstChild;
+    while (link) {
+      link->var->trace(indent, link->name);
+      link = link->nextSibling;
+    }
+}
+
+string CScriptVar::getFlagsAsString() {
+  string flagstr = "";
+  if (flags&SCRIPTVAR_FUNCTION) flagstr = flagstr + "FUNCTION ";
+  if (flags&SCRIPTVAR_OBJECT) flagstr = flagstr + "OBJECT ";
+  if (flags&SCRIPTVAR_ARRAY) flagstr = flagstr + "ARRAY ";
+  if (flags&SCRIPTVAR_NATIVE) flagstr = flagstr + "NATIVE ";
+  if (flags&SCRIPTVAR_DOUBLE) flagstr = flagstr + "DOUBLE ";
+  if (flags&SCRIPTVAR_INTEGER) flagstr = flagstr + "INTEGER ";
+  if (flags&SCRIPTVAR_STRING) flagstr = flagstr + "STRING ";
+  return flagstr;
+}
+
+string CScriptVar::getParsableString() {
+  // Numbers can just be put in directly
+  if (isNumeric())
+    return getString();
+  if (isFunction()) {
+#ifndef MBED
+    ostringstream funcStr;
+    funcStr << "function (";
+    // get list of parameters
+    CScriptVarLink *link = firstChild;
+    while (link) {
+      funcStr << link->name;
+      if (link->nextSibling) funcStr << ",";
+      link = link->nextSibling;
+    }
+    // add function body
+    funcStr << ") " << getString();
+    return funcStr.str();
+#else
+    string funcStr;
+    funcStr = "function (";
+    CScriptVarLink *link = firstChild;
+    while (link) {
+      funcStr += link->name;
+      if (link->nextSibling)  funcStr += ",";
+      link = link->nextSibling;
+    }
+    // add function body
+    funcStr += ") ";
+    funcStr += getString();
+    return funcStr;
+#endif
+  }
+  // if it is a string then we quote it
+  if (isString())
+    return getJSString(getString());
+  if (isNull())
+      return "null";
+  return "undefined";
+}
+
+#ifndef MBED
+void CScriptVar::getJSON(ostringstream &destination, const string linePrefix) {
+   if (isObject()) {
+      string indentedLinePrefix = linePrefix+"  ";
+      // children - handle with bracketed list
+      destination << "{ \n";
+      CScriptVarLink *link = firstChild;
+      while (link) {
+        destination << indentedLinePrefix;
+        destination  << getJSString(link->name);
+        destination  << " : ";
+        link->var->getJSON(destination, indentedLinePrefix);
+        link = link->nextSibling;
+        if (link) {
+          destination  << ",\n";
+        }
+      }
+      destination << "\n" << linePrefix << "}";
+    } else if (isArray()) {
+      string indentedLinePrefix = linePrefix+"  ";
+      destination << "[\n";
+      int len = getArrayLength();
+      if (len>10000) len=10000; // we don't want to get stuck here!
+
+      for (int i=0;i<len;i++) {
+        getArrayIndex(i)->getJSON(destination, indentedLinePrefix);
+        if (i<len-1) destination  << ",\n";
+      }
+
+      destination << "\n" << linePrefix << "]";
+    } else {
+      // no children or a function... just write value directly
+      destination << getParsableString();
+    }
+}
+#else
+void CScriptVar::getJSON(string &destination, const string linePrefix) {
+   if (isObject()) {
+      string indentedLinePrefix = linePrefix+"  ";
+      // children - handle with bracketed list
+      destination += "{ \n";
+      CScriptVarLink *link = firstChild;
+      while (link) {
+        destination += indentedLinePrefix;
+        destination += getJSString(link->name);
+        destination += " : ";
+        link->var->getJSON(destination, indentedLinePrefix);
+        link = link->nextSibling;
+        if (link) {
+          destination += ",\n";
+        }
+      }
+      destination += "\n";
+      destination += linePrefix;
+      destination += "}";
+    } else if (isArray()) {
+      string indentedLinePrefix = linePrefix+"  ";
+      destination + "[\n";
+      int len = getArrayLength();
+      if (len>10000) len=10000; // we don't want to get stuck here!
+
+      for (int i=0;i<len;i++) {
+        getArrayIndex(i)->getJSON(destination, indentedLinePrefix);
+        if (i<len-1) {
+            destination += ",\n";
+        }
+      }
+
+      destination += "\n";
+      destination += linePrefix;
+      destination += "]";
+    } else {
+      // no children or a function... just write value directly
+      destination += getParsableString();
+    }
+}
+#endif
+
+void CScriptVar::setCallback(JSCallback callback, void *userdata) {
+    jsCallback = callback;
+    jsCallbackUserData = userdata;
+}
+
+CScriptVar *CScriptVar::ref() {
+    refs++;
+    return this;
+}
+
+void CScriptVar::unref() {
+    if (refs<=0) printf("OMFG, we have unreffed too far!\n");
+    if ((--refs)==0) {
+      delete this;
+    }
+}
+
+int CScriptVar::getRefs() {
+    return refs;
+}
+
+
+// ----------------------------------------------------------------------------------- CSCRIPT
+
+CTinyJS::CTinyJS() {
+    l = 0;
+    root = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref();
+    // Add built-in classes
+    stringClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref();
+    arrayClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref();
+    objectClass = (new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT))->ref();
+    root->addChild("String", stringClass);
+    root->addChild("Array", arrayClass);
+    root->addChild("Object", objectClass);
+}
+
+CTinyJS::~CTinyJS() {
+    ASSERT(!l);
+    scopes.clear();
+    stringClass->unref();
+    arrayClass->unref();
+    objectClass->unref();
+    root->unref();
+
+#if DEBUG_MEMORY
+    show_allocated();
+#endif
+}
+
+void CTinyJS::trace() {
+    root->trace();
+}
+
+void CTinyJS::execute(const string &code) {
+    CScriptLex *oldLex = l;
+    vector<CScriptVar*> oldScopes = scopes;
+    l = new CScriptLex(code);
+#ifdef TINYJS_CALL_STACK
+    call_stack.clear();
+#endif
+    scopes.clear();
+    scopes.push_back(root);
+
+#ifndef MBED
+    try {
+        bool execute = true;
+        while (l->tk) statement(execute);
+    } catch (CScriptException *e) {
+        ostringstream msg;
+        msg << "Error " << e->text;
+#ifdef TINYJS_CALL_STACK
+        for (int i=(int)call_stack.size()-1;i>=0;i--)
+          msg << "\n" << i << ": " << call_stack.at(i);
+#endif
+        msg << " at " << l->getPosition();
+        delete l;
+        l = oldLex;
+
+        throw new CScriptException(msg.str());
+    }
+#else
+    {
+      bool execute = true;
+      while (l->tk) {
+        mbedErrorFlag = 0;
+        statement(execute);
+        if (mbedErrorFlag != 0) {
+          string msg;
+          msg = "Error ";
+          msg += mbedErrorMessage;
+#ifdef TINYJS_CALL_STACK
+          for (int i=(int)call_stack.size()-1;i>=0;i--) {
+            msg += "\n";
+            msg += i;
+            msg += ": ";
+            msg += call_stack.at(i);
+          }
+#endif
+          msg += " at ";
+          msg += l->getPosition();
+
+          delete l;
+          l = oldLex;
+
+          mbedErrorFlag = 1;
+          mbedErrorMessage = msg;
+          return;
+        }
+      }
+    }
+#endif
+
+
+    delete l;
+    l = oldLex;
+    scopes = oldScopes;
+}
+
+CScriptVarLink CTinyJS::evaluateComplex(const string &code) {
+    CScriptLex *oldLex = l;
+    vector<CScriptVar*> oldScopes = scopes;
+
+    l = new CScriptLex(code);
+#ifdef TINYJS_CALL_STACK
+    call_stack.clear();
+#endif
+    scopes.clear();
+    scopes.push_back(root);
+    CScriptVarLink *v = 0;
+#ifndef MBED
+    try {
+        bool execute = true;
+        do {
+          CLEAN(v);
+          v = base(execute);
+          if (l->tk!=LEX_EOF) l->match(';');
+        } while (l->tk!=LEX_EOF);
+    } catch (CScriptException *e) {
+      ostringstream msg;
+      msg << "Error " << e->text;
+#ifdef TINYJS_CALL_STACK
+      for (int i=(int)call_stack.size()-1;i>=0;i--)
+        msg << "\n" << i << ": " << call_stack.at(i);
+#endif
+      msg << " at " << l->getPosition();
+      delete l;
+      l = oldLex;
+
+        throw new CScriptException(msg.str());
+    }
+#else
+    {
+      bool execute = true;
+      do {
+        CLEAN(v);
+        mbedErrorFlag = 0;
+        v = base(execute);
+        if (mbedErrorFlag != 0) {
+          string msg;
+          msg = "Error ";
+          msg += mbedErrorMessage;
+#ifdef TINYJS_CALL_STACK
+          for (int i=(int)call_stack.size()-1;i>=0;i--) {
+            msg += "\n";
+            msg += i;
+            msg += ": ";
+            msg += call_stack.at(i);
+          }
+#endif
+          msg += " at ";
+          msg += l->getPosition();
+          delete l;
+          l = oldLex;
+
+          mbedErrorFlag = 1;
+          mbedErrorMessage = msg;
+          return 0;
+        }
+
+        if (l->tk!=LEX_EOF) {
+            mbedErrorFlag = 0;
+            l->match(';');
+            if (mbedErrorFlag != 0)  return 0;
+        }
+      } while (l->tk!=LEX_EOF);
+    }
+#endif
+    delete l;
+    l = oldLex;
+    scopes = oldScopes;
+
+    if (v) {
+        CScriptVarLink r = *v;
+        CLEAN(v);
+        return r;
+    }
+    // return undefined...
+    return CScriptVarLink(new CScriptVar());
+}
+
+string CTinyJS::evaluate(const string &code) {
+    return evaluateComplex(code).var->getString();
+}
+
+void CTinyJS::parseFunctionArguments(CScriptVar *funcVar) {
+#ifndef MBED
+  l->match('(');
+  while (l->tk!=')') {
+      funcVar->addChildNoDup(l->tkStr);
+      l->match(LEX_ID);
+      if (l->tk!=')') l->match(',');
+  }
+  l->match(')');
+#else
+  LMATCH_VOID('(');
+  while (l->tk!=')') {
+      funcVar->addChildNoDup(l->tkStr);
+      LMATCH_VOID(LEX_ID);
+      if (l->tk!=')') {
+        LMATCH_VOID(',');
+      }
+  }
+  LMATCH_VOID(')');
+#endif
+}
+
+void CTinyJS::addNative(const string &funcDesc, JSCallback ptr, void *userdata) {
+    CScriptLex *oldLex = l;
+    l = new CScriptLex(funcDesc);
+
+    CScriptVar *base = root;
+#ifndef MBED
+    l->match(LEX_R_FUNCTION);
+#else
+    LMATCH_VOID(LEX_R_FUNCTION);
+#endif
+    string funcName = l->tkStr;
+#ifndef MBED
+    l->match(LEX_ID);
+#else
+    LMATCH_VOID(LEX_ID);
+#endif
+    /* Check for dots, we might want to do something like function String.substring ... */
+    while (l->tk == '.') {
+#ifndef MBED
+      l->match('.');
+#else
+      LMATCH_VOID('.');
+#endif
+      CScriptVarLink *link = base->findChild(funcName);
+      // if it doesn't exist, make an object class
+      if (!link) link = base->addChild(funcName, new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT));
+      base = link->var;
+      funcName = l->tkStr;
+#ifndef MBED
+      l->match(LEX_ID);
+#else
+      LMATCH_VOID(LEX_ID);
+#endif
+    }
+
+    CScriptVar *funcVar = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION | SCRIPTVAR_NATIVE);
+    funcVar->setCallback(ptr, userdata);
+    parseFunctionArguments(funcVar);
+    delete l;
+    l = oldLex;
+
+    base->addChild(funcName, funcVar);
+}
+
+CScriptVarLink *CTinyJS::parseFunctionDefinition() {
+  // actually parse a function...
+#ifndef MBED
+  l->match(LEX_R_FUNCTION);
+#else
+  LMATCH(LEX_R_FUNCTION);
+#endif
+  string funcName = TINYJS_TEMP_NAME;
+  /* we can have functions without names */
+  if (l->tk==LEX_ID) {
+    funcName = l->tkStr;
+#ifndef MBED
+    l->match(LEX_ID);
+#else
+    LMATCH(LEX_ID);
+#endif
+  }
+  CScriptVarLink *funcVar = new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION), funcName);
+  parseFunctionArguments(funcVar->var);
+  int funcBegin = l->tokenStart;
+  bool noexecute = false;
+  block(noexecute);
+  funcVar->var->data = l->getSubString(funcBegin);
+  return funcVar;
+}
+
+/** Handle a function call (assumes we've parsed the function name and we're
+ * on the start bracket). 'parent' is the object that contains this method,
+ * if there was one (otherwise it's just a normnal function).
+ */
+CScriptVarLink *CTinyJS::functionCall(bool &execute, CScriptVarLink *function, CScriptVar *parent) {
+  if (execute) {
+    if (!function->var->isFunction()) {
+        string errorMsg = "Expecting '";
+        errorMsg = errorMsg + function->name + "' to be a function";
+#ifndef MBED
+        throw new CScriptException(errorMsg.c_str());
+#else
+        mbedErrorFlag = 1;
+        mbedErrorMessage = errorMsg;
+        return 0;
+#endif
+    }
+#ifndef MBED
+    l->match('(');
+#else
+    LMATCH('(');
+#endif
+    // create a new symbol table entry for execution of this function
+    CScriptVar *functionRoot = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_FUNCTION);
+    if (parent)
+      functionRoot->addChildNoDup("this", parent);
+    // grab in all parameters
+    CScriptVarLink *v = function->var->firstChild;
+    while (v) {
+        CScriptVarLink *value = base(execute);
+        if (execute) {
+            if (value->var->isBasic()) {
+              // pass by value
+              functionRoot->addChild(v->name, value->var->deepCopy());
+            } else {
+              // pass by reference
+              functionRoot->addChild(v->name, value->var);
+            }
+        }
+        CLEAN(value);
+#ifndef MBED
+        if (l->tk!=')') l->match(',');
+#else
+        if (l->tk!=')')  LMATCH(',');
+#endif
+        v = v->nextSibling;
+    }
+#ifndef MBED
+    l->match(')');
+#else
+    LMATCH(')');
+#endif
+    // setup a return variable
+    CScriptVarLink *returnVar = NULL;
+    // execute function!
+    // add the function's execute space to the symbol table so we can recurse
+    CScriptVarLink *returnVarLink = functionRoot->addChild(TINYJS_RETURN_VAR);
+    scopes.push_back(functionRoot);
+#ifdef TINYJS_CALL_STACK
+    call_stack.push_back(function->name + " from " + l->getPosition());
+#endif
+
+    if (function->var->isNative()) {
+        ASSERT(function->var->jsCallback);
+        function->var->jsCallback(functionRoot, function->var->jsCallbackUserData);
+    } else {
+        /* we just want to execute the block, but something could
+         * have messed up and left us with the wrong ScriptLex, so
+         * we want to be careful here... */
+#ifndef MBED
+        CScriptException *exception = 0;
+        CScriptLex *oldLex = l;
+        CScriptLex *newLex = new CScriptLex(function->var->getString());
+        l = newLex;
+
+        try {
+          block(execute);
+          // because return will probably have called this, and set execute to false
+          execute = true;
+        } catch (CScriptException *e) {
+          exception = e;
+        }
+        delete newLex;
+        l = oldLex;
+
+        if (exception)
+          throw exception;
+#else
+        CScriptLex *oldLex = l;
+        CScriptLex *newLex = new CScriptLex(function->var->getString());
+        l = newLex;
+
+        {
+          mbedErrorFlag = 0;
+          block(execute);
+          if (mbedErrorFlag != 0) {
+            delete newLex;
+            l = oldLex;
+            return 0;
+          } else {
+            execute = true;
+
+            delete newLex;
+            l = oldLex;
+          }
+        }
+#endif
+
+    }
+#ifdef TINYJS_CALL_STACK
+    if (!call_stack.empty()) call_stack.pop_back();
+#endif
+    scopes.pop_back();
+    /* get the real return var before we remove it from our function */
+    returnVar = new CScriptVarLink(returnVarLink->var);
+    functionRoot->removeLink(returnVarLink);
+    delete functionRoot;
+    if (returnVar)
+      return returnVar;
+    else
+      return new CScriptVarLink(new CScriptVar());
+  } else {
+    // function, but not executing - just parse args and be done
+#ifndef MBED
+    l->match('(');
+    while (l->tk != ')') {
+      CScriptVarLink *value = base(execute);
+      CLEAN(value);
+      if (l->tk!=')') l->match(',');
+    }
+    l->match(')');
+#else
+    LMATCH('(');
+    while (l->tk != ')') {
+      CScriptVarLink *value = base(execute);
+      CLEAN(value);
+      if (l->tk!=')') LMATCH(',');
+    }
+    LMATCH(')');
+#endif
+    if (l->tk == '{') { // TODO: why is this here?
+      block(execute);
+    }
+    /* function will be a blank scriptvarlink if we're not executing,
+     * so just return it rather than an alloc/free */
+    return function;
+  }
+}
+
+#ifndef MBED
+CScriptVarLink *CTinyJS::factor(bool &execute) {
+    if (l->tk=='(') {
+        l->match('(');
+        CScriptVarLink *a = base(execute);
+        l->match(')');
+        return a;
+    }
+    if (l->tk==LEX_R_TRUE) {
+        l->match(LEX_R_TRUE);
+        return new CScriptVarLink(new CScriptVar(1));
+    }
+    if (l->tk==LEX_R_FALSE) {
+        l->match(LEX_R_FALSE);
+        return new CScriptVarLink(new CScriptVar(0));
+    }
+    if (l->tk==LEX_R_NULL) {
+        l->match(LEX_R_NULL);
+        return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NULL));
+    }
+    if (l->tk==LEX_R_UNDEFINED) {
+        l->match(LEX_R_UNDEFINED);
+        return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED));
+    }
+    if (l->tk==LEX_ID) {
+        CScriptVarLink *a = execute ? findInScopes(l->tkStr) : new CScriptVarLink(new CScriptVar());
+        //printf("0x%08X for %s at %s\n", (unsigned int)a, l->tkStr.c_str(), l->getPosition().c_str());
+        /* The parent if we're executing a method call */
+        CScriptVar *parent = 0;
+
+        if (execute && !a) {
+          /* Variable doesn't exist! JavaScript says we should create it
+           * (we won't add it here. This is done in the assignment operator)*/
+          a = new CScriptVarLink(new CScriptVar(), l->tkStr);
+        }
+        l->match(LEX_ID);
+        while (l->tk=='(' || l->tk=='.' || l->tk=='[') {
+            if (l->tk=='(') { // ------------------------------------- Function Call
+                a = functionCall(execute, a, parent);
+            } else if (l->tk == '.') { // ------------------------------------- Record Access
+                l->match('.');
+                if (execute) {
+                  const string &name = l->tkStr;
+                  CScriptVarLink *child = a->var->findChild(name);
+                  if (!child) child = findInParentClasses(a->var, name);
+                  if (!child) {
+                    /* if we haven't found this defined yet, use the built-in
+                       'length' properly */
+                    if (a->var->isArray() && name == "length") {
+                      int l = a->var->getArrayLength();
+                      child = new CScriptVarLink(new CScriptVar(l));
+                    } else if (a->var->isString() && name == "length") {
+                      int l = a->var->getString().size();
+                      child = new CScriptVarLink(new CScriptVar(l));
+                    } else {
+                      child = a->var->addChild(name);
+                    }
+                  }
+                  parent = a->var;
+                  a = child;
+                }
+                l->match(LEX_ID);
+            } else if (l->tk == '[') { // ------------------------------------- Array Access
+                l->match('[');
+                CScriptVarLink *index = base(execute);
+                l->match(']');
+                if (execute) {
+                  CScriptVarLink *child = a->var->findChildOrCreate(index->var->getString());
+                  parent = a->var;
+                  a = child;
+                }
+                CLEAN(index);
+            } else ASSERT(0);
+        }
+        return a;
+    }
+    if (l->tk==LEX_INT || l->tk==LEX_FLOAT) {
+        CScriptVar *a = new CScriptVar(l->tkStr,
+            ((l->tk==LEX_INT)?SCRIPTVAR_INTEGER:SCRIPTVAR_DOUBLE));
+        l->match(l->tk);
+        return new CScriptVarLink(a);
+    }
+    if (l->tk==LEX_STR) {
+        CScriptVar *a = new CScriptVar(l->tkStr, SCRIPTVAR_STRING);
+        l->match(LEX_STR);
+        return new CScriptVarLink(a);
+    }
+    if (l->tk=='{') {
+        CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT);
+        /* JSON-style object definition */
+        l->match('{');
+        while (l->tk != '}') {
+          string id = l->tkStr;
+          // we only allow strings or IDs on the left hand side of an initialisation
+          if (l->tk==LEX_STR) l->match(LEX_STR);
+          else l->match(LEX_ID);
+          l->match(':');
+          if (execute) {
+            CScriptVarLink *a = base(execute);
+            contents->addChild(id, a->var);
+            CLEAN(a);
+          }
+          // no need to clean here, as it will definitely be used
+          if (l->tk != '}') l->match(',');
+        }
+
+        l->match('}');
+        return new CScriptVarLink(contents);
+    }
+    if (l->tk=='[') {
+        CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_ARRAY);
+        /* JSON-style array */
+        l->match('[');
+        int idx = 0;
+        while (l->tk != ']') {
+          if (execute) {
+            char idx_str[16]; // big enough for 2^32
+            sprintf_s(idx_str, sizeof(idx_str), "%d",idx);
+
+            CScriptVarLink *a = base(execute);
+            contents->addChild(idx_str, a->var);
+            CLEAN(a);
+          }
+          // no need to clean here, as it will definitely be used
+          if (l->tk != ']') l->match(',');
+          idx++;
+        }
+        l->match(']');
+        return new CScriptVarLink(contents);
+    }
+    if (l->tk==LEX_R_FUNCTION) {
+      CScriptVarLink *funcVar = parseFunctionDefinition();
+        if (funcVar->name != TINYJS_TEMP_NAME)
+          TRACE("Functions not defined at statement-level are not meant to have a name");
+        return funcVar;
+    }
+    if (l->tk==LEX_R_NEW) {
+      // new -> create a new object
+      l->match(LEX_R_NEW);
+      const string &className = l->tkStr;
+      if (execute) {
+        CScriptVarLink *objClassOrFunc = findInScopes(className);
+        if (!objClassOrFunc) {
+          TRACE("%s is not a valid class name", className.c_str());
+          return new CScriptVarLink(new CScriptVar());
+        }
+        l->match(LEX_ID);
+        CScriptVar *obj = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT);
+        CScriptVarLink *objLink = new CScriptVarLink(obj);
+        if (objClassOrFunc->var->isFunction()) {
+          CLEAN(functionCall(execute, objClassOrFunc, obj));
+        } else {
+          obj->addChild(TINYJS_PROTOTYPE_CLASS, objClassOrFunc->var);
+          if (l->tk == '(') {
+            l->match('(');
+            l->match(')');
+          }
+        }
+        return objLink;
+      } else {
+        l->match(LEX_ID);
+        if (l->tk == '(') {
+          l->match('(');
+          l->match(')');
+        }
+      }
+    }
+    // Nothing we can do here... just hope it's the end...
+    l->match(LEX_EOF);
+    return 0;
+}
+#else
+CScriptVarLink *CTinyJS::factor(bool &execute) {
+    if (l->tk=='(') {
+        LMATCH('(');
+        CScriptVarLink *a = base(execute);
+        LMATCH(')');
+        return a;
+    }
+    if (l->tk==LEX_R_TRUE) {
+        LMATCH(LEX_R_TRUE);
+        return new CScriptVarLink(new CScriptVar(1));
+    }
+    if (l->tk==LEX_R_FALSE) {
+        LMATCH(LEX_R_FALSE);
+        return new CScriptVarLink(new CScriptVar(0));
+    }
+    if (l->tk==LEX_R_NULL) {
+        LMATCH(LEX_R_NULL);
+        return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_NULL));
+    }
+    if (l->tk==LEX_R_UNDEFINED) {
+        LMATCH(LEX_R_UNDEFINED);
+        return new CScriptVarLink(new CScriptVar(TINYJS_BLANK_DATA,SCRIPTVAR_UNDEFINED));
+    }
+    if (l->tk==LEX_ID) {
+        CScriptVarLink *a = execute ? findInScopes(l->tkStr) : new CScriptVarLink(new CScriptVar());
+        //printf("0x%08X for %s at %s\n", (unsigned int)a, l->tkStr.c_str(), l->getPosition().c_str());
+        /* The parent if we're executing a method call */
+        CScriptVar *parent = 0;
+
+        if (execute && !a) {
+          /* Variable doesn't exist! JavaScript says we should create it
+           * (we won't add it here. This is done in the assignment operator)*/
+          a = new CScriptVarLink(new CScriptVar(), l->tkStr);
+        }
+        LMATCH(LEX_ID);
+        while (l->tk=='(' || l->tk=='.' || l->tk=='[') {
+            if (l->tk=='(') { // ------------------------------------- Function Call
+                a = functionCall(execute, a, parent);
+            } else if (l->tk == '.') { // ------------------------------------- Record Access
+                LMATCH('.');
+                if (execute) {
+                  const string &name = l->tkStr;
+                  CScriptVarLink *child = a->var->findChild(name);
+                  if (!child) child = findInParentClasses(a->var, name);
+                  if (!child) {
+                    /* if we haven't found this defined yet, use the built-in
+                       'length' properly */
+                    if (a->var->isArray() && name == "length") {
+                      int l = a->var->getArrayLength();
+                      child = new CScriptVarLink(new CScriptVar(l));
+                    } else if (a->var->isString() && name == "length") {
+                      int l = a->var->getString().size();
+                      child = new CScriptVarLink(new CScriptVar(l));
+                    } else {
+                      child = a->var->addChild(name);
+                    }
+                  }
+                  parent = a->var;
+                  a = child;
+                }
+                LMATCH(LEX_ID);
+            } else if (l->tk == '[') { // ------------------------------------- Array Access
+                LMATCH('[');
+                CScriptVarLink *index = base(execute);
+                LMATCH(']');
+                if (execute) {
+                  CScriptVarLink *child = a->var->findChildOrCreate(index->var->getString());
+                  parent = a->var;
+                  a = child;
+                }
+                CLEAN(index);
+            } else ASSERT(0);
+        }
+        return a;
+    }
+    if (l->tk==LEX_INT || l->tk==LEX_FLOAT) {
+        CScriptVar *a = new CScriptVar(l->tkStr,
+            ((l->tk==LEX_INT)?SCRIPTVAR_INTEGER:SCRIPTVAR_DOUBLE));
+        LMATCH(l->tk);
+        return new CScriptVarLink(a);
+    }
+    if (l->tk==LEX_STR) {
+        CScriptVar *a = new CScriptVar(l->tkStr, SCRIPTVAR_STRING);
+        LMATCH(LEX_STR);
+        return new CScriptVarLink(a);
+    }
+    if (l->tk=='{') {
+        CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT);
+        /* JSON-style object definition */
+        LMATCH('{');
+        while (l->tk != '}') {
+          string id = l->tkStr;
+          // we only allow strings or IDs on the left hand side of an initialisation
+          if (l->tk==LEX_STR) {
+              LMATCH(LEX_STR);
+          } else {
+              LMATCH(LEX_ID);
+          }
+          LMATCH(':');
+          if (execute) {
+            CScriptVarLink *a = base(execute);
+            contents->addChild(id, a->var);
+            CLEAN(a);
+          }
+          // no need to clean here, as it will definitely be used
+          if (l->tk != '}') LMATCH(',');
+        }
+
+        LMATCH('}');
+        return new CScriptVarLink(contents);
+    }
+    if (l->tk=='[') {
+        CScriptVar *contents = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_ARRAY);
+        /* JSON-style array */
+        LMATCH('[');
+        int idx = 0;
+        while (l->tk != ']') {
+          if (execute) {
+            char idx_str[16]; // big enough for 2^32
+            sprintf_s(idx_str, sizeof(idx_str), "%d",idx);
+
+            CScriptVarLink *a = base(execute);
+            contents->addChild(idx_str, a->var);
+            CLEAN(a);
+          }
+          // no need to clean here, as it will definitely be used
+          if (l->tk != ']') LMATCH(',');
+          idx++;
+        }
+        LMATCH(']');
+        return new CScriptVarLink(contents);
+    }
+    if (l->tk==LEX_R_FUNCTION) {
+      CScriptVarLink *funcVar = parseFunctionDefinition();
+        if (funcVar->name != TINYJS_TEMP_NAME)
+          TRACE("Functions not defined at statement-level are not meant to have a name");
+        return funcVar;
+    }
+    if (l->tk==LEX_R_NEW) {
+      // new -> create a new object
+      LMATCH(LEX_R_NEW);
+      const string &className = l->tkStr;
+      if (execute) {
+        CScriptVarLink *objClassOrFunc = findInScopes(className);
+        if (!objClassOrFunc) {
+          TRACE("%s is not a valid class name", className.c_str());
+          return new CScriptVarLink(new CScriptVar());
+        }
+        LMATCH(LEX_ID);
+        CScriptVar *obj = new CScriptVar(TINYJS_BLANK_DATA, SCRIPTVAR_OBJECT);
+        CScriptVarLink *objLink = new CScriptVarLink(obj);
+        if (objClassOrFunc->var->isFunction()) {
+          CLEAN(functionCall(execute, objClassOrFunc, obj));
+        } else {
+          obj->addChild(TINYJS_PROTOTYPE_CLASS, objClassOrFunc->var);
+          if (l->tk == '(') {
+            LMATCH('(');
+            LMATCH(')');
+          }
+        }
+        return objLink;
+      } else {
+        LMATCH(LEX_ID);
+        if (l->tk == '(') {
+          LMATCH('(');
+          LMATCH(')');
+        }
+      }
+    }
+    // Nothing we can do here... just hope it's the end...
+    LMATCH(LEX_EOF);
+    return 0;
+}
+#endif
+
+CScriptVarLink *CTinyJS::unary(bool &execute) {
+    CScriptVarLink *a;
+    if (l->tk=='!') {
+#ifndef MBED
+        l->match('!'); // binary not
+#else
+        LMATCH('!'); // binary not
+#endif
+        a = factor(execute);
+        if (execute) {
+            CScriptVar zero(0);
+            CScriptVar *res = a->var->mathsOp(&zero, LEX_EQUAL);
+            CREATE_LINK(a, res);
+        }
+    } else
+        a = factor(execute);
+    return a;
+}
+
+CScriptVarLink *CTinyJS::term(bool &execute) {
+    CScriptVarLink *a = unary(execute);
+    while (l->tk=='*' || l->tk=='/' || l->tk=='%') {
+        int op = l->tk;
+#ifndef MBED
+        l->match(l->tk);
+#else
+        LMATCH(l->tk);
+#endif
+        CScriptVarLink *b = unary(execute);
+        if (execute) {
+            CScriptVar *res = a->var->mathsOp(b->var, op);
+            CREATE_LINK(a, res);
+        }
+        CLEAN(b);
+    }
+    return a;
+}
+
+CScriptVarLink *CTinyJS::expression(bool &execute) {
+    bool negate = false;
+    if (l->tk=='-') {
+#ifndef MBED
+        l->match('-');
+#else
+        LMATCH('-');
+#endif
+        negate = true;
+    }
+    CScriptVarLink *a = term(execute);
+    if (negate) {
+        CScriptVar zero(0);
+        CScriptVar *res = zero.mathsOp(a->var, '-');
+        CREATE_LINK(a, res);
+    }
+
+    while (l->tk=='+' || l->tk=='-' ||
+        l->tk==LEX_PLUSPLUS || l->tk==LEX_MINUSMINUS) {
+        int op = l->tk;
+#ifndef MBED
+        l->match(l->tk);
+#else
+        LMATCH(l->tk);
+#endif
+        if (op==LEX_PLUSPLUS || op==LEX_MINUSMINUS) {
+            if (execute) {
+                CScriptVar one(1);
+                CScriptVar *res = a->var->mathsOp(&one, op==LEX_PLUSPLUS ? '+' : '-');
+                CScriptVarLink *oldValue = new CScriptVarLink(a->var);
+                // in-place add/subtract
+                a->replaceWith(res);
+                CLEAN(a);
+                a = oldValue;
+            }
+        } else {
+            CScriptVarLink *b = term(execute);
+            if (execute) {
+                // not in-place, so just replace
+                CScriptVar *res = a->var->mathsOp(b->var, op);
+                CREATE_LINK(a, res);
+            }
+            CLEAN(b);
+        }
+    }
+    return a;
+}
+
+CScriptVarLink *CTinyJS::shift(bool &execute) {
+  CScriptVarLink *a = expression(execute);
+  if (l->tk==LEX_LSHIFT || l->tk==LEX_RSHIFT || l->tk==LEX_RSHIFTUNSIGNED) {
+    int op = l->tk;
+#ifndef MBED
+    l->match(op);
+#else
+    LMATCH(op);
+#endif
+    CScriptVarLink *b = base(execute);
+    int shift = execute ? b->var->getInt() : 0;
+    CLEAN(b);
+    if (execute) {
+      if (op==LEX_LSHIFT) a->var->setInt(a->var->getInt() << shift);
+      if (op==LEX_RSHIFT) a->var->setInt(a->var->getInt() >> shift);
+      if (op==LEX_RSHIFTUNSIGNED) a->var->setInt(((unsigned int)a->var->getInt()) >> shift);
+    }
+  }
+  return a;
+}
+
+CScriptVarLink *CTinyJS::condition(bool &execute) {
+    CScriptVarLink *a = shift(execute);
+    CScriptVarLink *b;
+    while (l->tk==LEX_EQUAL || l->tk==LEX_NEQUAL ||
+           l->tk==LEX_TYPEEQUAL || l->tk==LEX_NTYPEEQUAL ||
+           l->tk==LEX_LEQUAL || l->tk==LEX_GEQUAL ||
+           l->tk=='<' || l->tk=='>') {
+        int op = l->tk;
+#ifndef MBED
+        l->match(l->tk);
+#else
+        LMATCH(l->tk);
+#endif
+        b = shift(execute);
+        if (execute) {
+            CScriptVar *res = a->var->mathsOp(b->var, op);
+            CREATE_LINK(a,res);
+        }
+        CLEAN(b);
+    }
+    return a;
+}
+
+CScriptVarLink *CTinyJS::logic(bool &execute) {
+    CScriptVarLink *a = condition(execute);
+    CScriptVarLink *b;
+    while (l->tk=='&' || l->tk=='|' || l->tk=='^' || l->tk==LEX_ANDAND || l->tk==LEX_OROR) {
+        bool noexecute = false;
+        int op = l->tk;
+#ifndef MBED
+        l->match(l->tk);
+#else
+        LMATCH(l->tk);
+#endif
+        bool shortCircuit = false;
+        bool boolean = false;
+        // if we have short-circuit ops, then if we know the outcome
+        // we don't bother to execute the other op. Even if not
+        // we need to tell mathsOp it's an & or |
+        if (op==LEX_ANDAND) {
+            op = '&';
+            shortCircuit = !a->var->getBool();
+            boolean = true;
+        } else if (op==LEX_OROR) {
+            op = '|';
+            shortCircuit = a->var->getBool();
+            boolean = true;
+        }
+        b = condition(shortCircuit ? noexecute : execute);
+        if (execute && !shortCircuit) {
+            if (boolean) {
+              CScriptVar *newa = new CScriptVar(a->var->getBool());
+              CScriptVar *newb = new CScriptVar(b->var->getBool());
+              CREATE_LINK(a, newa);
+              CREATE_LINK(b, newb);
+            }
+            CScriptVar *res = a->var->mathsOp(b->var, op);
+            CREATE_LINK(a, res);
+        }
+        CLEAN(b);
+    }
+    return a;
+}
+
+CScriptVarLink *CTinyJS::ternary(bool &execute) {
+  CScriptVarLink *lhs = logic(execute);
+  bool noexec = false;
+  if (l->tk=='?') {
+#ifndef MBED
+    l->match('?');
+#else
+    LMATCH('?');
+#endif
+    if (!execute) {
+      CLEAN(lhs);
+      CLEAN(base(noexec));
+#ifndef MBED
+      l->match(':');
+#else
+      LMATCH(':');
+#endif
+      CLEAN(base(noexec));
+    } else {
+      bool first = lhs->var->getBool();
+      CLEAN(lhs);
+      if (first) {
+        lhs = base(execute);
+#ifndef MBED
+        l->match(':');
+#else
+        LMATCH(':');
+#endif
+        CLEAN(base(noexec));
+      } else {
+        CLEAN(base(noexec));
+#ifndef MBED
+        l->match(':');
+#else
+        LMATCH(':');
+#endif
+        lhs = base(execute);
+      }
+    }
+  }
+
+  return lhs;
+}
+
+CScriptVarLink *CTinyJS::base(bool &execute) {
+    CScriptVarLink *lhs = ternary(execute);
+    if (l->tk=='=' || l->tk==LEX_PLUSEQUAL || l->tk==LEX_MINUSEQUAL) {
+        /* If we're assigning to this and we don't have a parent,
+         * add it to the symbol table root as per JavaScript. */
+        if (execute && !lhs->owned) {
+          if (lhs->name.length()>0) {
+            CScriptVarLink *realLhs = root->addChildNoDup(lhs->name, lhs->var);
+            CLEAN(lhs);
+            lhs = realLhs;
+          } else
+            TRACE("Trying to assign to an un-named type\n");
+        }
+
+        int op = l->tk;
+#ifndef MBED
+        l->match(l->tk);
+#else
+        LMATCH(l->tk);
+#endif
+        CScriptVarLink *rhs = base(execute);
+        if (execute) {
+            if (op=='=') {
+                lhs->replaceWith(rhs);
+            } else if (op==LEX_PLUSEQUAL) {
+                CScriptVar *res = lhs->var->mathsOp(rhs->var, '+');
+                lhs->replaceWith(res);
+            } else if (op==LEX_MINUSEQUAL) {
+                CScriptVar *res = lhs->var->mathsOp(rhs->var, '-');
+                lhs->replaceWith(res);
+            } else ASSERT(0);
+        }
+        CLEAN(rhs);
+    }
+    return lhs;
+}
+
+#ifndef MBED
+void CTinyJS::block(bool &execute) {
+    l->match('{');
+    if (execute) {
+      while (l->tk && l->tk!='}')
+        statement(execute);
+      l->match('}');
+    } else {
+      // fast skip of blocks
+      int brackets = 1;
+      while (l->tk && brackets) {
+        if (l->tk == '{') brackets++;
+        if (l->tk == '}') brackets--;
+        l->match(l->tk);
+      }
+    }
+}
+#else
+void CTinyJS::block(bool &execute) {
+    LMATCH_VOID('{');
+    if (execute) {
+      while (l->tk && l->tk!='}')
+        statement(execute);
+      LMATCH_VOID('}');
+    } else {
+      // fast skip of blocks
+      int brackets = 1;
+      while (l->tk && brackets) {
+        if (l->tk == '{') brackets++;
+        if (l->tk == '}') brackets--;
+        LMATCH_VOID(l->tk);
+      }
+    }
+}
+#endif
+
+#ifndef MBED
+void CTinyJS::statement(bool &execute) {
+    if (l->tk==LEX_ID ||
+        l->tk==LEX_INT ||
+        l->tk==LEX_FLOAT ||
+        l->tk==LEX_STR ||
+        l->tk=='-') {
+        /* Execute a simple statement that only contains basic arithmetic... */
+        CLEAN(base(execute));
+        l->match(';');
+    } else if (l->tk=='{') {
+        /* A block of code */
+        block(execute);
+    } else if (l->tk==';') {
+        /* Empty statement - to allow things like ;;; */
+        l->match(';');
+    } else if (l->tk==LEX_R_VAR) {
+        /* variable creation. TODO - we need a better way of parsing the left
+         * hand side. Maybe just have a flag called can_create_var that we
+         * set and then we parse as if we're doing a normal equals.*/
+        l->match(LEX_R_VAR);
+        while (l->tk != ';') {
+          CScriptVarLink *a = 0;
+          if (execute)
+            a = scopes.back()->findChildOrCreate(l->tkStr);
+          l->match(LEX_ID);
+          // now do stuff defined with dots
+          while (l->tk == '.') {
+              l->match('.');
+              if (execute) {
+                  CScriptVarLink *lastA = a;
+                  a = lastA->var->findChildOrCreate(l->tkStr);
+              }
+              l->match(LEX_ID);
+          }
+          // sort out initialiser
+          if (l->tk == '=') {
+              l->match('=');
+              CScriptVarLink *var = base(execute);
+              if (execute)
+                  a->replaceWith(var);
+              CLEAN(var);
+          }
+          if (l->tk != ';')
+            l->match(',');
+        }       
+        l->match(';');
+    } else if (l->tk==LEX_R_IF) {
+        l->match(LEX_R_IF);
+        l->match('(');
+        CScriptVarLink *var = base(execute);
+        l->match(')');
+        bool cond = execute && var->var->getBool();
+        CLEAN(var);
+        bool noexecute = false; // because we need to be abl;e to write to it
+        statement(cond ? execute : noexecute);
+        if (l->tk==LEX_R_ELSE) {
+            l->match(LEX_R_ELSE);
+            statement(cond ? noexecute : execute);
+        }
+    } else if (l->tk==LEX_R_WHILE) {
+        // We do repetition by pulling out the string representing our statement
+        // there's definitely some opportunity for optimisation here
+        l->match(LEX_R_WHILE);
+        l->match('(');
+        int whileCondStart = l->tokenStart;
+        bool noexecute = false;
+        CScriptVarLink *cond = base(execute);
+        bool loopCond = execute && cond->var->getBool();
+        CLEAN(cond);
+        CScriptLex *whileCond = l->getSubLex(whileCondStart);
+        l->match(')');
+        int whileBodyStart = l->tokenStart;
+        statement(loopCond ? execute : noexecute);
+        CScriptLex *whileBody = l->getSubLex(whileBodyStart);
+        CScriptLex *oldLex = l;
+        int loopCount = TINYJS_LOOP_MAX_ITERATIONS;
+        while (loopCond && loopCount-->0) {
+            whileCond->reset();
+            l = whileCond;
+            cond = base(execute);
+            loopCond = execute && cond->var->getBool();
+            CLEAN(cond);
+            if (loopCond) {
+                whileBody->reset();
+                l = whileBody;
+                statement(execute);
+            }
+        }
+        l = oldLex;
+        delete whileCond;
+        delete whileBody;
+
+        if (loopCount<=0) {
+            root->trace();
+            TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition().c_str());
+            throw new CScriptException("LOOP_ERROR");
+        }
+    } else if (l->tk==LEX_R_FOR) {
+        l->match(LEX_R_FOR);
+        l->match('(');
+        statement(execute); // initialisation
+        //l->match(';');
+        int forCondStart = l->tokenStart;
+        bool noexecute = false;
+        CScriptVarLink *cond = base(execute); // condition
+        bool loopCond = execute && cond->var->getBool();
+        CLEAN(cond);
+        CScriptLex *forCond = l->getSubLex(forCondStart);
+        l->match(';');
+        int forIterStart = l->tokenStart;
+        CLEAN(base(noexecute)); // iterator
+        CScriptLex *forIter = l->getSubLex(forIterStart);
+        l->match(')');
+        int forBodyStart = l->tokenStart;
+        statement(loopCond ? execute : noexecute);
+        CScriptLex *forBody = l->getSubLex(forBodyStart);
+        CScriptLex *oldLex = l;
+        if (loopCond) {
+            forIter->reset();
+            l = forIter;
+            CLEAN(base(execute));
+        }
+        int loopCount = TINYJS_LOOP_MAX_ITERATIONS;
+        while (execute && loopCond && loopCount-->0) {
+            forCond->reset();
+            l = forCond;
+            cond = base(execute);
+            loopCond = cond->var->getBool();
+            CLEAN(cond);
+            if (execute && loopCond) {
+                forBody->reset();
+                l = forBody;
+                statement(execute);
+            }
+            if (execute && loopCond) {
+                forIter->reset();
+                l = forIter;
+                CLEAN(base(execute));
+            }
+        }
+        l = oldLex;
+        delete forCond;
+        delete forIter;
+        delete forBody;
+        if (loopCount<=0) {
+            root->trace();
+            TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition().c_str());
+            throw new CScriptException("LOOP_ERROR");
+        }
+    } else if (l->tk==LEX_R_RETURN) {
+        l->match(LEX_R_RETURN);
+        CScriptVarLink *result = 0;
+        if (l->tk != ';')
+          result = base(execute);
+        if (execute) {
+          CScriptVarLink *resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR);
+          if (resultVar)
+            resultVar->replaceWith(result);
+          else
+            TRACE("RETURN statement, but not in a function.\n");
+          execute = false;
+        }
+        CLEAN(result);
+        l->match(';');
+    } else if (l->tk==LEX_R_FUNCTION) {
+        CScriptVarLink *funcVar = parseFunctionDefinition();
+        if (execute) {
+          if (funcVar->name == TINYJS_TEMP_NAME)
+            TRACE("Functions defined at statement-level are meant to have a name\n");
+          else
+            scopes.back()->addChildNoDup(funcVar->name, funcVar->var);
+        }
+        CLEAN(funcVar);
+    } else l->match(LEX_EOF);
+}
+
+#else
+
+void CTinyJS::statement(bool &execute) {
+    if (l->tk==LEX_ID ||
+        l->tk==LEX_INT ||
+        l->tk==LEX_FLOAT ||
+        l->tk==LEX_STR ||
+        l->tk=='-') {
+        /* Execute a simple statement that only contains basic arithmetic... */
+        CLEAN(base(execute));
+        LMATCH_VOID(';');
+    } else if (l->tk=='{') {
+        /* A block of code */
+        block(execute);
+    } else if (l->tk==';') {
+        /* Empty statement - to allow things like ;;; */
+        LMATCH_VOID(';');
+    } else if (l->tk==LEX_R_VAR) {
+        /* variable creation. TODO - we need a better way of parsing the left
+         * hand side. Maybe just have a flag called can_create_var that we
+         * set and then we parse as if we're doing a normal equals.*/
+        LMATCH_VOID(LEX_R_VAR);
+        while (l->tk != ';') {
+          CScriptVarLink *a = 0;
+          if (execute)
+            a = scopes.back()->findChildOrCreate(l->tkStr);
+          LMATCH_VOID(LEX_ID);
+          // now do stuff defined with dots
+          while (l->tk == '.') {
+              LMATCH_VOID('.');
+              if (execute) {
+                  CScriptVarLink *lastA = a;
+                  a = lastA->var->findChildOrCreate(l->tkStr);
+              }
+              LMATCH_VOID(LEX_ID);
+          }
+          // sort out initialiser
+          if (l->tk == '=') {
+              LMATCH_VOID('=');
+              CScriptVarLink *var = base(execute);
+              if (execute)
+                  a->replaceWith(var);
+              CLEAN(var);
+          }
+          if (l->tk != ';')
+            LMATCH_VOID(',');
+        }       
+        LMATCH_VOID(';');
+    } else if (l->tk==LEX_R_IF) {
+        LMATCH_VOID(LEX_R_IF);
+        LMATCH_VOID('(');
+        CScriptVarLink *var = base(execute);
+        LMATCH_VOID(')');
+        bool cond = execute && var->var->getBool();
+        CLEAN(var);
+        bool noexecute = false; // because we need to be abl;e to write to it
+        statement(cond ? execute : noexecute);
+        if (l->tk==LEX_R_ELSE) {
+            LMATCH_VOID(LEX_R_ELSE);
+            statement(cond ? noexecute : execute);
+        }
+    } else if (l->tk==LEX_R_WHILE) {
+        // We do repetition by pulling out the string representing our statement
+        // there's definitely some opportunity for optimisation here
+        LMATCH_VOID(LEX_R_WHILE);
+        LMATCH_VOID('(');
+        int whileCondStart = l->tokenStart;
+        bool noexecute = false;
+        CScriptVarLink *cond = base(execute);
+        bool loopCond = execute && cond->var->getBool();
+        CLEAN(cond);
+        CScriptLex *whileCond = l->getSubLex(whileCondStart);
+        LMATCH_VOID(')');
+        int whileBodyStart = l->tokenStart;
+        statement(loopCond ? execute : noexecute);
+        CScriptLex *whileBody = l->getSubLex(whileBodyStart);
+        CScriptLex *oldLex = l;
+        int loopCount = TINYJS_LOOP_MAX_ITERATIONS;
+        while (loopCond && loopCount-->0) {
+            whileCond->reset();
+            l = whileCond;
+            cond = base(execute);
+            loopCond = execute && cond->var->getBool();
+            CLEAN(cond);
+            if (loopCond) {
+                whileBody->reset();
+                l = whileBody;
+                statement(execute);
+            }
+        }
+        l = oldLex;
+        delete whileCond;
+        delete whileBody;
+
+        if (loopCount<=0) {
+            root->trace();
+            TRACE("WHILE Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition().c_str());
+            mbedErrorFlag = 1;
+            mbedErrorMessage = "LOOP_ERROR";
+            return;
+        }
+    } else if (l->tk==LEX_R_FOR) {
+        LMATCH_VOID(LEX_R_FOR);
+        LMATCH_VOID('(');
+        statement(execute); // initialisation
+        //LMATCH(';');
+        int forCondStart = l->tokenStart;
+        bool noexecute = false;
+        CScriptVarLink *cond = base(execute); // condition
+        bool loopCond = execute && cond->var->getBool();
+        CLEAN(cond);
+        CScriptLex *forCond = l->getSubLex(forCondStart);
+        LMATCH_VOID(';');
+        int forIterStart = l->tokenStart;
+        CLEAN(base(noexecute)); // iterator
+        CScriptLex *forIter = l->getSubLex(forIterStart);
+        LMATCH_VOID(')');
+        int forBodyStart = l->tokenStart;
+        statement(loopCond ? execute : noexecute);
+        CScriptLex *forBody = l->getSubLex(forBodyStart);
+        CScriptLex *oldLex = l;
+        if (loopCond) {
+            forIter->reset();
+            l = forIter;
+            CLEAN(base(execute));
+        }
+        int loopCount = TINYJS_LOOP_MAX_ITERATIONS;
+        while (execute && loopCond && loopCount-->0) {
+            forCond->reset();
+            l = forCond;
+            cond = base(execute);
+            loopCond = cond->var->getBool();
+            CLEAN(cond);
+            if (execute && loopCond) {
+                forBody->reset();
+                l = forBody;
+                statement(execute);
+            }
+            if (execute && loopCond) {
+                forIter->reset();
+                l = forIter;
+                CLEAN(base(execute));
+            }
+        }
+        l = oldLex;
+        delete forCond;
+        delete forIter;
+        delete forBody;
+        if (loopCount<=0) {
+            root->trace();
+            TRACE("FOR Loop exceeded %d iterations at %s\n", TINYJS_LOOP_MAX_ITERATIONS, l->getPosition().c_str());
+            mbedErrorFlag = 1;
+            mbedErrorMessage = "LOOP_ERROR";
+            return;
+        }
+    } else if (l->tk==LEX_R_RETURN) {
+        LMATCH_VOID(LEX_R_RETURN);
+        CScriptVarLink *result = 0;
+        if (l->tk != ';')
+          result = base(execute);
+        if (execute) {
+          CScriptVarLink *resultVar = scopes.back()->findChild(TINYJS_RETURN_VAR);
+          if (resultVar)
+            resultVar->replaceWith(result);
+          else
+            TRACE("RETURN statement, but not in a function.\n");
+          execute = false;
+        }
+        CLEAN(result);
+        LMATCH_VOID(';');
+    } else if (l->tk==LEX_R_FUNCTION) {
+        CScriptVarLink *funcVar = parseFunctionDefinition();
+        if (execute) {
+          if (funcVar->name == TINYJS_TEMP_NAME)
+            TRACE("Functions defined at statement-level are meant to have a name\n");
+          else
+            scopes.back()->addChildNoDup(funcVar->name, funcVar->var);
+        }
+        CLEAN(funcVar);
+    } else LMATCH_VOID(LEX_EOF);
+}
+#endif
+
+/// Get the given variable specified by a path (var1.var2.etc), or return 0
+CScriptVar *CTinyJS::getScriptVariable(const string &path) {
+    // traverse path
+    size_t prevIdx = 0;
+    size_t thisIdx = path.find('.');
+    if (thisIdx == string::npos) thisIdx = path.length();
+    CScriptVar *var = root;
+    while (var && prevIdx<path.length()) {
+        string el = path.substr(prevIdx, thisIdx-prevIdx);
+        CScriptVarLink *varl = var->findChild(el);
+        var = varl?varl->var:0;
+        prevIdx = thisIdx+1;
+        thisIdx = path.find('.', prevIdx);
+        if (thisIdx == string::npos) thisIdx = path.length();
+    }
+    return var;
+}
+
+/// Get the value of the given variable, or return 0
+const string *CTinyJS::getVariable(const string &path) {
+    CScriptVar *var = getScriptVariable(path);
+    // return result
+    if (var)
+        return &var->getString();
+    else
+        return 0;
+}
+
+/// set the value of the given variable, return trur if it exists and gets set
+bool CTinyJS::setVariable(const std::string &path, const std::string &varData) {
+    CScriptVar *var = getScriptVariable(path);
+    // return result
+    if (var) {
+        if (var->isInt())
+            var->setInt((int)strtol(varData.c_str(),0,0));
+        else if (var->isDouble())
+            var->setDouble(strtod(varData.c_str(),0));
+        else
+            var->setString(varData.c_str());
+        return true;
+    }    
+    else
+        return false;
+}
+
+/// Finds a child, looking recursively up the scopes
+CScriptVarLink *CTinyJS::findInScopes(const std::string &childName) {
+    for (int s=scopes.size()-1;s>=0;s--) {
+      CScriptVarLink *v = scopes[s]->findChild(childName);
+      if (v) return v;
+    }
+    return NULL;
+
+}
+
+/// Look up in any parent classes of the given object
+CScriptVarLink *CTinyJS::findInParentClasses(CScriptVar *object, const std::string &name) {
+    // Look for links to actual parent classes
+    CScriptVarLink *parentClass = object->findChild(TINYJS_PROTOTYPE_CLASS);
+    while (parentClass) {
+      CScriptVarLink *implementation = parentClass->var->findChild(name);
+      if (implementation) return implementation;
+      parentClass = parentClass->var->findChild(TINYJS_PROTOTYPE_CLASS);
+    }
+    // else fake it for strings and finally objects
+    if (object->isString()) {
+      CScriptVarLink *implementation = stringClass->findChild(name);
+      if (implementation) return implementation;
+    }
+    if (object->isArray()) {
+      CScriptVarLink *implementation = arrayClass->findChild(name);
+      if (implementation) return implementation;
+    }
+    CScriptVarLink *implementation = objectClass->findChild(name);
+    if (implementation) return implementation;
+
+    return 0;
+}
+