浏览代码

Merge branch 'master' of https://github.com/RT-Thread/rt-thread

shaolin 11 年之前
父节点
当前提交
020e95d91b
共有 100 个文件被更改,包括 27555 次插入52 次删除
  1. 227 17
      bsp/beaglebone/drivers/serial.c
  2. 13 1
      bsp/beaglebone/rtconfig.h
  3. 1 0
      components/dfs/filesystems/ramfs/dfs_ramfs.c
  4. 7 6
      components/dfs/filesystems/romfs/mkromfs.py
  5. 19 14
      components/drivers/serial/serial.c
  6. 46 0
      components/external/espruino/CONTRIBUTING.md
  7. 515 0
      components/external/espruino/ChangeLog
  8. 378 0
      components/external/espruino/LICENSE
  9. 969 0
      components/external/espruino/Makefile
  10. 161 0
      components/external/espruino/README.md
  11. 2 0
      components/external/espruino/README_cn.md
  12. 48 14
      components/external/espruino/SConscript
  13. 109 0
      components/external/espruino/dist_licences.txt
  14. 57 0
      components/external/espruino/dist_readme.txt
  15. 1 0
      components/external/espruino/gen/README
  16. 7 0
      components/external/espruino/gen/jspininfo.c
  17. 39 0
      components/external/espruino/gen/jspininfo.h
  18. 1203 0
      components/external/espruino/gen/jswrapper.c
  19. 53 0
      components/external/espruino/gen/platform_config.h
  20. 233 0
      components/external/espruino/libs/jswrap_math.c
  21. 28 0
      components/external/espruino/libs/jswrap_math.h
  22. 32 0
      components/external/espruino/libs/math/README
  23. 167 0
      components/external/espruino/libs/math/acosh.c
  24. 324 0
      components/external/espruino/libs/math/asin.c
  25. 165 0
      components/external/espruino/libs/math/asinh.c
  26. 393 0
      components/external/espruino/libs/math/atan.c
  27. 156 0
      components/external/espruino/libs/math/atanh.c
  28. 142 0
      components/external/espruino/libs/math/cbrt.c
  29. 82 0
      components/external/espruino/libs/math/chbevl.c
  30. 1043 0
      components/external/espruino/libs/math/clog.c
  31. 461 0
      components/external/espruino/libs/math/cmplx.c
  32. 252 0
      components/external/espruino/libs/math/const.c
  33. 83 0
      components/external/espruino/libs/math/cosh.c
  34. 99 0
      components/external/espruino/libs/math/descrip.mms
  35. 161 0
      components/external/espruino/libs/math/drand.c
  36. 543 0
      components/external/espruino/libs/math/dtestvec.c
  37. 203 0
      components/external/espruino/libs/math/exp.c
  38. 223 0
      components/external/espruino/libs/math/exp10.c
  39. 183 0
      components/external/espruino/libs/math/exp2.c
  40. 56 0
      components/external/espruino/libs/math/fabs.c
  41. 453 0
      components/external/espruino/libs/math/floor.c
  42. 289 0
      components/external/espruino/libs/math/ftilib.mak
  43. 17 0
      components/external/espruino/libs/math/ftilib.rsp
  44. 237 0
      components/external/espruino/libs/math/isnan.c
  45. 341 0
      components/external/espruino/libs/math/log.c
  46. 250 0
      components/external/espruino/libs/math/log10.c
  47. 348 0
      components/external/espruino/libs/math/log2.c
  48. 199 0
      components/external/espruino/libs/math/mconf.h
  49. 122 0
      components/external/espruino/libs/math/mod2pi.c
  50. 103 0
      components/external/espruino/libs/math/mtherr.c
  51. 518 0
      components/external/espruino/libs/math/mtst.c
  52. 30 0
      components/external/espruino/libs/math/mtst.opt
  53. 116 0
      components/external/espruino/libs/math/polevl.asm
  54. 97 0
      components/external/espruino/libs/math/polevl.c
  55. 756 0
      components/external/espruino/libs/math/pow.c
  56. 186 0
      components/external/espruino/libs/math/powi.c
  57. 184 0
      components/external/espruino/libs/math/protos.h
  58. 70 0
      components/external/espruino/libs/math/round.c
  59. 207 0
      components/external/espruino/libs/math/setprbor.asm
  60. 87 0
      components/external/espruino/libs/math/setprec.387
  61. 35 0
      components/external/espruino/libs/math/setprec.688
  62. 208 0
      components/external/espruino/libs/math/setprec.87
  63. 10 0
      components/external/espruino/libs/math/setprec.c
  64. 83 0
      components/external/espruino/libs/math/setprelf.387
  65. 387 0
      components/external/espruino/libs/math/sin.c
  66. 358 0
      components/external/espruino/libs/math/sincos.c
  67. 308 0
      components/external/espruino/libs/math/sindg.c
  68. 148 0
      components/external/espruino/libs/math/sinh.c
  69. 178 0
      components/external/espruino/libs/math/sqrt.c
  70. 304 0
      components/external/espruino/libs/math/tan.c
  71. 267 0
      components/external/espruino/libs/math/tandg.c
  72. 141 0
      components/external/espruino/libs/math/tanh.c
  73. 138 0
      components/external/espruino/libs/math/unity.c
  74. 109 0
      components/external/espruino/libs/math/unix.mak
  75. 336 0
      components/external/espruino/src/jsdevices.c
  76. 127 0
      components/external/espruino/src/jsdevices.h
  77. 223 0
      components/external/espruino/src/jshardware.h
  78. 1686 0
      components/external/espruino/src/jsinteractive.c
  79. 105 0
      components/external/espruino/src/jsinteractive.h
  80. 501 0
      components/external/espruino/src/jslex.c
  81. 59 0
      components/external/espruino/src/jslex.h
  82. 2213 0
      components/external/espruino/src/jsparse.c
  83. 126 0
      components/external/espruino/src/jsparse.h
  84. 150 0
      components/external/espruino/src/jspin.c
  85. 228 0
      components/external/espruino/src/jspin.h
  86. 439 0
      components/external/espruino/src/jsutils.c
  87. 374 0
      components/external/espruino/src/jsutils.h
  88. 2542 0
      components/external/espruino/src/jsvar.c
  89. 612 0
      components/external/espruino/src/jsvar.h
  90. 328 0
      components/external/espruino/src/jswrap_array.c
  91. 23 0
      components/external/espruino/src/jswrap_array.h
  92. 315 0
      components/external/espruino/src/jswrap_arraybuffer.c
  93. 19 0
      components/external/espruino/src/jswrap_arraybuffer.h
  94. 102 0
      components/external/espruino/src/jswrap_functions.c
  95. 19 0
      components/external/espruino/src/jswrap_functions.h
  96. 203 0
      components/external/espruino/src/jswrap_interactive.c
  97. 24 0
      components/external/espruino/src/jswrap_interactive.h
  98. 437 0
      components/external/espruino/src/jswrap_io.c
  99. 29 0
      components/external/espruino/src/jswrap_io.h
  100. 167 0
      components/external/espruino/src/jswrap_json.c

+ 227 - 17
bsp/beaglebone/drivers/serial.c

@@ -10,6 +10,7 @@
  * Change Logs:
  * Date           Author       Notes
  * 2013-07-06     Bernard    the first version
+ * 2014-01-11     RTsien     support UART0 to UART5 straightly
  */
 
 #include <rthw.h>
@@ -38,7 +39,7 @@ static void am33xx_uart_isr(int irqno, void* param)
 
     iir = UART_IIR_REG(uart->base);
 
-	if ((iir & (0x02 << 1)) || (iir & (0x6 << 1)))
+    if ((iir & (0x02 << 1)) || (iir & (0x6 << 1)))
     {
         rt_hw_serial_isr(serial);
     }
@@ -162,14 +163,66 @@ static const struct rt_uart_ops am33xx_uart_ops =
     am33xx_getc,
 };
 
-/* UART1 device driver structure */
-struct serial_ringbuffer uart1_int_rx;
-struct am33xx_uart uart1 =
+/* UART device driver structure */
+#ifdef RT_USING_UART0
+struct serial_ringbuffer uart0_int_rx;
+struct am33xx_uart uart0 =
 {
     UART0_BASE,
     UART0_INT,
 };
+struct rt_serial_device serial0;
+#endif
+
+#ifdef RT_USING_UART1
+struct serial_ringbuffer uart1_int_rx;
+struct am33xx_uart uart1 =
+{
+    UART1_BASE,
+    UART1_INT,
+};
 struct rt_serial_device serial1;
+#endif
+
+#ifdef RT_USING_UART2
+struct serial_ringbuffer uart2_int_rx;
+struct am33xx_uart uart2 =
+{
+    UART2_BASE,
+    UART2_INT,
+};
+struct rt_serial_device serial2;
+#endif
+
+#ifdef RT_USING_UART3
+struct serial_ringbuffer uart3_int_rx;
+struct am33xx_uart uart3 =
+{
+    UART3_BASE,
+    UART3_INT,
+};
+struct rt_serial_device serial3;
+#endif
+
+#ifdef RT_USING_UART4
+struct serial_ringbuffer uart4_int_rx;
+struct am33xx_uart uart4 =
+{
+    UART4_BASE,
+    UART4_INT,
+};
+struct rt_serial_device serial4;
+#endif
+
+#ifdef RT_USING_UART5
+struct serial_ringbuffer uart5_int_rx;
+struct am33xx_uart uart5 =
+{
+    UART5_BASE,
+    UART5_INT,
+};
+struct rt_serial_device serial5;
+#endif
 
 #define write_reg(base, value) *(int*)(base) = value
 #define read_reg(base)         *(int*)(base)
@@ -219,11 +272,41 @@ static void start_uart_clk(void)
         ;
 
     /* enable uart1 */
+#ifdef RT_USING_UART1
     CM_PER_UART1_CLKCTRL_REG(prcm_base) |= 0x2;
-
     /* wait for uart1 clk */
     while ((CM_PER_UART1_CLKCTRL_REG(prcm_base) & (0x3<<16)) != 0)
         ;
+#endif
+
+#ifdef RT_USING_UART2
+    CM_PER_UART2_CLKCTRL_REG(prcm_base) |= 0x2;
+    /* wait for uart2 clk */
+    while ((CM_PER_UART2_CLKCTRL_REG(prcm_base) & (0x3<<16)) != 0)
+        ;
+#endif
+
+#ifdef RT_USING_UART3
+    CM_PER_UART3_CLKCTRL_REG(prcm_base) |= 0x2;
+    /* wait for uart3 clk */
+    while ((CM_PER_UART3_CLKCTRL_REG(prcm_base) & (0x3<<16)) != 0)
+        ;
+#endif
+
+#ifdef RT_USING_UART4
+    CM_PER_UART4_CLKCTRL_REG(prcm_base) |= 0x2;
+    /* wait for uart4 clk */
+    while ((CM_PER_UART4_CLKCTRL_REG(prcm_base) & (0x3<<16)) != 0)
+        ;
+#endif
+
+#ifdef RT_USING_UART5
+    CM_PER_UART5_CLKCTRL_REG(prcm_base) |= 0x2;
+    /* wait for uart5 clk */
+    while ((CM_PER_UART5_CLKCTRL_REG(prcm_base) & (0x3<<16)) != 0)
+        ;
+#endif
+
     /* Waiting for the L4LS UART clock */
     while (!(CM_PER_L4LS_CLKSTCTRL_REG(prcm_base) & (1<<10)))
         ;
@@ -236,46 +319,173 @@ static void config_pinmux(void)
     ctlm_base = AM33XX_CTLM_REGS;
 
     /* make sure the pin mux is OK for uart */
+#ifdef RT_USING_UART1
     REG32(ctlm_base + 0x800 + 0x180) = 0x20;
     REG32(ctlm_base + 0x800 + 0x184) = 0x00;
+#endif
+
+#ifdef RT_USING_UART2
+    REG32(ctlm_base + 0x800 + 0x150) = 0x20;
+    REG32(ctlm_base + 0x800 + 0x154) = 0x00;
+#endif
+
+#ifdef RT_USING_UART3
+    REG32(ctlm_base + 0x800 + 0x164) = 0x01;
+#endif
+
+#ifdef RT_USING_UART4
+    REG32(ctlm_base + 0x800 + 0x070) = 0x26;
+    REG32(ctlm_base + 0x800 + 0x074) = 0x06;
+#endif
+
+#ifdef RT_USING_UART5
+    REG32(ctlm_base + 0x800 + 0x0C4) = 0x24;
+    REG32(ctlm_base + 0x800 + 0x0C0) = 0x04;
+#endif
 }
 
 int rt_hw_serial_init(void)
 {
-    struct am33xx_uart* uart;
     struct serial_configure config;
 
-    uart = &uart1;
-    uart->base = UART1_BASE;
-
     poweron_per_domain();
     start_uart_clk();
     config_pinmux();
 
+#ifdef RT_USING_UART0
     config.baud_rate = BAUD_RATE_115200;
     config.bit_order = BIT_ORDER_LSB;
     config.data_bits = DATA_BITS_8;
     config.parity    = PARITY_NONE;
     config.stop_bits = STOP_BITS_1;
     config.invert    = NRZ_NORMAL;
+    serial0.ops    = &am33xx_uart_ops;
+    serial0.int_rx = &uart0_int_rx;
+    serial0.config = config;
+    /* enable RX interrupt */
+    UART_IER_REG(uart0.base) = 0x01;
+    /* install ISR */
+    rt_hw_interrupt_install(uart0.irq, am33xx_uart_isr, &serial0, "uart0");
+    rt_hw_interrupt_control(uart0.irq, 0, 0);
+    rt_hw_interrupt_mask(uart0.irq);
+    /* register UART0 device */
+    rt_hw_serial_register(&serial0, "uart0",
+            RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM,
+            &uart0);
+#endif
 
+#ifdef RT_USING_UART1
+    config.baud_rate = BAUD_RATE_115200;
+    config.bit_order = BIT_ORDER_LSB;
+    config.data_bits = DATA_BITS_8;
+    config.parity    = PARITY_NONE;
+    config.stop_bits = STOP_BITS_1;
+    config.invert    = NRZ_NORMAL;
     serial1.ops    = &am33xx_uart_ops;
     serial1.int_rx = &uart1_int_rx;
     serial1.config = config;
+    /* enable RX interrupt */
+    UART_IER_REG(uart1.base) = 0x01;
+    /* install ISR */
+    rt_hw_interrupt_install(uart1.irq, am33xx_uart_isr, &serial1, "uart1");
+    rt_hw_interrupt_control(uart1.irq, 0, 0);
+    rt_hw_interrupt_mask(uart1.irq);
+    /* register UART0 device */
+    rt_hw_serial_register(&serial1, "uart1",
+            RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM,
+            &uart1);
+#endif
 
+#ifdef RT_USING_UART2
+    config.baud_rate = BAUD_RATE_115200;
+    config.bit_order = BIT_ORDER_LSB;
+    config.data_bits = DATA_BITS_8;
+    config.parity    = PARITY_NONE;
+    config.stop_bits = STOP_BITS_1;
+    config.invert    = NRZ_NORMAL;
+    serial2.ops    = &am33xx_uart_ops;
+    serial2.int_rx = &uart2_int_rx;
+    serial2.config = config;
     /* enable RX interrupt */
-    UART_IER_REG(uart->base) = 0x01;
+    UART_IER_REG(uart2.base) = 0x01;
     /* install ISR */
-    rt_hw_interrupt_install(uart->irq, am33xx_uart_isr, &serial1, "uart1");
-    rt_hw_interrupt_control(uart->irq, 0, 0);
-    rt_hw_interrupt_mask(uart->irq);
+    rt_hw_interrupt_install(uart2.irq, am33xx_uart_isr, &serial2, "uart2");
+    rt_hw_interrupt_control(uart2.irq, 0, 0);
+    rt_hw_interrupt_mask(uart2.irq);
+    /* register UART2 device */
+    rt_hw_serial_register(&serial2, "uart2",
+            RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM,
+            &uart2);
+#endif
 
-    /* register UART1 device */
-    rt_hw_serial_register(&serial1, "uart1",
+#ifdef RT_USING_UART3
+    config.baud_rate = BAUD_RATE_115200;
+    config.bit_order = BIT_ORDER_LSB;
+    config.data_bits = DATA_BITS_8;
+    config.parity    = PARITY_NONE;
+    config.stop_bits = STOP_BITS_1;
+    config.invert    = NRZ_NORMAL;
+    serial3.ops    = &am33xx_uart_ops;
+    serial3.int_rx = &uart_3_int_rx;
+    serial3.config = config;
+    /* enable RX interrupt */
+    UART_IER_REG(uart3.base) = 0x01;
+    /* install ISR */
+    rt_hw_interrupt_install(uart3.irq, am33xx_uart_isr, &serial3, "uart3");
+    rt_hw_interrupt_control(uart3.irq, 0, 0);
+    rt_hw_interrupt_mask(uart3.irq);
+    /* register UART3 device */
+    rt_hw_serial_register(&serial3, "uart3",
             RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM,
-            uart);
+            &uart3);
+#endif
+
+#ifdef RT_USING_UART4
+    config.baud_rate = BAUD_RATE_115200;
+    config.bit_order = BIT_ORDER_LSB;
+    config.data_bits = DATA_BITS_8;
+    config.parity    = PARITY_NONE;
+    config.stop_bits = STOP_BITS_1;
+    config.invert    = NRZ_NORMAL;
+
+    serial4.ops    = &am33xx_uart_ops;
+    serial4.int_rx = &uart4_int_rx;
+    serial4.config = config;
+    /* enable RX interrupt */
+    UART_IER_REG(uart4.base) = 0x01;
+    /* install ISR */
+    rt_hw_interrupt_install(uart4.irq, am33xx_uart_isr, &serial4, "uart4");
+    rt_hw_interrupt_control(uart4.irq, 0, 0);
+    rt_hw_interrupt_mask(uart4.irq);
+    /* register UART4 device */
+    rt_hw_serial_register(&serial4, "uart4",
+            RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM,
+            &uart4);
+#endif
+
+#ifdef RT_USING_UART5
+    config.baud_rate = BAUD_RATE_115200;
+    config.bit_order = BIT_ORDER_LSB;
+    config.data_bits = DATA_BITS_8;
+    config.parity    = PARITY_NONE;
+    config.stop_bits = STOP_BITS_1;
+    config.invert    = NRZ_NORMAL;
+  
+    serial5.ops    = &am33xx_uart_ops;
+    serial5.int_rx = &uart5_int_rx;
+    serial5.config = config;
+    /* enable RX interrupt */
+    UART_IER_REG(uart5.base) = 0x01;
+    /* install ISR */
+    rt_hw_interrupt_install(uart5.irq, am33xx_uart_isr, &serial5, "uart5");
+    rt_hw_interrupt_control(uart5.irq, 0, 0);
+    rt_hw_interrupt_mask(uart5.irq);
+    /* register UART4 device */
+    rt_hw_serial_register(&serial5, "uart5",
+            RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM,
+            &uart5);
+#endif
 
     return 0;
 }
 INIT_BOARD_EXPORT(rt_hw_serial_init);
-

+ 13 - 1
bsp/beaglebone/rtconfig.h

@@ -73,6 +73,18 @@
 #define RT_USING_DEVICE_IPC
 // <bool name="RT_USING_SERIAL" description="Using Serial Device Driver Framework" default="true" />
 #define RT_USING_SERIAL
+// <bool name="RT_USING_UART0" description="Using uart0" default="true" >
+#define RT_USING_UART0
+// <bool name="RT_USING_UART1" description="Using uart1" default="true" >
+#define RT_USING_UART1
+// <bool name="RT_USING_UART2" description="Using uart2" default="true" >
+#define RT_USING_UART2
+// <bool name="RT_USING_UART3" description="Using uart3" default="true" >
+//#define RT_USING_UART3
+// <bool name="RT_USING_UART4" description="Using uart4" default="true" >
+#define RT_USING_UART4
+// <bool name="RT_USING_UART5" description="Using uart5" default="true" >
+#define RT_USING_UART5
 // <integer name="RT_UART_RX_BUFFER_SIZE" description="The buffer size for UART reception" default="64" />
 #define RT_UART_RX_BUFFER_SIZE    64
 // <bool name=RT_USING_INTERRUPT_INFO description="Using interrupt information description" default="true" />
@@ -84,7 +96,7 @@
 // <integer name="RT_CONSOLEBUF_SIZE" description="The buffer size for console output" default="128" />
 #define RT_CONSOLEBUF_SIZE	128
 // <string name="RT_CONSOLE_DEVICE_NAME" description="The device name for console" default="uart" />
-#define RT_CONSOLE_DEVICE_NAME	"uart1"
+#define RT_CONSOLE_DEVICE_NAME	"uart0"
 // </section>
 
 // <bool name="RT_USING_COMPONENTS_INIT" description="Using RT-Thread components initialization" default="true" />

+ 1 - 0
components/dfs/filesystems/ramfs/dfs_ramfs.c

@@ -414,6 +414,7 @@ int dfs_ramfs_init(void)
 
     return 0;
 }
+INIT_FS_EXPORT(dfs_ramfs_init);
 
 struct dfs_ramfs* dfs_ramfs_create(rt_uint8_t *pool, rt_size_t size)
 {

+ 7 - 6
components/dfs/filesystems/romfs/mkromfs.py

@@ -4,6 +4,7 @@ import string
 
 basename = ''
 output = ''
+sep = os.sep
 
 def mkromfs_output(out):
     # print '%s' % out,
@@ -54,12 +55,12 @@ def mkromfs_dir(dirname, is_root = False):
         fullpath = os.path.join(path, item)
         if os.path.isfile(fullpath):
             subpath = fullpath[len(basename):]
-            array = subpath.split('\\')
+            array = subpath.split(sep)
             arrayname = string.join(array, '_')
             mkromfs_file(fullpath, arrayname)
 
     subpath = path[len(basename):]
-    dir = subpath.split('\\')
+    dir = subpath.split(sep)
     direntname = string.join(dir, '_')
     if is_root:
         mkromfs_output('const struct romfs_dirent _root_dirent[] = {\n')
@@ -69,12 +70,12 @@ def mkromfs_dir(dirname, is_root = False):
     for item in list:
         fullpath = os.path.join(path, item)
         fn = fullpath[len(dirname):]
-        if fn[0] == '\\':
+        if fn[0] == sep:
             fn = fn[1:]
         fn = fn.replace('\\', '/')
 
         subpath = fullpath[len(basename):]
-        items = subpath.split('\\')
+        items = subpath.split(sep)
         item_name = string.join(items, '_')
         item_name = item_name.replace('.', '_')
         item_name = item_name.replace('-', '_')
@@ -92,12 +93,12 @@ def mkromfs_dir(dirname, is_root = False):
     for item in list:
         fullpath = os.path.join(path, item)
         fn = fullpath[len(dirname):]
-        if fn[0] == '\\':
+        if fn[0] == sep:
             fn = fn[1:]
         fn = fn.replace('\\', '/')
     
         subpath = fullpath[len(basename):]
-        items = subpath.split('\\')
+        items = subpath.split(sep)
         item_name = string.join(items, '_')
         item_name = item_name.replace('.', '_')
         item_name = item_name.replace('-', '_')

+ 19 - 14
components/drivers/serial/serial.c

@@ -368,20 +368,25 @@ static rt_err_t rt_serial_control(struct rt_device *dev,
 
     switch (cmd)
     {
-    case RT_DEVICE_CTRL_SUSPEND:
-        /* suspend device */
-        dev->flag |= RT_DEVICE_FLAG_SUSPENDED;
-        break;
-
-    case RT_DEVICE_CTRL_RESUME:
-        /* resume device */
-        dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
-        break;
-
-    case RT_DEVICE_CTRL_CONFIG:
-        /* configure device */
-        serial->ops->configure(serial, (struct serial_configure *)args);
-        break;
+        case RT_DEVICE_CTRL_SUSPEND:
+            /* suspend device */
+            dev->flag |= RT_DEVICE_FLAG_SUSPENDED;
+            break;
+
+        case RT_DEVICE_CTRL_RESUME:
+            /* resume device */
+            dev->flag &= ~RT_DEVICE_FLAG_SUSPENDED;
+            break;
+
+        case RT_DEVICE_CTRL_CONFIG:
+            /* configure device */
+            serial->ops->configure(serial, (struct serial_configure *)args);
+            break;
+
+        default :
+            /* control device */
+            serial->ops->control(serial, cmd, args);
+            break;
     }
 
     return RT_EOK;

+ 46 - 0
components/external/espruino/CONTRIBUTING.md

@@ -0,0 +1,46 @@
+Contributing
+===========
+
+Thanks for thinking about contributing to Espruino! Anything you can add is hugely appreciated, but please can you follow a few simple rules:
+
+* Keep the same coding style (See **Coding Style** below)
+* Ensure that you are not contributing someone else's code, and that you are willing to add your code under Espruino's MPL Licence
+* Make sure that what you do doesn't break the Espruino board or the other boards we build for. We can't check all the boards for every commit, so if you break something you'll annoy a whole bunch of people.
+* Be aware that Espruino is designed for Microcontrollers - with very low amounts of flash and memory. Both are at a premium so don't statically allocate variables or do other stuff that will use up memory.
+* Avoid randomly adding newlines, spaces, refactoring everything or renaming things to your own personal style (some things really could do with renaming, but please check first or we may reject your pull request)
+* Don't add a whole bunch of indirection/abstraction for the sake of it - it'll probably just use of more of our precious memory.
+* If you add a new API, try and make it familiar to Arduino/JavaScript users.
+
+Target Areas
+-----------
+
+We'll keep the outstanding issues in GitHub's issue list, but general stuff that would really help us is:
+
+* Tests. If something doesn't work, please make a test for it. Even if you don't fix it it'll help others greatly. Bonus points if it's in a pull request :)
+* Documentation. Improving the documentation (either the EspruinoDocs project, or the auto-generated reference) would be fantastic.
+* Duplication. If the same code is used for multiple platforms, try and make sure it's shared, not duplicated.
+* Remove hard-coded stuff. Various things like the SPI filesystem are still hard-coded with ifdefs for each board - we want all that stuff to be generated from build_platform_info.py
+* Speed. There are a few areas this could be improved - but please benchmark what you're doing both before and afterwards on the Espruino board to check that what you've done helps
+* Memory Usage. Both RAM and Flash are at a premium. Ways of reducing this (including stack usage) and making usage more efficient are really appreciated.
+* JavaScript compliance (without affecting speed or memory usage too much).
+
+Contributing
+-----------
+
+* Please RUN THE TESTS to check that there are no regressions
+* Issue us a pull request to [www.github.com/espruino] via GitHub
+* Please keep each request small (just include one fix per request)
+
+Coding Style
+-----------
+
+The rough coding style is as follows, but you should get a good idea from the code. If we've missed anything obvious please let us know!
+
+* 2 Spaces for indents
+* Open curly braces on the same line
+* No Tabs used
+* Use bool for booleans - not int
+* ```//``` comments for single lines, ```/* ... */``` for multiple lines
+* Half-hearted Doxygen compatibility: use ```///<``` for function declaration documentation (if on same line), and ```/** ... */``` if doing it right before a function
+* Use new lines sparingly (only where it really makes sense)
+

+ 515 - 0
components/external/espruino/ChangeLog

@@ -0,0 +1,515 @@
+     1v46 : ...
+
+     1v45 : Fix parseFloat("foo") not returning NaN (and assert) - fix #149
+            Remove Integer.parseInt
+            Fix parseInt("bar") - fix #150
+            Ensure that maths ops with null do treat it as 0 rather than a string - fix #156
+            Fix digitalPulse length (properly!) - fix #154
+            Making sure that undefined gets cast to NaN
+            Fix Array.indexOf() returns undefined instead of -1 - fix #155
+            Moved memory() to process.memory() - added more info too    
+            Try and improve handling of PWM timer speeds
+            Fixed varying SPI baud rates depending on device
+            Makefile changes for OSX compile        
+
+     1v44 : Modified build system to store binary names in the python definition
+            Fix nasty regression involving losing code at the end of Strings
+            Fix segfault when pinMode is called on an invalid pin
+            Now disable interrupts during 4 bit SPI send - it's just too much otherwise
+            Detect unfinished block comments in console (fix #138)
+            Fix flash write on most 10XxB boards
+            Fix PWM output on all STM32 boards
+            General hardware tidy in prep for more intelligent device management
+            Run initialisation code before setWatch, to make sure pullup/down is set beforehand
+            Change 'Pin' datatype to be an unsigned char - makes tests easier
+            Now use the hardware RTC for keeping system time. Allows proper deep sleep on Espruino board
+            FINALLY - fix the USB VCP lost characters issue (#94)
+
+     1v43 : Added 'Modules' object with support for adding/removing cached modules
+            Allow product ID to be changed via Makefile
+            Fix documentation (and old-fashined Parsing style) for JSON
+            build_jswrapper now outputs errors to stderr (more compatible with default (silent) build process)
+            Fix issue when parsing quotes in strings
+            Added void operator for closure minification compatibility
+            Ensure that return takes the comma operator
+            Fix issue where printing Infinity would crash Espruino (fix #129)
+            Finally some working (extremely beta) cc3000 code
+            Added jsvObjectGet/SetChild to simplify some wrappers
+            'http' now uses JsVars for storage (so is suitable for non-linux devices)
+            Turned 'http' into a library
+            Added process.version and process.env (fix #131)
+            Changed handling of 2nd arg of << so that precedence is correct
+            Fixed handling of 'for (;;)'
+            Fix lock leak in Module handling
+            Update ST's library for the STM32F1
+            Update ST's VCP implementation
+            Added prefix operator (fix #130)
+            Allow espruino for linux to be run with '#!' in scripts
+            Fix indexOf on final element of strings (fix #133)
+            Remove JSV_PARENTINFO, as it turns out JS doesn't keep track of function scopes anyway (fix #109)
+            Make 'this' a keyword (now faster, more memory efficient)
+            Make 'Hardware' (root) the default value of 'this'
+            Add jsvArrayPushAndUnLock and modified code to use it (fix #135)
+            Now remember I2C state (partial fix for #13)
+            Replace 'pow' function with a smaller version - save ~2kb
+            Shaved another 1200 bytes off jslTokenAsString
+            Now store Pin state (fix for #13 on F1 parts, F4 still looks broken)
+            Added Graphics.stringWidth
+            Added internal Printf function
+	           Misc speed and code size improvements
+            This version has gone to Seeed for use on the KickStarter boards
+
+     1v42 : [ebirger] allowing 'new' with no brackets
+            Allow built-in functions with variable numbers of arguments (fix #83)  
+            Implement 'String' constructor in the normal way (fix #110)
+            Fix regression with parsing constructors while not executing
+            Allow multiple arguments to print and console.log (fix #92)
+            Make 'arguments' array available in functions (fix #100)
+            Fix an assert fail, and handle some potential memory leaks
+            Don't show __proto__ and constructor with for..in, keys(), or JSON.stringify
+            Make 'trace()' output more readable debug data for complex structures 
+            Fix memory leak whe parsing functions iwht variable numbers of arguments - fix #115
+            Defined NaN
+            Stop 'new undefined()' crashing Espruino - fix #120
+            Get A13/A14 working on Espruino board (these were JTAG)
+            Get bootloader size direct from Python (remove hard-coding)
+	           Fix '~' operator when acting on variables
+            Made bootloader a bit more error tolerate (CRC on write)
+            Added %=, /=, and *= operators (fix #121)
+            Allowed Object.toString() to take a radix argument for integers (fix #125)
+            Fix error message issue - broken strncat (fix #124) 
+            Add comma operator (fix #122) 
+            Added some basic code for STM32F429IDISCOVERY - not currently working though
+            This version is the one sent off on the Test Harness (so will probably appear on boards)
+
+     1v41 : Fix Olimexino compile (https://github.com/espruino/Espruino/issues/6)
+            [ebirger] Member constructors (eg. new a.b() )
+            [ebirger] Ensuring integers with radix specifiers can still be parsed if a radix is specified
+            Fix for tests/test_json_arraybuffer_001.js - iteration of arraybuffers of length==1
+            Add Object.keys(...)
+            More arraybuffer iteration fixes
+            On linux, use built-in stringToFloat to aid debugging. Handle exponentials, fix #31
+            'make serialflash' is now works correctly for Espruino Boards with bootloader
+            setWatch(..A0);setWatch(..A0);clearWatch(1) does not now kill the other watch, fix #25
+            One-based setTimeout/setWatch, fix #3
+            Added Function.call and Function.apply, fix #54
+            'http' and 'fs' are now libraries that need to be 'require'd, fix #8
+            Updated pin info for STM32F103xC/D/E chips, fix #84
+            Fixed linker script for STM32F4 (discovery board now works)
+            Object prototypes are now Objects, fix #101
+            Board docs now specify '3.3v' only pins fix #104
+            Add Array.forEach
+            Fix searching down >1 prototype to find functions (one more issue posted in #99) 
+            Fix "12345"/5 type issues (fix #90)
+            'Consting' some string functions
+            Fixing arrays with string indices that are actually numbers \o/ (fix #19)
+            Released onto website
+
+     1v40 : Ensure that LCD.prototype.setPixel = function actually works
+            Refactor LCD driver code to allow lcdInit (and start of making it non-platform-specific)
+            Built 'LCD' support into linux/raspi/carambola
+            Add initial SPI.send(ArrayBuffer) support - even if NO VALUES RETURNED
+            Start of built-in Nokia 5110 LCD support
+            Remove GPIO clock removal on sleep for now (it kills setWatch)
+            Fix (sub)ArrayBuffer problems (test100.js)
+            Added setDeepSleep - still beta put power consumption drops to 1mA
+            Fix broken name for httpCRq.write
+            Changed LCD to Graphics - added ability to render to ArrayBuffer
+            Fix 8 char built-in class names
+            Adding preliminary Sony SmartWatch support
+            Adding preliminary support for completely bare 36 pin chip
+            Fixing pin defs for Espruino board rev 1v1
+            Added proper SDL/ArrayBuffer and JS Callback graphics support
+            Added "ifdef" ability in build_jswrapper
+            Take JSVAR_CACHE_SIZE out of jsutils and put it in the board config file
+            Added JSV_PARENTINFO which will allow us to do things like setTimeout(foo.bar,10)		
+            Fixed arrays in non-executes streams - 'if (0) print([1,2,3]);'
+            Added 'require' function loading modules from node_modules on SD card
+            Added module cache to stop modules being re-loaded
+            Renamed internal vars to start with '>' - much easier to distinguish for 'dump'/etc
+            Only use parentInfo on functions
+            Load all tests in test dir - don't do them by number
+            Added 'zigzag' ordering for ArrayBuffer Graphics
+            Added 'vertical_byte' ordering for ArrayBuffer Graphics
+            toJSON now ignores 'hidden' object elements
+            Special-case jsvArrayBufferIteratorSetIntegerValue
+            Make SPI output an ArrayBuffer
+            Use best out of 3 for DelayMicroseconds calibration - something seems flaky right after bootup
+	           Lines now drawn from p1 to p2 inclusive
+	           Events now use jshPushIOWatchEvent (should cut down on code) also fixed bug with watching pin #11
+	           Now remember if pinMode was set or not
+       	    Transform ```code``` in JSON into a code tag in the documentation
+            Graphics now supports FSMC for HY boards again
+            Drawing vector fonts is now roughly the right size and position (still not 100%)
+            Remove registration code
+            Adding MPL licence
+            Remove Arduino bit manipulation functions - nobody seems to use them anyway
+            # of flash pages/etc now comes from board info            
+            [ebirger] Supply the correct arguments to Array.map
+            [ebirger] Method calls and membership evaluation should be done on all factors (ee. [1,2,3].foo())
+            [ebirger] When running multiple tests, only set up terminal once or it breaks the terminal window on exit
+            Added STM32-style USB CDC bootloader for Espruino Boards
+            Added scripts/create_espruino_image.sh to package up bootloader and espruino into one binary
+            SHIPPED on Impatient developer boards
+
+     1v39 : Added Bitwise NOT operator
+            Added Raspberry Pi version to ZIP (with HTTP support)
+            Fixed load/save on Linux Devices
+            Added pinMode function (to allow pull-ups/pull-downs to be turned on)
+            SPI.send4bit/send8bit will now not mess up the final element
+            changeInterval now clears up stored up callbacks (eg, setInterval(.., 0.01)...wait...changeInterval(...,20)
+            Ctrl-C no longer prints anything, which avoids lockups
+            No longer print "Execution Interrupted" if nothing was interrupted!
+            Added >>>= >>= and <<=
+            When entering text interactively, ensure that there are no trailing spaces
+
+     1v38 : Tweaks for Arduino IDE compile
+            Removed '(char #)' from stack trace, as a bit pointless now
+            Added better reporting of execution location when Ctrl-C pressed
+            Urgent fix for non-working Olimexino since 1v33
+            Fix string comparison when strings contain "\0"
+            Added LED1/2/OSC/SD/etc to Olimexino Board docs
+
+     1v37 : Urgent fix - power saving code made it difficult to re-flash Espruino (now only apply this to Espruino Board)
+
+     1v36 : Fix documentation for Array.pop()
+            Added some much better board documentation
+            Fixed DAC output on F3
+            Fixed DAC output on devices where PWM is also available and the alternate function is less than the DAC's
+
+     1v35 : Attempt to reduce power consumption when sleeping by turning off GPIO, and setting GPIOs to AIN on reset
+            Fix F3 issue where ADC/DAC weren't picked up properly
+            Tidy up register text and add KickStarter mention
+            var a = {}; a[LED1]=0; - not converted to String
+            JSON (and hence dump()) now dumps ArrayBuffer correctly
+
+     1v34 : Faster jshFromDeviceString
+            Preliminary support for flow control on Serial receive
+            Speed improvements by removing jsvGetRef from jsvUnLock
+            fast 4 byte pre-check in jsvFindChildFromString
+            Skip lock/unlock in FindChildFromString to help increase speed
+            When we unplug USB, only go to the default console device IF that is the device we're currently on
+            Support for custom Espruino board
+            Added ArrayBufferView.interpolate
+            16 bit SPI send for send4bit/sevrnd8bit (better reliability on low-end chips)
+            Fix JSON dump of typed array
+            Added Math.clip(x, min, max)
+            When saving on flash, don't do jslTokenAsString properly
+            B3/B4 move from alternate fn
+            Fix incorrect reporting of analog pins
+            Fix I2C.readFrom on STM32F1/4
+            Make 1/2 == 0.5 (was being sensible before, but now follow JS spec)
+            Ctrl-C while in timer fn clears timers (but not outside it)
+            Fixed broken clearInterval from within setInterval
+            Hopefully fixed issue on SSD1289 LCD controller
+            Trigger wheel handler to use interrupts
+            2D arraybuffer interpolation
+            Added Math.wrap, fixed a lot of trigger issues
+            Fixed SysTick priority/preempt problems
+            Slowed the SysTick timer back down for everything       
+            Fix Int8Array signedness on F4 boards 
+            Refactored source code tree
+
+     1v33 : fix character encoding issue of "\16"+"1" != "\161"
+            Refactoring of ArrayBuffer into iterator, and addition of a general purpose iterator
+            Fancier assert for debugging
+            jsvArrayJoin to use new iterator
+            for (i in ...) to use new iterator
+            I2C and SPI use new iterator
+            Serial.write() - to allow single ints to easily be written
+            changeInterval assert fail when given a function by accident
+            added peek8/poke8/peek16/poke16
+            memory() now takes account of command history size
+            memory() on ARM reports the end address of the stack - so it can be used as a scratchpad with peek and poke
+            Try and reduce code size by not inlining several functions
+            No refs for StringExts - so we get one more byte per JsVar (~5%) more storage efficiency
+            Move from jsvIsBuiltInFunction to computer-generated jswIsBuiltInFunction
+            When creating Objects, check for built-in function BEFORE creating an Object class for it
+            Built process now checks that flash usage is under the allowed value
+            Added short compare to jswHandleFunctionCall to reduce code size
+            Added 3 byte compare (4 byte read and AND off top byte) - faster, less code
+            Auto-generate jsvGetBasicObjectName from docs
+            No longer using refs for storing whether free or not - use flags with JSV_UNUSED and get one extra var with 8 bit refs
+            Switch to using STRING_0...STRING_MAX in flags, rather than specific bits in JsVarFlags - allows more that 15 chars to be used per JsVar
+            Make ArrayBuffers actually be ArrayBufferViews - saves on extra string-handling code at expense of one var
+            Make sure Uint8Array,etc inherits from ArrayBufferView
+            Fix issue where a '\0' coming in from serial was not put in e.data properly
+            Don't inline some functions when we're trying to save on flash
+            Re-use sin for cos, pow for sqrt to reduce code size
+
+     1v32 : Fixed embarassing issue with 0.999=="0.A"
+            Added and checked Pin.writeAtTime on STM32
+            Now don't allocate events array - just allocate directly, which saves memory and is faster (although slighty out of order)
+            Docs: now Alphabetically sorted, and class instances not listed by accident
+            Fix issue where Ctrl-C on ANY Serial port caused execution to be interrupted
+            Updated busy indicator to cope with recent change to not allocate events in an array
+            Fixed I2C on HY 2.4 board - I2C needed hard reset
+            Added basic ArrayBuffers/TypedArray support
+            Fix memory leak when error created with [] on a non-array/object
+            Improved hyperlinking in documentation
+            Fix I2C receive bug on F4
+            Increased VL board's input buffer size
+
+     1v31 : Fix PWM output on TIMER1/8 pins of the STM32F4
+            Fix PWM output for negated timers
+            memory() now runs a GC pass
+            Fixed multiple occurrence of functions in reference
+
+     1v30 : STM32F1: fixed AF issue meant peripherals would never return from AF mode
+            STM32F1: When given an invalid pin, now reports if pins are 'af' or not
+            Updated SPI.setup docs to mention that you can't mix AF and non-AF
+            If one SPI pin is specified but others aren't, only that pin will be set up
+            Added Olimexino hack so SPI1.setup works as expected
+            Allow using [] on a function
+            Fix precedence issue, so var a = function() { return 1; }(); works
+            Update SPI documentation
+            for (i in f) can now iterate over functions     
+            Optional argument to trace() for object to start tracing from        
+            Small steps towards ArrayBuffers
+            Added smart edit that checks for internal functions, and uses Function.replaceWith
+            Added Function.replaceWith to replace the internals of a function while keeping the scope
+
+     1v29 : Some hacky sysfs-based IO for running on Linux
+            HTTP Callbacks are now stored as names so they can be changed on the fly (Linux only)
+            Successful compile for Carambola 
+            Filesystem support on Linux
+            Switch to variable size ref counter (marginally more efficient on very small devices, safe on Linux)
+            Linux now has unlimited memory available
+            Added linux/sysfs 'setWatch' (non-irq driven, so very noddy)
+            Checked jswrapper check from using multi-char constants to a #define
+            Improve pin suggestions for SPI/I2C/USART
+            Auto-initialise USART with default values when setConsole is used
+            Support for new Graphics LCD types
+            3.2" VCT6 board support
+ 
+     1v28 : Faster LCD fillrect for HY 2.8
+            Fix for multi-byte SPI writes on HY board at 1Mhz (touchscreen control bug)
+            Fix issue with delayMicrosecond calibration on HY (and hence OneWire)
+            Fixed digitalPulse on STM32VLDISCOVERY
+
+     1v27 : Fixed problem with OneWire constructor execution
+            Added |=, &= and ^=
+            Added Array.splice()
+            Faster, more ROM-efficient built-in symbol table
+            Fix for potential issue when using field accessor on an undefined var
+
+     1v26 : I2C Support on STM32F1 and STM32F4 boards too
+            Emergency cut in variables for Olimexino with bootloader (as flash usage has got too high for save to flash!)
+
+     1v25 : http.writeHead to accept an empty header
+            Fixed issue finding methods on built in classes (0 termination)
+            make sure http server with no data still sends headers...
+            Start of MINI-HY-2.8" support (all ok, but no SD card yet)
+            Reduce RAM usage by consting some arrays that are not modified
+            Vector fonts now use polys - 8kb less ROM, and faster rendering
+            Start of LCD 'driver' code
+            Standard way of handling events, Object.on/emit/removeAllListeners - like Node.js's EventEmitter
+            Self-calibrating Microsecond delay (for intermal OneWire/etc)
+            OneWire class
+            Correct handling of built-in class constructors
+            Fix error when parsing a zero-argument function that has been given arguments
+            I2C support - currently ONLY tested on STM32F3 board
+
+     1v24 : SDIO-based fat driver on the HY STM32 board
+            Added DAC to the HY boards (103xE-based)
+            Re-named the fileSystem functions to make them more compatible with node.js (readFile/writeFile/etc)
+            Added fs.appendFile
+            Removed HTTP from the reference until it is included in some boards
+
+     1v23 : Fix 'ERROR: INTERNAL: stmADCChannel' on STM32VLDISCOVERY/F1 boards when accessing PA0
+            Reference now mentions which Espruino version it is for
+
+     1v22 : Important fix - Events got executed in the wrong order if they got queued up
+
+     1v21 : Ensure SPI clock does not stop between bytes
+            Added SPI.send4bit and SPI.send8bit
+            Made sure the VL board's code fits into available flash
+            Smart += that can append to a string rather than cloning it
+            dump() prints functions properly, rather than 'var f = function() {}'
+            Pageup/down move the cursor to the beginning/end of input
+            LCD draw/fill with negative x and y
+
+     1v20 : Add console.log
+            Fix automatic usleep for Linux
+            Added node.js-style HTTP server for Linux version
+            Fixed null-pointer issue when accessing something that doesn't exist on an object
+            Added node.js-style HTTP client for Linux version
+            Start of bit bashing functionality
+            for (i in "ABCD") console.log(i) -> 0,1,2,3
+            String array access (but not for writing - doesn't work in JS anyway)
+            String String.fromCharCode / charCodeAt
+            Added SPI baud rate setting
+            Vector font chars 'a' and '4' now work ok
+            Fix numeric exception when rendering a poly that has some identical points
+            Better digitalPulse (uses timer + interrupts)
+            analogWrite can now take an object with a 'frequency' argument for PWM
+
+     1v19 : Fixed issue where var M=Math;M.random() failed
+            Fixed issue with var U=USB;U.print("Hello");
+            Remove loop iteration limit
+            Fix memory leak when a syntax error is in for '(i in arr)'
+            Save state of pin on setWatch interrupt (e.state)
+            Change setWatch to allow only on rise or fall as an option
+            clearWatch() clears all watches
+
+     1v18 : DAC support on F3/F4
+            Serial.setup() can also take a second parameter of an object {tx,rx}
+            Better support for dump() with echo/setBusyIndicator/etc
+            Better dumping of prototypes on built-in vars
+            Don't add chars<32 (Except tab) to the input line
+            SPI1/2/3/4.setup() to take an object {baud,sck,miso,mosi}
+            Better hardware initialisation code (not UARTS auto-init if they are used)
+            Fix issues with prototypes
+            Peek/poke instructions
+            Start of I2C support (not usable yet)
+            Added Math. ceil/floor/exp/log
+
+     1v17 : Support for running alongside the Maple bootloader
+            Fix parsing of numbers beginning with 0 when forceRadix!=8
+            Fixed USART1 on Maple/Olimexino devices
+
+     1v16 : Inlining of jsvLock/UnLock in jsvar.h to improve speed
+            Move non-hardware-dependent stuff into jsdevices
+            Move jshardware.c into targets/stm32/jshardware.c, create 'targets/linux' and use a single makefile
+            For + While loops work without reallocating lex 
+            Fix AddNativeFunction when function already exists (and tests + saved state)
+            Change jsvFindChildFromX to use JsVar* from JsVarRef - saves a lot of lock/unlock
+            Handle new Foo() as per spec (return value + init of this+prototype) - still does not cope with non-object prototype
+            Beginning of SD card support (works on Olimexino, but not very flexible)
+            Fix for parse/eval when given non-strings
+            Strings can now contain '\0'
+            Jumptable-friendly reserved word check
+            Jumptable-friendly builtin functions (massive refactor)
+            SPI support
+            HY board support, and graphics LCD
+            Added fillPoly, and Vector fonts
+            Added Registration code
+            Fixed some undefined function/array warnings
+            Much better HTML function documentation
+            Fixed edit() function
+            STM32F3 support, and now peripheral stuff is done with a script
+            explain what pins are available if a pin is not capable of requested fn
+            power on ADCs only when needed
+            LCD fillPoly speed improvements, + drawLine
+            Add datatype for Pin, so pins written to console by pin name rather than integer value.
+            Added Pin.set/Pin.reset
+            Change warning about 'undefined.' into an error (foo.reset() had unexpected consequences!)
+            Fix parsing of '1.0/-3'!
+            Add typeof and instanceof operators
+            Ensure that Serial1/SPI1/etc are objects of type 'Serial'/'SPI' - so prototypes can be added
+
+     1v15 : Escaping JSON strings
+            Fix parsing of octal numbers in strings (so don't have to be 3 chars long)
+            Drastically improved stack usage using small stub functions (at expense of a bit of speed)
+            dump() also dumps out prototypes for functions
+
+     1v14 : Fix complaint about pins during setBusyIndicator()
+            Increase available memory on OLIMEXINO
+            Added function memory() to return memory usage
+            setWatch now links to function names (rather than just functions)
+            dump() also handles Serial.onData(...)
+            Fix issue with JSON printing functions with arguments to console
+            prefix builtin variables with '_'
+            fix ArrayIndexOf when array contains undefineds
+            move all devices into one git repository
+            USB on F4
+            call onInit function/string if it exists when Espruino powers on
+            Compile F4 with -O2 - as we have the program memory for it
+            Serial3/4/5/6 on F4
+            Serial3 on Olimexino
+            Make Serial.onData() clear onData handler
+
+     1v13 : Operations like + on Object/Array convert them to strings rather than error
+            var now doesn't error if there is no semi-colon
+            Allow new line or line delete in multi-line editing
+            add edit(functionName) - which copies function definition into inputline so it can be updated
+            When printing lines, delete current inputline and then put it back in idle loop (only if echo=1)
+            Support *,/ etc on numpad
+
+     1v12 : Issue when printing lots of data and then disconnect USB
+            Hide USB/Serial in Dump()
+            add Array.map(fn(x), thisArg)
+            For newline, count [] and () (as well as {}) - also knows about comments/strings/etc
+            Fix assert fail is setTimeout with non-function
+            If space at end of input line, enter still executes
+            Removed some hard-coded arrays in favour of JsVar strings
+            Fix confusion with jsvIsName/jsvIsString
+            Handle numpad end key
+            Add code to check stack and stop stack overflow if too much recursion
+            Ensure that setTimeout/setWatch store the link to a function, not the function
+            Fix nasty ref loop in ref loop GC issue
+            Add dotty output
+            Fix memory leak when error in jspParseSingleFunction
+            Now run Garbage collection if we're idle, and we know we have a few ms spare
+            Added setSleepIndicator
+            Fix line/col indicator in errors/warnings
+            Fix JSON parsing and printing when 'undefined' encountered
+            Rewritten object handling code to be way more standard JavaScript compliant
+            Array initialisation with 'new Array()', also for Strings
+            Added a few more built in functions
+            Nice error reporting with line + pointer
+            fixed Math.random
+            Binary style ops on doubles now work - they are just converted to ints
+            Added boolean datatype
+
+     1v11 : Add Math functions
+            Add command history (and dynamic history free if low memory)
+            Fix broken jsvArrayPop
+            Add tests for and fix Array.indexOf
+            In-line editing for commands
+            Fix bug in basicVarEquals for big strings
+            More fixes for low memory conditions
+            Multi-line edit for commands (but no newline or line delete yet)
+            Handle Home, End + reverse delete keys
+            Fix nested for loops not handling interrupts correctly
+            Fix AppendString issue when given start value greater than string
+            Add 'changeInterval' to allow things created with setInterval to have the frequency changed (eg. stepper motor control)
+            Now puts itself to sleep to save power, when it knows nothing is required and it'll be woken up by SysTick before
+            Change Math library to avoid putting constants in RAM
+
+     1v10 : Increase FIFO size for VL
+            Marginally decrease amount of F4 vars to ensure they all fit in one flash sector
+            Allow strings to be longer than the max token size
+            '"key" in obj' syntax
+            Detect if in FOR or WHILE loop, and if not, disallow break and continue
+            Change min setInterval time to 0.1ms - F4 can get close to this
+            Better analog pin error message
+            USB support on Olimexino/Maple
+            Start of multiple COM port support (ioEvent queue)
+            Ctrl-C now clears the input line
+            Save state of 'echo' into flash with save()
+            Add 'setBusyIndicator(pin)' to set pin high when Espruino is busy
+            Inbuilt function handling speed improvements
+            Allow Serial comms via other UARTS. Serial1/2.onData and print/println
+            now inserts elements into arrays in the correct order (GetLength can be (is) now much faster)
+            Faster code to work out pins from strings
+            Automatically convert IDs in form A#,A##,B#,B## etc into numbers.
+            Built-in constants for LED1/BTN/etc.
+
+     1v09 : Enabled 'abs' by default
+            Added flash programming to STM32F4
+            analogWrite now working!
+
+     1v08 : Add preliminary STM32F4 support
+            Allowed test cases to test timers - eg. code in jsinteractive.c
+            Fix memory leak for timer
+            Fix memory leak for digitalWrite
+
+     1v07 : Fix string charAt
+            Fix watch on different pin
+            Pass arguments to event handlers - eg. time
+            digitalWrite/Read to take arrays of pins, and int for value
+
+     1v06 : Add break + continue
+            Add switch statement
+            Handle /r, /r/n or just /n for newlines - phone compatible                            
+            Handle different type of delete
+
+     1v05 : Allow setWatch/setTimeout/setInterval with a string
+            Handle adding Open bracket then deleting it
+            When calling a NAMED function, zero the scopes - this stops scope table overflow
+
+     1v04 : Renamed to Espruino
+            Fixed issue with event add when out of memory
+            If out of memory happens during a timer, kill all timers
+

+ 378 - 0
components/external/espruino/LICENSE

@@ -0,0 +1,378 @@
+All files in this package are Copyright 2013 Gordon Williams, Pur3 Ltd unless
+otherwise noted.
+
+-------------------------------------------------------------------------------
+
+Mozilla Public License Version 2.0
+==================================
+
+1. Definitions
+--------------
+
+1.1. "Contributor"
+    means each individual or legal entity that creates, contributes to
+    the creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+    means the combination of the Contributions of others (if any) used
+    by a Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+    means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+    means Source Code Form to which the initial Contributor has attached
+    the notice in Exhibit A, the Executable Form of such Source Code
+    Form, and Modifications of such Source Code Form, in each case
+    including portions thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+    means
+
+    (a) that the initial Contributor has attached the notice described
+        in Exhibit B to the Covered Software; or
+
+    (b) that the Covered Software was made available under the terms of
+        version 1.1 or earlier of the License, but not also under the
+        terms of a Secondary License.
+
+1.6. "Executable Form"
+    means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+    means a work that combines Covered Software with other material, in 
+    a separate file or files, that is not Covered Software.
+
+1.8. "License"
+    means this document.
+
+1.9. "Licensable"
+    means having the right to grant, to the maximum extent possible,
+    whether at the time of the initial grant or subsequently, any and
+    all of the rights conveyed by this License.
+
+1.10. "Modifications"
+    means any of the following:
+
+    (a) any file in Source Code Form that results from an addition to,
+        deletion from, or modification of the contents of Covered
+        Software; or
+
+    (b) any new file in Source Code Form that contains any Covered
+        Software.
+
+1.11. "Patent Claims" of a Contributor
+    means any patent claim(s), including without limitation, method,
+    process, and apparatus claims, in any patent Licensable by such
+    Contributor that would be infringed, but for the grant of the
+    License, by the making, using, selling, offering for sale, having
+    made, import, or transfer of either its Contributions or its
+    Contributor Version.
+
+1.12. "Secondary License"
+    means either the GNU General Public License, Version 2.0, the GNU
+    Lesser General Public License, Version 2.1, the GNU Affero General
+    Public License, Version 3.0, or any later versions of those
+    licenses.
+
+1.13. "Source Code Form"
+    means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+    means an individual or a legal entity exercising rights under this
+    License. For legal entities, "You" includes any entity that
+    controls, is controlled by, or is under common control with You. For
+    purposes of this definition, "control" means (a) the power, direct
+    or indirect, to cause the direction or management of such entity,
+    whether by contract or otherwise, or (b) ownership of more than
+    fifty percent (50%) of the outstanding shares or beneficial
+    ownership of such entity.
+
+2. License Grants and Conditions
+--------------------------------
+
+2.1. Grants
+
+Each Contributor hereby grants You a world-wide, royalty-free,
+non-exclusive license:
+
+(a) under intellectual property rights (other than patent or trademark)
+    Licensable by such Contributor to use, reproduce, make available,
+    modify, display, perform, distribute, and otherwise exploit its
+    Contributions, either on an unmodified basis, with Modifications, or
+    as part of a Larger Work; and
+
+(b) under Patent Claims of such Contributor to make, use, sell, offer
+    for sale, have made, import, and otherwise transfer either its
+    Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+The licenses granted in Section 2.1 with respect to any Contribution
+become effective for each Contribution on the date the Contributor first
+distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+The licenses granted in this Section 2 are the only rights granted under
+this License. No additional rights or licenses will be implied from the
+distribution or licensing of Covered Software under this License.
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
+Contributor:
+
+(a) for any code that a Contributor has removed from Covered Software;
+    or
+
+(b) for infringements caused by: (i) Your and any other third party's
+    modifications of Covered Software, or (ii) the combination of its
+    Contributions with other software (except as part of its Contributor
+    Version); or
+
+(c) under Patent Claims infringed by Covered Software in the absence of
+    its Contributions.
+
+This License does not grant any rights in the trademarks, service marks,
+or logos of any Contributor (except as may be necessary to comply with
+the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+No Contributor makes additional grants as a result of Your choice to
+distribute the Covered Software under a subsequent version of this
+License (see Section 10.2) or under the terms of a Secondary License (if
+permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+Each Contributor represents that the Contributor believes its
+Contributions are its original creation(s) or it has sufficient rights
+to grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+This License is not intended to limit any rights You have under
+applicable copyright doctrines of fair use, fair dealing, or other
+equivalents.
+
+2.7. Conditions
+
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
+in Section 2.1.
+
+3. Responsibilities
+-------------------
+
+3.1. Distribution of Source Form
+
+All distribution of Covered Software in Source Code Form, including any
+Modifications that You create or to which You contribute, must be under
+the terms of this License. You must inform recipients that the Source
+Code Form of the Covered Software is governed by the terms of this
+License, and how they can obtain a copy of this License. You may not
+attempt to alter or restrict the recipients' rights in the Source Code
+Form.
+
+3.2. Distribution of Executable Form
+
+If You distribute Covered Software in Executable Form then:
+
+(a) such Covered Software must also be made available in Source Code
+    Form, as described in Section 3.1, and You must inform recipients of
+    the Executable Form how they can obtain a copy of such Source Code
+    Form by reasonable means in a timely manner, at a charge no more
+    than the cost of distribution to the recipient; and
+
+(b) You may distribute such Executable Form under the terms of this
+    License, or sublicense it under different terms, provided that the
+    license for the Executable Form does not attempt to limit or alter
+    the recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+You may create and distribute a Larger Work under terms of Your choice,
+provided that You also comply with the requirements of this License for
+the Covered Software. If the Larger Work is a combination of Covered
+Software with a work governed by one or more Secondary Licenses, and the
+Covered Software is not Incompatible With Secondary Licenses, this
+License permits You to additionally distribute such Covered Software
+under the terms of such Secondary License(s), so that the recipient of
+the Larger Work may, at their option, further distribute the Covered
+Software under the terms of either this License or such Secondary
+License(s).
+
+3.4. Notices
+
+You may not remove or alter the substance of any license notices
+(including copyright notices, patent notices, disclaimers of warranty,
+or limitations of liability) contained within the Source Code Form of
+the Covered Software, except that You may alter any license notices to
+the extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+You may choose to offer, and to charge a fee for, warranty, support,
+indemnity or liability obligations to one or more recipients of Covered
+Software. However, You may do so only on Your own behalf, and not on
+behalf of any Contributor. You must make it absolutely clear that any
+such warranty, support, indemnity, or liability obligation is offered by
+You alone, and You hereby agree to indemnify every Contributor for any
+liability incurred by such Contributor as a result of warranty, support,
+indemnity or liability terms You offer. You may include additional
+disclaimers of warranty and limitations of liability specific to any
+jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+---------------------------------------------------
+
+If it is impossible for You to comply with any of the terms of this
+License with respect to some or all of the Covered Software due to
+statute, judicial order, or regulation then You must: (a) comply with
+the terms of this License to the maximum extent possible; and (b)
+describe the limitations and the code they affect. Such description must
+be placed in a text file included with all distributions of the Covered
+Software under this License. Except to the extent prohibited by statute
+or regulation, such description must be sufficiently detailed for a
+recipient of ordinary skill to be able to understand it.
+
+5. Termination
+--------------
+
+5.1. The rights granted under this License will terminate automatically
+if You fail to comply with any of its terms. However, if You become
+compliant, then the rights granted under this License from a particular
+Contributor are reinstated (a) provisionally, unless and until such
+Contributor explicitly and finally terminates Your grants, and (b) on an
+ongoing basis, if such Contributor fails to notify You of the
+non-compliance by some reasonable means prior to 60 days after You have
+come back into compliance. Moreover, Your grants from a particular
+Contributor are reinstated on an ongoing basis if such Contributor
+notifies You of the non-compliance by some reasonable means, this is the
+first time You have received notice of non-compliance with this License
+from such Contributor, and You become compliant prior to 30 days after
+Your receipt of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+infringement claim (excluding declaratory judgment actions,
+counter-claims, and cross-claims) alleging that a Contributor Version
+directly or indirectly infringes any patent, then the rights granted to
+You by any and all Contributors for the Covered Software under Section
+2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
+end user license agreements (excluding distributors and resellers) which
+have been validly granted by You or Your distributors under this License
+prior to termination shall survive termination.
+
+************************************************************************
+*                                                                      *
+*  6. Disclaimer of Warranty                                           *
+*  -------------------------                                           *
+*                                                                      *
+*  Covered Software is provided under this License on an "as is"       *
+*  basis, without warranty of any kind, either expressed, implied, or  *
+*  statutory, including, without limitation, warranties that the       *
+*  Covered Software is free of defects, merchantable, fit for a        *
+*  particular purpose or non-infringing. The entire risk as to the     *
+*  quality and performance of the Covered Software is with You.        *
+*  Should any Covered Software prove defective in any respect, You     *
+*  (not any Contributor) assume the cost of any necessary servicing,   *
+*  repair, or correction. This disclaimer of warranty constitutes an   *
+*  essential part of this License. No use of any Covered Software is   *
+*  authorized under this License except under this disclaimer.         *
+*                                                                      *
+************************************************************************
+
+************************************************************************
+*                                                                      *
+*  7. Limitation of Liability                                          *
+*  --------------------------                                          *
+*                                                                      *
+*  Under no circumstances and under no legal theory, whether tort      *
+*  (including negligence), contract, or otherwise, shall any           *
+*  Contributor, or anyone who distributes Covered Software as          *
+*  permitted above, be liable to You for any direct, indirect,         *
+*  special, incidental, or consequential damages of any character      *
+*  including, without limitation, damages for lost profits, loss of    *
+*  goodwill, work stoppage, computer failure or malfunction, or any    *
+*  and all other commercial damages or losses, even if such party      *
+*  shall have been informed of the possibility of such damages. This   *
+*  limitation of liability shall not apply to liability for death or   *
+*  personal injury resulting from such party's negligence to the       *
+*  extent applicable law prohibits such limitation. Some               *
+*  jurisdictions do not allow the exclusion or limitation of           *
+*  incidental or consequential damages, so this exclusion and          *
+*  limitation may not apply to You.                                    *
+*                                                                      *
+************************************************************************
+
+8. Litigation
+-------------
+
+Any litigation relating to this License may be brought only in the
+courts of a jurisdiction where the defendant maintains its principal
+place of business and such litigation shall be governed by laws of that
+jurisdiction, without reference to its conflict-of-law provisions.
+Nothing in this Section shall prevent a party's ability to bring
+cross-claims or counter-claims.
+
+9. Miscellaneous
+----------------
+
+This License represents the complete agreement concerning the subject
+matter hereof. If any provision of this License is held to be
+unenforceable, such provision shall be reformed only to the extent
+necessary to make it enforceable. Any law or regulation which provides
+that the language of a contract shall be construed against the drafter
+shall not be used to construe this License against a Contributor.
+
+10. Versions of the License
+---------------------------
+
+10.1. New Versions
+
+Mozilla Foundation is the license steward. Except as provided in Section
+10.3, no one other than the license steward has the right to modify or
+publish new versions of this License. Each version will be given a
+distinguishing version number.
+
+10.2. Effect of New Versions
+
+You may distribute the Covered Software under the terms of the version
+of the License under which You originally received the Covered Software,
+or under the terms of any subsequent version published by the license
+steward.
+
+10.3. Modified Versions
+
+If you create software not governed by this License, and you want to
+create a new license for such software, you may create and use a
+modified version of this License if you rename the license and remove
+any references to the name of the license steward (except to note that
+such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+Licenses
+
+If You choose to distribute Source Code Form that is Incompatible With
+Secondary Licenses under the terms of this version of the License, the
+notice described in Exhibit B of this License must be attached.
+
+Exhibit A - Source Code Form License Notice
+-------------------------------------------
+
+  This Source Code Form is subject to the terms of the Mozilla Public
+  License, v. 2.0. If a copy of the MPL was not distributed with this
+  file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular
+file, then You may include the notice in a location (such as a LICENSE
+file in a relevant directory) where a recipient would be likely to look
+for such a notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+---------------------------------------------------------
+
+  This Source Code Form is "Incompatible With Secondary Licenses", as
+  defined by the Mozilla Public License, v. 2.0.

+ 969 - 0
components/external/espruino/Makefile

@@ -0,0 +1,969 @@
+# This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+#
+# Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# -----------------------------------------------------------------------------
+# Makefile for Espruino
+# -----------------------------------------------------------------------------
+# Set ONE of the following environment variables to compile for that board:
+#
+# ESPRUINO_1V0=1          # Espruino board rev 1.0
+# ESPRUINO_1V1=1          # Espruino board rev 1.1 and 1.2
+# ESPRUINO_1V3=1          # Espruino board rev 1.3
+# OLIMEXINO_STM32=1                # Olimexino STM32
+# OLIMEXINO_STM32_BOOTLOADER=1     # Olimexino STM32 with bootloader
+# EMBEDDED_PI=1           # COOCOX STM32 Embedded Pi boards
+# HYSTM32_24=1            # HY STM32 2.4 Ebay boards
+# HYSTM32_28=1            # HY STM32 2.8 Ebay boards
+# HYSTM32_32=1            # HY STM32 3.2 VCT6 Ebay boards
+# STM32VLDISCOVERY=1
+# STM32F3DISCOVERY=1
+# STM32F4DISCOVERY=1
+# STM32F429IDISCOVERY=1
+# CARAMBOLA=1
+# RASPBERRYPI=1
+# LPC1768=1 # beta
+# LCTECH_STM32F103RBT6=1 # LC Technology STM32F103RBT6 Ebay boards
+# Or nothing for standard linux compile
+#
+# Also:
+#
+# DEBUG=1                 # add debug symbols (-g)
+# RELEASE=1               # Force release-style compile (no asserts, etc)
+# SINGLETHREAD=1          # Compile single-threaded to make compilation errors easier to find
+# BOOTLOADER=1            # make the bootloader (not Espruino)
+# PROFILE=1               # Compile with gprof profiling info
+
+ifndef SINGLETHREAD
+MAKEFLAGS=-j5 # multicore
+endif
+
+INCLUDE=-I$(ROOT) -I$(ROOT)/targets -I$(ROOT)/src -I$(ROOT)/gen
+LIBS=
+DEFINES=
+CFLAGS=-Wall -Wextra -Wconversion -Werror=implicit-function-declaration -fdiagnostics-show-option
+OPTIMIZEFLAGS= 
+#-fdiagnostics-show-option - shows which flags can be used with -Werror 
+
+# Espruino flags...
+USE_MATH=1
+
+ifeq ($(shell uname -m),armv6l)
+RASPBERRYPI=1 # just a guess
+endif
+
+ifeq ($(shell uname),Darwin)
+MACOSX=1
+endif
+
+# Gordon's car ECU (extremely beta!)
+ifdef ECU
+STM32F4DISCOVERY=1
+#HYSTM32_32=1 
+USE_TRIGGER=1
+DEFINES += -DECU
+endif
+
+ifdef RELEASE
+# force no asserts to be compiled in
+DEFINES += -DNO_ASSERT
+endif
+
+CWD = $(shell pwd)
+ROOT = $(CWD)
+PRECOMPILED_OBJS=
+PLATFORM_CONFIG_FILE=gen/platform_config.h
+BASEADDRESS=0x08000000
+
+###################################################
+# When adding stuff here, also remember build_pininfo, platform_config.h, jshardware.c
+ifdef ESPRUINO_1V0
+USB=1
+#USE_NET=1
+#USE_CC3000=1
+USE_GRAPHICS=1
+USE_FILESYSTEM=1
+FAMILY=STM32F1
+CHIP=STM32F103RG
+BOARD=ESPRUINOBOARD_R1_0
+DEFINES+=-DESPRUINOBOARD
+STLIB=STM32F10X_XL
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f1/lib/startup_stm32f10x_hd.o
+OPTIMIZEFLAGS+=-O3
+else ifdef ESPRUINO_1V1
+DEFINES+=-DESPRUINO_1V1
+USE_BOOTLOADER=1
+BOOTLOADER_PROJ_NAME=bootloader_espruino_1v1
+USB=1
+USE_NET=1
+USE_CC3000=1
+USE_GRAPHICS=1
+USE_FILESYSTEM=1
+FAMILY=STM32F1
+CHIP=STM32F103RC
+BOARD=ESPRUINOBOARD_R1_1
+DEFINES+=-DESPRUINOBOARD
+STLIB=STM32F10X_XL
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f1/lib/startup_stm32f10x_hd.o
+OPTIMIZEFLAGS+=-Os # not that short on memory, but Travis compiler is old and uses more
+else ifdef ESPRUINO_1V3
+DEFINES+=-DESPRUINO_1V3
+USE_BOOTLOADER=1
+BOOTLOADER_PROJ_NAME=bootloader_espruino_1v3
+USB=1
+USE_NET=1
+USE_CC3000=1
+USE_GRAPHICS=1
+USE_FILESYSTEM=1
+FAMILY=STM32F1
+CHIP=STM32F103RC
+BOARD=ESPRUINOBOARD
+STLIB=STM32F10X_XL
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f1/lib/startup_stm32f10x_hd.o
+OPTIMIZEFLAGS+=-O3
+else ifdef OLIMEXINO_STM32
+USB=1
+USE_FILESYSTEM=1
+FAMILY=STM32F1
+CHIP=STM32F103RB
+BOARD=OLIMEXINO_STM32
+STLIB=STM32F10X_MD
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f1/lib/startup_stm32f10x_md.o
+OPTIMIZEFLAGS+=-Os # short on program memory
+else ifdef OLIMEXINO_STM32_BOOTLOADER
+USB=1
+USE_FILESYSTEM=1
+FAMILY=STM32F1
+CHIP=STM32F103RB_MAPLE
+DEFINES += -DSTM32F103RB
+SAVE_ON_FLASH=1
+BOARD=OLIMEXINO_STM32_BOOTLOADER
+STLIB=STM32F10X_MD
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f1/lib/startup_stm32f10x_md.o
+OPTIMIZEFLAGS+=-Os # short on program memory
+else ifdef EMBEDDED_PI
+USB=1
+# USE_FILESYSTEM=1 # no SD-CARD READER
+FAMILY=STM32F1
+CHIP=STM32F103RB
+BOARD=EMBEDDED_PI
+STLIB=STM32F10X_MD
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f1/lib/startup_stm32f10x_md.o
+OPTIMIZEFLAGS+=-Os # short on program memory
+else ifdef HYSTM32_24
+USB=1
+USE_GRAPHICS=1
+USE_LCD_FSMC=1
+USE_FILESYSTEM=1
+USE_FILESYSTEM_SDIO=1
+FAMILY=STM32F1
+CHIP=STM32F103VE
+BOARD=HYSTM32_24
+STLIB=STM32F10X_HD
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f1/lib/startup_stm32f10x_hd.o
+OPTIMIZEFLAGS+=-O3
+else ifdef HYSTM32_28
+USB=1
+USE_GRAPHICS=1
+USE_LCD_FSMC=1
+DEFINES+=-DILI9325_BITBANG # bit-bang the LCD driver
+SAVE_ON_FLASH=1
+#USE_FILESYSTEM=1 # just normal SPI
+FAMILY=STM32F1
+CHIP=STM32F103RB
+BOARD=HYSTM32_28
+STLIB=STM32F10X_MD
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f1/lib/startup_stm32f10x_md.o
+OPTIMIZEFLAGS+=-Os
+else ifdef HYSTM32_32
+USB=1
+USE_GRAPHICS=1
+USE_LCD_FSMC=1
+USE_FILESYSTEM=1
+USE_FILESYSTEM_SDIO=1
+FAMILY=STM32F1
+CHIP=STM32F103VC
+BOARD=HYSTM32_32
+STLIB=STM32F10X_HD
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f1/lib/startup_stm32f10x_hd.o
+OPTIMIZEFLAGS+=-O3
+else ifdef STM32F4DISCOVERY
+USB=1
+#USE_NET=1
+#USE_CC3000=1
+USE_GRAPHICS=1
+DEFINES += -DUSE_USB_OTG_FS=1
+FAMILY=STM32F4
+CHIP=STM32F407
+BOARD=STM32F4DISCOVERY
+STLIB=STM32F4XX
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f4/lib/startup_stm32f4xx.o
+OPTIMIZEFLAGS+=-O3
+else ifdef STM32F429IDISCOVERY
+USB=1
+USE_GRAPHICS=1
+#USE_LCD_FSMC=1
+DEFINES += -DUSE_USB_OTG_FS=1
+FAMILY=STM32F4
+CHIP=STM32F429
+BOARD=STM32F429IDISCOVERY
+STLIB=STM32F4XX
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f4/lib/startup_stm32f4xx.o
+OPTIMIZEFLAGS+=-O3
+else ifdef SMARTWATCH
+DEFINES+=-DHSE_VALUE=26000000UL
+USB=1
+FAMILY=STM32F2
+CHIP=STM32F205RG
+BOARD=SMARTWATCH
+STLIB=STM32F2XX
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f2/lib/startup_stm32f2xx.o
+OPTIMIZEFLAGS+=-O3
+else ifdef STM32F3DISCOVERY
+#USE_BOOTLOADER=1
+#BOOTLOADER_PROJ_NAME=bootloader_espruino_stm32f3discovery
+USB=1
+FAMILY=STM32F3
+CHIP=STM32F303
+BOARD=STM32F3DISCOVERY
+STLIB=STM32F3XX
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f3/lib/startup_stm32f30x.o
+OPTIMIZEFLAGS+=-O3
+else ifdef STM32VLDISCOVERY
+FAMILY=STM32F1
+CHIP=STM32F100RB
+BOARD=STM32VLDISCOVERY
+STLIB=STM32F10X_MD_VL
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f1/lib/startup_stm32f10x_md_vl.o
+OPTIMIZEFLAGS+=-Os # short on program memory
+else ifdef TINYCHIP
+FAMILY=STM32F1
+CHIP=STM32F103TB
+BOARD=TINYCHIP
+STLIB=STM32F10X_MD
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f1/lib/startup_stm32f10x_md.o
+OPTIMIZEFLAGS+=-Os # short on program memory
+else ifdef LPC1768
+MBED=1
+FAMILY=LPC1768
+CHIP=LPC1768
+BOARD=LPC1768
+MBED_GCC_CS_DIR=$(ROOT)/targets/libmbed/LPC1768/GCC_CS
+PRECOMPILED_OBJS+=$(MBED_GCC_CS_DIR)/sys.o $(MBED_GCC_CS_DIR)/cmsis_nvic.o $(MBED_GCC_CS_DIR)/system_LPC17xx.o $(MBED_GCC_CS_DIR)/core_cm3.o $(MBED_GCC_CS_DIR)/startup_LPC17xx.o 
+LIBS+=-L$(MBED_GCC_CS_DIR)  -lmbed 
+OPTIMIZEFLAGS+=-O3
+else ifdef CARAMBOLA
+BOARD=CARAMBOLA
+DEFINES += -DCARAMBOLA -DSYSFS_GPIO_DIR="\"/sys/class/gpio\""
+LINUX=1
+USE_FILESYSTEM=1
+USB=1
+USE_GRAPHICS=1
+USE_NET=1
+else ifdef RASPBERRYPI
+BOARD=RASPBERRYPI
+DEFINES += -DRASPBERRYPI -DSYSFS_GPIO_DIR="\"/sys/class/gpio\""
+LINUX=1
+USE_FILESYSTEM=1
+USB=1
+USE_GRAPHICS=1
+#USE_LCD_SDL=1
+USE_NET=1
+else ifdef LCTECH_STM32F103RBT6
+USB=1
+SAVE_ON_FLASH=1
+FAMILY=STM32F1
+CHIP=STM32F103RB
+BOARD=LCTECH_STM32F103RBT6
+STLIB=STM32F10X_MD
+PRECOMPILED_OBJS+=$(ROOT)/targetlibs/stm32f1/lib/startup_stm32f10x_md.o
+OPTIMIZEFLAGS+=-Os
+else
+BOARD=LINUX
+LINUX=1
+USE_FILESYSTEM=1
+USB=1
+USE_GRAPHICS=1
+#USE_LCD_SDL=1
+ifndef MACOSX
+# http libs need some tweaks before net can compile
+USE_NET=1
+endif
+endif
+
+PROJ_NAME=$(shell python scripts/get_binary_name.py $(BOARD)  | sed -e "s/.bin$$//")
+ifeq ($(PROJ_NAME),)
+$(error Unable to work out binary name (PROJ_NAME)) 
+endif
+ifeq ($(BOARD),LINUX)
+PROJ_NAME=espruino
+endif
+
+
+
+ifdef DEBUG
+#OPTIMIZEFLAGS=-Os -g
+OPTIMIZEFLAGS=-g
+endif
+
+ifdef PROFILE
+OPTIMIZEFLAGS+=-pg
+endif
+
+WRAPPERFILE=gen/jswrapper.c
+WRAPPERSOURCES = \
+src/jswrap_pin.c \
+src/jswrap_functions.c \
+src/jswrap_modules.c \
+src/jswrap_process.c \
+src/jswrap_interactive.c \
+src/jswrap_json.c \
+src/jswrap_object.c \
+src/jswrap_string.c \
+src/jswrap_array.c \
+src/jswrap_arraybuffer.c \
+src/jswrap_serial.c \
+src/jswrap_spi_i2c.c \
+src/jswrap_onewire.c \
+src/jswrap_io.c
+# it is important that _pin comes before stuff which uses
+# integers (as the check for int *includes* the chek for pin)
+
+SOURCES = \
+src/jslex.c \
+src/jsvar.c \
+src/jsutils.c \
+src/jsparse.c \
+src/jspin.c \
+src/jsinteractive.c \
+src/jsdevices.c \
+$(WRAPPERFILE)
+CPPSOURCES =
+
+ifdef BOOTLOADER
+ifndef USE_BOOTLOADER
+$(error Using bootloader on device that is not expecting one)
+endif
+BUILD_LINKER_FLAGS+=--bootloader
+PROJ_NAME=$(BOOTLOADER_PROJ_NAME)
+WRAPPERSOURCES =
+SOURCES = \
+targets/stm32_boot/main.c \
+targets/stm32_boot/utils.c
+ ifndef DEBUG
+  OPTIMIZEFLAGS=-Os
+ endif
+else # !BOOTLOADER
+ ifdef USE_BOOTLOADER
+  BUILD_LINKER_FLAGS+=--using_bootloader
+  STM32LOADER_FLAGS+=-p /dev/ttyACM0
+  BASEADDRESS=$(shell python -c "import sys;sys.path.append('scripts');import common;print hex(0x08000000+common.get_bootloader_size())")
+ endif
+endif
+
+ifdef USB_PRODUCT_ID
+DEFINES+=-DUSB_PRODUCT_ID=$(USB_PRODUCT_ID)
+endif
+
+ifdef SAVE_ON_FLASH
+DEFINES+=-DSAVE_ON_FLASH
+endif
+
+ifdef USE_FILESYSTEM
+DEFINES += -DUSE_FILESYSTEM
+WRAPPERSOURCES += libs/jswrap_fat.c
+ifndef LINUX 
+INCLUDE += -I$(ROOT)/libs/fat_sd
+SOURCES += \
+libs/fat_sd/fattime.c \
+libs/fat_sd/ff.c
+#libs/fat_sd/option/ccsbcs.c # for LFN support (see _USE_LFN in ff.h)
+
+ifdef USE_FILESYSTEM_SDIO
+DEFINES += -DUSE_FILESYSTEM_SDIO
+SOURCES += \
+libs/fat_sd/sdio_diskio.c \
+libs/fat_sd/sdio_sdcard.c
+else #USE_FILESYSTEM_SDIO
+SOURCES += \
+libs/fat_sd/spi_diskio.c
+endif #USE_FILESYSTEM_SDIO
+endif #!LINUX
+endif #USE_FILESYSTEM
+
+ifdef USE_MATH
+DEFINES += -DUSE_MATH
+WRAPPERSOURCES += libs/jswrap_math.c
+
+ifndef LINUX 
+INCLUDE += -I$(ROOT)/libs/math
+SOURCES += \
+libs/math/acosh.c \
+libs/math/asin.c \
+libs/math/asinh.c \
+libs/math/atan.c \
+libs/math/atanh.c \
+libs/math/cbrt.c \
+libs/math/chbevl.c \
+libs/math/clog.c \
+libs/math/cmplx.c \
+libs/math/const.c \
+libs/math/cosh.c \
+libs/math/drand.c \
+libs/math/exp10.c \
+libs/math/exp2.c \
+libs/math/exp.c \
+libs/math/fabs.c \
+libs/math/floor.c \
+libs/math/isnan.c \
+libs/math/log10.c \
+libs/math/log2.c \
+libs/math/log.c \
+libs/math/mtherr.c \
+libs/math/polevl.c \
+libs/math/pow.c \
+libs/math/powi.c \
+libs/math/round.c \
+libs/math/setprec.c \
+libs/math/sin.c \
+libs/math/sincos.c \
+libs/math/sindg.c \
+libs/math/sinh.c \
+libs/math/sqrt.c \
+libs/math/tan.c \
+libs/math/tandg.c \
+libs/math/tanh.c \
+libs/math/unity.c
+#libs/math/mod2pi.c 
+#libs/math/mtst.c 
+#libs/math/dtestvec.c 
+endif
+endif
+
+ifdef USE_GRAPHICS
+DEFINES += -DUSE_GRAPHICS
+WRAPPERSOURCES += libs/graphics/jswrap_graphics.c
+INCLUDE += -I$(ROOT)/libs/graphics
+SOURCES += \
+libs/graphics/bitmap_font_8x8.c \
+libs/graphics/graphics.c \
+libs/graphics/lcd_arraybuffer.c \
+libs/graphics/lcd_js.c
+
+ifdef USE_LCD_SDL
+DEFINES += -DUSE_LCD_SDL
+SOURCES += libs/graphics/lcd_sdl.c
+LIBS += -lSDL 
+INCLUDE += -I/usr/include/SDL
+endif
+
+ifdef USE_LCD_FSMC
+DEFINES += -DUSE_LCD_FSMC
+SOURCES += libs/graphics/lcd_fsmc.c
+endif
+
+endif
+
+ifdef USE_NET
+DEFINES += -DUSE_NET
+WRAPPERSOURCES += libs/network/http/jswrap_http.c
+INCLUDE += -I$(ROOT)/libs/network/http
+SOURCES += \
+libs/network/http/httpserver.c 
+ifdef LINUX
+#LIBS += -l... 
+#INCLUDE += -I...
+endif
+endif
+
+ifdef USE_CC3000
+DEFINES += -DUSE_CC3000 -DSEND_NON_BLOCKING
+WRAPPERSOURCES += libs/network/cc3000/jswrap_cc3000.c
+INCLUDE += -I$(ROOT)/libs/network/cc3000
+SOURCES += \
+libs/network/cc3000/board_spi.c \
+libs/network/cc3000/cc3000_common.c \
+libs/network/cc3000/evnt_handler.c \
+libs/network/cc3000/hci.c \
+libs/network/cc3000/netapp.c \
+libs/network/cc3000/nvmem.c \
+libs/network/cc3000/security.c \
+libs/network/cc3000/socket.c \
+libs/network/cc3000/wlan.c
+endif
+
+ifdef USE_TRIGGER
+DEFINES += -DUSE_TRIGGER 
+WRAPPERSOURCES += libs/trigger/jswrap_trigger.c
+INCLUDE += -I$(ROOT)/libs/trigger
+SOURCES += \
+./libs/trigger/trigger.c
+endif
+
+ifdef USB
+DEFINES += -DUSB
+endif
+
+ifeq ($(FAMILY), STM32F1)
+ARCHFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m3  -mfix-cortex-m3-ldrd  -mthumb-interwork -mfloat-abi=soft
+ARM=1
+STM32=1
+INCLUDE += -I$(ROOT)/targetlibs/stm32f1 -I$(ROOT)/targetlibs/stm32f1/lib
+DEFINES += -DSTM32F1
+SOURCES +=                              \
+targetlibs/stm32f1/lib/misc.c              \
+targetlibs/stm32f1/lib/stm32f10x_adc.c     \
+targetlibs/stm32f1/lib/stm32f10x_bkp.c     \
+targetlibs/stm32f1/lib/stm32f10x_can.c     \
+targetlibs/stm32f1/lib/stm32f10x_cec.c     \
+targetlibs/stm32f1/lib/stm32f10x_crc.c     \
+targetlibs/stm32f1/lib/stm32f10x_dac.c     \
+targetlibs/stm32f1/lib/stm32f10x_dbgmcu.c  \
+targetlibs/stm32f1/lib/stm32f10x_dma.c     \
+targetlibs/stm32f1/lib/stm32f10x_exti.c    \
+targetlibs/stm32f1/lib/stm32f10x_flash.c   \
+targetlibs/stm32f1/lib/stm32f10x_fsmc.c    \
+targetlibs/stm32f1/lib/stm32f10x_gpio.c    \
+targetlibs/stm32f1/lib/stm32f10x_i2c.c     \
+targetlibs/stm32f1/lib/stm32f10x_iwdg.c    \
+targetlibs/stm32f1/lib/stm32f10x_pwr.c     \
+targetlibs/stm32f1/lib/stm32f10x_rcc.c     \
+targetlibs/stm32f1/lib/stm32f10x_rtc.c     \
+targetlibs/stm32f1/lib/stm32f10x_sdio.c    \
+targetlibs/stm32f1/lib/stm32f10x_spi.c     \
+targetlibs/stm32f1/lib/stm32f10x_tim.c     \
+targetlibs/stm32f1/lib/stm32f10x_usart.c   \
+targetlibs/stm32f1/lib/stm32f10x_wwdg.c    \
+targetlibs/stm32f1/lib/system_stm32f10x.c
+
+ifdef USB
+INCLUDE += -I$(ROOT)/targetlibs/stm32f1/usblib -I$(ROOT)/targetlibs/stm32f1/usb
+SOURCES +=                              \
+targetlibs/stm32f1/usblib/otgd_fs_cal.c       \
+targetlibs/stm32f1/usblib/otgd_fs_dev.c       \
+targetlibs/stm32f1/usblib/otgd_fs_int.c       \
+targetlibs/stm32f1/usblib/otgd_fs_pcd.c       \
+targetlibs/stm32f1/usblib/usb_core.c          \
+targetlibs/stm32f1/usblib/usb_init.c          \
+targetlibs/stm32f1/usblib/usb_int.c           \
+targetlibs/stm32f1/usblib/usb_mem.c           \
+targetlibs/stm32f1/usblib/usb_regs.c          \
+targetlibs/stm32f1/usblib/usb_sil.c           \
+targetlibs/stm32f1/usb/usb_desc.c              \
+targetlibs/stm32f1/usb/usb_endp.c              \
+targetlibs/stm32f1/usb/usb_istr.c              \
+targetlibs/stm32f1/usb/usb_prop.c              \
+targetlibs/stm32f1/usb/usb_pwr.c               \
+targetlibs/stm32f1/usb/usb_utils.c
+endif #USB
+
+endif #STM32F1
+
+ifeq ($(FAMILY), STM32F2)
+ARCHFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m3 -mthumb-interwork -mfpu=fpv4-sp-d16 -mfloat-abi=softfp
+ARM=1
+STM32=1
+INCLUDE += -I$(ROOT)/targetlibs/stm32f2 -I$(ROOT)/targetlibs/stm32f2/lib
+DEFINES += -DSTM32F2
+SOURCES +=                                 \
+targetlibs/stm32f2/lib/misc.c              \
+targetlibs/stm32f2/lib/stm32f2xx_adc.c     \
+targetlibs/stm32f2/lib/stm32f2xx_can.c     \
+targetlibs/stm32f2/lib/stm32f2xx_crc.c     \
+targetlibs/stm32f2/lib/stm32f2xx_cryp_aes.c\
+targetlibs/stm32f2/lib/stm32f2xx_cryp.c    \
+targetlibs/stm32f2/lib/stm32f2xx_cryp_des.c\
+targetlibs/stm32f2/lib/stm32f2xx_cryp_tdes.c\
+targetlibs/stm32f2/lib/stm32f2xx_dac.c     \
+targetlibs/stm32f2/lib/stm32f2xx_dbgmcu.c     \
+targetlibs/stm32f2/lib/stm32f2xx_dcmi.c     \
+targetlibs/stm32f2/lib/stm32f2xx_dma.c     \
+targetlibs/stm32f2/lib/stm32f2xx_exti.c     \
+targetlibs/stm32f2/lib/stm32f2xx_flash.c     \
+targetlibs/stm32f2/lib/stm32f2xx_fsmc.c     \
+targetlibs/stm32f2/lib/stm32f2xx_gpio.c     \
+targetlibs/stm32f2/lib/stm32f2xx_hash.c     \
+targetlibs/stm32f2/lib/stm32f2xx_hash_md5.c     \
+targetlibs/stm32f2/lib/stm32f2xx_hash_sha1.c     \
+targetlibs/stm32f2/lib/stm32f2xx_i2c.c     \
+targetlibs/stm32f2/lib/stm32f2xx_iwdg.c     \
+targetlibs/stm32f2/lib/stm32f2xx_pwr.c     \
+targetlibs/stm32f2/lib/stm32f2xx_rcc.c     \
+targetlibs/stm32f2/lib/stm32f2xx_rng.c     \
+targetlibs/stm32f2/lib/stm32f2xx_rtc.c     \
+targetlibs/stm32f2/lib/stm32f2xx_sdio.c     \
+targetlibs/stm32f2/lib/stm32f2xx_spi.c     \
+targetlibs/stm32f2/lib/stm32f2xx_syscfg.c     \
+targetlibs/stm32f2/lib/stm32f2xx_tim.c     \
+targetlibs/stm32f2/lib/stm32f2xx_usart.c     \
+targetlibs/stm32f2/lib/stm32f2xx_wwdg.c    \
+targetlibs/stm32f2/lib/system_stm32f2xx.c
+
+
+ifdef USB
+INCLUDE += -I$(ROOT)/targetlibs/stm32f2/usblib -I$(ROOT)/targetlibs/stm32f2/usb
+SOURCES +=                                 \
+targetlibs/stm32f2/usb/usbd_cdc_vcp.c \
+targetlibs/stm32f2/usb/usb_irq_handlers.c \
+targetlibs/stm32f2/usb/usbd_desc.c \
+targetlibs/stm32f2/usb/usbd_usr.c \
+targetlibs/stm32f2/usb/usb_bsp.c \
+targetlibs/stm32f2/usblib/usbd_req.c \
+targetlibs/stm32f2/usblib/usb_dcd_int.c \
+targetlibs/stm32f2/usblib/usbd_core.c \
+targetlibs/stm32f2/usblib/usbd_cdc_core.c \
+targetlibs/stm32f2/usblib/usbd_ioreq.c \
+targetlibs/stm32f2/usblib/usb_core.c \
+targetlibs/stm32f2/usblib/usb_dcd.c
+#targetlibs/stm32f2/usblib/usb_otg.c \
+#targetlibs/stm32f2/usblib/usb_bsp_template.c \
+#targetlibs/stm32f2/usblib/usbd_cdc_if_template.c \
+#targetlibs/stm32f2/usblib/usb_hcd.c \
+#targetlibs/stm32f2/usblib/usb_hcd_int.c 
+endif #USB
+endif #STM32F2 
+
+ifeq ($(FAMILY), STM32F3)
+ARCHFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -mfpu=fpv4-sp-d16 -mfloat-abi=softfp
+ARM=1
+STM32=1
+INCLUDE += -I$(ROOT)/targetlibs/stm32f3 -I$(ROOT)/targetlibs/stm32f3/lib
+DEFINES += -DSTM32F3
+SOURCES +=                                 \
+targetlibs/stm32f3/lib/stm32f30x_adc.c        \
+targetlibs/stm32f3/lib/stm32f30x_can.c        \
+targetlibs/stm32f3/lib/stm32f30x_comp.c       \
+targetlibs/stm32f3/lib/stm32f30x_crc.c        \
+targetlibs/stm32f3/lib/stm32f30x_dac.c        \
+targetlibs/stm32f3/lib/stm32f30x_dbgmcu.c     \
+targetlibs/stm32f3/lib/stm32f30x_dma.c        \
+targetlibs/stm32f3/lib/stm32f30x_exti.c       \
+targetlibs/stm32f3/lib/stm32f30x_flash.c      \
+targetlibs/stm32f3/lib/stm32f30x_gpio.c       \
+targetlibs/stm32f3/lib/stm32f30x_i2c.c        \
+targetlibs/stm32f3/lib/stm32f30x_iwdg.c       \
+targetlibs/stm32f3/lib/stm32f30x_misc.c       \
+targetlibs/stm32f3/lib/stm32f30x_opamp.c      \
+targetlibs/stm32f3/lib/stm32f30x_pwr.c        \
+targetlibs/stm32f3/lib/stm32f30x_rcc.c        \
+targetlibs/stm32f3/lib/stm32f30x_rtc.c        \
+targetlibs/stm32f3/lib/stm32f30x_spi.c        \
+targetlibs/stm32f3/lib/stm32f30x_syscfg.c     \
+targetlibs/stm32f3/lib/stm32f30x_tim.c        \
+targetlibs/stm32f3/lib/stm32f30x_usart.c      \
+targetlibs/stm32f3/lib/stm32f30x_wwdg.c       \
+targetlibs/stm32f3/lib/system_stm32f30x.c
+
+ifdef USB
+INCLUDE += -I$(ROOT)/targetlibs/stm32f3/usblib -I$(ROOT)/targetlibs/stm32f3/usb
+SOURCES +=                                 \
+targetlibs/stm32f3/usblib/usb_core.c          \
+targetlibs/stm32f3/usblib/usb_init.c          \
+targetlibs/stm32f3/usblib/usb_int.c           \
+targetlibs/stm32f3/usblib/usb_mem.c           \
+targetlibs/stm32f3/usblib/usb_regs.c          \
+targetlibs/stm32f3/usblib/usb_sil.c           \
+targetlibs/stm32f3/usb/usb_desc.c              \
+targetlibs/stm32f3/usb/usb_endp.c              \
+targetlibs/stm32f3/usb/usb_istr.c              \
+targetlibs/stm32f3/usb/usb_prop.c              \
+targetlibs/stm32f3/usb/usb_pwr.c               \
+targetlibs/stm32f3/usb/usb_utils.c  
+endif #USB
+endif #STM32F3 
+
+ifeq ($(FAMILY), STM32F4)
+ARCHFLAGS += -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -mfpu=fpv4-sp-d16 -mfloat-abi=softfp
+ARM=1
+STM32=1
+INCLUDE += -I$(ROOT)/targetlibs/stm32f4 -I$(ROOT)/targetlibs/stm32f4/lib
+DEFINES += -DSTM32F4
+SOURCES +=                                 \
+targetlibs/stm32f4/lib/misc.c                 \
+targetlibs/stm32f4/lib/stm32f4xx_adc.c        \
+targetlibs/stm32f4/lib/stm32f4xx_can.c        \
+targetlibs/stm32f4/lib/stm32f4xx_crc.c        \
+targetlibs/stm32f4/lib/stm32f4xx_cryp_aes.c   \
+targetlibs/stm32f4/lib/stm32f4xx_cryp.c       \
+targetlibs/stm32f4/lib/stm32f4xx_cryp_des.c   \
+targetlibs/stm32f4/lib/stm32f4xx_cryp_tdes.c  \
+targetlibs/stm32f4/lib/stm32f4xx_dac.c        \
+targetlibs/stm32f4/lib/stm32f4xx_dbgmcu.c     \
+targetlibs/stm32f4/lib/stm32f4xx_dcmi.c       \
+targetlibs/stm32f4/lib/stm32f4xx_dma.c        \
+targetlibs/stm32f4/lib/stm32f4xx_exti.c       \
+targetlibs/stm32f4/lib/stm32f4xx_flash.c      \
+targetlibs/stm32f4/lib/stm32f4xx_fsmc.c       \
+targetlibs/stm32f4/lib/stm32f4xx_gpio.c       \
+targetlibs/stm32f4/lib/stm32f4xx_hash.c       \
+targetlibs/stm32f4/lib/stm32f4xx_hash_md5.c   \
+targetlibs/stm32f4/lib/stm32f4xx_hash_sha1.c  \
+targetlibs/stm32f4/lib/stm32f4xx_i2c.c        \
+targetlibs/stm32f4/lib/stm32f4xx_iwdg.c       \
+targetlibs/stm32f4/lib/stm32f4xx_pwr.c        \
+targetlibs/stm32f4/lib/stm32f4xx_rcc.c        \
+targetlibs/stm32f4/lib/stm32f4xx_rng.c        \
+targetlibs/stm32f4/lib/stm32f4xx_rtc.c        \
+targetlibs/stm32f4/lib/stm32f4xx_sdio.c       \
+targetlibs/stm32f4/lib/stm32f4xx_spi.c        \
+targetlibs/stm32f4/lib/stm32f4xx_syscfg.c     \
+targetlibs/stm32f4/lib/stm32f4xx_tim.c        \
+targetlibs/stm32f4/lib/stm32f4xx_usart.c      \
+targetlibs/stm32f4/lib/stm32f4xx_wwdg.c       \
+targetlibs/stm32f4/lib/system_stm32f4xx.c
+
+ifdef USB
+INCLUDE += -I$(ROOT)/targetlibs/stm32f4/usblib -I$(ROOT)/targetlibs/stm32f4/usb
+SOURCES +=                                 \
+targetlibs/stm32f4/usblib/usb_core.c          \
+targetlibs/stm32f4/usblib/usbd_cdc_core.c     \
+targetlibs/stm32f4/usblib/usb_dcd.c           \
+targetlibs/stm32f4/usblib/usb_dcd_int.c       \
+targetlibs/stm32f4/usblib/usbd_core.c         \
+targetlibs/stm32f4/usblib/usbd_ioreq.c        \
+targetlibs/stm32f4/usblib/usbd_req.c          \
+targetlibs/stm32f4/usb/usb_bsp.c              \
+targetlibs/stm32f4/usb/usbd_cdc_vcp.c         \
+targetlibs/stm32f4/usb/usbd_desc.c            \
+targetlibs/stm32f4/usb/usbd_usr.c
+#targetlibs/stm32f4/usblib/usb_hcd.c           
+#targetlibs/stm32f4/usblib/usb_hcd_int.c
+#targetlibs/stm32f4/usblib/usb_otg.c           
+endif #USB
+endif #STM32F4
+
+
+ifdef MBED
+ARCHFLAGS += -mcpu=cortex-m3 -mthumb
+ARM=1
+INCLUDE+=-I$(ROOT)/targetlibs/libmbed -I$(ROOT)/targetlibs/libmbed/$(CHIP) -I$(ROOT)/targetlibs/libmbed/$(CHIP)/GCC_CS
+DEFINES += -DMBED
+INCLUDE += -I$(ROOT)/targetlibs/mbed
+SOURCES += targets/mbed/main.c                     
+CPPSOURCES += targets/mbed/jshardware.cpp             
+endif
+
+ifdef ARM
+LINKER_FILE = gen/linker.ld
+DEFINES += -DARM 
+INCLUDE += -I$(ROOT)/targetlibs/arm
+OPTIMIZEFLAGS += -fno-common -fno-exceptions -fdata-sections -ffunction-sections
+# -flto -fuse-linker-plugin
+# -flto  - link time optimisation - could be good for ST's libs
+#          GCC suggests use of -fuse-linker-plugin with flto
+#          Does not work - get errors like : `sqrt' referenced in section `.text.asin' of /tmp/ccJheOub.ltrans9.ltrans.o: defined in discarded section `.text' of libs/math/sqrt.o (symbol from plugin)
+
+
+# 4.6
+#export CCPREFIX=arm-linux-gnueabi-
+# 4.5
+#export CCPREFIX=~/sat/bin/arm-none-eabi-
+# 4.4
+export CCPREFIX=arm-none-eabi-
+endif # ARM
+
+PININFOFILE=$(ROOT)/gen/jspininfo
+ifdef PININFOFILE
+SOURCES += $(PININFOFILE).c
+endif
+
+ifdef CARAMBOLA
+TOOLCHAIN_DIR=$(shell cd ~/workspace/carambola/staging_dir/toolchain-*/bin;pwd)
+export STAGING_DIR=$(TOOLCHAIN_DIR)
+export CCPREFIX=$(TOOLCHAIN_DIR)/mipsel-openwrt-linux-
+endif
+
+ifdef RASPBERRYPI
+  ifneq ($(shell uname -m),armv6l)
+    # eep. let's cross compile
+    export CCPREFIX=targetlibs/raspberrypi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-
+  else
+    # compiling in-place, so give it a normal name
+    PROJ_NAME=espruino
+  endif
+endif
+
+
+ifdef STM32
+DEFINES += -DFAKE_STDLIB
+# FAKE_STDLIB is for Espruino - it uses its own standard library so we don't have to link in the normal one + get bloated 
+DEFINES += -DSTM32 -DUSE_STDPERIPH_DRIVER=1 -D$(CHIP) -D$(BOARD) -D$(STLIB)
+INCLUDE += -I$(ROOT)/targets/stm32
+ifndef BOOTLOADER
+SOURCES +=                              \
+targets/stm32/main.c                    \
+targets/stm32/jshardware.c              \
+targets/stm32/stm32_it.c
+endif
+endif
+
+ifdef LINUX
+DEFINES += -DLINUX
+INCLUDE += -I$(ROOT)/targets/linux 
+SOURCES +=                              \
+targets/linux/main.c                    \
+targets/linux/jshardware.c  
+LIBS += -lm # maths lib
+endif
+
+SOURCES += $(WRAPPERSOURCES)
+SOURCEOBJS = $(SOURCES:.c=.o) $(CPPSOURCES:.cpp=.o)
+OBJS = $(SOURCEOBJS) $(PRECOMPILED_OBJS)
+
+
+# -ffreestanding -nodefaultlibs -nostdlib -fno-common
+# -nodefaultlibs -nostdlib -nostartfiles
+
+# -fdata-sections -ffunction-sections are to help remove unused code
+CFLAGS += $(OPTIMIZEFLAGS) -c $(ARCHFLAGS) $(DEFINES) $(INCLUDE)
+
+# -Wl,--gc-sections helps remove unused code
+# -Wl,--whole-archive checks for duplicates
+LDFLAGS += $(OPTIMIZEFLAGS) $(ARCHFLAGS)
+ifndef MACOSX
+LDFLAGS += -Wl,--gc-sections
+endif
+
+ifdef LINKER_FILE 
+LDFLAGS += -T$(LINKER_FILE)
+endif 
+
+export CC=$(CCPREFIX)gcc
+export LD=$(CCPREFIX)gcc
+export AR=$(CCPREFIX)ar
+export AS=$(CCPREFIX)as
+export OBJCOPY=$(CCPREFIX)objcopy
+export OBJDUMP=$(CCPREFIX)objdump
+export GDB=$(CCPREFIX)gdb
+
+
+.PHONY:  proj
+
+all: 	 proj
+
+ifeq ($(V),1)
+        quiet_=
+        Q=
+else
+        quiet_=quiet_
+        Q=@
+	export SILENT=1
+endif
+
+
+$(WRAPPERFILE): scripts/build_jswrapper.py $(WRAPPERSOURCES)
+	@echo Generating JS wrappers
+	$(Q)echo WRAPPERSOURCES = $(WRAPPERSOURCES)
+	$(Q)echo DEFINES =  $(DEFINES)
+	$(Q)python scripts/build_jswrapper.py $(WRAPPERSOURCES) $(DEFINES)
+
+ifdef PININFOFILE
+$(PININFOFILE).c $(PININFOFILE).h: scripts/build_pininfo.py 
+	@echo Generating pin info
+	$(Q)python scripts/build_pininfo.py $(BOARD) $(PININFOFILE).c $(PININFOFILE).h
+endif
+
+$(LINKER_FILE): scripts/build_linker.py 
+	@echo Generating linker scripts
+	$(Q)python scripts/build_linker.py $(BOARD) $(LINKER_FILE) $(BUILD_LINKER_FLAGS)
+
+$(PLATFORM_CONFIG_FILE): boards/$(BOARD).py scripts/build_platform_config.py
+	@echo Generating platform configs
+	$(Q)python scripts/build_platform_config.py $(BOARD)
+
+compile=$(CC) $(CFLAGS) $(DEFINES) $< -o $@
+link=$(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS)
+obj_dump=$(OBJDUMP) -x -S $(PROJ_NAME).elf > $(PROJ_NAME).lst
+obj_to_bin=$(OBJCOPY) -O $1 $(PROJ_NAME).elf $(PROJ_NAME).$2
+
+quiet_compile= CC $@
+quiet_link= LD $@
+quiet_obj_dump= GEN $(PROJ_NAME).lst
+quiet_obj_to_bin= GEN $(PROJ_NAME).$2
+
+%.o: %.c $(PLATFORM_CONFIG_FILE) $(PININFOFILE).h
+	@echo $($(quiet_)compile)
+	@$(call compile)
+
+.cpp.o: $(PLATFORM_CONFIG_FILE) $(PININFOFILE).h
+	@echo $($(quiet_)compile)
+	@$(call compile)
+
+.s.o:
+	@echo $($(quiet_)compile)
+	@$(call compile)
+	
+ifdef LINUX # ---------------------------------------------------
+proj: 	$(PLATFORM_CONFIG_FILE) $(PROJ_NAME)
+
+$(PROJ_NAME): $(OBJS)
+	@echo $($(quiet_)link)
+	@$(call link)
+
+else # embedded, so generate bin, etc ---------------------------
+
+$(PROJ_NAME).elf: $(OBJS) $(LINKER_FILE)
+	@echo $($(quiet_)link)
+	@$(call link)
+
+$(PROJ_NAME).lst : $(PROJ_NAME).elf
+	@echo $($(quiet_)obj_dump)
+	@$(call obj_dump)
+
+$(PROJ_NAME).hex: $(PROJ_NAME).elf
+	@echo $(call $(quiet_)obj_to_bin,ihex,hex)
+	@$(call obj_to_bin,ihex,hex)
+
+$(PROJ_NAME).srec : $(PROJ_NAME).elf
+	@echo $(call $(quiet_)obj_to_bin,srec,srec)
+	@$(call obj_to_bin,srec,srec)
+
+$(PROJ_NAME).bin : $(PROJ_NAME).elf
+	@echo $(call $(quiet_)obj_to_bin,binary,bin)
+	@$(call obj_to_bin,binary,bin)
+	bash scripts/check_size.sh $(PROJ_NAME).bin
+
+proj: $(PROJ_NAME).lst $(PROJ_NAME).bin
+#proj: $(PROJ_NAME).lst $(PROJ_NAME).hex $(PROJ_NAME).srec $(PROJ_NAME).bin
+
+flash: all
+ifdef OLIMEXINO_STM32_BOOTLOADER
+	echo Olimexino Serial bootloader
+	dfu-util -a1 -d 0x1EAF:0x0003 -D $(PROJ_NAME).bin
+else
+ifdef MBED
+	cp $(PROJ_NAME).bin /media/MBED;sync
+else
+	echo ST-LINK flash
+	~/bin/st-flash write $(PROJ_NAME).bin $(BASEADDRESS)
+endif	
+endif	
+
+serialflash: all
+	echo STM32 inbuilt serial bootloader, set BOOT0=1, BOOT1=0
+	python scripts/stm32loader.py -b 460800 -a $(BASEADDRESS) -ew $(STM32LOADER_FLAGS) $(PROJ_NAME).bin
+#	python scripts/stm32loader.py -b 460800 -a $(BASEADDRESS) -ewv $(STM32LOADER_FLAGS) $(PROJ_NAME).bin
+		
+gdb: 
+	echo "target extended-remote :4242" > gdbinit
+	echo "file $(PROJ_NAME).elf" >> gdbinit
+	#echo "load" >> gdbinit
+	echo "break main" >> gdbinit
+	echo "break HardFault_Handler" >> gdbinit
+	$(GDB) -x gdbinit
+	rm gdbinit
+endif	    # ---------------------------------------------------
+ 
+clean:
+	@echo Cleaning targets
+	$(Q)find . -name *.o | grep -v libmbed | grep -v arm-bcm2708 | xargs rm -f
+	$(Q)rm -f $(ROOT)/gen/*.c $(ROOT)/gen/*.h $(ROOT)/gen/*.ld
+	$(Q)rm -f $(PROJ_NAME).elf
+	$(Q)rm -f $(PROJ_NAME).hex
+	$(Q)rm -f $(PROJ_NAME).bin
+	$(Q)rm -f $(PROJ_NAME).srec
+	$(Q)rm -f $(PROJ_NAME).lst

+ 161 - 0
components/external/espruino/README.md

@@ -0,0 +1,161 @@
+Espruino JavaScript for Microcontrollers
+========================================
+<pre>
+ _____                 _
+|   __|___ ___ ___ _ _|_|___ ___
+|   __|_ -| . |  _| | | |   | . |
+|_____|___|  _|_| |___|_|_|_|___|
+          |_|      
+</pre>
+http://www.espruino.com
+
+**NOTE:** This software is beta and is provided as-is, and won't be considered even remotely final until we've released the Espruino Board. As such, don't expect support, and do expect it to change rapidly and without warning. Build your own documentation (see **Building**), as the API may be different from the one described on the Espruino website.
+
+The KickStarter campaign said the Espruino Board will have some things which this repository does not yet have (like working CC3000 support). These are works in progress and should be done by the time you get your board (or will be available as a software update).
+
+
+About
+-----
+
+It'd probably help to read the [FAQ](http://www.espruino.com/FAQ), and specifically the page about [Performance](http://www.espruino.com/Performance) as it contains information about how Espruino itself works.
+
+There's also the auto-generated [Reference](http://www.espruino.com/Reference) for JavaScript commands as well as the [Tutorials](http://www.espruino.com/Tutorials) on the website. However please note that this repository is under heavy development, and the documentation on the Espruino website will match the version [available for download](http://www.espruino.com/Download) but **not** the latest version from Git.
+
+
+License
+-------
+
+Please see the [LICENSE](LICENSE) file
+
+
+Found a Bug?
+------------
+
+Please check that:
+* It hasn't [already been found](https://github.com/espruino/Espruino/issues) or [been covered on our forum](www.espruino.com/Forum)
+* You're not just looking at outdated documentation (See the [Building](#Building) section to see how to build documentation)
+
+Please [submit bugs](https://github.com/espruino/Espruino/issues) with clear steps to reproduce them (and ideally a test case for the ```tests``` directory), and if at all possible try and include a patch to fix them. Please be aware that we have a whole bunch of outstanding issues (some quite large), so if you report something (especially if it doesn't contain a test or a pull request) it may not be fixed for quite some time.
+
+
+Contributing
+------------
+
+Please see [CONTRIBUTING.md](CONTRIBUTING.md)
+
+Current State
+-------------
+
+You can download binaries from http://www.espruino.com/Download (these aren't the latest, but are more likely to work with your board)
+
+Please note that this is BETA. We've been working hard on the Espruino Board support but we haven't had time to check the other boards properly.
+
+* Espruino Board - working
+* Linux - working
+* STM32VLDISCOVERY - WORKING
+* STM32F3DISCOVERY - WORKING
+* STM32F4DISCOVERY - WORKING
+* STM32F429IDISCOVERY - not working, currently no LCD support
+* HY STM32 2.4" - NOT WORKING - appears to crash after startup
+* HY STM32 2.8" - WORKING, but screen is not black at startup
+* HY STM32 3.2" - WORKING
+* Olimexino - WORKING
+* Carambola - ?
+* Raspberry Pi - WORKING
+* Sony SmartWatch - USB VCP support still needed
+* MBed platforms - have not worked for a while - hardware wrapper still needed
+* Arduino - has never worked. Compiles but doesn't even get past init
+* LC-TECH STM32F103RBT6 - WORKING, but with some issues (LED inverted logic, BTN needs pullup to work)
+
+Using
+-----
+
+If you're using Espruino for your own personal projects - go ahead, we hope you have fun - and please let us know what you do with it on http://www.espruino.com/Forum!
+
+However if you're planning on selling the Espruino software on your own board, please talk to us:
+
+* Read the terms of the MPLv2 Licence that Espruino is distributed under, and make sure you comply with it
+* You won't be able to call your board 'Espruino' but you must explain clearly that it uses 'Espruino' internally (we own the trademark)
+* If you're profiting from Espruino without contributing anything back, we won't support you (or your users)
+
+
+Building
+--------
+  
+Espruino is easy to build under Linux, and it is possible to build under MacOS. We'd strongly suggest that you DO NOT TRY AND BUILD UNDER WINDOWS, and instead use a Virtual Machine. There's a good post on this here: http://forum.espruino.com/conversations/151
+  
+We suggest that you use the CodeSourcery GCC compiler, but paths in Makefile may need changing...
+
+```  BOARDNAME=1 RELEASE=1 make```
+
+* See the top of Makefile for board names
+* Without `RELEASE=1`, assertions are kept in the code (which is good for debugging, bad for performance + code size)
+* `BOARDNAME=1 RELEASE=1 make serialflash` will flash to /dev/ttyUSB0 using the STM32 serial bootloader (what's needed for Espruino + HY boards)
+* `BOARDNAME=1 RELEASE=1 make flash` will flash using st-flash if discovery, or maple bootloader if using that board
+
+You can build documentation by running:
+
+```  python scripts/build_docs.py ```
+
+This will create a file called ```functions.html```
+
+Directories and Files
+---------------------
+
+* `ChangeLog`:          What's new
+* `TODO`:               List of things to do
+* `boards/`:            Information on boards, used to auto-generate a lot of the code
+* `code/`:              Example JavaScript code
+* `gen/`:               Auto-Generated Source Files
+* `libs/`:              Optional libraries to include in Espruino (Math, Filesystem, Graphics, etc)
+* `linker/`:            Linker files for various processors
+* `misc/`:              random other stuff
+* `scripts/`:           Scripts for generating files in gen, and for analysing code/compilation/etc
+* `src/`:               Main source code
+* `targetlibs/`:        Libraries for targeted architectures
+* `targets/`:           Specific code for targeted architectures
+* `tests/`:             JavaScript Testcases
+* `benchmark/`:         JavaScript Benchmarks
+* `dist_*`:             files to be copied into distribution zip file
+
+Adding more devices
+-------------------
+
+Currently there are a bunch of different files to modify. Eventually the plan is to fit everything into boards/BOARDNAME.py and to auto-generate the rest of the config files.
+
+* Most build options handled in `Makefile`
+* Extra libraries like USB/LCD/filesystem in `Makefile`
+* Linker Scripts are in `linker/`
+* `boards/*.py` files handle loading the list of available pins so the relevant headers + docs can be created
+* Processor-specific code in `targets/stm32`, `targets/linux`, etc.
+* Processor-specific libs in `targetlibs/foo` 
+* `src/jshardware.h` is effectively a simple abstraction layer for SPI/I2C/etc
+* `targets/stm32/jshardware.c` also has flash-size-specific defines
+* `libs/fat_sd` and `libs/lcd` still have some device-specific defines in too
+
+Adding libraries
+-------------------
+
+* Create `jswrap_mylib.c/h` in `libs/`
+* Create library functions (see examples in other jswrap files, also the comments in `scripts/common.py`)
+
+
+Arduino Compile (beta)
+----------------------
+* Ensure that `targets/arduino/utility` is symlinked to `src`
+* Symlink `...arduino_workspace/libraries/Espruino` to `targets/arduino`
+
+Cross Compile for Raspberry Pi
+------------------------------
+```
+cd targetlibs
+mkdir raspberrypi
+cd raspberrypi
+git clone git://github.com/raspberrypi/tools.git
+sudo apt-get install ia32-libs
+```
+
+Cross Compile for Carambola (OpenWRT)
+-------------------------------------
+* Follow instructions at <https://github.com/8devices/carambola> to set toolchain up in ```~/workspace/carambola```
+* Run ```CARAMBOLA=1 make```

+ 2 - 0
components/external/espruino/README_cn.md

@@ -0,0 +1,2 @@
+这是从Espruino移植过来的javascript解释器。
+这个组件依赖newlib。需要在rtconfig.h中开启RT_USING_JS宏来开启此组件。

+ 48 - 14
components/external/espruino/SConscript

@@ -1,19 +1,53 @@
 from building import *
-import os
 
 cwd = GetCurrentDir()
-# source files 
-src = Split('''
-''')
-
-ESPRUINO_SRC_PATH = cwd + '/src'
-if GetDepend('RT_USING_JS') and not os.path.exists(ESPRUINO_SRC_PATH):
-    print '================ERROR============================'
-    print 'Please get espruino source files and put them under espruino folder'
-    print '================================================='
-    exit(0)
-
-CPPPATH = [cwd + '/src', cwd + '/gen', cwd + '/libs', cwd + '/targets/rtthread']
-group = DefineGroup('Espruino', src, depend = ['RT_USING_JS'], CPPPATH = CPPPATH)
+path = [cwd, cwd + '/src', cwd + '/gen', cwd + '/libs', cwd + '/targets/rtthread']
+
+src	= Split("""
+src/jsdevices.c
+src/jslex.c
+src/jswrap_array.c
+src/jswrap_json.c
+src/jswrap_process.c
+src/jsutils.c
+src/jswrap_interactive.c
+src/jswrap_onewire.c
+src/jswrap_string.c
+src/jsparse.c
+src/jswrap_arraybuffer.c
+src/jswrap_modules.c
+src/jswrap_serial.c
+src/jsinteractive.c
+src/jsvar.c
+src/jswrap_io.c
+src/jswrap_pin.c
+src/jspin.c
+src/jswrap_functions.c
+src/jswrap_object.c
+src/jswrap_spi_i2c.c
+"""
+)
+
+gen = Split("""
+gen/jspininfo.c
+gen/jswrapper.c
+"""
+)
+
+target = Split("""
+targets/rtthread/espruino.c  
+targets/rtthread/jshardware.c
+"""
+)
+
+libs = Split(
+"""
+libs/jswrap_math.c
+"""
+)
+
+src	= src + gen + target + libs
+
+group = DefineGroup('Espruino', src, depend = ['RT_USING_JS', 'RT_USING_NEWLIB'], CPPPATH = path)
 
 Return('group')

+ 109 - 0
components/external/espruino/dist_licences.txt

@@ -0,0 +1,109 @@
+Espruino uses a few libraries of code that have been generously given away for
+free. Their licences are included below.
+
+-------------------------------------------------------------------------
+ FATFS
+-------------------------------------------------------------------------
+
+/*---------------------------------------------------------------------------/
+/  FatFs - FAT file system module include file  R0.07c       (C)ChaN, 2009
+/----------------------------------------------------------------------------/
+/ FatFs module is an open source software to implement FAT file system to
+/ small embedded systems. This is a free software and is opened for education,
+/ research and commercial developments under license policy of following terms.
+/
+/  Copyright (C) 2009, ChaN, all right reserved.
+/
+/ * The FatFs module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/   personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY.
+/ * Redistributions of source code must retain the above copyright notice.*/
+
+
+-------------------------------------------------------------------------
+ FATFS to SPI bridge
+-------------------------------------------------------------------------
+
+/* Copyright (c) 2009, Martin Thomas, ChaN
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+
+   * Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+   * Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+   * Neither the name of the copyright holders nor the names of
+     contributors may be used to endorse or promote products derived
+     from this software without specific prior written permission.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+  POSSIBILITY OF SUCH DAMAGE. */
+  
+-------------------------------------------------------------------------
+ FATFS to SDIO bridge
+-------------------------------------------------------------------------
+
+/* author: ickle */ -  No details given with source code. If you have 
+anything you'd like to include, pleas e-mail us and let us know. 
+
+-------------------------------------------------------------------------
+ 8x8 LCD Font
+-------------------------------------------------------------------------
+
+/*
+http://forum.osdev.org/viewtopic.php?f=2&t=22033
+
+Created Sunday, May 23, 2010 by Quinn Evans
+Renamed and updated Monday 24, 2010
+
+This font (Vincent) is released by me into the public domain. I claim no
+copyright, and hereby make this software available to the public for any use,
+at any time, free of restrictions, legal or otherwise.
+*/
+
+-------------------------------------------------------------------------
+ CC3000 Host Driver Implementation.
+-------------------------------------------------------------------------
+
+ Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+   Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+
+   Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the   
+   distribution.
+
+   Neither the name of Texas Instruments Incorporated nor the names of
+   its contributors may be used to endorse or promote products derived
+   from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 57 - 0
components/external/espruino/dist_readme.txt

@@ -0,0 +1,57 @@
+ _____                 _                                                        
+|   __|___ ___ ___ _ _|_|___ ___                                                
+|   __|_ -| . |  _| | | |   | . |                                               
+|_____|___|  _|_| |___|_|_|_|___|                                               
+          |_|                                                               
+   Copyright 2013 Gordon Williams
+                                               
+http://www.espruino.com
+
+--------------------------------------------------------------
+
+There are a few different binaries in this ZIP file, for different
+types of Microcontroller:
+
+espruino_#v##_hystm32_24_ve.bin 
+   - 'HY'STM32F103VET6 ARM with 2.4" LCD display
+     This is available from eBay
+
+espruino_#v##_hystm32_28_rb.bin 
+   - 'HY'STM32F103RBT6 ARM with 2.8" LCD display
+     This is available from eBay
+
+espruino_#v##_hystm32_32_vc.bin 
+   - 'HY'STM32F103VCT6 ARM with 3.2" LCD display
+     This is available from eBay
+      
+espruino_#v##_olimexino_stm32.bin
+   - You will need to overwrite the Maple bootloader to install this.
+     Espruino is now too large to fit in flash alongside it.
+   - Olimexino-STM32 Arduino form factor board
+   - Leaf Labs Maple Arduino form factor board
+
+espruino_#v##_stm32vldiscovery.bin
+   - STM32VLDISCOVERY board
+
+espruino_#v##_stm32f3discovery.bin
+   - STM32F3DISCOVERY board
+   
+espruino_#v##_stm32f4discovery.bin
+   - STM32F4DISCOVERY board
+
+espruino_#v##_raspberrypi
+   - Raspberry Pi executable (just copy it to the device and run it)
+   NOTE: There is GPIO support (which requires you to run Espruino as root)
+   however there is no Serial, SPI, OneWire or I2C support at the moment so
+   you're pretty limited!
+
+For information on devices, and on how to flash these binary files on to 
+each device, please see our website, http://www.espruino.com
+
+NOTES:
+
+* On the STM32F4DISCOVERY the default USART is USART2 (because
+USART1 shares some pins with USB). This means you must connect
+serial connections to PA2/PA3 NOT PA9/PA10 as you would for
+the STM32VLDISCOVERY.
+

+ 1 - 0
components/external/espruino/gen/README

@@ -0,0 +1 @@
+This directory contains auto-generated files

+ 7 - 0
components/external/espruino/gen/jspininfo.c

@@ -0,0 +1,7 @@
+// auto-generated pin info file
+// for board LINUX
+#include "jspininfo.h"
+
+const JshPinInfo pinInfo[JSH_PIN_COUNT] = {
+};
+

+ 39 - 0
components/external/espruino/gen/jspininfo.h

@@ -0,0 +1,39 @@
+// auto-generated pin info file
+// for board LINUX
+#ifndef __JSPININFO_H_
+#define __JSPININFO_H_
+
+#include "jspin.h"
+
+#define JSH_PIN_COUNT 0
+
+#define JSH_PORTA_COUNT 0
+#define JSH_PORTB_COUNT 0
+#define JSH_PORTC_COUNT 0
+#define JSH_PORTD_COUNT 0
+#define JSH_PORTE_COUNT 0
+#define JSH_PORTF_COUNT 0
+#define JSH_PORTG_COUNT 0
+#define JSH_PORTH_COUNT 0
+#define JSH_PORTA_OFFSET -1
+#define JSH_PORTB_OFFSET -1
+#define JSH_PORTC_OFFSET -1
+#define JSH_PORTD_OFFSET -1
+#define JSH_PORTE_OFFSET -1
+#define JSH_PORTF_OFFSET -1
+#define JSH_PORTG_OFFSET -1
+#define JSH_PORTH_OFFSET -1
+
+#define JSH_PININFO_FUNCTIONS 0
+
+typedef struct JshPinInfo {
+  JsvPinInfoPort port;
+  JsvPinInfoPin pin;
+  JsvPinInfoAnalog analog; // TODO: maybe we don't need to store analogs separately
+  JshPinFunction functions[JSH_PININFO_FUNCTIONS];
+} PACKED_FLAGS JshPinInfo;
+
+extern const JshPinInfo pinInfo[JSH_PIN_COUNT];
+
+#endif
+

+ 1203 - 0
components/external/espruino/gen/jswrapper.c

@@ -0,0 +1,1203 @@
+// Automatically generated wrapper file 
+// Generated by scripts/build_jsfunctions.py
+
+#include "jswrapper.h"
+#include "src/jswrap_pin.h"
+#include "src/jswrap_functions.h"
+#include "src/jswrap_modules.h"
+#include "src/jswrap_process.h"
+#include "src/jswrap_interactive.h"
+#include "src/jswrap_json.h"
+#include "src/jswrap_object.h"
+#include "src/jswrap_string.h"
+#include "src/jswrap_array.h"
+#include "src/jswrap_arraybuffer.h"
+#include "src/jswrap_serial.h"
+#include "src/jswrap_spi_i2c.h"
+#include "src/jswrap_onewire.h"
+#include "src/jswrap_io.h"
+#include "libs/jswrap_math.h"
+
+#if( 'q\0\0\0' & 'q' )
+  #error( "architecture is big-endian. need to test and make sure this still works" )
+#endif
+// beware big endian!
+#define CH2(a,b) ( ((b)<<8) | (a) )
+#define CH4(a,b,c,d) ( ((d)<<24) | ((c)<<16) | ((b)<<8) | (a) )
+#define CMP2(var, a,b) ((*(unsigned short*)&var)==CH2(a,b))
+#define CMP3(var, a,b,c) (((*(unsigned int*)&var)&0x00FFFFFF)==CH4(a,b,c,0))
+#define CMP4(var, a,b,c,d) ((*(unsigned int*)&var)==CH4(a,b,c,d))
+
+JsVar *jswHandleFunctionCall(JsVar *parent, JsVar *parentName, const char *name) {
+  if (parent) {
+    // ------------------------------------------ METHODS ON OBJECT
+    if (CMP4(name[0],'c','l','o','n') && CMP2(name[4],'e','\0')) {
+      // Object.clone  (src/jswrap_object.c)
+      jspParseEmptyFunction();
+      JsVar *_r = jswrap_object_clone(parent);
+      return _r;
+    } else if (CMP4(name[0],'e','m','i','t') && name[4]=='\0') {
+      // Object.emit  (src/jswrap_object.c)
+      JsVar *event, *v1, *v2;
+      jspParseFunction(0, &event, &v1, &v2, 0);
+      jswrap_object_emit(parent, event, v1, v2);
+      jsvUnLock(event);
+      jsvUnLock(v1);
+      jsvUnLock(v2);
+      return 0;
+    } else if (CMP4(name[0],'l','e','n','g') && CMP3(name[4],'t','h','\0')) {
+      // Object.length  (src/jswrap_object.c)
+      jspParseVariableName();
+      JsVar *_r = jswrap_object_length(parent);
+      return _r;
+    } else if (CMP3(name[0],'o','n','\0')) {
+      // Object.on  (src/jswrap_object.c)
+      JsVar *event, *listener;
+      jspParseFunction(0, &event, &listener, 0, 0);
+      jswrap_object_on(parent, event, listener);
+      jsvUnLock(event);
+      jsvUnLock(listener);
+      return 0;
+    } else if (CMP4(name[0],'r','e','m','o') && CMP4(name[4],'v','e','A','l') && CMP4(name[8],'l','L','i','s') && CMP4(name[12],'t','e','n','e') && CMP3(name[16],'r','s','\0')) {
+      // Object.removeAllListeners  (src/jswrap_object.c)
+      JsVar *event = jspParseSingleFunction();
+      jswrap_object_removeAllListeners(parent, event);
+      jsvUnLock(event);
+      return 0;
+    } else if (CMP4(name[0],'t','o','S','t') && CMP4(name[4],'r','i','n','g') && name[8]=='\0') {
+      // Object.toString  (src/jswrap_object.c)
+      JsVar *radix = jspParseSingleFunction();
+      JsVar *_r = jswrap_object_toString(parent, radix);
+      jsvUnLock(radix);
+      return _r;
+    }
+    // ------------------------------------------ INSTANCE + STATIC METHODS
+    if (CMP4(parent->varData.str[0],'p','r','o','c') && CMP4(parent->varData.str[4],'e','s','s','\0')) {
+      if (CMP4(name[0],'m','e','m','o') && CMP3(name[4],'r','y','\0')) {
+        // process.memory  (src/jswrap_process.c)
+        jspParseEmptyFunction();
+        JsVar *_r = jswrap_process_memory();
+        return _r;
+      } else if (CMP4(name[0],'e','n','v','\0')) {
+        // process.env  (src/jswrap_process.c)
+        jspParseVariableName();
+        JsVar *_r = jswrap_process_env();
+        return _r;
+      } else if (CMP4(name[0],'v','e','r','s') && CMP4(name[4],'i','o','n','\0')) {
+        // process.version  (src/jswrap_process.c)
+        jspParseVariableName();
+        JsVar *_r = jsvNewFromString(JS_VERSION);
+        return _r;
+      }
+    }
+    if (CMP4(parent->varData.str[0],'O','b','j','e') && CMP3(parent->varData.str[4],'c','t','\0')) {
+      if (CMP4(name[0],'k','e','y','s') && name[4]=='\0') {
+        // Object.keys  (src/jswrap_object.c)
+        JsVar *object = jspParseSingleFunction();
+        JsVar *_r = jswrap_object_keys(object);
+        jsvUnLock(object);
+        return _r;
+      }
+    }
+    if (jsvIsFunction(parent)) {
+      if (CMP4(name[0],'a','p','p','l') && CMP2(name[4],'y','\0')) {
+        // Function.apply  (src/jswrap_object.c)
+        JsVar *this, *args;
+        jspParseFunction(0, &this, &args, 0, 0);
+        JsVar *_r = jswrap_function_apply(parent, this, args);
+        jsvUnLock(this);
+        jsvUnLock(args);
+        return _r;
+      } else if (CMP4(name[0],'c','a','l','l') && name[4]=='\0') {
+        // Function.call  (src/jswrap_object.c)
+        JsVar *this, *a, *b, *c, *d;
+        jspParseFunction8(0, &this, &a, &b, &c, &d, 0, 0, 0);
+        JsVar *_r = jswrap_function_call(parent, this, a, b, c, d);
+        jsvUnLock(this);
+        jsvUnLock(a);
+        jsvUnLock(b);
+        jsvUnLock(c);
+        jsvUnLock(d);
+        return _r;
+      } else if (CMP4(name[0],'r','e','p','l') && CMP4(name[4],'a','c','e','W') && CMP4(name[8],'i','t','h','\0')) {
+        // Function.replaceWith  (src/jswrap_object.c)
+        JsVar *newFunc = jspParseSingleFunction();
+        jswrap_function_replaceWith(parent, newFunc);
+        jsvUnLock(newFunc);
+        return 0;
+      }
+    }
+    if (CMP4(parent->varData.str[0],'M','o','d','u') && CMP4(parent->varData.str[4],'l','e','s','\0')) {
+      if (CMP4(name[0],'a','d','d','C') && CMP4(name[4],'a','c','h','e') && CMP2(name[8],'d','\0')) {
+        // Modules.addCached  (src/jswrap_modules.c)
+        JsVar *id, *sourcecode;
+        jspParseFunction(0, &id, &sourcecode, 0, 0);
+        jswrap_modules_addCached(id, sourcecode);
+        jsvUnLock(id);
+        jsvUnLock(sourcecode);
+        return 0;
+      } else if (CMP4(name[0],'r','e','m','o') && CMP2(name[4],'v','e')) {
+        if (CMP4(name[6],'A','l','l','C') && CMP4(name[10],'a','c','h','e') && CMP2(name[14],'d','\0')) {
+          // Modules.removeAllCached  (src/jswrap_modules.c)
+          jspParseEmptyFunction();
+          jswrap_modules_removeAllCached();
+          return 0;
+        } else if (CMP4(name[6],'C','a','c','h') && CMP3(name[10],'e','d','\0')) {
+          // Modules.removeCached  (src/jswrap_modules.c)
+          JsVar *id = jspParseSingleFunction();
+          jswrap_modules_removeCached(id);
+          jsvUnLock(id);
+          return 0;
+        }
+      } else if (CMP4(name[0],'g','e','t','C') && CMP4(name[4],'a','c','h','e') && CMP2(name[8],'d','\0')) {
+        // Modules.getCached  (src/jswrap_modules.c)
+        jspParseEmptyFunction();
+        JsVar *_r = jswrap_modules_getCached();
+        return _r;
+      }
+    }
+    if (jsvIsArrayBuffer(parent) && parent->varData.arraybuffer.type!=ARRAYBUFFERVIEW_ARRAYBUFFER) {
+      if (CMP4(name[0],'i','n','t','e') && CMP4(name[4],'r','p','o','l') && CMP3(name[8],'a','t','e')) {
+        if (CMP3(name[11],'2','d','\0')) {
+          // ArrayBufferView.interpolate2d  (src/jswrap_arraybuffer.c)
+          JsVar *width, *x, *y;
+          jspParseFunction(0, &width, &x, &y, 0);
+          JsVar *_r = jsvNewFromFloat(jswrap_arraybufferview_interpolate2d(parent, jsvGetIntegerAndUnLock(width), jsvGetFloatAndUnLock(x), jsvGetFloatAndUnLock(y)));
+          return _r;
+        } else if (name[11]==0) {
+          // ArrayBufferView.interpolate  (src/jswrap_arraybuffer.c)
+          JsVar *index = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromFloat(jswrap_arraybufferview_interpolate(parent, jsvGetFloatAndUnLock(index)));
+          return _r;
+        }
+      } else if (name[0]=='b') {
+        if (CMP3(name[1],'y','t','e')) {
+          if (CMP4(name[4],'L','e','n','g') && CMP3(name[8],'t','h','\0')) {
+            // ArrayBufferView.byteLength  (src/jswrap_arraybuffer.c)
+            jspParseVariableName();
+            JsVar *_r = jsvNewFromInteger(parent->varData.arraybuffer.length * JSV_ARRAYBUFFER_GET_SIZE(parent->varData.arraybuffer.type));
+            return _r;
+          } else if (CMP4(name[4],'O','f','f','s') && CMP3(name[8],'e','t','\0')) {
+            // ArrayBufferView.byteOffset  (src/jswrap_arraybuffer.c)
+            jspParseVariableName();
+            JsVar *_r = jsvNewFromInteger(parent->varData.arraybuffer.byteOffset);
+            return _r;
+          }
+        } else if (CMP4(name[1],'u','f','f','e') && CMP2(name[5],'r','\0')) {
+          // ArrayBufferView.buffer  (src/jswrap_arraybuffer.c)
+          jspParseVariableName();
+          JsVar *_r = jsvLock(parent->firstChild);
+          return _r;
+        }
+      }
+    }
+    if (jsvIsPin(parent)) {
+      if (CMP4(name[0],'s','e','t','\0')) {
+        // Pin.set  (src/jswrap_pin.c)
+        jspParseEmptyFunction();
+        jswrap_pin_set(parent);
+        return 0;
+      } else if (CMP2(name[0],'r','e')) {
+        if (CMP3(name[2],'a','d','\0')) {
+          // Pin.read  (src/jswrap_pin.c)
+          jspParseEmptyFunction();
+          JsVar *_r = jsvNewFromBool(jswrap_pin_read(parent));
+          return _r;
+        } else if (CMP4(name[2],'s','e','t','\0')) {
+          // Pin.reset  (src/jswrap_pin.c)
+          jspParseEmptyFunction();
+          jswrap_pin_reset(parent);
+          return 0;
+        }
+      } else if (CMP4(name[0],'w','r','i','t') && name[4]=='e') {
+        if (CMP4(name[5],'A','t','T','i') && CMP3(name[9],'m','e','\0')) {
+          // Pin.writeAtTime  (src/jswrap_pin.c)
+          JsVar *value, *time;
+          jspParseFunction(0, &value, &time, 0, 0);
+          jswrap_pin_writeAtTime(parent, jsvGetBoolAndUnLock(value), jsvGetFloatAndUnLock(time));
+          return 0;
+        } else if (name[5]==0) {
+          // Pin.write  (src/jswrap_pin.c)
+          JsVar *value = jspParseSingleFunction();
+          jswrap_pin_write(parent, jsvGetBoolAndUnLock(value));
+          return 0;
+        }
+      }
+    }
+    if (jsvIsString(parent)) {
+      if (CMP4(name[0],'i','n','d','e') && CMP4(name[4],'x','O','f','\0')) {
+        // String.indexOf  (src/jswrap_string.c)
+        JsVar *substring = jspParseSingleFunction();
+        JsVar *_r = jsvNewFromInteger(jswrap_string_indexOf(parent, substring));
+        jsvUnLock(substring);
+        return _r;
+      } else if (CMP4(name[0],'c','h','a','r')) {
+        if (CMP3(name[4],'A','t','\0')) {
+          // String.charAt  (src/jswrap_string.c)
+          JsVar *pos = jspParseSingleFunction();
+          JsVar *_r = jswrap_string_charAt(parent, jsvGetIntegerAndUnLock(pos));
+          return _r;
+        } else if (CMP4(name[4],'C','o','d','e') && CMP3(name[8],'A','t','\0')) {
+          // String.charCodeAt  (src/jswrap_string.c)
+          JsVar *pos = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromInteger(jswrap_string_charCodeAt(parent, jsvGetIntegerAndUnLock(pos)));
+          return _r;
+        }
+      } else if (name[0]=='s') {
+        if (CMP4(name[1],'p','l','i','t') && name[5]=='\0') {
+          // String.split  (src/jswrap_string.c)
+          JsVar *separator = jspParseSingleFunction();
+          JsVar *_r = jswrap_string_split(parent, separator);
+          jsvUnLock(separator);
+          return _r;
+        } else if (CMP4(name[1],'u','b','s','t') && name[5]=='r') {
+          if (CMP4(name[6],'i','n','g','\0')) {
+            // String.substring  (src/jswrap_string.c)
+            JsVar *start, *end;
+            jspParseFunction(0, &start, &end, 0, 0);
+            JsVar *_r = jswrap_string_substring(parent, jsvGetIntegerAndUnLock(start), end);
+            jsvUnLock(end);
+            return _r;
+          } else if (name[6]==0) {
+            // String.substr  (src/jswrap_string.c)
+            JsVar *start, *len;
+            jspParseFunction(0, &start, &len, 0, 0);
+            JsVar *_r = jswrap_string_substr(parent, jsvGetIntegerAndUnLock(start), len);
+            jsvUnLock(len);
+            return _r;
+          }
+        }
+      }
+    }
+    if (CMP4(parent->varData.str[0],'M','a','t','h') && parent->varData.str[4]=='\0') {
+      if (name[0]=='a') {
+        if (CMP4(name[1],'c','o','s','\0')) {
+          // Math.acos  (libs/jswrap_math.c)
+          JsVar *x = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromFloat(acos(jsvGetFloatAndUnLock(x)));
+          return _r;
+        } else if (CMP3(name[1],'b','s','\0')) {
+          // Math.abs  (libs/jswrap_math.c)
+          JsVar *x = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromFloat(jswrap_math_abs(jsvGetFloatAndUnLock(x)));
+          return _r;
+        } else if (CMP4(name[1],'s','i','n','\0')) {
+          // Math.asin  (libs/jswrap_math.c)
+          JsVar *x = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromFloat(asin(jsvGetFloatAndUnLock(x)));
+          return _r;
+        } else if (CMP3(name[1],'t','a','n')) {
+          if (CMP2(name[4],'2','\0')) {
+            // Math.atan2  (libs/jswrap_math.c)
+            JsVar *y, *x;
+            jspParseFunction(0, &y, &x, 0, 0);
+            JsVar *_r = jsvNewFromFloat(atan2(jsvGetFloatAndUnLock(y), jsvGetFloatAndUnLock(x)));
+            return _r;
+          } else if (name[4]==0) {
+            // Math.atan  (libs/jswrap_math.c)
+            JsVar *x = jspParseSingleFunction();
+            JsVar *_r = jsvNewFromFloat(atan(jsvGetFloatAndUnLock(x)));
+            return _r;
+          }
+        }
+      } else if (name[0]=='c') {
+        if (CMP4(name[1],'e','i','l','\0')) {
+          // Math.ceil  (libs/jswrap_math.c)
+          JsVar *x = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromFloat(ceil(jsvGetFloatAndUnLock(x)));
+          return _r;
+        } else if (CMP4(name[1],'l','i','p','\0')) {
+          // Math.clip  (libs/jswrap_math.c)
+          JsVar *x, *min, *max;
+          jspParseFunction(0, &x, &min, &max, 0);
+          JsVar *_r = jsvNewFromFloat(jswrap_math_clip(jsvGetFloatAndUnLock(x), jsvGetFloatAndUnLock(min), jsvGetFloatAndUnLock(max)));
+          return _r;
+        } else if (CMP3(name[1],'o','s','\0')) {
+          // Math.cos  (libs/jswrap_math.c)
+          JsVar *theta = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromFloat(sin(jsvGetFloat(theta) + (3.14159265359/2.0)));
+          jsvUnLock(theta);
+          return _r;
+        }
+      } else if (CMP2(name[0],'E','\0')) {
+        // Math.E  (libs/jswrap_math.c)
+        jspParseVariableName();
+        JsVar *_r = jsvNewFromFloat(2.71828182846);
+        return _r;
+      } else if (CMP4(name[0],'p','o','w','\0')) {
+        // Math.pow  (libs/jswrap_math.c)
+        JsVar *x, *y;
+        jspParseFunction(0, &x, &y, 0, 0);
+        JsVar *_r = jsvNewFromFloat(jswrap_math_pow(jsvGetFloatAndUnLock(x), jsvGetFloatAndUnLock(y)));
+        return _r;
+      } else if (CMP4(name[0],'f','l','o','o') && CMP2(name[4],'r','\0')) {
+        // Math.floor  (libs/jswrap_math.c)
+        JsVar *x = jspParseSingleFunction();
+        JsVar *_r = jsvNewFromFloat(floor(jsvGetFloatAndUnLock(x)));
+        return _r;
+      } else if (CMP4(name[0],'l','o','g','\0')) {
+        // Math.log  (libs/jswrap_math.c)
+        JsVar *x = jspParseSingleFunction();
+        JsVar *_r = jsvNewFromFloat(log(jsvGetFloatAndUnLock(x)));
+        return _r;
+      } else if (CMP3(name[0],'P','I','\0')) {
+        // Math.PI  (libs/jswrap_math.c)
+        jspParseVariableName();
+        JsVar *_r = jsvNewFromFloat(3.14159265359);
+        return _r;
+      } else if (name[0]=='s') {
+        if (CMP3(name[1],'i','n','\0')) {
+          // Math.sin  (libs/jswrap_math.c)
+          JsVar *theta = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromFloat(sin(jsvGetFloatAndUnLock(theta)));
+          return _r;
+        } else if (CMP4(name[1],'q','r','t','\0')) {
+          // Math.sqrt  (libs/jswrap_math.c)
+          JsVar *x = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromFloat(jswrap_math_pow(jsvGetFloat(x),0.5));
+          jsvUnLock(x);
+          return _r;
+        }
+      } else if (name[0]=='r') {
+        if (CMP4(name[1],'a','n','d','o') && CMP2(name[5],'m','\0')) {
+          // Math.random  (libs/jswrap_math.c)
+          jspParseEmptyFunction();
+          JsVar *_r = jsvNewFromFloat((JsVarFloat)rand() / (JsVarFloat)RAND_MAX);
+          return _r;
+        } else if (CMP4(name[1],'o','u','n','d') && name[5]=='\0') {
+          // Math.round  (libs/jswrap_math.c)
+          JsVar *x = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromInteger((JsVarInt)round(jsvGetFloatAndUnLock(x)));
+          return _r;
+        }
+      } else if (CMP4(name[0],'w','r','a','p') && name[4]=='\0') {
+        // Math.wrap  (libs/jswrap_math.c)
+        JsVar *x, *max;
+        jspParseFunction(0, &x, &max, 0, 0);
+        JsVar *_r = jsvNewFromFloat(wrapAround(jsvGetFloatAndUnLock(x), jsvGetFloatAndUnLock(max)));
+        return _r;
+      } else if (CMP4(name[0],'e','x','p','\0')) {
+        // Math.exp  (libs/jswrap_math.c)
+        JsVar *x = jspParseSingleFunction();
+        JsVar *_r = jsvNewFromFloat(exp(jsvGetFloatAndUnLock(x)));
+        return _r;
+      }
+    }
+    if (CMP4(parent->varData.str[0],'D','o','u','b') && CMP3(parent->varData.str[4],'l','e','\0')) {
+      if (CMP4(name[0],'d','o','u','b') && CMP4(name[4],'l','e','T','o') && CMP4(name[8],'I','n','t','B') && CMP4(name[12],'i','t','s','\0')) {
+        // Double.doubleToIntBits  (libs/jswrap_math.c)
+        JsVar *x = jspParseSingleFunction();
+        JsVar *_r = jsvNewFromInteger(*(JsVarInt*)&x);
+        jsvUnLock(x);
+        return _r;
+      }
+    }
+    if (jsvIsArray(parent)) {
+      if (CMP4(name[0],'c','o','n','t') && CMP4(name[4],'a','i','n','s') && name[8]=='\0') {
+        // Array.contains  (src/jswrap_array.c)
+        JsVar *value = jspParseSingleFunction();
+        JsVar *_r = jsvNewFromBool(jswrap_array_contains(parent, value));
+        jsvUnLock(value);
+        return _r;
+      } else if (CMP4(name[0],'f','o','r','E') && CMP4(name[4],'a','c','h','\0')) {
+        // Array.forEach  (src/jswrap_array.c)
+        JsVar *function, *thisArg;
+        jspParseFunction(0, &function, &thisArg, 0, 0);
+        jswrap_array_forEach(parent, function, thisArg);
+        jsvUnLock(function);
+        jsvUnLock(thisArg);
+        return 0;
+      } else if (CMP4(name[0],'i','n','d','e') && CMP4(name[4],'x','O','f','\0')) {
+        // Array.indexOf  (src/jswrap_array.c)
+        JsVar *value = jspParseSingleFunction();
+        JsVar *_r = jswrap_array_indexOf(parent, value);
+        jsvUnLock(value);
+        return _r;
+      } else if (CMP4(name[0],'j','o','i','n') && name[4]=='\0') {
+        // Array.join  (src/jswrap_array.c)
+        JsVar *separator = jspParseSingleFunction();
+        JsVar *_r = jswrap_array_join(parent, separator);
+        jsvUnLock(separator);
+        return _r;
+      } else if (CMP4(name[0],'m','a','p','\0')) {
+        // Array.map  (src/jswrap_array.c)
+        JsVar *function, *thisArg;
+        jspParseFunction(0, &function, &thisArg, 0, 0);
+        JsVar *_r = jswrap_array_map(parent, function, thisArg);
+        jsvUnLock(function);
+        jsvUnLock(thisArg);
+        return _r;
+      } else if (name[0]=='p') {
+        if (CMP4(name[1],'u','s','h','\0')) {
+          // Array.push  (src/jswrap_array.c)
+          JsVar *value = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromInteger(jsvArrayPush(parent, value));
+          jsvUnLock(value);
+          return _r;
+        } else if (CMP3(name[1],'o','p','\0')) {
+          // Array.pop  (src/jswrap_array.c)
+          jspParseEmptyFunction();
+          JsVar *_r = jsvArrayPop(parent);
+          return _r;
+        }
+      } else if (name[0]=='s') {
+        if (CMP4(name[1],'p','l','i','c') && CMP2(name[5],'e','\0')) {
+          // Array.splice  (src/jswrap_array.c)
+          JsVar *index, *howMany, *element1, *element2, *element3, *element4, *element5, *element6;
+          jspParseFunction8(0, &index, &howMany, &element1, &element2, &element3, &element4, &element5, &element6);
+          JsVar *_r = jswrap_array_splice(parent, jsvGetIntegerAndUnLock(index), howMany, element1, element2, element3, element4, element5, element6);
+          jsvUnLock(howMany);
+          jsvUnLock(element1);
+          jsvUnLock(element2);
+          jsvUnLock(element3);
+          jsvUnLock(element4);
+          jsvUnLock(element5);
+          jsvUnLock(element6);
+          return _r;
+        } else if (CMP4(name[1],'l','i','c','e') && name[5]=='\0') {
+          // Array.slice  (src/jswrap_array.c)
+          JsVar *start, *end;
+          jspParseFunction(0, &start, &end, 0, 0);
+          JsVar *_r = jswrap_array_slice(parent, start, end);
+          jsvUnLock(start);
+          jsvUnLock(end);
+          return _r;
+        }
+      }
+    }
+    if (CMP4(parent->varData.str[0],'c','o','n','s') && CMP4(parent->varData.str[4],'o','l','e','\0')) {
+      if (CMP4(name[0],'l','o','g','\0')) {
+        // console.log  (src/jswrap_interactive.c)
+        JsVar *text = jspParseFunctionAsArray();
+        if (!text) return 0; // if parse error
+        jswrap_interface_print(text);
+        jsvUnLock(text);
+        return 0;
+      }
+    }
+    if (CMP4(parent->varData.str[0],'J','S','O','N') && parent->varData.str[4]=='\0') {
+      if (CMP4(name[0],'p','a','r','s') && CMP2(name[4],'e','\0')) {
+        // JSON.parse  (src/jswrap_json.c)
+        JsVar *string = jspParseSingleFunction();
+        JsVar *_r = jswrap_json_parse(string);
+        jsvUnLock(string);
+        return _r;
+      } else if (CMP4(name[0],'s','t','r','i') && CMP4(name[4],'n','g','i','f') && CMP2(name[8],'y','\0')) {
+        // JSON.stringify  (src/jswrap_json.c)
+        JsVar *data = jspParseSingleFunction();
+        JsVar *_r = jswrap_json_stringify(data);
+        jsvUnLock(data);
+        return _r;
+      }
+    }
+    if (CMP4(parent->varData.str[0],'S','t','r','i') && CMP3(parent->varData.str[4],'n','g','\0')) {
+      if (CMP4(name[0],'f','r','o','m') && CMP4(name[4],'C','h','a','r') && CMP4(name[8],'C','o','d','e') && name[12]=='\0') {
+        // String.fromCharCode  (src/jswrap_string.c)
+        JsVar *code = jspParseSingleFunction();
+        JsVar *_r = jswrap_string_fromCharCode(jsvGetIntegerAndUnLock(code));
+        return _r;
+      }
+    }
+    if (CMP4(parent->varData.str[0],'I','n','t','e') && CMP4(parent->varData.str[4],'g','e','r','\0')) {
+      if (CMP4(name[0],'v','a','l','u') && CMP4(name[4],'e','O','f','\0')) {
+        // Integer.valueOf  (libs/jswrap_math.c)
+        JsVar *character = jspParseSingleFunction();
+        JsVar *_r = jsvNewFromInteger(jswrap_integer_valueOf(character));
+        jsvUnLock(character);
+        return _r;
+      }
+    }
+    // ------------------------------------------ INSTANCE METHODS WE MUST CHECK CONSTRUCTOR FOR
+    JsVar *constructorName = jsvIsObject(parent)?jsvSkipOneNameAndUnLock(jsvFindChildFromString(parent, JSPARSE_CONSTRUCTOR_VAR, false)):0;
+    if (constructorName && jsvIsName(constructorName)) {
+    if (CMP4(constructorName->varData.str[0],'S','e','r','i') && CMP3(constructorName->varData.str[4],'a','l','\0')) {
+      jsvUnLock(constructorName);constructorName=0;
+          if (CMP4(name[0],'p','r','i','n') && name[4]=='t') {
+            if (CMP3(name[5],'l','n','\0')) {
+              // Serial.println  (src/jswrap_serial.c)
+              JsVar *string = jspParseSingleFunction();
+              jswrap_serial_println(parent, string);
+              jsvUnLock(string);
+              return 0;
+            } else if (name[5]==0) {
+              // Serial.print  (src/jswrap_serial.c)
+              JsVar *string = jspParseSingleFunction();
+              jswrap_serial_print(parent, string);
+              jsvUnLock(string);
+              return 0;
+            }
+          } else if (CMP3(name[0],'s','e','t')) {
+            if (CMP4(name[3],'C','o','n','s') && CMP4(name[7],'o','l','e','\0')) {
+              // Serial.setConsole  (src/jswrap_serial.c)
+              jspParseEmptyFunction();
+              jsiSetConsoleDevice(jsiGetDeviceFromClass(parent));
+              return 0;
+            } else if (CMP3(name[3],'u','p','\0')) {
+              // Serial.setup  (src/jswrap_serial.c)
+              JsVar *baudrate, *options;
+              jspParseFunction(0, &baudrate, &options, 0, 0);
+              jswrap_serial_setup(parent, jsvGetIntegerAndUnLock(baudrate), options);
+              jsvUnLock(options);
+              return 0;
+            }
+          } else if (CMP4(name[0],'o','n','D','a') && CMP3(name[4],'t','a','\0')) {
+            // Serial.onData  (src/jswrap_serial.c)
+            JsVar *function;
+            jspParseFunction(0|JSP_NOSKIP_A, &function, 0, 0, 0);
+            jswrap_serial_onData(parent, function);
+            jsvUnLock(function);
+            return 0;
+          } else if (CMP4(name[0],'w','r','i','t') && CMP2(name[4],'e','\0')) {
+            // Serial.write  (src/jswrap_serial.c)
+            JsVar *data = jspParseSingleFunction();
+            jswrap_serial_write(parent, data);
+            jsvUnLock(data);
+            return 0;
+          }
+    } else if (CMP4(constructorName->varData.str[0],'O','n','e','W') && CMP4(constructorName->varData.str[4],'i','r','e','\0')) {
+      jsvUnLock(constructorName);constructorName=0;
+          if (name[0]=='s') {
+            if (CMP4(name[1],'k','i','p','\0')) {
+              // OneWire.skip  (src/jswrap_onewire.c)
+              jspParseEmptyFunction();
+              jswrap_onewire_skip(parent);
+              return 0;
+            } else if (name[1]=='e') {
+              if (CMP4(name[2],'a','r','c','h') && name[6]=='\0') {
+                // OneWire.search  (src/jswrap_onewire.c)
+                jspParseEmptyFunction();
+                JsVar *_r = jswrap_onewire_search(parent);
+                return _r;
+              } else if (CMP4(name[2],'l','e','c','t') && name[6]=='\0') {
+                // OneWire.select  (src/jswrap_onewire.c)
+                JsVar *rom = jspParseSingleFunction();
+                jswrap_onewire_select(parent, jsvGetIntegerAndUnLock(rom));
+                return 0;
+              }
+            }
+          } else if (CMP2(name[0],'r','e')) {
+            if (CMP3(name[2],'a','d','\0')) {
+              // OneWire.read  (src/jswrap_onewire.c)
+              jspParseEmptyFunction();
+              JsVar *_r = jsvNewFromInteger(jswrap_onewire_read(parent));
+              return _r;
+            } else if (CMP4(name[2],'s','e','t','\0')) {
+              // OneWire.reset  (src/jswrap_onewire.c)
+              jspParseEmptyFunction();
+              JsVar *_r = jsvNewFromBool(jswrap_onewire_reset(parent));
+              return _r;
+            }
+          } else if (CMP4(name[0],'w','r','i','t') && CMP2(name[4],'e','\0')) {
+            // OneWire.write  (src/jswrap_onewire.c)
+            JsVar *data, *power;
+            jspParseFunction(0, &data, &power, 0, 0);
+            jswrap_onewire_write(parent, jsvGetIntegerAndUnLock(data), jsvGetBoolAndUnLock(power));
+            return 0;
+          }
+    } else if (CMP4(constructorName->varData.str[0],'I','2','C','\0')) {
+      jsvUnLock(constructorName);constructorName=0;
+          if (CMP4(name[0],'s','e','t','u') && CMP2(name[4],'p','\0')) {
+            // I2C.setup  (src/jswrap_spi_i2c.c)
+            JsVar *options = jspParseSingleFunction();
+            jswrap_i2c_setup(parent, options);
+            jsvUnLock(options);
+            return 0;
+          } else if (CMP4(name[0],'r','e','a','d') && CMP4(name[4],'F','r','o','m') && name[8]=='\0') {
+            // I2C.readFrom  (src/jswrap_spi_i2c.c)
+            JsVar *address, *quantity;
+            jspParseFunction(0, &address, &quantity, 0, 0);
+            JsVar *_r = jswrap_i2c_readFrom(parent, jsvGetIntegerAndUnLock(address), jsvGetIntegerAndUnLock(quantity));
+            return _r;
+          } else if (CMP4(name[0],'w','r','i','t') && CMP4(name[4],'e','T','o','\0')) {
+            // I2C.writeTo  (src/jswrap_spi_i2c.c)
+            JsVar *address, *data;
+            jspParseFunction(0, &address, &data, 0, 0);
+            jswrap_i2c_writeTo(parent, jsvGetIntegerAndUnLock(address), data);
+            jsvUnLock(data);
+            return 0;
+          }
+    } else if (CMP4(constructorName->varData.str[0],'S','P','I','\0')) {
+      jsvUnLock(constructorName);constructorName=0;
+          if (CMP2(name[0],'s','e')) {
+            if (CMP4(name[2],'t','u','p','\0')) {
+              // SPI.setup  (src/jswrap_spi_i2c.c)
+              JsVar *options = jspParseSingleFunction();
+              jswrap_spi_setup(parent, options);
+              jsvUnLock(options);
+              return 0;
+            } else if (CMP2(name[2],'n','d')) {
+              if (CMP4(name[4],'8','b','i','t') && name[8]=='\0') {
+                // SPI.send8bit  (src/jswrap_spi_i2c.c)
+                JsVar *data, *bit0, *bit1, *nss_pin;
+                jspParseFunction(0, &data, &bit0, &bit1, &nss_pin);
+                jswrap_spi_send8bit(parent, data, jsvGetIntegerAndUnLock(bit0), jsvGetIntegerAndUnLock(bit1), jshGetPinFromVarAndUnLock(nss_pin));
+                jsvUnLock(data);
+                return 0;
+              } else if (CMP4(name[4],'4','b','i','t') && name[8]=='\0') {
+                // SPI.send4bit  (src/jswrap_spi_i2c.c)
+                JsVar *data, *bit0, *bit1, *nss_pin;
+                jspParseFunction(0, &data, &bit0, &bit1, &nss_pin);
+                jswrap_spi_send4bit(parent, data, jsvGetIntegerAndUnLock(bit0), jsvGetIntegerAndUnLock(bit1), jshGetPinFromVarAndUnLock(nss_pin));
+                jsvUnLock(data);
+                return 0;
+              } else if (name[4]==0) {
+                // SPI.send  (src/jswrap_spi_i2c.c)
+                JsVar *data, *nss_pin;
+                jspParseFunction(0, &data, &nss_pin, 0, 0);
+                JsVar *_r = jswrap_spi_send(parent, data, jshGetPinFromVarAndUnLock(nss_pin));
+                jsvUnLock(data);
+                return _r;
+              }
+            }
+          }
+    } else 
+      jsvUnLock(constructorName);
+    }
+  } else { /* if (!parent) */
+    // ------------------------------------------ FUNCTIONS
+    // Handle pin names - eg LED1 or D5 (this is hardcoded in build_jsfunctions.py)
+    Pin pin = jshGetPinFromString(name);
+    if (pin != PIN_UNDEFINED) {
+      jspParseVariableName();
+      return jsvNewFromPin(pin);
+    }
+    if (name[0]=='a') {
+      if (CMP4(name[1],'r','g','u','m') && CMP4(name[5],'e','n','t','s') && name[9]=='\0') {
+        // arguments  (src/jswrap_functions.c)
+        jspParseVariableName();
+        JsVar *_r = jswrap_arguments();
+        return _r;
+      } else if (CMP4(name[1],'n','a','l','o') && name[5]=='g') {
+        if (CMP4(name[6],'R','e','a','d') && name[10]=='\0') {
+          // analogRead  (src/jswrap_io.c)
+          JsVar *pin = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromFloat(jshPinAnalog(jshGetPinFromVarAndUnLock(pin)));
+          return _r;
+        } else if (CMP4(name[6],'W','r','i','t') && CMP2(name[10],'e','\0')) {
+          // analogWrite  (src/jswrap_io.c)
+          JsVar *pin, *value, *options;
+          jspParseFunction(0, &pin, &value, &options, 0);
+          jswrap_io_analogWrite(jshGetPinFromVarAndUnLock(pin), jsvGetFloatAndUnLock(value), options);
+          jsvUnLock(options);
+          return 0;
+        }
+      }
+    } else if (CMP4(name[0],'A','r','r','a') && name[4]=='y') {
+      if (CMP4(name[5],'B','u','f','f') && CMP3(name[9],'e','r','\0')) {
+        // ArrayBuffer.ArrayBuffer  (src/jswrap_arraybuffer.c)
+        JsVar *byteLength = jspParseSingleFunction();
+        JsVar *_r = jswrap_arraybuffer_constructor(jsvGetIntegerAndUnLock(byteLength));
+        return _r;
+      } else if (name[5]==0) {
+        // Array.Array  (src/jswrap_array.c)
+        JsVar *args = jspParseFunctionAsArray();
+        if (!args) return 0; // if parse error
+        JsVar *_r = jswrap_array_constructor(args);
+        jsvUnLock(args);
+        return _r;
+      }
+    } else if (name[0]=='c') {
+      if (CMP4(name[1],'h','a','n','g') && CMP4(name[5],'e','I','n','t') && CMP4(name[9],'e','r','v','a') && CMP2(name[13],'l','\0')) {
+        // changeInterval  (src/jswrap_io.c)
+        JsVar *id, *time;
+        jspParseFunction(0, &id, &time, 0, 0);
+        jswrap_interface_changeInterval(id, jsvGetFloatAndUnLock(time));
+        jsvUnLock(id);
+        return 0;
+      } else if (CMP4(name[1],'l','e','a','r')) {
+        if (CMP4(name[5],'I','n','t','e') && CMP4(name[9],'r','v','a','l') && name[13]=='\0') {
+          // clearInterval  (src/jswrap_io.c)
+          JsVar *id = jspParseSingleFunction();
+          jswrap_interface_clearInterval(id);
+          jsvUnLock(id);
+          return 0;
+        } else if (CMP4(name[5],'T','i','m','e') && CMP4(name[9],'o','u','t','\0')) {
+          // clearTimeout  (src/jswrap_io.c)
+          JsVar *id = jspParseSingleFunction();
+          jswrap_interface_clearTimeout(id);
+          jsvUnLock(id);
+          return 0;
+        } else if (CMP4(name[5],'W','a','t','c') && CMP2(name[9],'h','\0')) {
+          // clearWatch  (src/jswrap_io.c)
+          JsVar *id = jspParseSingleFunction();
+          jswrap_interface_clearWatch(id);
+          jsvUnLock(id);
+          return 0;
+        }
+      }
+    } else if (name[0]=='e') {
+      if (CMP4(name[1],'c','h','o','\0')) {
+        // echo  (src/jswrap_interactive.c)
+        JsVar *echoOn = jspParseSingleFunction();
+        jswrap_interface_echo(jsvGetBoolAndUnLock(echoOn));
+        return 0;
+      } else if (CMP4(name[1],'d','i','t','\0')) {
+        // edit  (src/jswrap_interactive.c)
+        JsVar *funcName;
+        jspParseFunction(0|JSP_NOSKIP_A, &funcName, 0, 0, 0);
+        jswrap_interface_edit(funcName);
+        jsvUnLock(funcName);
+        return 0;
+      } else if (CMP4(name[1],'v','a','l','\0')) {
+        // eval  (src/jswrap_functions.c)
+        JsVar *code = jspParseSingleFunction();
+        JsVar *_r = jswrap_eval(code);
+        jsvUnLock(code);
+        return _r;
+      }
+    } else if (name[0]=='d') {
+      if (CMP4(name[1],'i','g','i','t') && CMP2(name[5],'a','l')) {
+        if (CMP4(name[7],'P','u','l','s') && CMP2(name[11],'e','\0')) {
+          // digitalPulse  (src/jswrap_io.c)
+          JsVar *pin, *value, *time;
+          jspParseFunction(0, &pin, &value, &time, 0);
+          jswrap_io_digitalPulse(jshGetPinFromVarAndUnLock(pin), jsvGetBoolAndUnLock(value), jsvGetFloatAndUnLock(time));
+          return 0;
+        } else if (CMP4(name[7],'R','e','a','d') && name[11]=='\0') {
+          // digitalRead  (src/jswrap_io.c)
+          JsVar *pin = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromInteger(jswrap_io_digitalRead(pin));
+          jsvUnLock(pin);
+          return _r;
+        } else if (CMP4(name[7],'W','r','i','t') && CMP2(name[11],'e','\0')) {
+          // digitalWrite  (src/jswrap_io.c)
+          JsVar *pin, *value;
+          jspParseFunction(0, &pin, &value, 0, 0);
+          jswrap_io_digitalWrite(pin, jsvGetIntegerAndUnLock(value));
+          jsvUnLock(pin);
+          return 0;
+        }
+      } else if (CMP4(name[1],'u','m','p','\0')) {
+        // dump  (src/jswrap_interactive.c)
+        jspParseEmptyFunction();
+        jsiDumpState();
+        return 0;
+      }
+    } else if (CMP3(name[0],'g','e','t')) {
+      if (CMP4(name[3],'S','e','r','i') && CMP3(name[7],'a','l','\0')) {
+        // getSerial  (src/jswrap_interactive.c)
+        jspParseEmptyFunction();
+        JsVar *_r = jswrap_interface_getSerial();
+        return _r;
+      } else if (CMP4(name[3],'T','i','m','e') && name[7]=='\0') {
+        // getTime  (src/jswrap_interactive.c)
+        jspParseEmptyFunction();
+        JsVar *_r = jsvNewFromFloat((JsVarFloat)jshGetSystemTime() / (JsVarFloat)jshGetTimeFromMilliseconds(1000));
+        return _r;
+      }
+    } else if (CMP4(name[0],'F','l','o','a') && name[4]=='t') {
+      if (CMP4(name[5],'3','2','A','r') && CMP4(name[9],'r','a','y','\0')) {
+        // Float32Array.Float32Array  (src/jswrap_arraybuffer.c)
+        JsVar *arr, *byteOffset, *length;
+        jspParseFunction(0, &arr, &byteOffset, &length, 0);
+        JsVar *_r = jswrap_typedarray_constructor(ARRAYBUFFERVIEW_FLOAT32, arr, jsvGetInteger(byteOffset), jsvGetInteger(length));
+        jsvUnLock(arr);
+        jsvUnLock(byteOffset);
+        jsvUnLock(length);
+        return _r;
+      } else if (CMP4(name[5],'6','4','A','r') && CMP4(name[9],'r','a','y','\0')) {
+        // Float64Array.Float64Array  (src/jswrap_arraybuffer.c)
+        JsVar *arr, *byteOffset, *length;
+        jspParseFunction(0, &arr, &byteOffset, &length, 0);
+        JsVar *_r = jswrap_typedarray_constructor(ARRAYBUFFERVIEW_FLOAT64, arr, jsvGetInteger(byteOffset), jsvGetInteger(length));
+        jsvUnLock(arr);
+        jsvUnLock(byteOffset);
+        jsvUnLock(length);
+        return _r;
+      }
+    } else if (name[0]=='S') {
+      if (CMP2(name[1],'P','I')) {
+        if (CMP2(name[3],'1','\0')) {
+          // Object SPI1  (src/jswrap_spi_i2c.c)
+          #if SPIS>=1
+          jspParseVariableName();
+          return jspNewObject(jsiGetParser(), "SPI1", "SPI");
+          #endif //SPIS>=1
+        } else if (CMP2(name[3],'3','\0')) {
+          // Object SPI3  (src/jswrap_spi_i2c.c)
+          #if SPIS>=3
+          jspParseVariableName();
+          return jspNewObject(jsiGetParser(), "SPI3", "SPI");
+          #endif //SPIS>=3
+        } else if (CMP2(name[3],'2','\0')) {
+          // Object SPI2  (src/jswrap_spi_i2c.c)
+          #if SPIS>=2
+          jspParseVariableName();
+          return jspNewObject(jsiGetParser(), "SPI2", "SPI");
+          #endif //SPIS>=2
+        }
+      } else if (CMP4(name[1],'e','r','i','a') && name[5]=='l') {
+        if (CMP2(name[6],'1','\0')) {
+          // Object Serial1  (src/jswrap_serial.c)
+          #if USARTS>=1
+          jspParseVariableName();
+          return jspNewObject(jsiGetParser(), "Serial1", "Serial");
+          #endif //USARTS>=1
+        } else if (CMP2(name[6],'3','\0')) {
+          // Object Serial3  (src/jswrap_serial.c)
+          #if USARTS>=3
+          jspParseVariableName();
+          return jspNewObject(jsiGetParser(), "Serial3", "Serial");
+          #endif //USARTS>=3
+        } else if (CMP2(name[6],'2','\0')) {
+          // Object Serial2  (src/jswrap_serial.c)
+          #if USARTS>=2
+          jspParseVariableName();
+          return jspNewObject(jsiGetParser(), "Serial2", "Serial");
+          #endif //USARTS>=2
+        } else if (CMP2(name[6],'5','\0')) {
+          // Object Serial5  (src/jswrap_serial.c)
+          #if USARTS>=5
+          jspParseVariableName();
+          return jspNewObject(jsiGetParser(), "Serial5", "Serial");
+          #endif //USARTS>=5
+        } else if (CMP2(name[6],'4','\0')) {
+          // Object Serial4  (src/jswrap_serial.c)
+          #if USARTS>=4
+          jspParseVariableName();
+          return jspNewObject(jsiGetParser(), "Serial4", "Serial");
+          #endif //USARTS>=4
+        } else if (CMP2(name[6],'6','\0')) {
+          // Object Serial6  (src/jswrap_serial.c)
+          #if USARTS>=6
+          jspParseVariableName();
+          return jspNewObject(jsiGetParser(), "Serial6", "Serial");
+          #endif //USARTS>=6
+        }
+      } else if (CMP4(name[1],'t','r','i','n') && CMP2(name[5],'g','\0')) {
+        // String.String  (src/jswrap_string.c)
+        JsVar *str = jspParseSingleFunction();
+        JsVar *_r = jswrap_string_constructor(str);
+        jsvUnLock(str);
+        return _r;
+      }
+    } else if (CMP4(name[0],'l','o','a','d') && name[4]=='\0') {
+      // load  (src/jswrap_interactive.c)
+      jspParseEmptyFunction();
+      jsiSetTodo(TODO_FLASH_LOAD);
+      return 0;
+    } else if (CMP4(name[0],'O','n','e','W') && CMP4(name[4],'i','r','e','\0')) {
+      // OneWire.OneWire  (src/jswrap_onewire.c)
+      JsVar *pin = jspParseSingleFunction();
+      JsVar *_r = jswrap_onewire_constructor(jshGetPinFromVarAndUnLock(pin));
+      return _r;
+    } else if (CMP4(name[0],'N','a','N','\0')) {
+      // NaN  (libs/jswrap_math.c)
+      jspParseVariableName();
+      JsVar *_r = jsvNewFromFloat(NAN);
+      return _r;
+    } else if (name[0]=='p') {
+      if (CMP4(name[1],'a','r','s','e')) {
+        if (CMP4(name[5],'I','n','t','\0')) {
+          // parseInt  (src/jswrap_functions.c)
+          JsVar *string, *radix;
+          jspParseFunction(0, &string, &radix, 0, 0);
+          JsVar *_r = jswrap_parseInt(string, radix);
+          jsvUnLock(string);
+          jsvUnLock(radix);
+          return _r;
+        } else if (CMP4(name[5],'F','l','o','a') && CMP2(name[9],'t','\0')) {
+          // parseFloat  (src/jswrap_functions.c)
+          JsVar *string = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromFloat(jswrap_parseFloat(string));
+          jsvUnLock(string);
+          return _r;
+        }
+      } else if (CMP4(name[1],'i','n','M','o') && CMP3(name[5],'d','e','\0')) {
+        // pinMode  (src/jswrap_io.c)
+        JsVar *pin, *mode;
+        jspParseFunction(0, &pin, &mode, 0, 0);
+        jswrap_io_pinMode(jshGetPinFromVarAndUnLock(pin), mode);
+        jsvUnLock(mode);
+        return 0;
+      } else if (CMP4(name[1],'r','i','n','t') && name[5]=='\0') {
+        // print  (src/jswrap_interactive.c)
+        JsVar *text = jspParseFunctionAsArray();
+        if (!text) return 0; // if parse error
+        jswrap_interface_print(text);
+        jsvUnLock(text);
+        return 0;
+      } else if (CMP3(name[1],'e','e','k')) {
+        if (CMP3(name[4],'1','6','\0')) {
+          // peek16  (src/jswrap_io.c)
+          JsVar *addr = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromInteger((JsVarInt)*(unsigned short*)jsvGetInteger(addr));
+          jsvUnLock(addr);
+          return _r;
+        } else if (CMP2(name[4],'8','\0')) {
+          // peek8  (src/jswrap_io.c)
+          JsVar *addr = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromInteger((JsVarInt)*(unsigned char*)jsvGetInteger(addr));
+          jsvUnLock(addr);
+          return _r;
+        } else if (CMP3(name[4],'3','2','\0')) {
+          // peek32  (src/jswrap_io.c)
+          JsVar *addr = jspParseSingleFunction();
+          JsVar *_r = jsvNewFromInteger((JsVarInt)*(unsigned int*)jsvGetInteger(addr));
+          jsvUnLock(addr);
+          return _r;
+        }
+      } else if (CMP3(name[1],'o','k','e')) {
+        if (CMP3(name[4],'1','6','\0')) {
+          // poke16  (src/jswrap_io.c)
+          JsVar *addr, *value;
+          jspParseFunction(0, &addr, &value, 0, 0);
+          (*(unsigned short*)jsvGetInteger(addr)) = (unsigned short)jsvGetInteger(value);
+          jsvUnLock(addr);
+          jsvUnLock(value);
+          return 0;
+        } else if (CMP2(name[4],'8','\0')) {
+          // poke8  (src/jswrap_io.c)
+          JsVar *addr, *value;
+          jspParseFunction(0, &addr, &value, 0, 0);
+          (*(unsigned char*)jsvGetInteger(addr)) = (unsigned char)jsvGetInteger(value);
+          jsvUnLock(addr);
+          jsvUnLock(value);
+          return 0;
+        } else if (CMP3(name[4],'3','2','\0')) {
+          // poke32  (src/jswrap_io.c)
+          JsVar *addr, *value;
+          jspParseFunction(0, &addr, &value, 0, 0);
+          (*(unsigned int*)jsvGetInteger(addr)) = (unsigned int)jsvGetInteger(value);
+          jsvUnLock(addr);
+          jsvUnLock(value);
+          return 0;
+        }
+      }
+    } else if (name[0]=='s') {
+      if (CMP4(name[1],'a','v','e','\0')) {
+        // save  (src/jswrap_interactive.c)
+        jspParseEmptyFunction();
+        jsiSetTodo(TODO_FLASH_SAVE);
+        return 0;
+      } else if (CMP2(name[1],'e','t')) {
+        if (CMP4(name[3],'B','u','s','y') && CMP4(name[7],'I','n','d','i') && CMP4(name[11],'c','a','t','o') && CMP2(name[15],'r','\0')) {
+          // setBusyIndicator  (src/jswrap_interactive.c)
+          JsVar *pin = jspParseSingleFunction();
+          jswrap_interface_setBusyIndicator(pin);
+          jsvUnLock(pin);
+          return 0;
+        } else if (CMP4(name[3],'D','e','e','p') && CMP4(name[7],'S','l','e','e') && CMP2(name[11],'p','\0')) {
+          // setDeepSleep  (src/jswrap_interactive.c)
+          JsVar *sleep = jspParseSingleFunction();
+          jswrap_interface_setDeepSleep(jsvGetBoolAndUnLock(sleep));
+          return 0;
+        } else if (CMP4(name[3],'I','n','t','e') && CMP4(name[7],'r','v','a','l') && name[11]=='\0') {
+          // setInterval  (src/jswrap_io.c)
+          JsVar *function, *timeout;
+          jspParseFunction(0|JSP_NOSKIP_A, &function, &timeout, 0, 0);
+          JsVar *_r = jswrap_interface_setInterval(function, jsvGetFloatAndUnLock(timeout));
+          jsvUnLock(function);
+          return _r;
+        } else if (CMP4(name[3],'S','l','e','e') && CMP4(name[7],'p','I','n','d') && CMP4(name[11],'i','c','a','t') && CMP3(name[15],'o','r','\0')) {
+          // setSleepIndicator  (src/jswrap_interactive.c)
+          JsVar *pin = jspParseSingleFunction();
+          jswrap_interface_setSleepIndicator(pin);
+          jsvUnLock(pin);
+          return 0;
+        } else if (CMP4(name[3],'T','i','m','e') && CMP4(name[7],'o','u','t','\0')) {
+          // setTimeout  (src/jswrap_io.c)
+          JsVar *function, *timeout;
+          jspParseFunction(0|JSP_NOSKIP_A, &function, &timeout, 0, 0);
+          JsVar *_r = jswrap_interface_setTimeout(function, jsvGetFloatAndUnLock(timeout));
+          jsvUnLock(function);
+          return _r;
+        } else if (CMP4(name[3],'W','a','t','c') && CMP2(name[7],'h','\0')) {
+          // setWatch  (src/jswrap_io.c)
+          JsVar *function, *pin, *options;
+          jspParseFunction(0|JSP_NOSKIP_A, &function, &pin, &options, 0);
+          JsVar *_r = jswrap_interface_setWatch(function, jshGetPinFromVarAndUnLock(pin), options);
+          jsvUnLock(function);
+          jsvUnLock(options);
+          return _r;
+        }
+      }
+    } else if (CMP2(name[0],'r','e')) {
+      if (CMP4(name[2],'q','u','i','r') && CMP2(name[6],'e','\0')) {
+        // require  (src/jswrap_modules.c)
+        JsVar *moduleName = jspParseSingleFunction();
+        JsVar *_r = jswrap_require(moduleName);
+        jsvUnLock(moduleName);
+        return _r;
+      } else if (CMP4(name[2],'s','e','t','\0')) {
+        // reset  (src/jswrap_interactive.c)
+        jspParseEmptyFunction();
+        jsiSetTodo(TODO_RESET);
+        return 0;
+      }
+    } else if (name[0]=='U') {
+      if (CMP3(name[1],'i','n','t')) {
+        if (CMP4(name[4],'1','6','A','r') && CMP4(name[8],'r','a','y','\0')) {
+          // Uint16Array.Uint16Array  (src/jswrap_arraybuffer.c)
+          JsVar *arr, *byteOffset, *length;
+          jspParseFunction(0, &arr, &byteOffset, &length, 0);
+          JsVar *_r = jswrap_typedarray_constructor(ARRAYBUFFERVIEW_UINT16, arr, jsvGetInteger(byteOffset), jsvGetInteger(length));
+          jsvUnLock(arr);
+          jsvUnLock(byteOffset);
+          jsvUnLock(length);
+          return _r;
+        } else if (CMP4(name[4],'8','A','r','r') && CMP3(name[8],'a','y','\0')) {
+          // Uint8Array.Uint8Array  (src/jswrap_arraybuffer.c)
+          JsVar *arr, *byteOffset, *length;
+          jspParseFunction(0, &arr, &byteOffset, &length, 0);
+          JsVar *_r = jswrap_typedarray_constructor(ARRAYBUFFERVIEW_UINT8, arr, jsvGetInteger(byteOffset), jsvGetInteger(length));
+          jsvUnLock(arr);
+          jsvUnLock(byteOffset);
+          jsvUnLock(length);
+          return _r;
+        } else if (CMP4(name[4],'3','2','A','r') && CMP4(name[8],'r','a','y','\0')) {
+          // Uint32Array.Uint32Array  (src/jswrap_arraybuffer.c)
+          JsVar *arr, *byteOffset, *length;
+          jspParseFunction(0, &arr, &byteOffset, &length, 0);
+          JsVar *_r = jswrap_typedarray_constructor(ARRAYBUFFERVIEW_UINT32, arr, jsvGetInteger(byteOffset), jsvGetInteger(length));
+          jsvUnLock(arr);
+          jsvUnLock(byteOffset);
+          jsvUnLock(length);
+          return _r;
+        }
+      } else if (CMP3(name[1],'S','B','\0')) {
+        // Object USB  (src/jswrap_serial.c)
+        #if defined(USB)
+        jspParseVariableName();
+        return jspNewObject(jsiGetParser(), "USB", "Serial");
+        #endif //defined(USB)
+      }
+    } else if (CMP4(name[0],'t','r','a','c') && CMP2(name[4],'e','\0')) {
+      // trace  (src/jswrap_interactive.c)
+      JsVar *root;
+      jspParseFunction(0|JSP_NOSKIP_A, &root, 0, 0, 0);
+      jswrap_interface_trace(root);
+      jsvUnLock(root);
+      return 0;
+    } else if (name[0]=='I') {
+      if (CMP2(name[1],'2','C')) {
+        if (CMP2(name[3],'1','\0')) {
+          // Object I2C1  (src/jswrap_spi_i2c.c)
+          #if I2CS>=1
+          jspParseVariableName();
+          return jspNewObject(jsiGetParser(), "I2C1", "I2C");
+          #endif //I2CS>=1
+        } else if (CMP2(name[3],'3','\0')) {
+          // Object I2C3  (src/jswrap_spi_i2c.c)
+          #if I2CS>=3
+          jspParseVariableName();
+          return jspNewObject(jsiGetParser(), "I2C3", "I2C");
+          #endif //I2CS>=3
+        } else if (CMP2(name[3],'2','\0')) {
+          // Object I2C2  (src/jswrap_spi_i2c.c)
+          #if I2CS>=2
+          jspParseVariableName();
+          return jspNewObject(jsiGetParser(), "I2C2", "I2C");
+          #endif //I2CS>=2
+        }
+      } else if (CMP2(name[1],'n','t')) {
+        if (CMP4(name[3],'1','6','A','r') && CMP4(name[7],'r','a','y','\0')) {
+          // Int16Array.Int16Array  (src/jswrap_arraybuffer.c)
+          JsVar *arr, *byteOffset, *length;
+          jspParseFunction(0, &arr, &byteOffset, &length, 0);
+          JsVar *_r = jswrap_typedarray_constructor(ARRAYBUFFERVIEW_INT16, arr, jsvGetInteger(byteOffset), jsvGetInteger(length));
+          jsvUnLock(arr);
+          jsvUnLock(byteOffset);
+          jsvUnLock(length);
+          return _r;
+        } else if (CMP4(name[3],'8','A','r','r') && CMP3(name[7],'a','y','\0')) {
+          // Int8Array.Int8Array  (src/jswrap_arraybuffer.c)
+          JsVar *arr, *byteOffset, *length;
+          jspParseFunction(0, &arr, &byteOffset, &length, 0);
+          JsVar *_r = jswrap_typedarray_constructor(ARRAYBUFFERVIEW_INT8, arr, jsvGetInteger(byteOffset), jsvGetInteger(length));
+          jsvUnLock(arr);
+          jsvUnLock(byteOffset);
+          jsvUnLock(length);
+          return _r;
+        } else if (CMP4(name[3],'3','2','A','r') && CMP4(name[7],'r','a','y','\0')) {
+          // Int32Array.Int32Array  (src/jswrap_arraybuffer.c)
+          JsVar *arr, *byteOffset, *length;
+          jspParseFunction(0, &arr, &byteOffset, &length, 0);
+          JsVar *_r = jswrap_typedarray_constructor(ARRAYBUFFERVIEW_INT32, arr, jsvGetInteger(byteOffset), jsvGetInteger(length));
+          jsvUnLock(arr);
+          jsvUnLock(byteOffset);
+          jsvUnLock(length);
+          return _r;
+        }
+      }
+    }
+  }
+  return JSW_HANDLEFUNCTIONCALL_UNHANDLED;
+}
+
+
+bool jswIsBuiltInObject(const char *name) {
+  return
+strcmp(name, "Pin")==0 ||
+    strcmp(name, "Modules")==0 ||
+    strcmp(name, "process")==0 ||
+    strcmp(name, "console")==0 ||
+    strcmp(name, "JSON")==0 ||
+    strcmp(name, "Hardware")==0 ||
+    strcmp(name, "Object")==0 ||
+    strcmp(name, "Function")==0 ||
+    strcmp(name, "Integer")==0 ||
+    strcmp(name, "Double")==0 ||
+    strcmp(name, "String")==0 ||
+    strcmp(name, "Array")==0 ||
+    strcmp(name, "ArrayBuffer")==0 ||
+    strcmp(name, "ArrayBufferView")==0 ||
+    strcmp(name, "Uint8Array")==0 ||
+    strcmp(name, "Int8Array")==0 ||
+    strcmp(name, "Uint16Array")==0 ||
+    strcmp(name, "Int16Array")==0 ||
+    strcmp(name, "Uint32Array")==0 ||
+    strcmp(name, "Int32Array")==0 ||
+    strcmp(name, "Float32Array")==0 ||
+    strcmp(name, "Float64Array")==0 ||
+    strcmp(name, "Serial")==0 ||
+    strcmp(name, "SPI")==0 ||
+    strcmp(name, "I2C")==0 ||
+    strcmp(name, "OneWire")==0 ||
+    strcmp(name, "Math")==0;
+}
+
+
+bool jswIsBuiltInLibrary(const char *name) {
+  return
+;
+}
+
+
+/** Given a variable, return the basic object name of it */
+const char *jswGetBasicObjectName(JsVar *var) {
+  if (jsvIsFunction(var)) return "Function";
+  if (jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_INT32) return "Int32Array";
+  if (jsvIsString(var)) return "String";
+  if (jsvIsPin(var)) return "Pin";
+  if (jsvIsFloat(var)) return "Double";
+  if (jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_ARRAYBUFFER) return "ArrayBuffer";
+  if (jsvIsObject(var)) return "Object";
+  if (jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_UINT8) return "Uint8Array";
+  if (jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_UINT16) return "Uint16Array";
+  if (jsvIsRoot(var)) return "Hardware";
+  if (jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_FLOAT64) return "Float64Array";
+  if (jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_INT8) return "Int8Array";
+  if (jsvIsInt(var)) return "Integer";
+  if (jsvIsArray(var)) return "Array";
+  if (jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_INT16) return "Int16Array";
+  if (jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_FLOAT32) return "Float32Array";
+  if (jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_UINT32) return "Uint32Array";
+  return 0;
+}
+
+
+/** Given the name of a Basic Object, eg, Uint8Array, String, etc. Return the prototype object's name - or 0. */
+const char *jswGetBasicObjectPrototypeName(const char *objectName) {
+  if (!strcmp(objectName, "Uint8Array")) return "ArrayBufferView";
+  if (!strcmp(objectName, "Int8Array")) return "ArrayBufferView";
+  if (!strcmp(objectName, "Uint16Array")) return "ArrayBufferView";
+  if (!strcmp(objectName, "Int16Array")) return "ArrayBufferView";
+  if (!strcmp(objectName, "Uint32Array")) return "ArrayBufferView";
+  if (!strcmp(objectName, "Int32Array")) return "ArrayBufferView";
+  if (!strcmp(objectName, "Float32Array")) return "ArrayBufferView";
+  if (!strcmp(objectName, "Float64Array")) return "ArrayBufferView";
+  return strcmp(objectName,"Object") ? "Object" : 0;
+}
+
+

+ 53 - 0
components/external/espruino/gen/platform_config.h

@@ -0,0 +1,53 @@
+
+// Automatically generated header file for LINUX
+// Generated by scripts/build_platform_config.py
+
+#ifndef _PLATFORM_CONFIG_H
+#define _PLATFORM_CONFIG_H
+#include <rtthread.h>
+
+#define PC_BOARD_ID "LINUX"
+#define PC_BOARD_CHIP "LINUX"
+#define PC_BOARD_CHIP_FAMILY "LINUX"
+
+
+
+// SYSTICK is the counter that counts up and that we use as the real-time clock
+// The smaller this is, the longer we spend in interrupts, but also the more we can sleep!
+#define SYSTICK_RANGE 0x1000000 // the Maximum (it is a 24 bit counter) - on Olimexino this is about 0.6 sec
+#define SYSTICKS_BEFORE_USB_DISCONNECT 2
+
+#define DEFAULT_BUSY_PIN_INDICATOR (Pin)-1 // no indicator
+#define DEFAULT_SLEEP_PIN_INDICATOR (Pin)-1 // no indicator
+
+// When to send the message that the IO buffer is getting full
+#define IOBUFFER_XOFF ((TXBUFFERMASK)*6/8)
+// When to send the message that we can start receiving again
+#define IOBUFFER_XON ((TXBUFFERMASK)*3/8)
+
+
+
+#define RAM_TOTAL (-1*1024)
+#define FLASH_TOTAL (-1*1024)
+
+#define RESIZABLE_JSVARS // Allocate variables in blocks using malloc
+
+#define USARTS                          0
+#define SPIS                            1
+#define I2CS                            0
+#define ADCS                            0
+#define DACS                            0
+
+#define DEFAULT_CONSOLE_DEVICE              EV_USBSERIAL
+
+#define IOBUFFERMASK 31 // (max 255) amount of items in event buffer - events take ~9 bytes each
+#define TXBUFFERMASK 31 // (max 255)
+
+
+// definition to avoid compilation when Pin/platform config is not defined
+#define IS_PIN_USED_INTERNALLY(PIN) ((false))
+#define IS_PIN_A_LED(PIN) ((false))
+#define IS_PIN_A_BUTTON(PIN) ((false))
+
+#endif // _PLATFORM_CONFIG_H
+

+ 233 - 0
components/external/espruino/libs/jswrap_math.c

@@ -0,0 +1,233 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * This file is designed to be parsed during the build process
+ *
+ * Contains built-in functions for Maths
+ * ----------------------------------------------------------------------------
+ */
+#include "jswrap_math.h"
+
+/*JSON{ "type":"class",
+        "class" : "Math",
+        "description" : "This is a standard JavaScript class that contains useful Maths routines"
+}*/
+
+// -------------------------------------------------------------------- Integer
+/*JSON{ "type":"staticmethod",
+         "class" : "Integer", "name" : "valueOf",
+         "generate" : "jswrap_integer_valueOf",
+         "description" : "Given a string containing a single character, return the numeric value of it",
+         "params" : [ [ "character" ,"JsVar", "A string containing a single character"] ],
+         "return" : ["int", "The integer value of char"]
+}*/
+JsVarInt jswrap_integer_valueOf(JsVar *v) {
+  if (!jsvIsString(v) || jsvGetStringLength(v)!=1)
+    return 0;
+  return (int)v->varData.str[0];
+}
+/*JSON{ "type":"variable", "name" : "NaN",
+         "generate_full" : "NAN",
+         "return" : ["float", "Not a  Number"]
+}*/
+ // -------------------------------------------------------------------- Double
+/*JSON{ "type":"staticmethod",
+         "class" : "Double", "name" : "doubleToIntBits",
+         "generate_full" : "*(JsVarInt*)&x",
+         "description" : " Convert the floating point value given into an integer representing the bits contained in it",
+         "params" : [ [ "x", "float", "A floating point number"] ],
+         "return" : ["int", "The integer representation of x"]
+}*/
+// -------------------------------------------------------------------- Math
+/*JSON{ "type":"staticproperty",
+         "class" : "Math", "name" : "E",
+         "generate_full" : "2.71828182846",
+         "return" : ["float", "The value of E - 2.71828182846"]
+}*/
+/*JSON{ "type":"staticproperty",
+         "class" : "Math", "name" : "PI",
+         "generate_full" : "3.14159265359",
+         "return" : ["float", "The value of PI - 3.14159265359"]
+}*/
+/*JSON{ "type":"staticmethod",
+         "class" : "Math", "name" : "abs",
+         "generate" : "jswrap_math_abs",
+         "params" : [ [ "x", "float", "A floating point value"] ],
+         "return" : ["float", "The absolute value of x (eg, ```Math.abs(2)==2```, but also ```Math.abs(-2)==2```)"]
+}*/
+JsVarFloat jswrap_math_abs(JsVarFloat x) {
+  return (x<0)?-x:x;
+}
+/*JSON{ "type":"staticmethod",
+         "class" : "Math", "name" : "acos",
+         "generate" : "acos",
+         "params" : [ [ "x", "float", "The value to get the arc cosine of"] ],
+         "return" : ["float", "The arc cosine of x, between 0 and PI"]
+}*/
+/*JSON{ "type":"staticmethod",
+         "class" : "Math", "name" : "asin",
+         "generate" : "asin",
+         "params" : [ [ "x", "float", "The value to get the arc sine of"] ],
+         "return" : ["float", "The arc sine of x, between -PI/2 and PI/2"]
+}*/
+/*JSON{ "type":"staticmethod",
+         "class" : "Math", "name" : "atan",
+         "generate" : "atan",
+         "params" : [ [ "x", "float", "The value to get the arc tangent  of"] ],
+         "return" : ["float", "The arc tangent of x, between -PI/2 and PI/2"]
+}*/
+/*JSON{ "type":"staticmethod",
+        "class" : "Math", "name" : "atan2",
+         "generate" : "atan2",
+         "params" : [ [ "y", "float", "The Y-part of the angle to get the arc tangent of"],
+                      [ "x", "float", "The X-part of the angle to get the arc tangent of"] ],
+         "return" : ["float", "The arctangent of Y/X, between -PI and PI"]
+}*/
+
+/* we use sin here, not cos, to try and save a bit of code space */
+/*JSON{ "type":"staticmethod",
+         "class" : "Math", "name" : "cos",
+         "generate_full" : "sin(jsvGetFloat(theta) + (3.14159265359/2.0))",
+         "params" : [ [ "theta", "float", "The angle to get the cosine of"] ],
+         "return" : ["float", "The cosine of theta"]
+}*/
+
+#define DBL_MAX    1.7976931348623157E+308
+
+double fs_fmod(double x, double y)
+{
+  double a, b;
+  const double c = x;
+
+  if (0 > c) {
+    x = -x;
+  }
+  if (0 > y) {
+    y = -y;
+  }
+  if (y != 0 && DBL_MAX >= y && DBL_MAX >= x) {
+    while (x >= y) {
+      a = x / 2;
+      b = y;
+      while (a >= b) {
+        b *= 2;
+      }
+      x -= b;
+    }
+  } else {
+    x = 0;
+  }
+  return 0 > c ? -x : x;
+}
+
+double jswrap_math_pow(double x, double y)
+{
+  double p;
+  if (0 > x && fs_fmod(y, 1) == 0) {
+    if (fs_fmod(y, 2) == 0) {
+      p = exp(log(-x) * y);
+    } else {
+      p = -exp(log(-x) * y);
+    }
+  } else {
+    if (x != 0 || 0 >= y) {
+      p = exp(log( x) * y);
+    } else {
+      p = 0;
+    }
+  }
+  return p;
+}
+
+
+/*JSON{ "type":"staticmethod",
+         "class" : "Math", "name" : "pow",
+         "generate" : "jswrap_math_pow",
+         "params" : [ [ "x", "float", "The value to raise to the power"],
+                      [ "y", "float", "The power x should be raised to"] ],
+         "return" : ["float", "x raised to the power y (x^y)"]
+}*/
+/*JSON{ "type":"staticmethod",
+         "class" : "Math", "name" : "random",
+         "generate_full" : "(JsVarFloat)rand() / (JsVarFloat)RAND_MAX",
+         "return" : ["float", "A random number between 0 and 1"]
+}*/
+/*JSON{ "type":"staticmethod",
+         "class" : "Math", "name" : "round",
+         "generate" : "(JsVarInt)round",
+         "params" : [ [ "x", "float", "The value to round"] ],
+         "return" : ["int", "x, rounded to the nearest integer"]
+}*/
+/*JSON{ "type":"staticmethod",
+         "class" : "Math", "name" : "sin",
+         "generate" : "sin",
+         "params" : [ [ "theta", "float", "The angle to get the sine of"] ],
+         "return" : ["float", "The sine of theta"]
+}*/
+
+/* we could use the real sqrt - but re-use pow to save on code space */
+/*JSON{ "type":"staticmethod",
+         "class" : "Math", "name" : "sqrt",
+         "generate_full" : "jswrap_math_pow(jsvGetFloat(x),0.5)",
+         "params" : [ [ "x", "float", "The value to take the square root of"] ],
+         "return" : ["float", "The square root of x"]
+}*/
+
+/*JSON{ "type":"staticmethod",
+         "class" : "Math", "name" : "ceil",
+         "generate" : "ceil",
+         "params" : [ [ "x", "float", "The value to round up"] ],
+         "return" : ["float", "x, rounded upwards to the nearest integer"]
+}*/
+/*JSON{ "type":"staticmethod",
+         "class" : "Math", "name" : "floor",
+         "generate" : "floor",
+         "params" : [ [ "x", "float", "The value to round down"] ],
+         "return" : ["float", "x, rounded downwards to the nearest integer"]
+}*/
+
+/*JSON{ "type":"staticmethod",
+         "class" : "Math", "name" : "exp",
+         "generate" : "exp",
+         "params" : [ [ "x", "float", "The value raise E to the power of"] ],
+         "return" : ["float", "E^x"]
+}*/
+/*JSON{ "type":"staticmethod",
+         "class" : "Math", "name" : "log",
+         "generate" : "log",
+         "params" : [ [ "x", "float", "The value to take the logarithm (base E) root of"] ],
+         "return" : ["float", "The log (base E) of x"]
+}*/
+
+/*JSON{ "type":"staticmethod", "ifndef" : "SAVE_ON_FLASH",
+         "class" : "Math", "name" : "clip",
+         "generate" : "jswrap_math_clip",
+         "description" : "Clip a number to be between min and max (inclusive)",
+         "params" : [ [ "x", "float", "A floating point value to clip"],
+                      [ "min", "float", "The smallest the value should be"],
+                      [ "max", "float", "The largest the value should be"] ],
+         "return" : ["float", "The value of x, clipped so as not to be below min or above max."]
+}*/
+JsVarFloat jswrap_math_clip(JsVarFloat x, JsVarFloat min, JsVarFloat max) {
+  if (x<min) x=min;
+  if (x>max) x=max;
+  return x;
+}
+
+/*JSON{ "type":"staticmethod", "ifndef" : "SAVE_ON_FLASH",
+         "class" : "Math", "name" : "wrap",
+         "generate" : "wrapAround",
+         "description" : "Wrap a number around if it is less than 0 or greater than or equal to max. For instance you might do: ```Math.wrap(angleInDegrees, 360)```",
+         "params" : [ [ "x", "float", "A floating point value to wrap"],
+                      [ "max", "float", "The largest the value should be"] ],
+         "return" : ["float", "The value of x, wrapped so as not to be below min or above max."]
+}*/
+
+

+ 28 - 0
components/external/espruino/libs/jswrap_math.h

@@ -0,0 +1,28 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Contains built-in functions for Maths
+ * ----------------------------------------------------------------------------
+ */
+#include "jsutils.h"
+#include "jsvar.h"
+
+#ifdef ARM
+#include "mconf.h"
+#include "protos.h"
+#else
+#include <math.h>
+#endif
+
+
+JsVarInt jswrap_integer_valueOf(JsVar *v);
+JsVarFloat jswrap_math_abs(JsVarFloat x);
+double jswrap_math_pow(double x, double y);
+JsVarFloat jswrap_math_clip(JsVarFloat x, JsVarFloat min, JsVarFloat max);

+ 32 - 0
components/external/espruino/libs/math/README

@@ -0,0 +1,32 @@
+
+This suite of C language elementary functions offers support for
+not-a-number (NaN) and infinity rules, subnormal numbers, and minus
+zero as described by IEEE standard 754 and the Numerical C Extensions
+Group (NCEG).  For a variety of reasons, many computers cannot take
+advantage of these features.  You can disable any or all of them by
+removing the corresponding preprocessor macros.  Check the files
+mconf.h and const.c carefully to be sure they are appropriate for your
+system.
+
+
+
+------------------------------------------
+
+http://www.netlib.org/cephes/readme
+
+   Some software in this archive may be from the book _Methods and
+Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
+International, 1989) or from the Cephes Mathematical Library, a
+commercial product. In either event, it is copyrighted by the author.
+What you see here may be used freely but it comes with no support or
+guarantee.
+
+   The two known misprints in the book are repaired here in the
+source listings for the gamma function and the incomplete beta
+integral.
+
+
+   Stephen L. Moshier
+   moshier@na-net.ornl.gov
+
+

+ 167 - 0
components/external/espruino/libs/math/acosh.c

@@ -0,0 +1,167 @@
+/*							acosh.c
+ *
+ *	Inverse hyperbolic cosine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, acosh();
+ *
+ * y = acosh( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns inverse hyperbolic cosine of argument.
+ *
+ * If 1 <= x < 1.5, a rational approximation
+ *
+ *	sqrt(z) * P(z)/Q(z)
+ *
+ * where z = x-1, is used.  Otherwise,
+ *
+ * acosh(x)  =  log( x + sqrt( (x-1)(x+1) ).
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       1,3         30000       4.2e-17     1.1e-17
+ *    IEEE      1,3         30000       4.6e-16     8.7e-17
+ *
+ *
+ * ERROR MESSAGES:
+ *
+ *   message         condition      value returned
+ * acosh domain       |x| < 1            NAN
+ *
+ */
+
+/*							acosh.c	*/
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+
+/* acosh(z) = sqrt(x) * R(x), z = x + 1, interval 0 < x < 0.5 */
+
+#include "mconf.h"
+
+#ifdef UNK
+const static double P[] = {
+ 1.18801130533544501356E2,
+ 3.94726656571334401102E3,
+ 3.43989375926195455866E4,
+ 1.08102874834699867335E5,
+ 1.10855947270161294369E5
+};
+const static double Q[] = {
+/* 1.00000000000000000000E0,*/
+ 1.86145380837903397292E2,
+ 4.15352677227719831579E3,
+ 2.97683430363289370382E4,
+ 8.29725251988426222434E4,
+ 7.83869920495893927727E4
+};
+#endif
+
+#ifdef DEC
+static unsigned short P[] = {
+0041755,0115055,0144002,0146444,
+0043166,0132103,0155150,0150302,
+0044006,0057360,0003021,0162753,
+0044323,0021557,0175225,0056253,
+0044330,0101771,0040046,0006636
+};
+static unsigned short Q[] = {
+/*0040200,0000000,0000000,0000000,*/
+0042072,0022467,0126670,0041232,
+0043201,0146066,0152142,0034015,
+0043750,0110257,0121165,0026100,
+0044242,0007103,0034667,0033173,
+0044231,0014576,0175573,0017472
+};
+#endif
+
+#ifdef IBMPC
+static unsigned short P[] = {
+0x59a4,0xb900,0xb345,0x405d,
+0x1a18,0x7b4d,0xd688,0x40ae,
+0x3cbd,0x00c2,0xcbde,0x40e0,
+0xab95,0xff52,0x646d,0x40fa,
+0xc1b4,0x2804,0x107f,0x40fb
+};
+static unsigned short Q[] = {
+/*0x0000,0x0000,0x0000,0x3ff0,*/
+0x0853,0xf5b7,0x44a6,0x4067,
+0x4702,0xda8c,0x3986,0x40b0,
+0xa588,0xf44e,0x1215,0x40dd,
+0xe6cf,0x6736,0x41c8,0x40f4,
+0x63e7,0xdf6f,0x232f,0x40f3
+};
+#endif
+
+#ifdef MIEEE
+static unsigned short P[] = {
+0x405d,0xb345,0xb900,0x59a4,
+0x40ae,0xd688,0x7b4d,0x1a18,
+0x40e0,0xcbde,0x00c2,0x3cbd,
+0x40fa,0x646d,0xff52,0xab95,
+0x40fb,0x107f,0x2804,0xc1b4
+};
+static unsigned short Q[] = {
+0x4067,0x44a6,0xf5b7,0x0853,
+0x40b0,0x3986,0xda8c,0x4702,
+0x40dd,0x1215,0xf44e,0xa588,
+0x40f4,0x41c8,0x6736,0xe6cf,
+0x40f3,0x232f,0xdf6f,0x63e7,
+};
+#endif
+
+#ifdef ANSIPROT
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern double log ( double );
+extern double sqrt ( double );
+#else
+double log(), sqrt(), polevl(), p1evl();
+#endif
+extern double LOGE2, INFINITY, NAN;
+
+double acosh(x)
+double x;
+{
+double a, z;
+
+if( x < 1.0 )
+	{
+	mtherr( "acosh", DOMAIN );
+	return(NAN);
+	}
+
+if( x > 1.0e8 )
+	{
+#ifdef INFINITIES
+	if( x == INFINITY )
+		return( INFINITY );
+#endif
+	return( log(x) + LOGE2 );
+	}
+
+z = x - 1.0;
+
+if( z < 0.5 )
+	{
+	a = sqrt(z) * (polevl(z, P, 4) / p1evl(z, Q, 5) );
+	return( a );
+	}
+
+a = sqrt( z*(x+1.0) );
+return( log(x + a) );
+}

+ 324 - 0
components/external/espruino/libs/math/asin.c

@@ -0,0 +1,324 @@
+/*							asin.c
+ *
+ *	Inverse circular sine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, asin();
+ *
+ * y = asin( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns radian angle between -pi/2 and +pi/2 whose sine is x.
+ *
+ * A rational function of the form x + x**3 P(x**2)/Q(x**2)
+ * is used for |x| in the interval [0, 0.5].  If |x| > 0.5 it is
+ * transformed by the identity
+ *
+ *    asin(x) = pi/2 - 2 asin( sqrt( (1-x)/2 ) ).
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC      -1, 1        40000       2.6e-17     7.1e-18
+ *    IEEE     -1, 1        10^6        1.9e-16     5.4e-17
+ *
+ *
+ * ERROR MESSAGES:
+ *
+ *   message         condition      value returned
+ * asin domain        |x| > 1           NAN
+ *
+ */
+/*							acos()
+ *
+ *	Inverse circular cosine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, acos();
+ *
+ * y = acos( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns radian angle between 0 and pi whose cosine
+ * is x.
+ *
+ * Analytically, acos(x) = pi/2 - asin(x).  However if |x| is
+ * near 1, there is cancellation error in subtracting asin(x)
+ * from pi/2.  Hence if x < -0.5,
+ *
+ *    acos(x) =	 pi - 2.0 * asin( sqrt((1+x)/2) );
+ *
+ * or if x > +0.5,
+ *
+ *    acos(x) =	 2.0 * asin(  sqrt((1-x)/2) ).
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -1, 1       50000       3.3e-17     8.2e-18
+ *    IEEE      -1, 1       10^6        2.2e-16     6.5e-17
+ *
+ *
+ * ERROR MESSAGES:
+ *
+ *   message         condition      value returned
+ * asin domain        |x| > 1           NAN
+ */
+
+/*							asin.c	*/
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+#include "mconf.h"
+
+/* arcsin(x)  =  x + x^3 P(x^2)/Q(x^2)
+   0 <= x <= 0.625
+   Peak relative error = 1.2e-18  */
+#if UNK
+const static double P[6] = {
+ 4.253011369004428248960E-3,
+-6.019598008014123785661E-1,
+ 5.444622390564711410273E0,
+-1.626247967210700244449E1,
+ 1.956261983317594739197E1,
+-8.198089802484824371615E0,
+};
+const static double Q[5] = {
+/* 1.000000000000000000000E0, */
+-1.474091372988853791896E1,
+ 7.049610280856842141659E1,
+-1.471791292232726029859E2,
+ 1.395105614657485689735E2,
+-4.918853881490881290097E1,
+};
+#endif
+#if DEC
+static short P[24] = {
+0036213,0056330,0057244,0053234,
+0140032,0015011,0114762,0160255,
+0040656,0035130,0136121,0067313,
+0141202,0014616,0170474,0101731,
+0041234,0100076,0151674,0111310,
+0141003,0025540,0033165,0077246,
+};
+static short Q[20] = {
+/* 0040200,0000000,0000000,0000000, */
+0141153,0155310,0055360,0072530,
+0041614,0177001,0027764,0101237,
+0142023,0026733,0064653,0133266,
+0042013,0101264,0023775,0176351,
+0141504,0140420,0050660,0036543,
+};
+#endif
+#if IBMPC
+static short P[24] = {
+0x8ad3,0x0bd4,0x6b9b,0x3f71,
+0x5c16,0x333e,0x4341,0xbfe3,
+0x2dd9,0x178a,0xc74b,0x4015,
+0x907b,0xde27,0x4331,0xc030,
+0x9259,0xda77,0x9007,0x4033,
+0xafd5,0x06ce,0x656c,0xc020,
+};
+static short Q[20] = {
+/* 0x0000,0x0000,0x0000,0x3ff0, */
+0x0eab,0x0b5e,0x7b59,0xc02d,
+0x9054,0x25fe,0x9fc0,0x4051,
+0x76d7,0x6d35,0x65bb,0xc062,
+0xbf9d,0x84ff,0x7056,0x4061,
+0x07ac,0x0a36,0x9822,0xc048,
+};
+#endif
+#if MIEEE
+static short P[24] = {
+0x3f71,0x6b9b,0x0bd4,0x8ad3,
+0xbfe3,0x4341,0x333e,0x5c16,
+0x4015,0xc74b,0x178a,0x2dd9,
+0xc030,0x4331,0xde27,0x907b,
+0x4033,0x9007,0xda77,0x9259,
+0xc020,0x656c,0x06ce,0xafd5,
+};
+static short Q[20] = {
+/* 0x3ff0,0x0000,0x0000,0x0000, */
+0xc02d,0x7b59,0x0b5e,0x0eab,
+0x4051,0x9fc0,0x25fe,0x9054,
+0xc062,0x65bb,0x6d35,0x76d7,
+0x4061,0x7056,0x84ff,0xbf9d,
+0xc048,0x9822,0x0a36,0x07ac,
+};
+#endif
+
+/* arcsin(1-x) = pi/2 - sqrt(2x)(1+R(x))
+   0 <= x <= 0.5
+   Peak relative error = 4.2e-18  */
+#if UNK
+const static double R[5] = {
+ 2.967721961301243206100E-3,
+-5.634242780008963776856E-1,
+ 6.968710824104713396794E0,
+-2.556901049652824852289E1,
+ 2.853665548261061424989E1,
+};
+const static double S[4] = {
+/* 1.000000000000000000000E0, */
+-2.194779531642920639778E1,
+ 1.470656354026814941758E2,
+-3.838770957603691357202E2,
+ 3.424398657913078477438E2,
+};
+#endif
+#if DEC
+static short R[20] = {
+0036102,0077034,0142164,0174103,
+0140020,0036222,0147711,0044173,
+0040736,0177655,0153631,0171523,
+0141314,0106525,0060015,0055474,
+0041344,0045422,0003630,0040344,
+};
+static short S[16] = {
+/* 0040200,0000000,0000000,0000000, */
+0141257,0112425,0132772,0166136,
+0042023,0010315,0075523,0175020,
+0142277,0170104,0126203,0017563,
+0042253,0034115,0102662,0022757,
+};
+#endif
+#if IBMPC
+static short R[20] = {
+0x9f08,0x988e,0x4fc3,0x3f68,
+0x290f,0x59f9,0x0792,0xbfe2,
+0x3e6a,0xbaf3,0xdff5,0x401b,
+0xab68,0xac01,0x91aa,0xc039,
+0x081d,0x40f3,0x8962,0x403c,
+};
+static short S[16] = {
+/* 0x0000,0x0000,0x0000,0x3ff0, */
+0x5d8c,0xb6bf,0xf2a2,0xc035,
+0x7f42,0xaf6a,0x6219,0x4062,
+0x63ee,0x9590,0xfe08,0xc077,
+0x44be,0xb0b6,0x6709,0x4075,
+};
+#endif
+#if MIEEE
+static short R[20] = {
+0x3f68,0x4fc3,0x988e,0x9f08,
+0xbfe2,0x0792,0x59f9,0x290f,
+0x401b,0xdff5,0xbaf3,0x3e6a,
+0xc039,0x91aa,0xac01,0xab68,
+0x403c,0x8962,0x40f3,0x081d,
+};
+static short S[16] = {
+/* 0x3ff0,0x0000,0x0000,0x0000, */
+0xc035,0xf2a2,0xb6bf,0x5d8c,
+0x4062,0x6219,0xaf6a,0x7f42,
+0xc077,0xfe08,0x9590,0x63ee,
+0x4075,0x6709,0xb0b6,0x44be,
+};
+#endif
+
+/* pi/2 = PIO2 + MOREBITS.  */
+#ifdef DEC
+#define MOREBITS 5.721188726109831840122E-18
+#else
+#define MOREBITS 6.123233995736765886130E-17
+#endif
+
+#ifdef ANSIPROT
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern double sqrt ( double );
+double asin ( double );
+#else
+double sqrt(), polevl(), p1evl();
+double asin();
+#endif
+extern double PIO2, PIO4, NAN;
+
+double asin(x)
+double x;
+{
+double a, p, z, zz;
+short sign;
+
+if( x > 0 )
+	{
+	sign = 1;
+	a = x;
+	}
+else
+	{
+	sign = -1;
+	a = -x;
+	}
+
+if( a > 1.0 )
+	{
+	mtherr( "asin", DOMAIN );
+	return( NAN );
+	}
+
+if( a > 0.625 )
+	{
+	/* arcsin(1-x) = pi/2 - sqrt(2x)(1+R(x))  */
+	zz = 1.0 - a;
+	p = zz * polevl( zz, R, 4)/p1evl( zz, S, 4);
+	zz = sqrt(zz+zz);
+	z = PIO4 - zz;
+	zz = zz * p - MOREBITS;
+	z = z - zz;
+	z = z + PIO4;
+	}
+else
+	{
+	if( a < 1.0e-8 )
+		{
+		return(x);
+		}
+	zz = a * a;
+	z = zz * polevl( zz, P, 5)/p1evl( zz, Q, 5);
+	z = a * z + a;
+	}
+if( sign < 0 )
+	z = -z;
+return(z);
+}
+
+
+
+double acos(x)
+double x;
+{
+double z;
+
+if( (x < -1.0) || (x > 1.0) )
+	{
+	mtherr( "acos", DOMAIN );
+	return( NAN );
+	}
+if( x > 0.5 )
+	{
+	return( 2.0 * asin(  sqrt(0.5 - 0.5*x) ) );
+	}
+z = PIO4 - asin(x);
+z = z + MOREBITS;
+z = z + PIO4;
+return( z );
+}

+ 165 - 0
components/external/espruino/libs/math/asinh.c

@@ -0,0 +1,165 @@
+/*							asinh.c
+ *
+ *	Inverse hyperbolic sine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, asinh();
+ *
+ * y = asinh( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns inverse hyperbolic sine of argument.
+ *
+ * If |x| < 0.5, the function is approximated by a rational
+ * form  x + x**3 P(x)/Q(x).  Otherwise,
+ *
+ *     asinh(x) = log( x + sqrt(1 + x*x) ).
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC      -3,3         75000       4.6e-17     1.1e-17
+ *    IEEE     -1,1         30000       3.7e-16     7.8e-17
+ *    IEEE      1,3         30000       2.5e-16     6.7e-17
+ *
+ */
+
+/*						asinh.c	*/
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+
+#include "mconf.h"
+
+#ifdef UNK
+const static double P[] = {
+-4.33231683752342103572E-3,
+-5.91750212056387121207E-1,
+-4.37390226194356683570E0,
+-9.09030533308377316566E0,
+-5.56682227230859640450E0
+};
+const static double Q[] = {
+/* 1.00000000000000000000E0,*/
+ 1.28757002067426453537E1,
+ 4.86042483805291788324E1,
+ 6.95722521337257608734E1,
+ 3.34009336338516356383E1
+};
+#endif
+
+#ifdef DEC
+static unsigned short P[] = {
+0136215,0173033,0110410,0105475,
+0140027,0076361,0020056,0164520,
+0140613,0173401,0160136,0053142,
+0141021,0070744,0000503,0176261,
+0140662,0021550,0073106,0133351
+};
+static unsigned short Q[] = {
+/* 0040200,0000000,0000000,0000000,*/
+0041116,0001336,0034120,0173054,
+0041502,0065300,0013144,0021231,
+0041613,0022376,0035516,0153063,
+0041405,0115216,0054265,0004557
+};
+#endif
+
+#ifdef IBMPC
+static unsigned short P[] = {
+0x1168,0x7221,0xbec3,0xbf71,
+0xdd2a,0x2405,0xef9e,0xbfe2,
+0xcacc,0x3c0b,0x7ee0,0xc011,
+0x7f96,0x8028,0x2e3c,0xc022,
+0xd6dd,0x0ec8,0x446d,0xc016
+};
+static unsigned short Q[] = {
+/* 0x0000,0x0000,0x0000,0x3ff0,*/
+0x1ec5,0xc70a,0xc05b,0x4029,
+0x8453,0x02cc,0x4d58,0x4048,
+0xdac6,0xc769,0x649f,0x4051,
+0xa12e,0xcb16,0xb351,0x4040
+};
+#endif
+
+#ifdef MIEEE
+static unsigned short P[] = {
+0xbf71,0xbec3,0x7221,0x1168,
+0xbfe2,0xef9e,0x2405,0xdd2a,
+0xc011,0x7ee0,0x3c0b,0xcacc,
+0xc022,0x2e3c,0x8028,0x7f96,
+0xc016,0x446d,0x0ec8,0xd6dd
+};
+static unsigned short Q[] = {
+0x4029,0xc05b,0xc70a,0x1ec5,
+0x4048,0x4d58,0x02cc,0x8453,
+0x4051,0x649f,0xc769,0xdac6,
+0x4040,0xb351,0xcb16,0xa12e
+};
+#endif
+
+#ifdef ANSIPROT
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern double sqrt ( double );
+extern double log ( double );
+#else
+double log(), sqrt(), polevl(), p1evl();
+#endif
+extern double LOGE2, INFINITY;
+
+double asinh(xx)
+double xx;
+{
+double a, z, x;
+int sign;
+
+#ifdef MINUSZERO
+if( xx == 0.0 )
+  return(xx);
+#endif
+if( xx < 0.0 )
+	{
+	sign = -1;
+	x = -xx;
+	}
+else
+	{
+	sign = 1;
+	x = xx;
+	}
+
+if( x > 1.0e8 )
+	{
+#ifdef INFINITIES
+	  if( x == INFINITY )
+	    return(xx);
+#endif
+	return( sign * (log(x) + LOGE2) );
+	}
+
+z = x * x;
+if( x < 0.5 )
+	{
+	a = ( polevl(z, P, 4)/p1evl(z, Q, 4) ) * z;
+	a = a * x  +  x;
+	if( sign < 0 )
+		a = -a;
+	return(a);
+	}	
+
+a = sqrt( z + 1.0 );
+return( sign * log(x + a) );
+}

+ 393 - 0
components/external/espruino/libs/math/atan.c

@@ -0,0 +1,393 @@
+/*							atan.c
+ *
+ *	Inverse circular tangent
+ *      (arctangent)
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, atan();
+ *
+ * y = atan( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns radian angle between -pi/2 and +pi/2 whose tangent
+ * is x.
+ *
+ * Range reduction is from three intervals into the interval
+ * from zero to 0.66.  The approximant uses a rational
+ * function of degree 4/5 of the form x + x**3 P(x)/Q(x).
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -10, 10     50000       2.4e-17     8.3e-18
+ *    IEEE      -10, 10      10^6       1.8e-16     5.0e-17
+ *
+ */
+/*							atan2()
+ *
+ *	Quadrant correct inverse circular tangent
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, z, atan2();
+ *
+ * z = atan2( y, x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns radian angle whose tangent is y/x.
+ * Define compile time symbol ANSIC = 1 for ANSI standard,
+ * range -PI < z <= +PI, args (y,x); else ANSIC = 0 for range
+ * 0 to 2PI, args (x,y).
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE      -10, 10      10^6       2.5e-16     6.9e-17
+ * See atan.c.
+ *
+ */
+
+/*							atan.c */
+
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+
+#include "mconf.h"
+
+/* arctan(x)  = x + x^3 P(x^2)/Q(x^2)
+   0 <= x <= 0.66
+   Peak relative error = 2.6e-18  */
+#ifdef UNK
+const static double P[5] = {
+-8.750608600031904122785E-1,
+-1.615753718733365076637E1,
+-7.500855792314704667340E1,
+-1.228866684490136173410E2,
+-6.485021904942025371773E1,
+};
+const static double Q[5] = {
+/* 1.000000000000000000000E0, */
+ 2.485846490142306297962E1,
+ 1.650270098316988542046E2,
+ 4.328810604912902668951E2,
+ 4.853903996359136964868E2,
+ 1.945506571482613964425E2,
+};
+
+/* tan( 3*pi/8 ) */
+const static double T3P8 = 2.41421356237309504880;
+#endif
+
+#ifdef DEC
+static short P[20] = {
+0140140,0001775,0007671,0026242,
+0141201,0041242,0155534,0001715,
+0141626,0002141,0132100,0011625,
+0141765,0142771,0064055,0150453,
+0141601,0131517,0164507,0062164,
+};
+static short Q[20] = {
+/* 0040200,0000000,0000000,0000000, */
+0041306,0157042,0154243,0000742,
+0042045,0003352,0016707,0150452,
+0042330,0070306,0113425,0170730,
+0042362,0130770,0116602,0047520,
+0042102,0106367,0156753,0013541,
+};
+
+/* tan( 3*pi/8 ) = 2.41421356237309504880 */
+static unsigned short T3P8A[] = {040432,0101171,0114774,0167462,};
+#define T3P8 *(double *)T3P8A
+#endif
+
+#ifdef IBMPC
+static short P[20] = {
+0x2594,0xa1f7,0x007f,0xbfec,
+0x807a,0x5b6b,0x2854,0xc030,
+0x0273,0x3688,0xc08c,0xc052,
+0xba25,0x2d05,0xb8bf,0xc05e,
+0xec8e,0xfd28,0x3669,0xc050,
+};
+static short Q[20] = {
+/* 0x0000,0x0000,0x0000,0x3ff0, */
+0x603c,0x5b14,0xdbc4,0x4038,
+0xfa25,0x43b8,0xa0dd,0x4064,
+0xbe3b,0xd2e2,0x0e18,0x407b,
+0x49ea,0x13b0,0x563f,0x407e,
+0x62ec,0xfbbd,0x519e,0x4068,
+};
+
+/* tan( 3*pi/8 ) = 2.41421356237309504880 */
+static unsigned short T3P8A[] = {0x9de6,0x333f,0x504f,0x4003};
+#define T3P8 *(double *)T3P8A
+#endif
+
+#ifdef MIEEE
+static short P[20] = {
+0xbfec,0x007f,0xa1f7,0x2594,
+0xc030,0x2854,0x5b6b,0x807a,
+0xc052,0xc08c,0x3688,0x0273,
+0xc05e,0xb8bf,0x2d05,0xba25,
+0xc050,0x3669,0xfd28,0xec8e,
+};
+static short Q[20] = {
+/* 0x3ff0,0x0000,0x0000,0x0000, */
+0x4038,0xdbc4,0x5b14,0x603c,
+0x4064,0xa0dd,0x43b8,0xfa25,
+0x407b,0x0e18,0xd2e2,0xbe3b,
+0x407e,0x563f,0x13b0,0x49ea,
+0x4068,0x519e,0xfbbd,0x62ec,
+};
+
+/* tan( 3*pi/8 ) = 2.41421356237309504880 */
+static unsigned short T3P8A[] = {
+0x4003,0x504f,0x333f,0x9de6
+};
+#define T3P8 *(double *)T3P8A
+#endif
+
+#ifdef ANSIPROT
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern double atan ( double );
+extern double fabs ( double );
+extern int signbit ( double );
+extern int isnan ( double );
+#else
+double polevl(), p1evl(), atan(), fabs();
+int signbit(), isnan();
+#endif
+extern double PI, PIO2, PIO4, INFINITY, NEGZERO, MAXNUM;
+
+/* pi/2 = PIO2 + MOREBITS.  */
+#ifdef DEC
+#define MOREBITS 5.721188726109831840122E-18
+#else
+#define MOREBITS 6.123233995736765886130E-17
+#endif
+
+
+double atan(x)
+double x;
+{
+double y, z;
+short sign, flag;
+
+#ifdef MINUSZERO
+if( x == 0.0 )
+	return(x);
+#endif
+#ifdef INFINITIES
+if(x == INFINITY)
+	return(PIO2);
+if(x == -INFINITY)
+	return(-PIO2);
+#endif
+/* make argument positive and save the sign */
+sign = 1;
+if( x < 0.0 )
+	{
+	sign = -1;
+	x = -x;
+	}
+/* range reduction */
+flag = 0;
+if( x > T3P8 )
+	{
+	y = PIO2;
+	flag = 1;
+	x = -( 1.0/x );
+	}
+else if( x <= 0.66 )
+	{
+	y = 0.0;
+	}
+else
+	{
+	y = PIO4;
+	flag = 2;
+	x = (x-1.0)/(x+1.0);
+	}
+z = x * x;
+z = z * polevl( z, P, 4 ) / p1evl( z, Q, 5 );
+z = x * z + x;
+if( flag == 2 )
+	z += 0.5 * MOREBITS;
+else if( flag == 1 )
+	z += MOREBITS;
+y = y + z;
+if( sign < 0 )
+	y = -y;
+return(y);
+}
+
+/*							atan2	*/
+
+#ifdef ANSIC
+double atan2( y, x )
+#else
+double atan2( x, y )
+#endif
+double x, y;
+{
+double z, w;
+short code;
+
+code = 0;
+
+#ifdef NANS
+if( isnan(x) )
+	return(x);
+if( isnan(y) )
+	return(y);
+#endif
+#ifdef MINUSZERO
+if( y == 0.0 )
+	{
+	if( signbit(y) )
+		{
+		if( x > 0.0 )
+			z = y;
+		else if( x < 0.0 )
+			z = -PI;
+		else
+			{
+			if( signbit(x) )
+				z = -PI;
+			else
+				z = y;
+			}
+		}
+	else /* y is +0 */
+		{
+		if( x == 0.0 )
+			{
+			if( signbit(x) )
+				z = PI;
+			else
+				z = 0.0;
+			}
+		else if( x > 0.0 )
+			z = 0.0;
+		else
+			z = PI;
+		}
+	return z;
+	}
+if( x == 0.0 )
+	{
+	if( y > 0.0 )
+		z = PIO2;
+	else
+		z = -PIO2;
+	return z;
+	}
+#endif /* MINUSZERO */
+#ifdef INFINITIES
+if( x == INFINITY )
+	{
+	if( y == INFINITY )
+		z = 0.25 * PI;
+	else if( y == -INFINITY )
+		z = -0.25 * PI;
+	else if( y < 0.0 )
+		z = NEGZERO;
+	else
+		z = 0.0;
+	return z;
+	}
+if( x == -INFINITY )
+	{
+	if( y == INFINITY )
+		z = 0.75 * PI;
+	else if( y <= -INFINITY )
+		z = -0.75 * PI;
+	else if( y >= 0.0 )
+		z = PI;
+	else
+		z = -PI;
+	return z;
+	}
+if( y == INFINITY )
+	return( PIO2 );
+if( y == -INFINITY )
+	return( -PIO2 );
+#endif
+
+if( x < 0.0 )
+	code = 2;
+if( y < 0.0 )
+	code |= 1;
+
+#ifdef INFINITIES
+if( x == 0.0 )
+#else
+if( fabs(x) <= (fabs(y) / MAXNUM) )
+#endif
+	{
+	if( code & 1 )
+		{
+#if ANSIC
+		return( -PIO2 );
+#else
+		return( 3.0*PIO2 );
+#endif
+		}
+	if( y == 0.0 )
+		return( 0.0 );
+	return( PIO2 );
+	}
+
+if( y == 0.0 )
+	{
+	if( code & 2 )
+		return( PI );
+	return( 0.0 );
+	}
+
+
+switch( code )
+	{
+#if ANSIC
+	default:
+	case 0:
+	case 1: w = 0.0; break;
+	case 2: w = PI; break;
+	case 3: w = -PI; break;
+#else
+	default:
+	case 0: w = 0.0; break;
+	case 1: w = 2.0 * PI; break;
+	case 2:
+	case 3: w = PI; break;
+#endif
+	}
+
+z = w + atan( y/x );
+#ifdef MINUSZERO
+if( z == 0.0 && y < 0 )
+	z = NEGZERO;
+#endif
+return( z );
+}

+ 156 - 0
components/external/espruino/libs/math/atanh.c

@@ -0,0 +1,156 @@
+/*							atanh.c
+ *
+ *	Inverse hyperbolic tangent
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, atanh();
+ *
+ * y = atanh( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns inverse hyperbolic tangent of argument in the range
+ * MINLOG to MAXLOG.
+ *
+ * If |x| < 0.5, the rational form x + x**3 P(x)/Q(x) is
+ * employed.  Otherwise,
+ *        atanh(x) = 0.5 * log( (1+x)/(1-x) ).
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -1,1        50000       2.4e-17     6.4e-18
+ *    IEEE      -1,1        30000       1.9e-16     5.2e-17
+ *
+ */
+
+/*						atanh.c	*/
+
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright (C) 1987, 1995, 2000 by Stephen L. Moshier
+*/
+
+#include "mconf.h"
+
+#ifdef UNK
+const static double P[] = {
+-8.54074331929669305196E-1,
+ 1.20426861384072379242E1,
+-4.61252884198732692637E1,
+ 6.54566728676544377376E1,
+-3.09092539379866942570E1
+};
+const static double Q[] = {
+/* 1.00000000000000000000E0,*/
+-1.95638849376911654834E1,
+ 1.08938092147140262656E2,
+-2.49839401325893582852E2,
+ 2.52006675691344555838E2,
+-9.27277618139601130017E1
+};
+#endif
+#ifdef DEC
+static unsigned short P[] = {
+0140132,0122235,0105775,0130300,
+0041100,0127327,0124407,0034722,
+0141470,0100113,0115607,0130535,
+0041602,0164721,0003257,0013673,
+0141367,0043046,0166673,0045750
+};
+static unsigned short Q[] = {
+/*0040200,0000000,0000000,0000000,*/
+0141234,0101326,0015460,0134564,
+0041731,0160115,0116451,0032045,
+0142171,0153343,0000532,0167226,
+0042174,0000665,0077604,0000310,
+0141671,0072235,0031114,0074377
+};
+#endif
+
+#ifdef IBMPC
+static unsigned short P[] = {
+0xb618,0xb17f,0x5493,0xbfeb,
+0xe73a,0xf520,0x15da,0x4028,
+0xf62c,0x7370,0x1009,0xc047,
+0xe2f7,0x20d5,0x5d3a,0x4050,
+0x697d,0xddb7,0xe8c4,0xc03e
+};
+static unsigned short Q[] = {
+/*0x0000,0x0000,0x0000,0x3ff0,*/
+0x172f,0xc366,0x905a,0xc033,
+0x2685,0xb3a5,0x3c09,0x405b,
+0x5dd3,0x602b,0x3adc,0xc06f,
+0x8019,0xaff0,0x8036,0x406f,
+0x8f20,0xa649,0x2e93,0xc057
+};
+#endif
+
+#ifdef MIEEE
+static unsigned short P[] = {
+0xbfeb,0x5493,0xb17f,0xb618,
+0x4028,0x15da,0xf520,0xe73a,
+0xc047,0x1009,0x7370,0xf62c,
+0x4050,0x5d3a,0x20d5,0xe2f7,
+0xc03e,0xe8c4,0xddb7,0x697d
+};
+static unsigned short Q[] = {
+0xc033,0x905a,0xc366,0x172f,
+0x405b,0x3c09,0xb3a5,0x2685,
+0xc06f,0x3adc,0x602b,0x5dd3,
+0x406f,0x8036,0xaff0,0x8019,
+0xc057,0x2e93,0xa649,0x8f20
+};
+#endif
+
+#ifdef ANSIPROT
+extern double fabs ( double );
+extern double log ( double x );
+extern double polevl ( double x, void *P, int N );
+extern double p1evl ( double x, void *P, int N );
+#else
+double fabs(), log(), polevl(), p1evl();
+#endif
+extern double INFINITY, NAN;
+
+double atanh(x)
+double x;
+{
+double s, z;
+
+#ifdef MINUSZERO
+if( x == 0.0 )
+	return(x);
+#endif
+z = fabs(x);
+if( z >= 1.0 )
+	{
+	if( x == 1.0 )
+		return( INFINITY );
+	if( x == -1.0 )
+		return( -INFINITY );
+	mtherr( "atanh", DOMAIN );
+	return( NAN );
+	}
+
+if( z < 1.0e-7 )
+	return(x);
+
+if( z < 0.5 )
+	{
+	z = x * x;
+	s = x   +  x * z * (polevl(z, P, 4) / p1evl(z, Q, 5));
+	return(s);
+	}
+
+return( 0.5 * log((1.0+x)/(1.0-x)) );
+}

+ 142 - 0
components/external/espruino/libs/math/cbrt.c

@@ -0,0 +1,142 @@
+/*							cbrt.c
+ *
+ *	Cube root
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, cbrt();
+ *
+ * y = cbrt( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns the cube root of the argument, which may be negative.
+ *
+ * Range reduction involves determining the power of 2 of
+ * the argument.  A polynomial of degree 2 applied to the
+ * mantissa, and multiplication by the cube root of 1, 2, or 4
+ * approximates the root to within about 0.1%.  Then Newton's
+ * iteration is used three times to converge to an accurate
+ * result.
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC        -10,10     200000      1.8e-17     6.2e-18
+ *    IEEE       0,1e308     30000      1.5e-16     5.0e-17
+ *
+ */
+/*							cbrt.c  */
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1991, 2000 by Stephen L. Moshier
+*/
+
+
+#include "mconf.h"
+
+const static double CBRT2  = 1.2599210498948731647672;
+const static double CBRT4  = 1.5874010519681994747517;
+const static double CBRT2I = 0.79370052598409973737585;
+const static double CBRT4I = 0.62996052494743658238361;
+
+#ifdef ANSIPROT
+extern double frexp ( double, int * );
+extern double ldexp ( double, int );
+extern int isnan ( double );
+extern int isfinite ( double );
+#else
+double frexp(), ldexp();
+int isnan(), isfinite();
+#endif
+
+double cbrt(x)
+double x;
+{
+int e, rem, sign;
+double z;
+
+#ifdef NANS
+if( isnan(x) )
+  return x;
+#endif
+#ifdef INFINITIES
+if( !isfinite(x) )
+  return x;
+#endif
+if( x == 0 )
+	return( x );
+if( x > 0 )
+	sign = 1;
+else
+	{
+	sign = -1;
+	x = -x;
+	}
+
+z = x;
+/* extract power of 2, leaving
+ * mantissa between 0.5 and 1
+ */
+x = frexp( x, &e );
+
+/* Approximate cube root of number between .5 and 1,
+ * peak relative error = 9.2e-6
+ */
+x = (((-1.3466110473359520655053e-1  * x
+      + 5.4664601366395524503440e-1) * x
+      - 9.5438224771509446525043e-1) * x
+      + 1.1399983354717293273738e0 ) * x
+      + 4.0238979564544752126924e-1;
+
+/* exponent divided by 3 */
+if( e >= 0 )
+	{
+	rem = e;
+	e /= 3;
+	rem -= 3*e;
+	if( rem == 1 )
+		x *= CBRT2;
+	else if( rem == 2 )
+		x *= CBRT4;
+	}
+
+
+/* argument less than 1 */
+
+else
+	{
+	e = -e;
+	rem = e;
+	e /= 3;
+	rem -= 3*e;
+	if( rem == 1 )
+		x *= CBRT2I;
+	else if( rem == 2 )
+		x *= CBRT4I;
+	e = -e;
+	}
+
+/* multiply by power of 2 */
+x = ldexp( x, e );
+
+/* Newton iteration */
+x -= ( x - (z/(x*x)) )*0.33333333333333333333;
+#ifdef DEC
+x -= ( x - (z/(x*x)) )/3.0;
+#else
+x -= ( x - (z/(x*x)) )*0.33333333333333333333;
+#endif
+
+if( sign < 0 )
+	x = -x;
+return(x);
+}

+ 82 - 0
components/external/espruino/libs/math/chbevl.c

@@ -0,0 +1,82 @@
+/*							chbevl.c
+ *
+ *	Evaluate Chebyshev series
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * int N;
+ * double x, y, coef[N], chebevl();
+ *
+ * y = chbevl( x, coef, N );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Evaluates the series
+ *
+ *        N-1
+ *         - '
+ *  y  =   >   coef[i] T (x/2)
+ *         -            i
+ *        i=0
+ *
+ * of Chebyshev polynomials Ti at argument x/2.
+ *
+ * Coefficients are stored in reverse order, i.e. the zero
+ * order term is last in the array.  Note N is the number of
+ * coefficients, not the order.
+ *
+ * If coefficients are for the interval a to b, x must
+ * have been transformed to x -> 2(2x - b - a)/(b-a) before
+ * entering the routine.  This maps x from (a, b) to (-1, 1),
+ * over which the Chebyshev polynomials are defined.
+ *
+ * If the coefficients are for the inverted interval, in
+ * which (a, b) is mapped to (1/b, 1/a), the transformation
+ * required is x -> 2(2ab/x - b - a)/(b-a).  If b is infinity,
+ * this becomes x -> 4a/x - 1.
+ *
+ *
+ *
+ * SPEED:
+ *
+ * Taking advantage of the recurrence properties of the
+ * Chebyshev polynomials, the routine requires one more
+ * addition per loop than evaluating a nested polynomial of
+ * the same degree.
+ *
+ */
+/*							chbevl.c	*/
+
+/*
+Cephes Math Library Release 2.0:  April, 1987
+Copyright 1985, 1987 by Stephen L. Moshier
+Direct inquiries to 30 Frost Street, Cambridge, MA 02140
+*/
+
+double chbevl( x, array, n )
+double x;
+double array[];
+int n;
+{
+double b0, b1, b2, *p;
+int i;
+
+p = array;
+b0 = *p++;
+b1 = 0.0;
+i = n - 1;
+
+do
+	{
+	b2 = b1;
+	b1 = b0;
+	b0 = x * b1  -  b2  + *p++;
+	}
+while( --i );
+
+return( 0.5*(b0-b2) );
+}

+ 1043 - 0
components/external/espruino/libs/math/clog.c

@@ -0,0 +1,1043 @@
+/*							clog.c
+ *
+ *	Complex natural logarithm
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void clog();
+ * cmplx z, w;
+ *
+ * clog( &z, &w );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns complex logarithm to the base e (2.718...) of
+ * the complex argument x.
+ *
+ * If z = x + iy, r = sqrt( x**2 + y**2 ),
+ * then
+ *       w = log(r) + i arctan(y/x).
+ * 
+ * The arctangent ranges from -PI to +PI.
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -10,+10      7000       8.5e-17     1.9e-17
+ *    IEEE      -10,+10     30000       5.0e-15     1.1e-16
+ *
+ * Larger relative error can be observed for z near 1 +i0.
+ * In IEEE arithmetic the peak absolute error is 5.2e-16, rms
+ * absolute error 1.0e-16.
+ */
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+#include "mconf.h"
+#ifdef ANSIPROT
+static void cchsh ( double x, double *c, double *s );
+const static double redupi ( double x );
+const static double ctans ( cmplx *z );
+/* These are supposed to be in some standard place. */
+double fabs (double);
+double sqrt (double);
+double pow (double, double);
+double log (double);
+double exp (double);
+double atan2 (double, double);
+double cosh (double);
+double sinh (double);
+double asin (double);
+double sin (double);
+double cos (double);
+double cabs (cmplx *);
+void cadd ( cmplx *, cmplx *, cmplx * );
+void cmul ( cmplx *, cmplx *, cmplx * );
+void csqrt ( cmplx *, cmplx * );
+static void cchsh ( double, double *, double * );
+const static double redupi ( double );
+const static double ctans ( cmplx * );
+void clog ( cmplx *, cmplx * );
+void casin ( cmplx *, cmplx * );
+void cacos ( cmplx *, cmplx * );
+void catan ( cmplx *, cmplx * );
+#else
+static void cchsh();
+const static double redupi();
+const static double ctans();
+double cabs(), fabs(), sqrt(), pow();
+double log(), exp(), atan2(), cosh(), sinh();
+double asin(), sin(), cos();
+void cadd(), cmul(), csqrt();
+void clog(), casin(), cacos(), catan();
+#endif
+
+
+extern double MAXNUM, MACHEP, PI, PIO2;
+
+void clog( z, w )
+register cmplx *z, *w;
+{
+double p, rr;
+
+/*rr = sqrt( z->r * z->r  +  z->i * z->i );*/
+rr = cabs(z);
+p = log(rr);
+#if ANSIC
+rr = atan2( z->i, z->r );
+#else
+rr = atan2( z->r, z->i );
+if( rr > PI )
+	rr -= PI + PI;
+#endif
+w->i = rr;
+w->r = p;
+}
+/*							cexp()
+ *
+ *	Complex exponential function
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void cexp();
+ * cmplx z, w;
+ *
+ * cexp( &z, &w );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns the exponential of the complex argument z
+ * into the complex result w.
+ *
+ * If
+ *     z = x + iy,
+ *     r = exp(x),
+ *
+ * then
+ *
+ *     w = r cos y + i r sin y.
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -10,+10      8700       3.7e-17     1.1e-17
+ *    IEEE      -10,+10     30000       3.0e-16     8.7e-17
+ *
+ */
+
+void cexp( z, w )
+register cmplx *z, *w;
+{
+double r;
+
+r = exp( z->r );
+w->r = r * cos( z->i );
+w->i = r * sin( z->i );
+}
+/*							csin()
+ *
+ *	Complex circular sine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void csin();
+ * cmplx z, w;
+ *
+ * csin( &z, &w );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * If
+ *     z = x + iy,
+ *
+ * then
+ *
+ *     w = sin x  cosh y  +  i cos x sinh y.
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -10,+10      8400       5.3e-17     1.3e-17
+ *    IEEE      -10,+10     30000       3.8e-16     1.0e-16
+ * Also tested by csin(casin(z)) = z.
+ *
+ */
+
+void csin( z, w )
+register cmplx *z, *w;
+{
+double ch, sh;
+
+cchsh( z->i, &ch, &sh );
+w->r = sin( z->r ) * ch;
+w->i = cos( z->r ) * sh;
+}
+
+
+
+/* calculate cosh and sinh */
+
+static void cchsh( x, c, s )
+double x, *c, *s;
+{
+double e, ei;
+
+if( fabs(x) <= 0.5 )
+	{
+	*c = cosh(x);
+	*s = sinh(x);
+	}
+else
+	{
+	e = exp(x);
+	ei = 0.5/e;
+	e = 0.5 * e;
+	*s = e - ei;
+	*c = e + ei;
+	}
+}
+
+/*							ccos()
+ *
+ *	Complex circular cosine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void ccos();
+ * cmplx z, w;
+ *
+ * ccos( &z, &w );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * If
+ *     z = x + iy,
+ *
+ * then
+ *
+ *     w = cos x  cosh y  -  i sin x sinh y.
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -10,+10      8400       4.5e-17     1.3e-17
+ *    IEEE      -10,+10     30000       3.8e-16     1.0e-16
+ */
+
+void ccos( z, w )
+register cmplx *z, *w;
+{
+double ch, sh;
+
+cchsh( z->i, &ch, &sh );
+w->r = cos( z->r ) * ch;
+w->i = -sin( z->r ) * sh;
+}
+/*							ctan()
+ *
+ *	Complex circular tangent
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void ctan();
+ * cmplx z, w;
+ *
+ * ctan( &z, &w );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * If
+ *     z = x + iy,
+ *
+ * then
+ *
+ *           sin 2x  +  i sinh 2y
+ *     w  =  --------------------.
+ *            cos 2x  +  cosh 2y
+ *
+ * On the real axis the denominator is zero at odd multiples
+ * of PI/2.  The denominator is evaluated by its Taylor
+ * series near these points.
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -10,+10      5200       7.1e-17     1.6e-17
+ *    IEEE      -10,+10     30000       7.2e-16     1.2e-16
+ * Also tested by ctan * ccot = 1 and catan(ctan(z))  =  z.
+ */
+
+void ctan( z, w )
+register cmplx *z, *w;
+{
+double d;
+
+d = cos( 2.0 * z->r ) + cosh( 2.0 * z->i );
+
+if( fabs(d) < 0.25 )
+	d = ctans(z);
+
+if( d == 0.0 )
+	{
+	mtherr( "ctan", OVERFLOW );
+	w->r = MAXNUM;
+	w->i = MAXNUM;
+	return;
+	}
+
+w->r = sin( 2.0 * z->r ) / d;
+w->i = sinh( 2.0 * z->i ) / d;
+}
+/*							ccot()
+ *
+ *	Complex circular cotangent
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void ccot();
+ * cmplx z, w;
+ *
+ * ccot( &z, &w );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * If
+ *     z = x + iy,
+ *
+ * then
+ *
+ *           sin 2x  -  i sinh 2y
+ *     w  =  --------------------.
+ *            cosh 2y  -  cos 2x
+ *
+ * On the real axis, the denominator has zeros at even
+ * multiples of PI/2.  Near these points it is evaluated
+ * by a Taylor series.
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -10,+10      3000       6.5e-17     1.6e-17
+ *    IEEE      -10,+10     30000       9.2e-16     1.2e-16
+ * Also tested by ctan * ccot = 1 + i0.
+ */
+
+void ccot( z, w )
+register cmplx *z, *w;
+{
+double d;
+
+d = cosh(2.0 * z->i) - cos(2.0 * z->r);
+
+if( fabs(d) < 0.25 )
+	d = ctans(z);
+
+if( d == 0.0 )
+	{
+	mtherr( "ccot", OVERFLOW );
+	w->r = MAXNUM;
+	w->i = MAXNUM;
+	return;
+	}
+
+w->r = sin( 2.0 * z->r ) / d;
+w->i = -sinh( 2.0 * z->i ) / d;
+}
+
+/* Program to subtract nearest integer multiple of PI */
+/* extended precision value of PI: */
+#ifdef UNK
+const static double DP1 = 3.14159265160560607910E0;
+const static double DP2 = 1.98418714791870343106E-9;
+const static double DP3 = 1.14423774522196636802E-17;
+#endif
+
+#ifdef DEC
+static unsigned short P1[] = {0040511,0007732,0120000,0000000,};
+static unsigned short P2[] = {0031010,0055060,0100000,0000000,};
+static unsigned short P3[] = {0022123,0011431,0105056,0001560,};
+#define DP1 *(double *)P1
+#define DP2 *(double *)P2
+#define DP3 *(double *)P3
+#endif
+
+#ifdef IBMPC
+static unsigned short P1[] = {0x0000,0x5400,0x21fb,0x4009};
+static unsigned short P2[] = {0x0000,0x1000,0x0b46,0x3e21};
+static unsigned short P3[] = {0xc06e,0x3145,0x6263,0x3c6a};
+#define DP1 *(double *)P1
+#define DP2 *(double *)P2
+#define DP3 *(double *)P3
+#endif
+
+#ifdef MIEEE
+static unsigned short P1[] = {
+0x4009,0x21fb,0x5400,0x0000
+};
+static unsigned short P2[] = {
+0x3e21,0x0b46,0x1000,0x0000
+};
+static unsigned short P3[] = {
+0x3c6a,0x6263,0x3145,0xc06e
+};
+#define DP1 *(double *)P1
+#define DP2 *(double *)P2
+#define DP3 *(double *)P3
+#endif
+
+const static double redupi(x)
+double x;
+{
+double t;
+long i;
+
+t = x/PI;
+if( t >= 0.0 )
+	t += 0.5;
+else
+	t -= 0.5;
+
+i = t;	/* the multiple */
+t = i;
+t = ((x - t * DP1) - t * DP2) - t * DP3;
+return(t);
+}
+
+/*  Taylor series expansion for cosh(2y) - cos(2x)	*/
+
+const static double ctans(z)
+cmplx *z;
+{
+double f, x, x2, y, y2, rn, t;
+double d;
+
+x = fabs( 2.0 * z->r );
+y = fabs( 2.0 * z->i );
+
+x = redupi(x);
+
+x = x * x;
+y = y * y;
+x2 = 1.0;
+y2 = 1.0;
+f = 1.0;
+rn = 0.0;
+d = 0.0;
+do
+	{
+	rn += 1.0;
+	f *= rn;
+	rn += 1.0;
+	f *= rn;
+	x2 *= x;
+	y2 *= y;
+	t = y2 + x2;
+	t /= f;
+	d += t;
+
+	rn += 1.0;
+	f *= rn;
+	rn += 1.0;
+	f *= rn;
+	x2 *= x;
+	y2 *= y;
+	t = y2 - x2;
+	t /= f;
+	d += t;
+	}
+while( fabs(t/d) > MACHEP );
+return(d);
+}
+/*							casin()
+ *
+ *	Complex circular arc sine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void casin();
+ * cmplx z, w;
+ *
+ * casin( &z, &w );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Inverse complex sine:
+ *
+ *                               2
+ * w = -i clog( iz + csqrt( 1 - z ) ).
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -10,+10     10100       2.1e-15     3.4e-16
+ *    IEEE      -10,+10     30000       2.2e-14     2.7e-15
+ * Larger relative error can be observed for z near zero.
+ * Also tested by csin(casin(z)) = z.
+ */
+
+void casin( z, w )
+cmplx *z, *w;
+{
+static cmplx ca, ct, zz, z2;
+double x, y;
+
+x = z->r;
+y = z->i;
+
+if( y == 0.0 )
+	{
+	if( fabs(x) > 1.0 )
+		{
+		w->r = PIO2;
+		w->i = 0.0;
+		mtherr( "casin", DOMAIN );
+		}
+	else
+		{
+		w->r = asin(x);
+		w->i = 0.0;
+		}
+	return;
+	}
+
+/* Power series expansion */
+/*
+b = cabs(z);
+if( b < 0.125 )
+{
+z2.r = (x - y) * (x + y);
+z2.i = 2.0 * x * y;
+
+cn = 1.0;
+n = 1.0;
+ca.r = x;
+ca.i = y;
+sum.r = x;
+sum.i = y;
+do
+	{
+	ct.r = z2.r * ca.r  -  z2.i * ca.i;
+	ct.i = z2.r * ca.i  +  z2.i * ca.r;
+	ca.r = ct.r;
+	ca.i = ct.i;
+
+	cn *= n;
+	n += 1.0;
+	cn /= n;
+	n += 1.0;
+	b = cn/n;
+
+	ct.r *= b;
+	ct.i *= b;
+	sum.r += ct.r;
+	sum.i += ct.i;
+	b = fabs(ct.r) + fabs(ct.i);
+	}
+while( b > MACHEP );
+w->r = sum.r;
+w->i = sum.i;
+return;
+}
+*/
+
+
+ca.r = x;
+ca.i = y;
+
+ct.r = -ca.i;	/* iz */
+ct.i = ca.r;
+
+	/* sqrt( 1 - z*z) */
+/* cmul( &ca, &ca, &zz ) */
+zz.r = (ca.r - ca.i) * (ca.r + ca.i);	/*x * x  -  y * y */
+zz.i = 2.0 * ca.r * ca.i;
+
+zz.r = 1.0 - zz.r;
+zz.i = -zz.i;
+csqrt( &zz, &z2 );
+
+cadd( &z2, &ct, &zz );
+clog( &zz, &zz );
+w->r = zz.i;	/* mult by 1/i = -i */
+w->i = -zz.r;
+return;
+}
+/*							cacos()
+ *
+ *	Complex circular arc cosine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void cacos();
+ * cmplx z, w;
+ *
+ * cacos( &z, &w );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ *
+ * w = arccos z  =  PI/2 - arcsin z.
+ *
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -10,+10      5200      1.6e-15      2.8e-16
+ *    IEEE      -10,+10     30000      1.8e-14      2.2e-15
+ */
+
+void cacos( z, w )
+cmplx *z, *w;
+{
+
+casin( z, w );
+w->r = PIO2  -  w->r;
+w->i = -w->i;
+}
+/*							catan()
+ *
+ *	Complex circular arc tangent
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void catan();
+ * cmplx z, w;
+ *
+ * catan( &z, &w );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * If
+ *     z = x + iy,
+ *
+ * then
+ *          1       (    2x     )
+ * Re w  =  - arctan(-----------)  +  k PI
+ *          2       (     2    2)
+ *                  (1 - x  - y )
+ *
+ *               ( 2         2)
+ *          1    (x  +  (y+1) )
+ * Im w  =  - log(------------)
+ *          4    ( 2         2)
+ *               (x  +  (y-1) )
+ *
+ * Where k is an arbitrary integer.
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -10,+10      5900       1.3e-16     7.8e-18
+ *    IEEE      -10,+10     30000       2.3e-15     8.5e-17
+ * The check catan( ctan(z) )  =  z, with |x| and |y| < PI/2,
+ * had peak relative error 1.5e-16, rms relative error
+ * 2.9e-17.  See also clog().
+ */
+
+void catan( z, w )
+cmplx *z, *w;
+{
+double a, t, x, x2, y;
+
+x = z->r;
+y = z->i;
+
+if( (x == 0.0) && (y > 1.0) )
+	goto ovrf;
+
+x2 = x * x;
+a = 1.0 - x2 - (y * y);
+if( a == 0.0 )
+	goto ovrf;
+
+#if ANSIC
+t = atan2( 2.0 * x, a )/2.0;
+#else
+t = atan2( a, 2.0 * x )/2.0;
+#endif
+w->r = redupi( t );
+
+t = y - 1.0;
+a = x2 + (t * t);
+if( a == 0.0 )
+	goto ovrf;
+
+t = y + 1.0;
+a = (x2 + (t * t))/a;
+w->i = log(a)/4.0;
+return;
+
+ovrf:
+mtherr( "catan", OVERFLOW );
+w->r = MAXNUM;
+w->i = MAXNUM;
+}
+
+
+/*							csinh
+ *
+ *	Complex hyperbolic sine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void csinh();
+ * cmplx z, w;
+ *
+ * csinh( &z, &w );
+ *
+ *
+ * DESCRIPTION:
+ *
+ * csinh z = (cexp(z) - cexp(-z))/2
+ *         = sinh x * cos y  +  i cosh x * sin y .
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE      -10,+10     30000       3.1e-16     8.2e-17
+ *
+ */
+
+void
+csinh (z, w)
+     cmplx *z, *w;
+{
+  double x, y;
+
+  x = z->r;
+  y = z->i;
+  w->r = sinh (x) * cos (y);
+  w->i = cosh (x) * sin (y);
+}
+
+
+/*							casinh
+ *
+ *	Complex inverse hyperbolic sine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void casinh();
+ * cmplx z, w;
+ *
+ * casinh (&z, &w);
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * casinh z = -i casin iz .
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE      -10,+10     30000       1.8e-14     2.6e-15
+ *
+ */
+
+void
+casinh (z, w)
+     cmplx *z, *w;
+{
+  cmplx u;
+
+  u.r = 0.0;
+  u.i = 1.0;
+  cmul( z, &u, &u );
+  casin( &u, w );
+  u.r = 0.0;
+  u.i = -1.0;
+  cmul( &u, w, w );
+}
+
+/*							ccosh
+ *
+ *	Complex hyperbolic cosine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void ccosh();
+ * cmplx z, w;
+ *
+ * ccosh (&z, &w);
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * ccosh(z) = cosh x  cos y + i sinh x sin y .
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE      -10,+10     30000       2.9e-16     8.1e-17
+ *
+ */
+
+void
+ccosh (z, w)
+     cmplx *z, *w;
+{
+  double x, y;
+
+  x = z->r;
+  y = z->i;
+  w->r = cosh (x) * cos (y);
+  w->i = sinh (x) * sin (y);
+}
+
+
+/*							cacosh
+ *
+ *	Complex inverse hyperbolic cosine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void cacosh();
+ * cmplx z, w;
+ *
+ * cacosh (&z, &w);
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * acosh z = i acos z .
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE      -10,+10     30000       1.6e-14     2.1e-15
+ *
+ */
+
+void
+cacosh (z, w)
+     cmplx *z, *w;
+{
+  cmplx u;
+
+  cacos( z, w );
+  u.r = 0.0;
+  u.i = 1.0;
+  cmul( &u, w, w );
+}
+
+
+/*							ctanh
+ *
+ *	Complex hyperbolic tangent
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void ctanh();
+ * cmplx z, w;
+ *
+ * ctanh (&z, &w);
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * tanh z = (sinh 2x  +  i sin 2y) / (cosh 2x + cos 2y) .
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE      -10,+10     30000       1.7e-14     2.4e-16
+ *
+ */
+
+/* 5.253E-02,1.550E+00 1.643E+01,6.553E+00 1.729E-14  21355  */
+
+void
+ctanh (z, w)
+     cmplx *z, *w;
+{
+  double x, y, d;
+
+  x = z->r;
+  y = z->i;
+  d = cosh (2.0 * x) + cos (2.0 * y);
+  w->r = sinh (2.0 * x) / d;
+  w->i = sin (2.0 * y) / d;
+  return;
+}
+
+
+/*							catanh
+ *
+ *	Complex inverse hyperbolic tangent
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void catanh();
+ * cmplx z, w;
+ *
+ * catanh (&z, &w);
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Inverse tanh, equal to  -i catan (iz);
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE      -10,+10     30000       2.3e-16     6.2e-17
+ *
+ */
+
+void
+catanh (z, w)
+     cmplx *z, *w;
+{
+  cmplx u;
+
+  u.r = 0.0;
+  u.i = 1.0;
+  cmul (z, &u, &u);  /* i z */
+  catan (&u, w);
+  u.r = 0.0;
+  u.i = -1.0;
+  cmul (&u, w, w);  /* -i catan iz */
+  return;
+}
+
+
+/*							cpow
+ *
+ *	Complex power function
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void cpow();
+ * cmplx a, z, w;
+ *
+ * cpow (&a, &z, &w);
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Raises complex A to the complex Zth power.
+ * Definition is per AMS55 # 4.2.8,
+ * analytically equivalent to cpow(a,z) = cexp(z clog(a)).
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE      -10,+10     30000       9.4e-15     1.5e-15
+ *
+ */
+
+
+void
+cpow (a, z, w)
+     cmplx *a, *z, *w;
+{
+  double x, y, r, theta, absa, arga;
+
+  x = z->r;
+  y = z->i;
+  absa = cabs (a);
+  if (absa == 0.0)
+    {
+      w->r = 0.0;
+      w->i = 0.0;
+      return;
+    }
+  arga = atan2 (a->i, a->r);
+  r = pow (absa, x);
+  theta = x * arga;
+  if (y != 0.0)
+    {
+      r = r * exp (-y * arga);
+      theta = theta + y * log (absa);
+    }
+  w->r = r * cos (theta);
+  w->i = r * sin (theta);
+  return;
+}

+ 461 - 0
components/external/espruino/libs/math/cmplx.c

@@ -0,0 +1,461 @@
+/*							cmplx.c
+ *
+ *	Complex number arithmetic
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * typedef struct {
+ *      double r;     real part
+ *      double i;     imaginary part
+ *     }cmplx;
+ *
+ * cmplx *a, *b, *c;
+ *
+ * cadd( a, b, c );     c = b + a
+ * csub( a, b, c );     c = b - a
+ * cmul( a, b, c );     c = b * a
+ * cdiv( a, b, c );     c = b / a
+ * cneg( c );           c = -c
+ * cmov( b, c );        c = b
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Addition:
+ *    c.r  =  b.r + a.r
+ *    c.i  =  b.i + a.i
+ *
+ * Subtraction:
+ *    c.r  =  b.r - a.r
+ *    c.i  =  b.i - a.i
+ *
+ * Multiplication:
+ *    c.r  =  b.r * a.r  -  b.i * a.i
+ *    c.i  =  b.r * a.i  +  b.i * a.r
+ *
+ * Division:
+ *    d    =  a.r * a.r  +  a.i * a.i
+ *    c.r  = (b.r * a.r  + b.i * a.i)/d
+ *    c.i  = (b.i * a.r  -  b.r * a.i)/d
+ * ACCURACY:
+ *
+ * In DEC arithmetic, the test (1/z) * z = 1 had peak relative
+ * error 3.1e-17, rms 1.2e-17.  The test (y/z) * (z/y) = 1 had
+ * peak relative error 8.3e-17, rms 2.1e-17.
+ *
+ * Tests in the rectangle {-10,+10}:
+ *                      Relative error:
+ * arithmetic   function  # trials      peak         rms
+ *    DEC        cadd       10000       1.4e-17     3.4e-18
+ *    IEEE       cadd      100000       1.1e-16     2.7e-17
+ *    DEC        csub       10000       1.4e-17     4.5e-18
+ *    IEEE       csub      100000       1.1e-16     3.4e-17
+ *    DEC        cmul        3000       2.3e-17     8.7e-18
+ *    IEEE       cmul      100000       2.1e-16     6.9e-17
+ *    DEC        cdiv       18000       4.9e-17     1.3e-17
+ *    IEEE       cdiv      100000       3.7e-16     1.1e-16
+ */
+/*				cmplx.c
+ * complex number arithmetic
+ */
+
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+
+#include "mconf.h"
+
+#ifdef ANSIPROT
+extern double fabs ( double );
+extern double cabs ( cmplx * );
+extern double sqrt ( double );
+extern double atan2 ( double, double );
+extern double cos ( double );
+extern double sin ( double );
+extern double sqrt ( double );
+extern double frexp ( double, int * );
+extern double ldexp ( double, int );
+int isnan ( double );
+void cdiv ( cmplx *, cmplx *, cmplx * );
+void cadd ( cmplx *, cmplx *, cmplx * );
+#else
+double fabs(), cabs(), sqrt(), atan2(), cos(), sin();
+double sqrt(), frexp(), ldexp();
+int isnan();
+void cdiv(), cadd();
+#endif
+
+extern double MAXNUM, MACHEP, PI, PIO2, INFINITY, NAN;
+/*
+typedef struct
+	{
+	double r;
+	double i;
+	}cmplx;
+*/
+cmplx czero = {0.0, 0.0};
+extern cmplx czero;
+cmplx cone = {1.0, 0.0};
+extern cmplx cone;
+
+/*	c = b + a	*/
+
+void cadd( a, b, c )
+register cmplx *a, *b;
+cmplx *c;
+{
+
+c->r = b->r + a->r;
+c->i = b->i + a->i;
+}
+
+
+/*	c = b - a	*/
+
+void csub( a, b, c )
+register cmplx *a, *b;
+cmplx *c;
+{
+
+c->r = b->r - a->r;
+c->i = b->i - a->i;
+}
+
+/*	c = b * a */
+
+void cmul( a, b, c )
+register cmplx *a, *b;
+cmplx *c;
+{
+double y;
+
+y    = b->r * a->r  -  b->i * a->i;
+c->i = b->r * a->i  +  b->i * a->r;
+c->r = y;
+}
+
+
+
+/*	c = b / a */
+
+void cdiv( a, b, c )
+register cmplx *a, *b;
+cmplx *c;
+{
+double y, p, q, w;
+
+
+y = a->r * a->r  +  a->i * a->i;
+p = b->r * a->r  +  b->i * a->i;
+q = b->i * a->r  -  b->r * a->i;
+
+if( y < 1.0 )
+	{
+	w = MAXNUM * y;
+	if( (fabs(p) > w) || (fabs(q) > w) || (y == 0.0) )
+		{
+		c->r = MAXNUM;
+		c->i = MAXNUM;
+		mtherr( "cdiv", OVERFLOW );
+		return;
+		}
+	}
+c->r = p/y;
+c->i = q/y;
+}
+
+
+/*	b = a
+   Caution, a `short' is assumed to be 16 bits wide.  */
+
+void cmov( a, b )
+void *a, *b;
+{
+register short *pa, *pb;
+int i;
+
+pa = (short *) a;
+pb = (short *) b;
+i = 8;
+do
+	*pb++ = *pa++;
+while( --i );
+}
+
+
+void cneg( a )
+register cmplx *a;
+{
+
+a->r = -a->r;
+a->i = -a->i;
+}
+
+/*							cabs()
+ *
+ *	Complex absolute value
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double cabs();
+ * cmplx z;
+ * double a;
+ *
+ * a = cabs( &z );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ *
+ * If z = x + iy
+ *
+ * then
+ *
+ *       a = sqrt( x**2 + y**2 ).
+ * 
+ * Overflow and underflow are avoided by testing the magnitudes
+ * of x and y before squaring.  If either is outside half of
+ * the floating point full scale range, both are rescaled.
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -30,+30     30000       3.2e-17     9.2e-18
+ *    IEEE      -10,+10    100000       2.7e-16     6.9e-17
+ */
+
+
+/*
+Cephes Math Library Release 2.1:  January, 1989
+Copyright 1984, 1987, 1989 by Stephen L. Moshier
+Direct inquiries to 30 Frost Street, Cambridge, MA 02140
+*/
+
+
+/*
+typedef struct
+	{
+	double r;
+	double i;
+	}cmplx;
+*/
+
+#ifdef UNK
+#define PREC 27
+#define MAXEXP 1024
+#define MINEXP -1077
+#endif
+#ifdef DEC
+#define PREC 29
+#define MAXEXP 128
+#define MINEXP -128
+#endif
+#ifdef IBMPC
+#define PREC 27
+#define MAXEXP 1024
+#define MINEXP -1077
+#endif
+#ifdef MIEEE
+#define PREC 27
+#define MAXEXP 1024
+#define MINEXP -1077
+#endif
+
+
+double cabs( z )
+register cmplx *z;
+{
+double x, y, b, re, im;
+int ex, ey, e;
+
+#ifdef INFINITIES
+/* Note, cabs(INFINITY,NAN) = INFINITY. */
+if( z->r == INFINITY || z->i == INFINITY
+   || z->r == -INFINITY || z->i == -INFINITY )
+  return( INFINITY );
+#endif
+
+#ifdef NANS
+if( isnan(z->r) )
+  return(z->r);
+if( isnan(z->i) )
+  return(z->i);
+#endif
+
+re = fabs( z->r );
+im = fabs( z->i );
+
+if( re == 0.0 )
+	return( im );
+if( im == 0.0 )
+	return( re );
+
+/* Get the exponents of the numbers */
+x = frexp( re, &ex );
+y = frexp( im, &ey );
+
+/* Check if one number is tiny compared to the other */
+e = ex - ey;
+if( e > PREC )
+	return( re );
+if( e < -PREC )
+	return( im );
+
+/* Find approximate exponent e of the geometric mean. */
+e = (ex + ey) >> 1;
+
+/* Rescale so mean is about 1 */
+x = ldexp( re, -e );
+y = ldexp( im, -e );
+		
+/* Hypotenuse of the right triangle */
+b = sqrt( x * x  +  y * y );
+
+/* Compute the exponent of the answer. */
+y = frexp( b, &ey );
+ey = e + ey;
+
+/* Check it for overflow and underflow. */
+if( ey > MAXEXP )
+	{
+	mtherr( "cabs", OVERFLOW );
+	return( INFINITY );
+	}
+if( ey < MINEXP )
+	return(0.0);
+
+/* Undo the scaling */
+b = ldexp( b, e );
+return( b );
+}
+/*							csqrt()
+ *
+ *	Complex square root
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * void csqrt();
+ * cmplx z, w;
+ *
+ * csqrt( &z, &w );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ *
+ * If z = x + iy,  r = |z|, then
+ *
+ *                       1/2
+ * Im w  =  [ (r - x)/2 ]   ,
+ *
+ * Re w  =  y / 2 Im w.
+ *
+ *
+ * Note that -w is also a square root of z.  The root chosen
+ * is always in the upper half plane.
+ *
+ * Because of the potential for cancellation error in r - x,
+ * the result is sharpened by doing a Heron iteration
+ * (see sqrt.c) in complex arithmetic.
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -10,+10     25000       3.2e-17     9.6e-18
+ *    IEEE      -10,+10    100000       3.2e-16     7.7e-17
+ *
+ *                        2
+ * Also tested by csqrt( z ) = z, and tested by arguments
+ * close to the real axis.
+ */
+
+
+void csqrt( z, w )
+cmplx *z, *w;
+{
+cmplx q, s;
+double x, y, r, t;
+
+x = z->r;
+y = z->i;
+
+if( y == 0.0 )
+	{
+	if( x < 0.0 )
+		{
+		w->r = 0.0;
+		w->i = sqrt(-x);
+		return;
+		}
+	else
+		{
+		w->r = sqrt(x);
+		w->i = 0.0;
+		return;
+		}
+	}
+
+
+if( x == 0.0 )
+	{
+	r = fabs(y);
+	r = sqrt(0.5*r);
+	if( y > 0 )
+		w->r = r;
+	else
+		w->r = -r;
+	w->i = r;
+	return;
+	}
+
+/* Approximate  sqrt(x^2+y^2) - x  =  y^2/2x - y^4/24x^3 + ... .
+ * The relative error in the first term is approximately y^2/12x^2 .
+ */
+if( (fabs(y) < 2.e-4 * fabs(x))
+   && (x > 0) )
+	{
+	t = 0.25*y*(y/x);
+	}
+else
+	{
+	r = cabs(z);
+	t = 0.5*(r - x);
+	}
+
+r = sqrt(t);
+q.i = r;
+q.r = y/(2.0*r);
+/* Heron iteration in complex arithmetic */
+cdiv( &q, z, &s );
+cadd( &q, &s, w );
+w->r *= 0.5;
+w->i *= 0.5;
+}
+
+
+double hypot( x, y )
+double x, y;
+{
+cmplx z;
+
+z.r = x;
+z.i = y;
+return( cabs(&z) );
+}

+ 252 - 0
components/external/espruino/libs/math/const.c

@@ -0,0 +1,252 @@
+/*							const.c
+ *
+ *	Globally declared constants
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * extern const double nameofconstant;
+ *
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * This file contains a number of mathematical constants and
+ * also some needed size parameters of the computer arithmetic.
+ * The values are supplied as arrays of hexadecimal integers
+ * for IEEE arithmetic; arrays of octal constants for DEC
+ * arithmetic; and in a normal decimal scientific notation for
+ * other machines.  The particular notation used is determined
+ * by a symbol (DEC, IBMPC, or UNK) defined in the include file
+ * mconf.h.
+ *
+ * The default size parameters are as follows.
+ *
+ * For DEC and UNK modes:
+ * MACHEP =  1.38777878078144567553E-17       2**-56
+ * MAXLOG =  8.8029691931113054295988E1       log(2**127)
+ * MINLOG = -8.872283911167299960540E1        log(2**-128)
+ * MAXNUM =  1.701411834604692317316873e38    2**127
+ *
+ * For IEEE arithmetic (IBMPC):
+ * MACHEP =  1.11022302462515654042E-16       2**-53
+ * MAXLOG =  7.09782712893383996843E2         log(2**1024)
+ * MINLOG = -7.08396418532264106224E2         log(2**-1022)
+ * MAXNUM =  1.7976931348623158E308           2**1024
+ *
+ * The global symbols for mathematical constants are
+ * PI     =  3.14159265358979323846           pi
+ * PIO2   =  1.57079632679489661923           pi/2
+ * PIO4   =  7.85398163397448309616E-1        pi/4
+ * SQRT2  =  1.41421356237309504880           sqrt(2)
+ * SQRTH  =  7.07106781186547524401E-1        sqrt(2)/2
+ * LOG2E  =  1.4426950408889634073599         1/log(2)
+ * SQ2OPI =  7.9788456080286535587989E-1      sqrt( 2/pi )
+ * LOGE2  =  6.93147180559945309417E-1        log(2)
+ * LOGSQ2 =  3.46573590279972654709E-1        log(2)/2
+ * THPIO4 =  2.35619449019234492885           3*pi/4
+ * TWOOPI =  6.36619772367581343075535E-1     2/pi
+ *
+ * These lists are subject to change.
+ */
+
+/*							const.c */
+
+/*
+Cephes Math Library Release 2.3:  March, 1995
+Copyright 1984, 1995 by Stephen L. Moshier
+*/
+
+#include "mconf.h"
+
+#ifdef UNK
+#if 1
+const double MACHEP =  1.11022302462515654042E-16;   /* 2**-53 */
+#else
+const double MACHEP =  1.38777878078144567553E-17;   /* 2**-56 */
+#endif
+const double UFLOWTHRESH =  2.22507385850720138309E-308; /* 2**-1022 */
+#ifdef DENORMAL
+const double MAXLOG =  7.09782712893383996732E2;     /* log(MAXNUM) */
+/* const double MINLOG = -7.44440071921381262314E2; */     /* log(2**-1074) */
+const double MINLOG = -7.451332191019412076235E2;     /* log(2**-1075) */
+#else
+const double MAXLOG =  7.08396418532264106224E2;     /* log 2**1022 */
+const double MINLOG = -7.08396418532264106224E2;     /* log 2**-1022 */
+#endif
+const double MAXNUM =  1.79769313486231570815E308;    /* 2**1024*(1-MACHEP) */
+const double PI     =  3.14159265358979323846;       /* pi */
+const double PIO2   =  1.57079632679489661923;       /* pi/2 */
+const double PIO4   =  7.85398163397448309616E-1;    /* pi/4 */
+const double SQRT2  =  1.41421356237309504880;       /* sqrt(2) */
+const double SQRTH  =  7.07106781186547524401E-1;    /* sqrt(2)/2 */
+const double LOG2E  =  1.4426950408889634073599;     /* 1/log(2) */
+const double SQ2OPI =  7.9788456080286535587989E-1;  /* sqrt( 2/pi ) */
+const double LOGE2  =  6.93147180559945309417E-1;    /* log(2) */
+const double LOGSQ2 =  3.46573590279972654709E-1;    /* log(2)/2 */
+const double THPIO4 =  2.35619449019234492885;       /* 3*pi/4 */
+const double TWOOPI =  6.36619772367581343075535E-1; /* 2/pi */
+#ifdef INFINITIES
+const double INFINITY = 1.0/0.0;  /* 99e999; */
+#else
+const double INFINITY =  1.79769313486231570815E308;    /* 2**1024*(1-MACHEP) */
+#endif
+#ifdef NANS
+const double NAN = 1.0/0.0 - 1.0/0.0;
+#else
+const double NAN = 0.0;
+#endif
+#ifdef MINUSZERO
+const double NEGZERO = -0.0;
+#else
+const double NEGZERO = 0.0;
+#endif
+#endif
+
+#ifdef IBMPC
+			/* 2**-53 =  1.11022302462515654042E-16 */
+const unsigned short MACHEP[4] = {0x0000,0x0000,0x0000,0x3ca0};
+const unsigned short UFLOWTHRESH[4] = {0x0000,0x0000,0x0000,0x0010};
+#ifdef DENORMAL
+			/* log(MAXNUM) =  7.09782712893383996732224E2 */
+const unsigned short MAXLOG[4] = {0x39ef,0xfefa,0x2e42,0x4086};
+			/* log(2**-1074) = - -7.44440071921381262314E2 */
+/*const unsigned short MINLOG[4] = {0x71c3,0x446d,0x4385,0xc087};*/
+const unsigned short MINLOG[4] = {0x3052,0xd52d,0x4910,0xc087};
+#else
+			/* log(2**1022) =   7.08396418532264106224E2 */
+const unsigned short MAXLOG[4] = {0xbcd2,0xdd7a,0x232b,0x4086};
+			/* log(2**-1022) = - 7.08396418532264106224E2 */
+const unsigned short MINLOG[4] = {0xbcd2,0xdd7a,0x232b,0xc086};
+#endif
+			/* 2**1024*(1-MACHEP) =  1.7976931348623158E308 */
+const unsigned short MAXNUM[4] = {0xffff,0xffff,0xffff,0x7fef};
+const unsigned short PI[4]     = {0x2d18,0x5444,0x21fb,0x4009};
+const unsigned short PIO2[4]   = {0x2d18,0x5444,0x21fb,0x3ff9};
+const unsigned short PIO4[4]   = {0x2d18,0x5444,0x21fb,0x3fe9};
+const unsigned short SQRT2[4]  = {0x3bcd,0x667f,0xa09e,0x3ff6};
+const unsigned short SQRTH[4]  = {0x3bcd,0x667f,0xa09e,0x3fe6};
+const unsigned short LOG2E[4]  = {0x82fe,0x652b,0x1547,0x3ff7};
+const unsigned short SQ2OPI[4] = {0x3651,0x33d4,0x8845,0x3fe9};
+const unsigned short LOGE2[4]  = {0x39ef,0xfefa,0x2e42,0x3fe6};
+const unsigned short LOGSQ2[4] = {0x39ef,0xfefa,0x2e42,0x3fd6};
+const unsigned short THPIO4[4] = {0x21d2,0x7f33,0xd97c,0x4002};
+const unsigned short TWOOPI[4] = {0xc883,0x6dc9,0x5f30,0x3fe4};
+#ifdef INFINITIES
+const unsigned short INFINITY[4] = {0x0000,0x0000,0x0000,0x7ff0};
+#else
+const unsigned short INFINITY[4] = {0xffff,0xffff,0xffff,0x7fef};
+#endif
+#ifdef NANS
+const unsigned short NAN[4] = {0x0000,0x0000,0x0000,0x7ffc};
+#else
+const unsigned short NAN[4] = {0x0000,0x0000,0x0000,0x0000};
+#endif
+#ifdef MINUSZERO
+const unsigned short NEGZERO[4] = {0x0000,0x0000,0x0000,0x8000};
+#else
+const unsigned short NEGZERO[4] = {0x0000,0x0000,0x0000,0x0000};
+#endif
+#endif
+
+#ifdef MIEEE
+			/* 2**-53 =  1.11022302462515654042E-16 */
+const unsigned short MACHEP[4] = {0x3ca0,0x0000,0x0000,0x0000};
+const unsigned short UFLOWTHRESH[4] = {0x0010,0x0000,0x0000,0x0000};
+#ifdef DENORMAL
+			/* log(2**1024) =   7.09782712893383996843E2 */
+const unsigned short MAXLOG[4] = {0x4086,0x2e42,0xfefa,0x39ef};
+			/* log(2**-1074) = - -7.44440071921381262314E2 */
+/* const unsigned short MINLOG[4] = {0xc087,0x4385,0x446d,0x71c3}; */
+const unsigned short MINLOG[4] = {0xc087,0x4910,0xd52d,0x3052};
+#else
+			/* log(2**1022) =  7.08396418532264106224E2 */
+const unsigned short MAXLOG[4] = {0x4086,0x232b,0xdd7a,0xbcd2};
+			/* log(2**-1022) = - 7.08396418532264106224E2 */
+const unsigned short MINLOG[4] = {0xc086,0x232b,0xdd7a,0xbcd2};
+#endif
+			/* 2**1024*(1-MACHEP) =  1.7976931348623158E308 */
+const unsigned short MAXNUM[4] = {0x7fef,0xffff,0xffff,0xffff};
+const unsigned short PI[4]     = {0x4009,0x21fb,0x5444,0x2d18};
+const unsigned short PIO2[4]   = {0x3ff9,0x21fb,0x5444,0x2d18};
+const unsigned short PIO4[4]   = {0x3fe9,0x21fb,0x5444,0x2d18};
+const unsigned short SQRT2[4]  = {0x3ff6,0xa09e,0x667f,0x3bcd};
+const unsigned short SQRTH[4]  = {0x3fe6,0xa09e,0x667f,0x3bcd};
+const unsigned short LOG2E[4]  = {0x3ff7,0x1547,0x652b,0x82fe};
+const unsigned short SQ2OPI[4] = {0x3fe9,0x8845,0x33d4,0x3651};
+const unsigned short LOGE2[4]  = {0x3fe6,0x2e42,0xfefa,0x39ef};
+const unsigned short LOGSQ2[4] = {0x3fd6,0x2e42,0xfefa,0x39ef};
+const unsigned short THPIO4[4] = {0x4002,0xd97c,0x7f33,0x21d2};
+const unsigned short TWOOPI[4] = {0x3fe4,0x5f30,0x6dc9,0xc883};
+#ifdef INFINITIES
+const unsigned short INFINITY[4] = {0x7ff0,0x0000,0x0000,0x0000};
+#else
+const unsigned short INFINITY[4] = {0x7fef,0xffff,0xffff,0xffff};
+#endif
+#ifdef NANS
+const unsigned short NAN[4] = {0x7ff8,0x0000,0x0000,0x0000};
+#else
+const unsigned short NAN[4] = {0x0000,0x0000,0x0000,0x0000};
+#endif
+#ifdef MINUSZERO
+const unsigned short NEGZERO[4] = {0x8000,0x0000,0x0000,0x0000};
+#else
+const unsigned short NEGZERO[4] = {0x0000,0x0000,0x0000,0x0000};
+#endif
+#endif
+
+#ifdef DEC
+			/* 2**-56 =  1.38777878078144567553E-17 */
+const unsigned short MACHEP[4] = {0022200,0000000,0000000,0000000};
+const unsigned short UFLOWTHRESH[4] = {0x0080,0x0000,0x0000,0x0000};
+			/* log 2**127 = 88.029691931113054295988 */
+const unsigned short MAXLOG[4] = {041660,007463,0143742,025733,};
+			/* log 2**-128 = -88.72283911167299960540 */
+const unsigned short MINLOG[4] = {0141661,071027,0173721,0147572,};
+			/* 2**127 = 1.701411834604692317316873e38 */
+const unsigned short MAXNUM[4] = {077777,0177777,0177777,0177777,};
+const unsigned short PI[4]     = {040511,007732,0121041,064302,};
+const unsigned short PIO2[4]   = {040311,007732,0121041,064302,};
+const unsigned short PIO4[4]   = {040111,007732,0121041,064302,};
+const unsigned short SQRT2[4]  = {040265,002363,031771,0157145,};
+const unsigned short SQRTH[4]  = {040065,002363,031771,0157144,};
+const unsigned short LOG2E[4]  = {040270,0125073,024534,013761,};
+const unsigned short SQ2OPI[4] = {040114,041051,0117241,0131204,};
+const unsigned short LOGE2[4]  = {040061,071027,0173721,0147572,};
+const unsigned short LOGSQ2[4] = {037661,071027,0173721,0147572,};
+const unsigned short THPIO4[4] = {040426,0145743,0174631,007222,};
+const unsigned short TWOOPI[4] = {040042,0174603,067116,042025,};
+/* Approximate infinity by MAXNUM.  */
+const unsigned short INFINITY[4] = {077777,0177777,0177777,0177777,};
+const unsigned short NAN[4] = {0000000,0000000,0000000,0000000};
+#ifdef MINUSZERO
+const unsigned short NEGZERO[4] = {0000000,0000000,0000000,0100000};
+#else
+const unsigned short NEGZERO[4] = {0000000,0000000,0000000,0000000};
+#endif
+#endif
+
+#ifndef UNK
+extern const unsigned short MACHEP[];
+extern const unsigned short UFLOWTHRESH[];
+extern const unsigned short MAXLOG[];
+extern const unsigned short UNDLOG[];
+extern const unsigned short MINLOG[];
+extern const unsigned short MAXNUM[];
+extern const unsigned short PI[];
+extern const unsigned short PIO2[];
+extern const unsigned short PIO4[];
+extern const unsigned short SQRT2[];
+extern const unsigned short SQRTH[];
+extern const unsigned short LOG2E[];
+extern const unsigned short SQ2OPI[];
+extern const unsigned short LOGE2[];
+extern const unsigned short LOGSQ2[];
+extern const unsigned short THPIO4[];
+extern const unsigned short TWOOPI[];
+extern const unsigned short INFINITY[];
+extern const unsigned short NAN[];
+extern const unsigned short NEGZERO[];
+#endif

+ 83 - 0
components/external/espruino/libs/math/cosh.c

@@ -0,0 +1,83 @@
+/*							cosh.c
+ *
+ *	Hyperbolic cosine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, cosh();
+ *
+ * y = cosh( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns hyperbolic cosine of argument in the range MINLOG to
+ * MAXLOG.
+ *
+ * cosh(x)  =  ( exp(x) + exp(-x) )/2.
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       +- 88       50000       4.0e-17     7.7e-18
+ *    IEEE     +-MAXLOG     30000       2.6e-16     5.7e-17
+ *
+ *
+ * ERROR MESSAGES:
+ *
+ *   message         condition      value returned
+ * cosh overflow    |x| > MAXLOG       MAXNUM
+ *
+ *
+ */
+
+/*							cosh.c */
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1985, 1995, 2000 by Stephen L. Moshier
+*/
+
+#include "mconf.h"
+#ifdef ANSIPROT
+extern double exp ( double );
+extern int isnan ( double );
+extern int isfinite ( double );
+#else
+double exp();
+int isnan(), isfinite();
+#endif
+extern double MAXLOG, INFINITY, LOGE2;
+
+double cosh(x)
+double x;
+{
+double y;
+
+#ifdef NANS
+if( isnan(x) )
+	return(x);
+#endif
+if( x < 0 )
+	x = -x;
+if( x > (MAXLOG + LOGE2) )
+	{
+	mtherr( "cosh", OVERFLOW );
+	return( INFINITY );
+	}	
+if( x >= (MAXLOG - LOGE2) )
+	{
+	y = exp(0.5 * x);
+	y = (0.5 * y) * y;
+	return(y);
+	}
+y = exp(x);
+y = 0.5 * (y + 1.0 / y);
+return( y );
+}

+ 99 - 0
components/external/espruino/libs/math/descrip.mms

@@ -0,0 +1,99 @@
+CFLAGS=		/DEBUG/NOLIST
+hfiles=		mconf.h-
+
+ofiles=		acosh.obj-
+		asin.obj-
+		asinh.obj-
+		atan.obj-
+		atanh.obj-
+		cbrt.obj-
+		chbevl.obj-
+		const.obj-
+		cosh.obj-
+		drand.obj-
+		exp.obj-
+		exp10.obj-
+		fabs.obj-
+		floor.obj-
+		log.obj-
+		log10.obj-
+		polevl.obj-
+		pow.obj-
+		powi.obj-
+		round.obj-
+		sin.obj-
+		sinh.obj-
+		tan.obj-
+		tanh.obj-
+		unity.obj-
+		sqrt.obj-
+		floor.obj-
+		polevl.obj-
+		mtherr.obj
+
+mtst.exe : $(ofiles)
+	LINK  mtst/option
+acosh.obj : acosh.c,$(HFILES)
+    CC $(CFLAGS) acosh
+asin.obj : asin.c,$(HFILES)
+    CC $(CFLAGS) asin
+asinh.obj : asinh.c,$(HFILES)
+    CC $(CFLAGS) asinh
+atan.obj : atan.c,$(HFILES)
+    CC $(CFLAGS) atan
+atan.obj : atan.c,$(HFILES)
+    CC $(CFLAGS) atan
+atanh.obj : atanh.c,$(HFILES)
+    CC $(CFLAGS) atanh
+cbrt.obj : cbrt.c,$(HFILES)
+    CC $(CFLAGS) cbrt
+chbevl.obj : chbevl.c,$(HFILES)
+    CC $(CFLAGS) chbevl
+const.obj : const.c,$(HFILES)
+    CC $(CFLAGS) const
+cosh.obj : cosh.c,$(HFILES)
+    CC $(CFLAGS) cosh
+drand.obj : drand.c,$(HFILES)
+    CC $(CFLAGS) drand
+exp.obj : exp.c,$(HFILES)
+    CC $(CFLAGS) exp
+exp10.obj : exp10.c,$(HFILES)
+    CC $(CFLAGS) exp10
+fabs.obj : fabs.c,$(HFILES)
+    CC $(CFLAGS) fabs
+floor.obj : floor.c,$(HFILES)
+    CC $(CFLAGS) floor
+log.obj : log.c,$(HFILES)
+    CC $(CFLAGS) log
+log10.obj : log10.c,$(HFILES)
+    CC $(CFLAGS) log10
+polevl.obj : polevl.c,$(HFILES)
+    CC $(CFLAGS) polevl
+pow.obj : pow.c,$(HFILES)
+    CC $(CFLAGS) pow
+powi.obj : powi.c,$(HFILES)
+    CC $(CFLAGS) powi
+round.obj : round.c,$(HFILES)
+    CC $(CFLAGS) round
+sin.obj : sin.c,$(HFILES)
+    CC $(CFLAGS) sin
+sinh.obj : sinh.c,$(HFILES)
+    CC $(CFLAGS) sinh
+tan.obj : tan.c,$(HFILES)
+    CC $(CFLAGS) tan
+tanh.obj : tanh.c,$(HFILES)
+    CC $(CFLAGS) tanh
+unity.obj : unity.c,$(HFILES)
+    CC $(CFLAGS) unity
+sqrt.obj : sqrt.c,$(HFILES)
+    CC $(CFLAGS) sqrt
+mtherr.obj : mtherr.c,$(HFILES)
+    CC $(CFLAGS) mtherr
+
+
+
+
+
+
+
+

+ 161 - 0
components/external/espruino/libs/math/drand.c

@@ -0,0 +1,161 @@
+/*							drand.c
+ *
+ *	Pseudorandom number generator
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double y, drand();
+ *
+ * drand( &y );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Yields a random number 1.0 <= y < 2.0.
+ *
+ * The three-generator congruential algorithm by Brian
+ * Wichmann and David Hill (BYTE magazine, March, 1987,
+ * pp 127-8) is used. The period, given by them, is
+ * 6953607871644.
+ *
+ * Versions invoked by the different arithmetic compile
+ * time options DEC, IBMPC, and MIEEE, produce
+ * approximately the same sequences, differing only in the
+ * least significant bits of the numbers. The UNK option
+ * implements the algorithm as recommended in the BYTE
+ * article.  It may be used on all computers. However,
+ * the low order bits of a double precision number may
+ * not be adequately random, and may vary due to arithmetic
+ * implementation details on different computers.
+ *
+ * The other compile options generate an additional random
+ * integer that overwrites the low order bits of the double
+ * precision number.  This reduces the period by a factor of
+ * two but tends to overcome the problems mentioned.
+ *
+ */
+
+
+/*  Three-generator random number algorithm
+ * of Brian Wichmann and David Hill
+ * BYTE magazine, March, 1987 pp 127-8
+ *
+ * The period, given by them, is (p-1)(q-1)(r-1)/4 = 6.95e12.
+ */
+
+#include "mconf.h"
+#ifdef ANSIPROT
+static int ranwh ( void );
+#else
+static int ranwh();
+#endif
+
+static int sx = 1;
+static int sy = 10000;
+static int sz = 3000;
+
+static union {
+ double d;
+ unsigned short s[4];
+} unkans;
+
+/* This function implements the three
+ * congruential generators.
+ */
+ 
+static int ranwh()
+{
+int r, s;
+
+/*  sx = sx * 171 mod 30269 */
+r = sx/177;
+s = sx - 177 * r;
+sx = 171 * s - 2 * r;
+if( sx < 0 )
+	sx += 30269;
+
+
+/* sy = sy * 172 mod 30307 */
+r = sy/176;
+s = sy - 176 * r;
+sy = 172 * s - 35 * r;
+if( sy < 0 )
+	sy += 30307;
+
+/* sz = 170 * sz mod 30323 */
+r = sz/178;
+s = sz - 178 * r;
+sz = 170 * s - 63 * r;
+if( sz < 0 )
+	sz += 30323;
+/* The results are in static sx, sy, sz. */
+return 0;
+}
+
+/*	drand.c
+ *
+ * Random double precision floating point number between 1 and 2.
+ *
+ * C callable:
+ *	drand( &x );
+ */
+
+int drand( a )
+double *a;
+{
+unsigned short r;
+#ifdef DEC
+unsigned short s, t;
+#endif
+
+/* This algorithm of Wichmann and Hill computes a floating point
+ * result:
+ */
+ranwh();
+unkans.d = sx/30269.0  +  sy/30307.0  +  sz/30323.0;
+r = unkans.d;
+unkans.d -= r;
+unkans.d += 1.0;
+
+/* if UNK option, do nothing further.
+ * Otherwise, make a random 16 bit integer
+ * to overwrite the least significant word
+ * of unkans.
+ */
+#ifdef UNK
+/* do nothing */
+#else
+ranwh();
+r = sx * sy + sz;
+#endif
+
+#ifdef DEC
+/* To make the numbers as similar as possible
+ * in all arithmetics, the random integer has
+ * to be inserted 3 bits higher up in a DEC number.
+ * An alternative would be put it 3 bits lower down
+ * in all the other number types.
+ */
+s = unkans.s[2];
+t = s & 07;	/* save these bits to put in at the bottom */
+s &= 0177770;
+s |= (r >> 13) & 07;
+unkans.s[2] = s;
+t |= r << 3;
+unkans.s[3] = t;
+#endif
+
+#ifdef IBMPC
+unkans.s[0] = r;
+#endif
+
+#ifdef MIEEE
+unkans.s[3] = r;
+#endif
+
+*a = unkans.d;
+return 0;
+}

+ 543 - 0
components/external/espruino/libs/math/dtestvec.c

@@ -0,0 +1,543 @@
+
+/* Test vectors for math functions.
+   See C9X section F.9.  */
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1998, 2000 by Stephen L. Moshier
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+int isfinite (double);
+
+/* C9X spells lgam lgamma.  */
+#define GLIBC2 0
+
+extern double PI;
+const static double MPI, PIO2, MPIO2, PIO4, MPIO4, THPIO4, MTHPIO4;
+
+#if 0
+#define PI 3.141592653589793238463E0
+#define PIO2 1.570796326794896619231E0
+#define PIO4 7.853981633974483096157E-1
+#define THPIO4 2.35619449019234492884698
+#define SQRT2 1.414213562373095048802E0
+#define SQRTH 7.071067811865475244008E-1
+#define INF (1.0/0.0)
+#define MINF (-1.0/0.0)
+#endif
+
+extern double MACHEP, SQRTH, SQRT2;
+extern double NAN, INFINITY, NEGZERO;
+const static double INF, MINF;
+const static double ZERO, MZERO, HALF, MHALF, ONE, MONE, TWO, MTWO, THREE, MTHREE;
+/* #define NAN (1.0/0.0 - 1.0/0.0) */
+
+/* Functions of one variable.  */
+double log (double);
+double exp ( double);
+double atan (double);
+double sin (double);
+double cos (double);
+double tan (double);
+double acos (double);
+double asin (double);
+double acosh (double);
+double asinh (double);
+double atanh (double);
+double sinh (double);
+double cosh (double);
+double tanh (double);
+double exp2 (double);
+double expm1 (double);
+double log10 (double);
+double log1p (double);
+double log2 (double);
+double fabs (double);
+double erf (double);
+double erfc (double);
+double gamma (double);
+double floor (double);
+double ceil (double);
+double cbrt (double);
+#if GLIBC2
+double lgamma (double);
+#else
+double lgam (double);
+#endif
+
+struct oneargument
+  {
+    char *name;			/* Name of the function. */
+    double (*func) (double);
+    double *arg1;
+    double *answer;
+    int thresh;			/* Error report threshold. */
+  };
+
+struct oneargument test1[] =
+{
+  {"atan", atan, &ONE, &PIO4, 0},
+  {"sin", sin, &PIO2, &ONE, 0},
+#if 0
+  {"cos", cos, &PIO4, &SQRTH, 0},
+  {"sin", sin, 32767., 1.8750655394138942394239E-1, 0},
+  {"cos", cos, 32767., 9.8226335176928229845654E-1, 0},
+  {"tan", tan, 32767., 1.9089234430221485740826E-1, 0},
+  {"sin", sin, 8388607., 9.9234509376961249835628E-1, 0},
+  {"cos", cos, 8388607., -1.2349580912475928183718E-1, 0},
+  {"tan", tan, 8388607., -8.0354556223613614748329E0, 0},
+  /*
+  {"sin", sin, 2147483647., -7.2491655514455639054829E-1, 0},
+  {"cos", cos, 2147483647., -6.8883669187794383467976E-1, 0},
+  {"tan", tan, 2147483647., 1.0523779637351339136698E0, 0},
+  */
+  {"cos", cos, &PIO2, 6.1232339957367574e-17, 1},
+  {"sin", sin, &PIO4, &SQRTH, 1},
+#endif
+  {"acos", acos, &NAN, &NAN, 0},
+  {"acos", acos, &ONE, &ZERO, 0},
+  {"acos", acos, &TWO, &NAN, 0},
+  {"acos", acos, &MTWO, &NAN, 0},
+  {"asin", asin, &NAN, &NAN, 0},
+  {"asin", asin, &ZERO, &ZERO, 0},
+  {"asin", asin, &MZERO, &MZERO, 0},
+  {"asin", asin, &TWO, &NAN, 0},
+  {"asin", asin, &MTWO, &NAN, 0},
+  {"atan", atan, &NAN, &NAN, 0},
+  {"atan", atan, &ZERO, &ZERO, 0},
+  {"atan", atan, &MZERO, &MZERO, 0},
+  {"atan", atan, &INF, &PIO2, 0},
+  {"atan", atan, &MINF, &MPIO2, 0},
+  {"cos", cos, &NAN, &NAN, 0},
+  {"cos", cos, &ZERO, &ONE, 0},
+  {"cos", cos, &MZERO, &ONE, 0},
+  {"cos", cos, &INF, &NAN, 0},
+  {"cos", cos, &MINF, &NAN, 0},
+  {"sin", sin, &NAN, &NAN, 0},
+  {"sin", sin, &MZERO, &MZERO, 0},
+  {"sin", sin, &ZERO, &ZERO, 0},
+  {"sin", sin, &INF, &NAN, 0},
+  {"sin", sin, &MINF, &NAN, 0},
+  {"tan", tan, &NAN, &NAN, 0},
+  {"tan", tan, &ZERO, &ZERO, 0},
+  {"tan", tan, &MZERO, &MZERO, 0},
+  {"tan", tan, &INF, &NAN, 0},
+  {"tan", tan, &MINF, &NAN, 0},
+  {"acosh", acosh, &NAN, &NAN, 0},
+  {"acosh", acosh, &ONE, &ZERO, 0},
+  {"acosh", acosh, &INF, &INF, 0},
+  {"acosh", acosh, &HALF, &NAN, 0},
+  {"acosh", acosh, &MONE, &NAN, 0},
+  {"asinh", asinh, &NAN, &NAN, 0},
+  {"asinh", asinh, &ZERO, &ZERO, 0},
+  {"asinh", asinh, &MZERO, &MZERO, 0},
+  {"asinh", asinh, &INF, &INF, 0},
+  {"asinh", asinh, &MINF, &MINF, 0},
+  {"atanh", atanh, &NAN, &NAN, 0},
+  {"atanh", atanh, &ZERO, &ZERO, 0},
+  {"atanh", atanh, &MZERO, &MZERO, 0},
+  {"atanh", atanh, &ONE, &INF, 0},
+  {"atanh", atanh, &MONE, &MINF, 0},
+  {"atanh", atanh, &TWO, &NAN, 0},
+  {"atanh", atanh, &MTWO, &NAN, 0},
+  {"cosh", cosh, &NAN, &NAN, 0},
+  {"cosh", cosh, &ZERO, &ONE, 0},
+  {"cosh", cosh, &MZERO, &ONE, 0},
+  {"cosh", cosh, &INF, &INF, 0},
+  {"cosh", cosh, &MINF, &INF, 0},
+  {"sinh", sinh, &NAN, &NAN, 0},
+  {"sinh", sinh, &ZERO, &ZERO, 0},
+  {"sinh", sinh, &MZERO, &MZERO, 0},
+  {"sinh", sinh, &INF, &INF, 0},
+  {"sinh", sinh, &MINF, &MINF, 0},
+  {"tanh", tanh, &NAN, &NAN, 0},
+  {"tanh", tanh, &ZERO, &ZERO, 0},
+  {"tanh", tanh, &MZERO, &MZERO, 0},
+  {"tanh", tanh, &INF, &ONE, 0},
+  {"tanh", tanh, &MINF, &MONE, 0},
+  {"exp", exp, &NAN, &NAN, 0},
+  {"exp", exp, &ZERO, &ONE, 0},
+  {"exp", exp, &MZERO, &ONE, 0},
+  {"exp", exp, &INF, &INF, 0},
+  {"exp", exp, &MINF, &ZERO, 0},
+#if !GLIBC2
+  {"exp2", exp2, &NAN, &NAN, 0},
+  {"exp2", exp2, &ZERO, &ONE, 0},
+  {"exp2", exp2, &MZERO, &ONE, 0},
+  {"exp2", exp2, &INF, &INF, 0},
+  {"exp2", exp2, &MINF, &ZERO, 0},
+#endif
+  {"expm1", expm1, &NAN, &NAN, 0},
+  {"expm1", expm1, &ZERO, &ZERO, 0},
+  {"expm1", expm1, &MZERO, &MZERO, 0},
+  {"expm1", expm1, &INF, &INF, 0},
+  {"expm1", expm1, &MINF, &MONE, 0},
+  {"log", log, &NAN, &NAN, 0},
+  {"log", log, &ZERO, &MINF, 0},
+  {"log", log, &MZERO, &MINF, 0},
+  {"log", log, &ONE, &ZERO, 0},
+  {"log", log, &MONE, &NAN, 0},
+  {"log", log, &INF, &INF, 0},
+  {"log10", log10, &NAN, &NAN, 0},
+  {"log10", log10, &ZERO, &MINF, 0},
+  {"log10", log10, &MZERO, &MINF, 0},
+  {"log10", log10, &ONE, &ZERO, 0},
+  {"log10", log10, &MONE, &NAN, 0},
+  {"log10", log10, &INF, &INF, 0},
+  {"log1p", log1p, &NAN, &NAN, 0},
+  {"log1p", log1p, &ZERO, &ZERO, 0},
+  {"log1p", log1p, &MZERO, &MZERO, 0},
+  {"log1p", log1p, &MONE, &MINF, 0},
+  {"log1p", log1p, &MTWO, &NAN, 0},
+  {"log1p", log1p, &INF, &INF, 0},
+#if !GLIBC2
+  {"log2", log2, &NAN, &NAN, 0},
+  {"log2", log2, &ZERO, &MINF, 0},
+  {"log2", log2, &MZERO, &MINF, 0},
+  {"log2", log2, &MONE, &NAN, 0},
+  {"log2", log2, &INF, &INF, 0},
+#endif
+  /*  {"fabs", fabs, NAN, NAN, 0}, */
+  {"fabs", fabs, &ONE, &ONE, 0},
+  {"fabs", fabs, &MONE, &ONE, 0},
+  {"fabs", fabs, &ZERO, &ZERO, 0},
+  {"fabs", fabs, &MZERO, &ZERO, 0},
+  {"fabs", fabs, &INF, &INF, 0},
+  {"fabs", fabs, &MINF, &INF, 0},
+  {"cbrt", cbrt, &NAN, &NAN, 0},
+  {"cbrt", cbrt, &ZERO, &ZERO, 0},
+  {"cbrt", cbrt, &MZERO, &MZERO, 0},
+  {"cbrt", cbrt, &INF, &INF, 0},
+  {"cbrt", cbrt, &MINF, &MINF, 0},
+  {"erf", erf, &NAN, &NAN, 0},
+  {"erf", erf, &ZERO, &ZERO, 0},
+  {"erf", erf, &MZERO, &MZERO, 0},
+  {"erf", erf, &INF, &ONE, 0},
+  {"erf", erf, &MINF, &MONE, 0},
+  {"erfc", erfc, &NAN, &NAN, 0},
+  {"erfc", erfc, &INF, &ZERO, 0},
+  {"erfc", erfc, &MINF, &TWO, 0},
+  {"gamma", gamma, &NAN, &NAN, 0},
+  {"gamma", gamma, &INF, &INF, 0},
+  {"gamma", gamma, &MONE, &NAN, 0},
+  {"gamma", gamma, &ZERO, &NAN, 0},
+  {"gamma", gamma, &MINF, &NAN, 0},
+#if GLIBC2
+  {"lgamma", lgamma, &NAN, &NAN, 0},
+  {"lgamma", lgamma, &INF, &INF, 0},
+  {"lgamma", lgamma, &MONE, &INF, 0},
+  {"lgamma", lgamma, &ZERO, &INF, 0},
+  {"lgamma", lgamma, &MINF, &INF, 0},
+#else
+  {"lgam", lgam, &NAN, &NAN, 0},
+  {"lgam", lgam, &INF, &INF, 0},
+  {"lgam", lgam, &MONE, &INF, 0},
+  {"lgam", lgam, &ZERO, &INF, 0},
+  {"lgam", lgam, &MINF, &INF, 0},
+#endif
+  {"ceil", ceil, &NAN, &NAN, 0},
+  {"ceil", ceil, &ZERO, &ZERO, 0},
+  {"ceil", ceil, &MZERO, &MZERO, 0},
+  {"ceil", ceil, &INF, &INF, 0},
+  {"ceil", ceil, &MINF, &MINF, 0},
+  {"floor", floor, &NAN, &NAN, 0},
+  {"floor", floor, &ZERO, &ZERO, 0},
+  {"floor", floor, &MZERO, &MZERO, 0},
+  {"floor", floor, &INF, &INF, 0},
+  {"floor", floor, &MINF, &MINF, 0},
+  {"null", NULL, &ZERO, &ZERO, 0},
+};
+
+/* Functions of two variables.  */
+double atan2 (double, double);
+double pow (double, double);
+
+struct twoarguments
+  {
+    char *name;			/* Name of the function. */
+    double (*func) (double, double);
+    double *arg1;
+    double *arg2;
+    double *answer;
+    int thresh;
+  };
+
+struct twoarguments test2[] =
+{
+  {"atan2", atan2, &ZERO, &ONE, &ZERO, 0},
+  {"atan2", atan2, &MZERO, &ONE, &MZERO, 0},
+  {"atan2", atan2, &ZERO, &ZERO, &ZERO, 0},
+  {"atan2", atan2, &MZERO, &ZERO, &MZERO, 0},
+  {"atan2", atan2, &ZERO, &MONE, &PI, 0},
+  {"atan2", atan2, &MZERO, &MONE, &MPI, 0},
+  {"atan2", atan2, &ZERO, &MZERO, &PI, 0},
+  {"atan2", atan2, &MZERO, &MZERO, &MPI, 0},
+  {"atan2", atan2, &ONE, &ZERO, &PIO2, 0},
+  {"atan2", atan2, &ONE, &MZERO, &PIO2, 0},
+  {"atan2", atan2, &MONE, &ZERO, &MPIO2, 0},
+  {"atan2", atan2, &MONE, &MZERO, &MPIO2, 0},
+  {"atan2", atan2, &ONE, &INF, &ZERO, 0},
+  {"atan2", atan2, &MONE, &INF, &MZERO, 0},
+  {"atan2", atan2, &INF, &ONE, &PIO2, 0},
+  {"atan2", atan2, &INF, &MONE, &PIO2, 0},
+  {"atan2", atan2, &MINF, &ONE, &MPIO2, 0},
+  {"atan2", atan2, &MINF, &MONE, &MPIO2, 0},
+  {"atan2", atan2, &ONE, &MINF, &PI, 0},
+  {"atan2", atan2, &MONE, &MINF, &MPI, 0},
+  {"atan2", atan2, &INF, &INF, &PIO4, 0},
+  {"atan2", atan2, &MINF, &INF, &MPIO4, 0},
+  {"atan2", atan2, &INF, &MINF, &THPIO4, 0},
+  {"atan2", atan2, &MINF, &MINF, &MTHPIO4, 0},
+  {"atan2", atan2, &ONE, &ONE, &PIO4, 0},
+  {"atan2", atan2, &NAN, &ONE, &NAN, 0},
+  {"atan2", atan2, &ONE, &NAN, &NAN, 0},
+  {"atan2", atan2, &NAN, &NAN, &NAN, 0},
+  {"pow", pow, &ONE, &ZERO, &ONE, 0},
+  {"pow", pow, &ONE, &MZERO, &ONE, 0},
+  {"pow", pow, &MONE, &ZERO, &ONE, 0},
+  {"pow", pow, &MONE, &MZERO, &ONE, 0},
+  {"pow", pow, &INF, &ZERO, &ONE, 0},
+  {"pow", pow, &INF, &MZERO, &ONE, 0},
+  {"pow", pow, &NAN, &ZERO, &ONE, 0},
+  {"pow", pow, &NAN, &MZERO, &ONE, 0},
+  {"pow", pow, &TWO, &INF, &INF, 0},
+  {"pow", pow, &MTWO, &INF, &INF, 0},
+  {"pow", pow, &HALF, &INF, &ZERO, 0},
+  {"pow", pow, &MHALF, &INF, &ZERO, 0},
+  {"pow", pow, &TWO, &MINF, &ZERO, 0},
+  {"pow", pow, &MTWO, &MINF, &ZERO, 0},
+  {"pow", pow, &HALF, &MINF, &INF, 0},
+  {"pow", pow, &MHALF, &MINF, &INF, 0},
+  {"pow", pow, &INF, &HALF, &INF, 0},
+  {"pow", pow, &INF, &TWO, &INF, 0},
+  {"pow", pow, &INF, &MHALF, &ZERO, 0},
+  {"pow", pow, &INF, &MTWO, &ZERO, 0},
+  {"pow", pow, &MINF, &THREE, &MINF, 0},
+  {"pow", pow, &MINF, &TWO, &INF, 0},
+  {"pow", pow, &MINF, &MTHREE, &MZERO, 0},
+  {"pow", pow, &MINF, &MTWO, &ZERO, 0},
+  {"pow", pow, &NAN, &ONE, &NAN, 0},
+  {"pow", pow, &ONE, &NAN, &NAN, 0},
+  {"pow", pow, &NAN, &NAN, &NAN, 0},
+  {"pow", pow, &ONE, &INF, &NAN, 0},
+  {"pow", pow, &MONE, &INF, &NAN, 0},
+  {"pow", pow, &ONE, &MINF, &NAN, 0},
+  {"pow", pow, &MONE, &MINF, &NAN, 0},
+  {"pow", pow, &MTWO, &HALF, &NAN, 0},
+  {"pow", pow, &ZERO, &MTHREE, &INF, 0},
+  {"pow", pow, &MZERO, &MTHREE, &MINF, 0},
+  {"pow", pow, &ZERO, &MHALF, &INF, 0},
+  {"pow", pow, &MZERO, &MHALF, &INF, 0},
+  {"pow", pow, &ZERO, &THREE, &ZERO, 0},
+  {"pow", pow, &MZERO, &THREE, &MZERO, 0},
+  {"pow", pow, &ZERO, &HALF, &ZERO, 0},
+  {"pow", pow, &MZERO, &HALF, &ZERO, 0},
+  {"null", NULL, &ZERO, &ZERO, &ZERO, 0},
+};
+
+/* Integer functions of one variable.  */
+
+int isnan (double);
+int signbit (double);
+
+struct intans
+  {
+    char *name;			/* Name of the function. */
+    int (*func) (double);
+    double *arg1;
+    int ianswer;
+  };
+
+struct intans test3[] =
+{
+  {"isfinite", isfinite, &ZERO, 1},
+  {"isfinite", isfinite, &INF, 0},
+  {"isfinite", isfinite, &MINF, 0},
+  {"isnan", isnan, &NAN, 1},
+  {"isnan", isnan, &INF, 0},
+  {"isnan", isnan, &ZERO, 0},
+  {"isnan", isnan, &MZERO, 0},
+  {"signbit", signbit, &MZERO, 1},
+  {"signbit", signbit, &MONE, 1},
+  {"signbit", signbit, &ZERO, 0},
+  {"signbit", signbit, &ONE, 0},
+  {"signbit", signbit, &MINF, 1},
+  {"signbit", signbit, &INF, 0},
+  {"null", NULL, &ZERO, 0},
+};
+
+static volatile double x1;
+static volatile double x2;
+static volatile double y;
+static volatile double answer;
+
+void
+pvec(x)
+double x;
+{
+  union
+  {
+    double d;
+    unsigned short s[4];
+  } u;
+  int i;
+
+  u.d = x;
+  for (i = 0; i < 4; i++)
+    printf ("0x%04x ", u.s[i]);
+  printf ("\n");
+}
+
+
+int
+main ()
+{
+  int i, nerrors, k, ianswer, ntests;
+  double (*fun1) (double);
+  double (*fun2) (double, double);
+  int (*fun3) (double);
+  double e;
+  union
+    {
+      double d;
+      char c[8];
+    } u, v;
+
+  ZERO = 0.0;
+  MZERO = NEGZERO;
+  HALF = 0.5;
+  MHALF = -HALF;
+  ONE = 1.0;
+  MONE = -ONE;
+  TWO = 2.0;
+  MTWO = -TWO;
+  THREE = 3.0;
+  MTHREE = -THREE;
+  INF = INFINITY;
+  MINF = -INFINITY;
+  MPI = -PI;
+  PIO2 = 0.5 * PI;
+  MPIO2 = -PIO2;
+  PIO4 = 0.5 * PIO2;
+  MPIO4 = -PIO4;
+  THPIO4 = 3.0 * PIO4;
+  MTHPIO4 = -THPIO4;
+
+  nerrors = 0;
+  ntests = 0;
+  i = 0;
+  for (;;)
+    {
+      fun1 = test1[i].func;
+      if (fun1 == NULL)
+	break;
+      x1 = *(test1[i].arg1);
+      y = (*(fun1)) (x1);
+      answer = *(test1[i].answer);
+      if (test1[i].thresh == 0)
+	{
+	  v.d = answer;
+	  u.d = y;
+	  if (memcmp(u.c, v.c, 8) != 0)
+	    {
+	      if( isnan(v.d) && isnan(u.d) )
+		goto nxttest1;
+	      goto wrongone;
+	    }
+	  else
+	    goto nxttest1;
+	}
+      if (y != answer)
+	{
+	  e = y - answer;
+	  if (answer != 0.0)
+	    e = e / answer;
+	  if (e < 0)
+	    e = -e;
+	  if (e > test1[i].thresh * MACHEP)
+	    {
+wrongone:
+	      printf ("%s (%.16e) = %.16e\n    should be %.16e\n",
+		      test1[i].name, x1, y, answer);
+	      nerrors += 1;
+	    }
+	}
+nxttest1:
+      ntests += 1;
+      i += 1;
+    }
+
+  i = 0;
+  for (;;)
+    {
+      fun2 = test2[i].func;
+      if (fun2 == NULL)
+	break;
+      x1 = *(test2[i].arg1);
+      x2 = *(test2[i].arg2);
+      y = (*(fun2)) (x1, x2);
+      answer = *(test2[i].answer);
+      if (test2[i].thresh == 0)
+	{
+	  v.d = answer;
+	  u.d = y;
+	  if (memcmp(u.c, v.c, 8) != 0)
+	    {
+	      if( isnan(v.d) && isnan(u.d) )
+		goto nxttest2;
+#if 0
+	      if( isnan(v.d) )
+		pvec(v.d);
+	      if( isnan(u.d) )
+		pvec(u.d);
+#endif
+	    goto wrongtwo;
+	    }
+	  else
+	    goto nxttest2;
+	}
+      if (y != answer)
+	{
+	  e = y - answer;
+	  if (answer != 0.0)
+	    e = e / answer;
+	  if (e < 0)
+	    e = -e;
+	  if (e > test2[i].thresh * MACHEP)
+	    {
+wrongtwo:
+	      printf ("%s (%.16e, %.16e) = %.16e\n    should be %.16e\n",
+		      test2[i].name, x1, x2, y, answer);
+	      nerrors += 1;
+	    }
+	}
+nxttest2:
+      ntests += 1;
+      i += 1;
+    }
+
+
+  i = 0;
+  for (;;)
+    {
+      fun3 = test3[i].func;
+      if (fun3 == NULL)
+	break;
+      x1 = *(test3[i].arg1);
+      k = (*(fun3)) (x1);
+      ianswer = test3[i].ianswer;
+      if (k != ianswer)
+	{
+	  printf ("%s (%.16e) = %d\n    should be. %d\n",
+		  test3[i].name, x1, k, ianswer);
+	  nerrors += 1;
+	}
+      ntests += 1;
+      i += 1;
+    }
+
+  printf ("testvect: %d errors in %d tests\n", nerrors, ntests);
+  exit (0);
+}

+ 203 - 0
components/external/espruino/libs/math/exp.c

@@ -0,0 +1,203 @@
+/*							exp.c
+ *
+ *	Exponential function
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, exp();
+ *
+ * y = exp( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns e (2.71828...) raised to the x power.
+ *
+ * Range reduction is accomplished by separating the argument
+ * into an integer k and fraction f such that
+ *
+ *     x    k  f
+ *    e  = 2  e.
+ *
+ * A Pade' form  1 + 2x P(x**2)/( Q(x**2) - P(x**2) )
+ * of degree 2/3 is used to approximate exp(f) in the basic
+ * interval [-0.5, 0.5].
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       +- 88       50000       2.8e-17     7.0e-18
+ *    IEEE      +- 708      40000       2.0e-16     5.6e-17
+ *
+ *
+ * Error amplification in the exponential function can be
+ * a serious matter.  The error propagation involves
+ * exp( X(1+delta) ) = exp(X) ( 1 + X*delta + ... ),
+ * which shows that a 1 lsb error in representing X produces
+ * a relative error of X times 1 lsb in the function.
+ * While the routine gives an accurate result for arguments
+ * that are exactly represented by a double precision
+ * computer number, the result contains amplified roundoff
+ * error for large arguments not exactly represented.
+ *
+ *
+ * ERROR MESSAGES:
+ *
+ *   message         condition      value returned
+ * exp underflow    x < MINLOG         0.0
+ * exp overflow     x > MAXLOG         INFINITY
+ *
+ */
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+
+/*	Exponential function	*/
+
+#include "mconf.h"
+
+#ifdef UNK
+
+const static double P[] = {
+ 1.26177193074810590878E-4,
+ 3.02994407707441961300E-2,
+ 9.99999999999999999910E-1,
+};
+const static double Q[] = {
+ 3.00198505138664455042E-6,
+ 2.52448340349684104192E-3,
+ 2.27265548208155028766E-1,
+ 2.00000000000000000009E0,
+};
+const static double C1 = 6.93145751953125E-1;
+const static double C2 = 1.42860682030941723212E-6;
+#endif
+
+#ifdef DEC
+static unsigned short P[] = {
+0035004,0047156,0127442,0057502,
+0036770,0033210,0063121,0061764,
+0040200,0000000,0000000,0000000,
+};
+static unsigned short Q[] = {
+0033511,0072665,0160662,0176377,
+0036045,0070715,0124105,0132777,
+0037550,0134114,0142077,0001637,
+0040400,0000000,0000000,0000000,
+};
+static unsigned short sc1[] = {0040061,0071000,0000000,0000000};
+#define C1 (*(double *)sc1)
+static unsigned short sc2[] = {0033277,0137216,0075715,0057117};
+#define C2 (*(double *)sc2)
+#endif
+
+#ifdef IBMPC
+static unsigned short P[] = {
+0x4be8,0xd5e4,0x89cd,0x3f20,
+0x2c7e,0x0cca,0x06d1,0x3f9f,
+0x0000,0x0000,0x0000,0x3ff0,
+};
+static unsigned short Q[] = {
+0x5fa0,0xbc36,0x2eb6,0x3ec9,
+0xb6c0,0xb508,0xae39,0x3f64,
+0xe074,0x9887,0x1709,0x3fcd,
+0x0000,0x0000,0x0000,0x4000,
+};
+static unsigned short sc1[] = {0x0000,0x0000,0x2e40,0x3fe6};
+#define C1 (*(double *)sc1)
+static unsigned short sc2[] = {0xabca,0xcf79,0xf7d1,0x3eb7};
+#define C2 (*(double *)sc2)
+#endif
+
+#ifdef MIEEE
+static unsigned short P[] = {
+0x3f20,0x89cd,0xd5e4,0x4be8,
+0x3f9f,0x06d1,0x0cca,0x2c7e,
+0x3ff0,0x0000,0x0000,0x0000,
+};
+static unsigned short Q[] = {
+0x3ec9,0x2eb6,0xbc36,0x5fa0,
+0x3f64,0xae39,0xb508,0xb6c0,
+0x3fcd,0x1709,0x9887,0xe074,
+0x4000,0x0000,0x0000,0x0000,
+};
+static unsigned short sc1[] = {0x3fe6,0x2e40,0x0000,0x0000};
+#define C1 (*(double *)sc1)
+static unsigned short sc2[] = {0x3eb7,0xf7d1,0xcf79,0xabca};
+#define C2 (*(double *)sc2)
+#endif
+
+#ifdef ANSIPROT
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern double floor ( double );
+extern double ldexp ( double, int );
+extern int isnan ( double );
+extern int isfinite ( double );
+#else
+double polevl(), p1evl(), floor(), ldexp();
+int isnan(), isfinite();
+#endif
+extern double LOGE2, LOG2E, MAXLOG, MINLOG, MAXNUM;
+#ifdef INFINITIES
+extern double INFINITY;
+#endif
+
+double exp(x)
+double x;
+{
+double px, xx;
+int n;
+
+#ifdef NANS
+if( isnan(x) )
+	return(x);
+#endif
+if( x > MAXLOG)
+	{
+#ifdef INFINITIES
+	return( INFINITY );
+#else
+	mtherr( "exp", OVERFLOW );
+	return( MAXNUM );
+#endif
+	}
+
+if( x < MINLOG )
+	{
+#ifndef INFINITIES
+	mtherr( "exp", UNDERFLOW );
+#endif
+	return(0.0);
+	}
+
+/* Express e**x = e**g 2**n
+ *   = e**g e**( n loge(2) )
+ *   = e**( g + n loge(2) )
+ */
+px = floor( LOG2E * x + 0.5 ); /* floor() truncates toward -infinity. */
+n = px;
+x -= px * C1;
+x -= px * C2;
+
+/* rational approximation for exponential
+ * of the fractional part:
+ * e**x = 1 + 2x P(x**2)/( Q(x**2) - P(x**2) )
+ */
+xx = x * x;
+px = x * polevl( xx, P, 2 );
+x =  px/( polevl( xx, Q, 3 ) - px );
+x = 1.0 + 2.0 * x;
+
+/* multiply by power of 2 */
+x = ldexp( x, n );
+return(x);
+}

+ 223 - 0
components/external/espruino/libs/math/exp10.c

@@ -0,0 +1,223 @@
+/*							exp10.c
+ *
+ *	Base 10 exponential function
+ *      (Common antilogarithm)
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, exp10();
+ *
+ * y = exp10( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns 10 raised to the x power.
+ *
+ * Range reduction is accomplished by expressing the argument
+ * as 10**x = 2**n 10**f, with |f| < 0.5 log10(2).
+ * The Pade' form
+ *
+ *    1 + 2x P(x**2)/( Q(x**2) - P(x**2) )
+ *
+ * is used to approximate 10**f.
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE     -307,+307    30000       2.2e-16     5.5e-17
+ * Test result from an earlier version (2.1):
+ *    DEC       -38,+38     70000       3.1e-17     7.0e-18
+ *
+ * ERROR MESSAGES:
+ *
+ *   message         condition      value returned
+ * exp10 underflow    x < -MAXL10        0.0
+ * exp10 overflow     x > MAXL10       MAXNUM
+ *
+ * DEC arithmetic: MAXL10 = 38.230809449325611792.
+ * IEEE arithmetic: MAXL10 = 308.2547155599167.
+ *
+ */
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1991, 2000 by Stephen L. Moshier
+*/
+
+
+#include "mconf.h"
+
+#ifdef UNK
+const static double P[] = {
+ 4.09962519798587023075E-2,
+ 1.17452732554344059015E1,
+ 4.06717289936872725516E2,
+ 2.39423741207388267439E3,
+};
+const static double Q[] = {
+/* 1.00000000000000000000E0,*/
+ 8.50936160849306532625E1,
+ 1.27209271178345121210E3,
+ 2.07960819286001865907E3,
+};
+/* const static double LOG102 = 3.01029995663981195214e-1; */
+const static double LOG210 = 3.32192809488736234787e0;
+const static double LG102A = 3.01025390625000000000E-1;
+const static double LG102B = 4.60503898119521373889E-6;
+/* const static double MAXL10 = 38.230809449325611792; */
+const static double MAXL10 = 308.2547155599167;
+#endif
+
+#ifdef DEC
+static unsigned short P[] = {
+0037047,0165657,0114061,0067234,
+0041073,0166243,0123052,0144643,
+0042313,0055720,0024032,0047443,
+0043025,0121714,0070232,0050007,
+};
+static unsigned short Q[] = {
+/*0040200,0000000,0000000,0000000,*/
+0041652,0027756,0071216,0050075,
+0042637,0001367,0077263,0136017,
+0043001,0174673,0024157,0133416,
+};
+/*
+static unsigned short L102[] = {0037632,0020232,0102373,0147770};
+#define LOG102 *(double *)L102
+*/
+static unsigned short L210[] = {0040524,0115170,0045715,0015613};
+#define LOG210 *(double *)L210
+static unsigned short L102A[] = {0037632,0020000,0000000,0000000,};
+#define LG102A *(double *)L102A
+static unsigned short L102B[] = {0033632,0102373,0147767,0114220,};
+#define LG102B *(double *)L102B
+static unsigned short MXL[] = {0041430,0166131,0047761,0154130,};
+#define MAXL10 ( *(double *)MXL )
+#endif
+
+#ifdef IBMPC
+static unsigned short P[] = {
+0x2dd4,0xf306,0xfd75,0x3fa4,
+0x5934,0x74c5,0x7d94,0x4027,
+0x49e4,0x0503,0x6b7a,0x4079,
+0x4a01,0x8e13,0xb479,0x40a2,
+};
+static unsigned short Q[] = {
+/*0x0000,0x0000,0x0000,0x3ff0,*/
+0xca08,0xce51,0x45fd,0x4055,
+0x7782,0xefd6,0xe05e,0x4093,
+0xf6e2,0x650d,0x3f37,0x40a0,
+};
+/*
+static unsigned short L102[] = {0x79ff,0x509f,0x4413,0x3fd3};
+#define LOG102 *(double *)L102
+*/
+static unsigned short L210[] = {0xa371,0x0979,0x934f,0x400a};
+#define LOG210 *(double *)L210
+static unsigned short L102A[] = {0x0000,0x0000,0x4400,0x3fd3,};
+#define LG102A *(double *)L102A
+static unsigned short L102B[] = {0xf312,0x79fe,0x509f,0x3ed3,};
+#define LG102B *(double *)L102B
+const static double MAXL10 = 308.2547155599167;
+#endif
+
+#ifdef MIEEE
+static unsigned short P[] = {
+0x3fa4,0xfd75,0xf306,0x2dd4,
+0x4027,0x7d94,0x74c5,0x5934,
+0x4079,0x6b7a,0x0503,0x49e4,
+0x40a2,0xb479,0x8e13,0x4a01,
+};
+static unsigned short Q[] = {
+/*0x3ff0,0x0000,0x0000,0x0000,*/
+0x4055,0x45fd,0xce51,0xca08,
+0x4093,0xe05e,0xefd6,0x7782,
+0x40a0,0x3f37,0x650d,0xf6e2,
+};
+/*
+static unsigned short L102[] = {0x3fd3,0x4413,0x509f,0x79ff};
+#define LOG102 *(double *)L102
+*/
+static unsigned short L210[] = {0x400a,0x934f,0x0979,0xa371};
+#define LOG210 *(double *)L210
+static unsigned short L102A[] = {0x3fd3,0x4400,0x0000,0x0000,};
+#define LG102A *(double *)L102A
+static unsigned short L102B[] = {0x3ed3,0x509f,0x79fe,0xf312,};
+#define LG102B *(double *)L102B
+const static double MAXL10 = 308.2547155599167;
+#endif
+
+#ifdef ANSIPROT
+extern double floor ( double );
+extern double ldexp ( double, int );
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern int isnan ( double );
+extern int isfinite ( double );
+#else
+double floor(), ldexp(), polevl(), p1evl();
+int isnan(), isfinite();
+#endif
+extern double MAXNUM;
+#ifdef INFINITIES
+extern double INFINITY;
+#endif
+
+double exp10(x)
+double x;
+{
+double px, xx;
+short n;
+
+#ifdef NANS
+if( isnan(x) )
+	return(x);
+#endif
+if( x > MAXL10 )
+	{
+#ifdef INFINITIES
+	return( INFINITY );
+#else
+	mtherr( "exp10", OVERFLOW );
+	return( MAXNUM );
+#endif
+	}
+
+if( x < -MAXL10 )	/* Would like to use MINLOG but can't */
+	{
+#ifndef INFINITIES
+	mtherr( "exp10", UNDERFLOW );
+#endif
+	return(0.0);
+	}
+
+/* Express 10**x = 10**g 2**n
+ *   = 10**g 10**( n log10(2) )
+ *   = 10**( g + n log10(2) )
+ */
+px = floor( LOG210 * x + 0.5 );
+n = px;
+x -= px * LG102A;
+x -= px * LG102B;
+
+/* rational approximation for exponential
+ * of the fractional part:
+ * 10**x = 1 + 2x P(x**2)/( Q(x**2) - P(x**2) )
+ */
+xx = x * x;
+px = x * polevl( xx, P, 3 );
+x =  px/( p1evl( xx, Q, 3 ) - px );
+x = 1.0 + ldexp( x, 1 );
+
+/* multiply by power of 2 */
+x = ldexp( x, n );
+
+return(x);
+}

+ 183 - 0
components/external/espruino/libs/math/exp2.c

@@ -0,0 +1,183 @@
+/*							exp2.c
+ *
+ *	Base 2 exponential function
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, exp2();
+ *
+ * y = exp2( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns 2 raised to the x power.
+ *
+ * Range reduction is accomplished by separating the argument
+ * into an integer k and fraction f such that
+ *     x    k  f
+ *    2  = 2  2.
+ *
+ * A Pade' form
+ *
+ *   1 + 2x P(x**2) / (Q(x**2) - x P(x**2) )
+ *
+ * approximates 2**x in the basic range [-0.5, 0.5].
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE    -1022,+1024   30000       1.8e-16     5.4e-17
+ *
+ *
+ * See exp.c for comments on error amplification.
+ *
+ *
+ * ERROR MESSAGES:
+ *
+ *   message         condition      value returned
+ * exp underflow    x < -MAXL2        0.0
+ * exp overflow     x > MAXL2         MAXNUM
+ *
+ * For DEC arithmetic, MAXL2 = 127.
+ * For IEEE arithmetic, MAXL2 = 1024.
+ */
+
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+
+
+#include "mconf.h"
+
+#ifdef UNK
+const static double P[] = {
+ 2.30933477057345225087E-2,
+ 2.02020656693165307700E1,
+ 1.51390680115615096133E3,
+};
+const static double Q[] = {
+/* 1.00000000000000000000E0,*/
+ 2.33184211722314911771E2,
+ 4.36821166879210612817E3,
+};
+#define MAXL2 1024.0
+#define MINL2 -1024.0
+#endif
+
+#ifdef DEC
+static unsigned short P[] = {
+0036675,0027102,0122327,0053227,
+0041241,0116724,0115412,0157355,
+0042675,0036404,0101733,0132226,
+};
+static unsigned short Q[] = {
+/*0040200,0000000,0000000,0000000,*/
+0042151,0027450,0077732,0160744,
+0043210,0100661,0077550,0056560,
+};
+#define MAXL2 127.0
+#define MINL2 -127.0
+#endif
+
+#ifdef IBMPC
+static unsigned short P[] = {
+0xead3,0x549a,0xa5c8,0x3f97,
+0x5bde,0x9361,0x33ba,0x4034,
+0x7693,0x907b,0xa7a0,0x4097,
+};
+static unsigned short Q[] = {
+/*0x0000,0x0000,0x0000,0x3ff0,*/
+0x5c3c,0x0ffb,0x25e5,0x406d,
+0x0bae,0x2fed,0x1036,0x40b1,
+};
+#define MAXL2 1024.0
+#define MINL2 -1022.0
+#endif
+
+#ifdef MIEEE
+static unsigned short P[] = {
+0x3f97,0xa5c8,0x549a,0xead3,
+0x4034,0x33ba,0x9361,0x5bde,
+0x4097,0xa7a0,0x907b,0x7693,
+};
+static unsigned short Q[] = {
+/*0x3ff0,0x0000,0x0000,0x0000,*/
+0x406d,0x25e5,0x0ffb,0x5c3c,
+0x40b1,0x1036,0x2fed,0x0bae,
+};
+#define MAXL2 1024.0
+#define MINL2 -1022.0
+#endif
+
+#ifdef ANSIPROT
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern double floor ( double );
+extern double ldexp ( double, int );
+extern int isnan ( double );
+extern int isfinite ( double );
+#else
+double polevl(), p1evl(), floor(), ldexp();
+int isnan(), isfinite();
+#endif
+#ifdef INFINITIES
+extern double INFINITY;
+#endif
+extern double MAXNUM;
+
+double exp2(x)
+double x;
+{
+double px, xx;
+short n;
+
+#ifdef NANS
+if( isnan(x) )
+	return(x);
+#endif
+if( x > MAXL2)
+	{
+#ifdef INFINITIES
+	return( INFINITY );
+#else
+	mtherr( "exp2", OVERFLOW );
+	return( MAXNUM );
+#endif
+	}
+
+if( x < MINL2 )
+	{
+#ifndef INFINITIES
+	mtherr( "exp2", UNDERFLOW );
+#endif
+	return(0.0);
+	}
+
+xx = x;	/* save x */
+/* separate into integer and fractional parts */
+px = floor(x+0.5);
+n = px;
+x = x - px;
+
+/* rational approximation
+ * exp2(x) = 1 +  2xP(xx)/(Q(xx) - P(xx))
+ * where xx = x**2
+ */
+xx = x * x;
+px = x * polevl( xx, P, 2 );
+x =  px / ( p1evl( xx, Q, 2 ) - px );
+x = 1.0 + ldexp( x, 1 );
+
+/* scale by power of 2 */
+x = ldexp( x, n );
+return(x);
+}

+ 56 - 0
components/external/espruino/libs/math/fabs.c

@@ -0,0 +1,56 @@
+/*							fabs.c
+ *
+ *		Absolute value
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y;
+ *
+ * y = fabs( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ * 
+ * Returns the absolute value of the argument.
+ *
+ */
+
+
+#include "mconf.h"
+/* Avoid using UNK if possible.  */
+#ifdef UNK
+#if BIGENDIAN
+#define MIEEE 1
+#else
+#define IBMPC 1
+#endif
+#endif
+
+double fabs(x)
+double x;
+{
+union
+  {
+    double d;
+    short i[4];
+  } u;
+
+u.d = x;
+#ifdef IBMPC
+    u.i[3] &= 0x7fff;
+#endif
+#ifdef MIEEE
+    u.i[0] &= 0x7fff;
+#endif
+#ifdef DEC
+    u.i[3] &= 0x7fff;
+#endif
+#ifdef UNK
+if( u.d < 0 )
+   u.d = -u.d;
+#endif
+return( u.d );
+}

+ 453 - 0
components/external/espruino/libs/math/floor.c

@@ -0,0 +1,453 @@
+/*							ceil()
+ *							floor()
+ *							frexp()
+ *							ldexp()
+ *							signbit()
+ *							isnan()
+ *							isfinite()
+ *
+ *	Floating point numeric utilities
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double ceil(), floor(), frexp(), ldexp();
+ * int signbit(), isnan(), isfinite();
+ * double x, y;
+ * int expnt, n;
+ *
+ * y = floor(x);
+ * y = ceil(x);
+ * y = frexp( x, &expnt );
+ * y = ldexp( x, n );
+ * n = signbit(x);
+ * n = isnan(x);
+ * n = isfinite(x);
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * All four routines return a double precision floating point
+ * result.
+ *
+ * floor() returns the largest integer less than or equal to x.
+ * It truncates toward minus infinity.
+ *
+ * ceil() returns the smallest integer greater than or equal
+ * to x.  It truncates toward plus infinity.
+ *
+ * frexp() extracts the exponent from x.  It returns an integer
+ * power of two to expnt and the significand between 0.5 and 1
+ * to y.  Thus  x = y * 2**expn.
+ *
+ * ldexp() multiplies x by 2**n.
+ *
+ * signbit(x) returns 1 if the sign bit of x is 1, else 0.
+ *
+ * These functions are part of the standard C run time library
+ * for many but not all C compilers.  The ones supplied are
+ * written in C for either DEC or IEEE arithmetic.  They should
+ * be used only if your compiler library does not already have
+ * them.
+ *
+ * The IEEE versions assume that denormal numbers are implemented
+ * in the arithmetic.  Some modifications will be required if
+ * the arithmetic has abrupt rather than gradual underflow.
+ */
+
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+
+#include "mconf.h"
+
+#ifdef UNK
+/* ceil(), floor(), frexp(), ldexp() may need to be rewritten. */
+#undef UNK
+#if BIGENDIAN
+#define MIEEE 1
+#else
+#define IBMPC 1
+#endif
+#endif
+
+#ifdef DEC
+#define EXPMSK 0x807f
+#define MEXP 255
+#define NBITS 56
+#endif
+
+#ifdef IBMPC
+#define EXPMSK 0x800f
+#define MEXP 0x7ff
+#define NBITS 53
+#endif
+
+#ifdef MIEEE
+#define EXPMSK 0x800f
+#define MEXP 0x7ff
+#define NBITS 53
+#endif
+
+extern double MAXNUM, NEGZERO;
+#ifdef ANSIPROT
+double floor ( double );
+int isnan ( double );
+int isfinite ( double );
+double ldexp ( double, int );
+#else
+double floor();
+int isnan(), isfinite();
+double ldexp();
+#endif
+
+double ceil(x)
+double x;
+{
+double y;
+
+#ifdef UNK
+mtherr( "ceil", DOMAIN );
+return(0.0);
+#endif
+#ifdef NANS
+if( isnan(x) )
+	return( x );
+#endif
+#ifdef INFINITIES
+if(!isfinite(x))
+	return(x);
+#endif
+
+y = floor(x);
+if( y < x )
+	y += 1.0;
+#ifdef MINUSZERO
+if( y == 0.0 && x < 0.0 )
+	return( NEGZERO );
+#endif
+return(y);
+}
+
+
+
+
+/* Bit clearing masks: */
+
+static unsigned short bmask[] = {
+0xffff,
+0xfffe,
+0xfffc,
+0xfff8,
+0xfff0,
+0xffe0,
+0xffc0,
+0xff80,
+0xff00,
+0xfe00,
+0xfc00,
+0xf800,
+0xf000,
+0xe000,
+0xc000,
+0x8000,
+0x0000,
+};
+
+
+
+
+
+double floor(x)
+double x;
+{
+union
+	{
+	double y;
+	unsigned short sh[4];
+	} u;
+unsigned short *p;
+int e;
+
+#ifdef UNK
+mtherr( "floor", DOMAIN );
+return(0.0);
+#endif
+#ifdef NANS
+if( isnan(x) )
+	return( x );
+#endif
+#ifdef INFINITIES
+if(!isfinite(x))
+	return(x);
+#endif
+#ifdef MINUSZERO
+if(x == 0.0L)
+	return(x);
+#endif
+u.y = x;
+/* find the exponent (power of 2) */
+#ifdef DEC
+p = (unsigned short *)&u.sh[0];
+e = (( *p  >> 7) & 0377) - 0201;
+p += 3;
+#endif
+
+#ifdef IBMPC
+p = (unsigned short *)&u.sh[3];
+e = (( *p >> 4) & 0x7ff) - 0x3ff;
+p -= 3;
+#endif
+
+#ifdef MIEEE
+p = (unsigned short *)&u.sh[0];
+e = (( *p >> 4) & 0x7ff) - 0x3ff;
+p += 3;
+#endif
+
+if( e < 0 )
+	{
+	if( u.y < 0.0 )
+		return( -1.0 );
+	else
+		return( 0.0 );
+	}
+
+e = (NBITS -1) - e;
+/* clean out 16 bits at a time */
+while( e >= 16 )
+	{
+#ifdef IBMPC
+	*p++ = 0;
+#endif
+
+#ifdef DEC
+	*p-- = 0;
+#endif
+
+#ifdef MIEEE
+	*p-- = 0;
+#endif
+	e -= 16;
+	}
+
+/* clear the remaining bits */
+if( e > 0 )
+	*p &= bmask[e];
+
+if( (x < 0) && (u.y != x) )
+	u.y -= 1.0;
+
+return(u.y);
+}
+
+
+
+
+double frexp( x, pw2 )
+double x;
+int *pw2;
+{
+union
+	{
+	double y;
+	unsigned short sh[4];
+	} u;
+int i;
+#ifdef DENORMAL
+int k;
+#endif
+short *q;
+
+u.y = x;
+
+#ifdef UNK
+mtherr( "frexp", DOMAIN );
+return(0.0);
+#endif
+
+#ifdef IBMPC
+q = (short *)&u.sh[3];
+#endif
+
+#ifdef DEC
+q = (short *)&u.sh[0];
+#endif
+
+#ifdef MIEEE
+q = (short *)&u.sh[0];
+#endif
+
+/* find the exponent (power of 2) */
+#ifdef DEC
+i  = ( *q >> 7) & 0377;
+if( i == 0 )
+	{
+	*pw2 = 0;
+	return(0.0);
+	}
+i -= 0200;
+*pw2 = i;
+*q &= 0x807f;	/* strip all exponent bits */
+*q |= 040000;	/* mantissa between 0.5 and 1 */
+return(u.y);
+#endif
+
+#ifdef IBMPC
+i  = ( *q >> 4) & 0x7ff;
+if( i != 0 )
+	goto ieeedon;
+#endif
+
+#ifdef MIEEE
+i  =  *q >> 4;
+i &= 0x7ff;
+if( i != 0 )
+	goto ieeedon;
+#ifdef DENORMAL
+
+#else
+*pw2 = 0;
+return(0.0);
+#endif
+
+#endif
+
+
+#ifndef DEC
+/* Number is denormal or zero */
+#ifdef DENORMAL
+if( u.y == 0.0 )
+	{
+	*pw2 = 0;
+	return( 0.0 );
+	}
+
+
+/* Handle denormal number. */
+do
+	{
+	u.y *= 2.0;
+	i -= 1;
+	k  = ( *q >> 4) & 0x7ff;
+	}
+while( k == 0 );
+i = i + k;
+#endif /* DENORMAL */
+
+ieeedon:
+
+i -= 0x3fe;
+*pw2 = i;
+*q &= 0x800f;
+*q |= 0x3fe0;
+return( u.y );
+#endif
+}
+
+
+
+
+
+
+
+double ldexp( x, pw2 )
+double x;
+int pw2;
+{
+union
+	{
+	double y;
+	unsigned short sh[4];
+	} u;
+short *q;
+int e;
+
+#ifdef UNK
+mtherr( "ldexp", DOMAIN );
+return(0.0);
+#endif
+
+u.y = x;
+#ifdef DEC
+q = (short *)&u.sh[0];
+e  = ( *q >> 7) & 0377;
+if( e == 0 )
+	return(0.0);
+#else
+
+#ifdef IBMPC
+q = (short *)&u.sh[3];
+#endif
+#ifdef MIEEE
+q = (short *)&u.sh[0];
+#endif
+while( (e = (*q & 0x7ff0) >> 4) == 0 )
+	{
+	if( u.y == 0.0 )
+		{
+		return( 0.0 );
+		}
+/* Input is denormal. */
+	if( pw2 > 0 )
+		{
+		u.y *= 2.0;
+		pw2 -= 1;
+		}
+	if( pw2 < 0 )
+		{
+		if( pw2 < -53 )
+			return(0.0);
+		u.y /= 2.0;
+		pw2 += 1;
+		}
+	if( pw2 == 0 )
+		return(u.y);
+	}
+#endif /* not DEC */
+
+e += pw2;
+
+/* Handle overflow */
+#ifdef DEC
+if( e > MEXP )
+	return( MAXNUM );
+#else
+if( e >= MEXP )
+	return( 2.0*MAXNUM );
+#endif
+
+/* Handle denormalized results */
+if( e < 1 )
+	{
+#ifdef DENORMAL
+	if( e < -53 )
+		return(0.0);
+	*q &= 0x800f;
+	*q |= 0x10;
+	/* For denormals, significant bits may be lost even
+	   when dividing by 2.  Construct 2^-(1-e) so the result
+	   is obtained with only one multiplication.  */
+	u.y *= ldexp(1.0, e-1);
+	return(u.y);
+#else
+	return(0.0);
+#endif
+	}
+else
+	{
+#ifdef DEC
+	*q &= 0x807f;	/* strip all exponent bits */
+	*q |= (e & 0xff) << 7;
+#else
+	*q &= 0x800f;
+	*q |= (e & 0x7ff) << 4;
+#endif
+	return(u.y);
+	}
+}

+ 289 - 0
components/external/espruino/libs/math/ftilib.mak

@@ -0,0 +1,289 @@
+# MSDOS Microsoft C makefile for Cephes library
+CFLAGS=/c
+# For large memory model:
+#CFLAGS=/c /AL
+# Add /FPa to the CFLAGS if you want to use the fast software FPa arithmetic.
+#
+# Use the following with /FPa if you do not want to use the 80x87 coprocessor
+# or software emulator.
+#polevl.obj: polevl.c mconf.h
+#	cl /c /Ox polevl.c
+#
+# Use the following instead if you want to use an 80x87 chip or
+# software emulator for maximum accuracy computation of the
+# polynomial expansions:
+polevl.obj: polevl.asm mconf.h
+	masm polevl.asm/r;
+
+floor.obj: floor.asm
+	masm floor.asm;
+
+#floor.obj: floor.c mconf.h
+#	cl $(CFLAGS) floor.c
+
+acosh.obj: acosh.c mconf.h
+	cl $(CFLAGS) acosh.c
+
+airy.obj: airy.c mconf.h
+	cl $(CFLAGS) airy.c
+
+asin.obj: asin.c  mconf.h
+	cl $(CFLAGS) asin.c
+
+asinh.obj: asinh.c mconf.h
+	cl $(CFLAGS) asinh.c
+
+atan.obj: atan.c mconf.h
+	cl $(CFLAGS) atan.c
+
+atanh.obj: atanh.c mconf.h
+	cl $(CFLAGS) atanh.c
+
+asinh.obj: asinh.c mconf.h
+	cl $(CFLAGS) asinh.c
+
+bdtr.obj: bdtr.c mconf.h
+	cl $(CFLAGS) bdtr.c
+
+beta.obj: beta.c mconf.h
+	cl $(CFLAGS) beta.c
+
+btdtr.obj: btdtr.c mconf.h
+	cl $(CFLAGS) btdtr.c
+
+cbrt.obj: cbrt.c mconf.h
+	cl $(CFLAGS) cbrt.c
+
+chbevl.obj: chbevl.c mconf.h
+	cl $(CFLAGS) chbevl.c
+
+chdtr.obj: chdtr.c mconf.h
+	cl $(CFLAGS) chdtr.c
+
+clog.obj: clog.c mconf.h
+	cl $(CFLAGS) clog.c
+
+cmplx.obj: cmplx.c mconf.h
+	cl $(CFLAGS) cmplx.c
+
+const.obj: const.c mconf.h
+	cl $(CFLAGS) const.c
+
+cosh.obj: cosh.c mconf.h
+	cl $(CFLAGS) cosh.c
+
+dawsn.obj: dawsn.c mconf.h
+	cl $(CFLAGS) dawsn.c
+
+drand.obj: drand.c mconf.h
+	cl $(CFLAGS) drand.c
+
+ellie.obj: ellie.c mconf.h
+	cl $(CFLAGS) ellie.c
+
+ellik.obj: ellik.c mconf.h
+	cl $(CFLAGS) ellik.c
+
+ellpe.obj: ellpe.c mconf.h
+	cl $(CFLAGS) ellpe.c
+
+ellpj.obj: ellpj.c mconf.h
+	cl $(CFLAGS) ellpj.c
+
+ellpk.obj: ellpk.c mconf.h
+	cl $(CFLAGS) ellpk.c
+
+exp.obj: exp.c mconf.h
+	cl $(CFLAGS) exp.c
+
+exp10.obj: exp10.c mconf.h
+	cl $(CFLAGS) exp10.c
+
+exp2.obj: exp2.c mconf.h
+	cl $(CFLAGS) exp2.c
+
+expn.obj: expn.c mconf.h
+	cl $(CFLAGS) expn.c
+
+fabs.obj: fabs.c mconf.h
+	cl $(CFLAGS) fabs.c
+
+fac.obj: fac.c mconf.h
+	cl $(CFLAGS) fac.c
+
+fdtr.obj: fdtr.c mconf.h
+	cl $(CFLAGS) fdtr.c
+
+fresnl.obj: fresnl.c mconf.h
+	cl $(CFLAGS) fresnl.c
+
+gamma.obj: gamma.c mconf.h
+	cl $(CFLAGS) gamma.c
+
+gdtr.obj: gdtr.c mconf.h
+	cl $(CFLAGS) gdtr.c
+
+hyp2f1.obj: hyp2f1.c mconf.h
+	cl $(CFLAGS) hyp2f1.c
+
+hyperg.obj: hyperg.c mconf.h
+	cl $(CFLAGS) hyperg.c
+
+i0.obj: i0.c mconf.h
+	cl $(CFLAGS) i0.c
+
+i1.obj: i1.c mconf.h
+	cl $(CFLAGS) i1.c
+
+igam.obj: igam.c mconf.h
+	cl $(CFLAGS) igam.c
+
+igami.obj: igami.c mconf.h
+	cl $(CFLAGS) igami.c
+
+incbet.obj: incbet.c mconf.h
+	cl $(CFLAGS) incbet.c
+
+incbi.obj: incbi.c mconf.h
+	cl $(CFLAGS) incbi.c
+
+isnan.obj: isnan.c mconf.h
+	cl $(CFLAGS) isnan.c
+
+iv.obj: iv.c mconf.h
+	cl $(CFLAGS) iv.c
+
+j0.obj: j0.c mconf.h
+	cl $(CFLAGS) j0.c
+
+j1.obj: j1.c mconf.h
+	cl $(CFLAGS) j1.c
+
+jn.obj: jn.c mconf.h
+	cl $(CFLAGS) jn.c
+
+jv.obj: jv.c mconf.h
+	cl $(CFLAGS) jv.c
+
+k0.obj: k0.c mconf.h
+	cl $(CFLAGS) k0.c
+
+k1.obj: k1.c mconf.h
+	cl $(CFLAGS) k1.c
+
+kn.obj: kn.c mconf.h
+	cl $(CFLAGS) kn.c
+
+log.obj: log.c mconf.h
+	cl $(CFLAGS) log.c
+
+log2.obj: log2.c mconf.h
+	cl $(CFLAGS) log2.c
+
+log10.obj: log10.c mconf.h
+	cl $(CFLAGS) log10.c
+
+mtherr.obj: mtherr.c mconf.h
+	cl $(CFLAGS) mtherr.c
+
+nbdtr.obj: nbdtr.c mconf.h
+	cl $(CFLAGS) nbdtr.c
+
+ndtr.obj: ndtr.c mconf.h
+	cl $(CFLAGS) ndtr.c
+
+ndtri.obj: ndtri.c mconf.h
+	cl $(CFLAGS) ndtri.c
+
+pdtr.obj: pdtr.c mconf.h
+	cl $(CFLAGS) pdtr.c
+
+pow.obj: pow.c mconf.h
+	cl $(CFLAGS) pow.c
+
+powi.obj: powi.c mconf.h
+	cl $(CFLAGS) powi.c
+
+psi.obj: psi.c mconf.h
+	cl $(CFLAGS) psi.c
+
+rgamma.obj: rgamma.c mconf.h
+	cl $(CFLAGS) rgamma.c
+
+round.obj: round.c mconf.h
+	cl $(CFLAGS) round.c
+
+setprec.obj: setprec.87
+	masm setprec.87;
+
+shichi.obj: shichi.c mconf.h
+	cl $(CFLAGS) shichi.c
+
+sici.obj: sici.c mconf.h
+	cl $(CFLAGS) sici.c
+
+sin.obj: sin.c mconf.h
+	cl $(CFLAGS) sin.c
+
+sindg.obj: sindg.c mconf.h
+	cl $(CFLAGS) sindg.c
+
+sinh.obj: sinh.c mconf.h
+	cl $(CFLAGS) sinh.c
+
+spence.obj: spence.c mconf.h
+	cl $(CFLAGS) spence.c
+
+sqrt.obj: sqrt.87
+	masm sqrt.87;
+
+#sqrt.obj: sqrt.c
+#	cl $(CFLAGS) sqrt.c
+
+stdtr.obj: stdtr.c mconf.h
+	cl $(CFLAGS) stdtr.c
+
+struve.obj: struve.c mconf.h
+	cl $(CFLAGS) struve.c
+
+tan.obj: tan.c mconf.h
+	cl $(CFLAGS) tan.c
+
+tandg.obj: tandg.c mconf.h
+	cl $(CFLAGS) tandg.c 
+
+tanh.obj: tanh.c mconf.h
+	cl $(CFLAGS) tanh.c
+
+yn.obj: yn.c mconf.h
+	cl $(CFLAGS) yn.c
+
+zeta.obj: zeta.c mconf.h
+	cl $(CFLAGS) zeta.c
+
+zetac.obj: zetac.c mconf.h
+	cl $(CFLAGS) zetac.c
+
+polyn.obj: polyn.c mconf.h
+	cl $(CFLAGS) polyn.c
+
+polmisc.obj: polmisc.c mconf.h
+	cl $(CFLAGS) polmisc.c
+
+unity.obj: unity.c mconf.h
+	cl $(CFLAGS) unity.c
+
+fti.lib: acosh.obj airy.obj asin.obj asinh.obj atan.obj atanh.obj bdtr.obj \
+beta.obj btdtr.obj cbrt.obj chbevl.obj chdtr.obj clog.obj \
+cmplx.obj const.obj cosh.obj dawsn.obj drand.obj ellie.obj ellik.obj \
+ellpe.obj ellpj.obj ellpk.obj exp.obj exp10.obj \
+exp2.obj expn.obj fabs.obj fac.obj fdtr.obj floor.obj fresnl.obj gamma.obj \
+gdtr.obj hyp2f1.obj hyperg.obj i0.c i1.c igam.c igami.obj incbet.obj \
+incbi.obj isnan.obj iv.obj j0.obj j1.obj jn.obj jv.obj k0.obj k1.obj \
+kn.obj log.obj log2.obj log10.obj mtherr.obj nbdtr.obj ndtr.obj ndtri.obj \
+pdtr.obj polevl.obj polmisc.obj polyn.obj pow.obj powi.obj psi.obj \
+rgamma.obj round.obj shichi.obj sici.obj sin.obj sindg.obj sinh.obj \
+spence.obj sqrt.obj stdtr.obj setprec.obj struve.obj tan.obj \
+tandg.obj tanh.obj unity.obj yn.obj zeta.obj zetac.obj \
+mconf.h
+	lib @ftilib.rsp

+ 17 - 0
components/external/espruino/libs/math/ftilib.rsp

@@ -0,0 +1,17 @@
+fti
+y
+acosh airy asin asinh atan &
+atanh bdtr beta btdtr cbrt chbevl &
+chdtr clog cmplx const &
+cosh dawsn drand ellie ellik ellpe ellpk &
+ellpj exp exp2 exp10 expn fac &
+fdtr fresnl gamma gdtr &
+hyperg hyp2f1 incbet incbi igam igami isnan &
+iv i0 i1 jn jv j0 j1 k0 k1 kn log log2 log10 &
+mtherr nbdtr ndtr ndtri pdtr &
+polmisc polyn pow powi psi &
+rgamma round shichi sici sin sindg &
+sinh spence sqrt stdtr struve tan tandg &
+tanh unity yn zeta zetac floor fabs polevl
+fti.lst
+fti

+ 237 - 0
components/external/espruino/libs/math/isnan.c

@@ -0,0 +1,237 @@
+/*							isnan()
+ *							signbit()
+ *							isfinite()
+ *
+ *	Floating point numeric utilities
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double ceil(), floor(), frexp(), ldexp();
+ * int signbit(), isnan(), isfinite();
+ * double x, y;
+ * int expnt, n;
+ *
+ * y = floor(x);
+ * y = ceil(x);
+ * y = frexp( x, &expnt );
+ * y = ldexp( x, n );
+ * n = signbit(x);
+ * n = isnan(x);
+ * n = isfinite(x);
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * All four routines return a double precision floating point
+ * result.
+ *
+ * floor() returns the largest integer less than or equal to x.
+ * It truncates toward minus infinity.
+ *
+ * ceil() returns the smallest integer greater than or equal
+ * to x.  It truncates toward plus infinity.
+ *
+ * frexp() extracts the exponent from x.  It returns an integer
+ * power of two to expnt and the significand between 0.5 and 1
+ * to y.  Thus  x = y * 2**expn.
+ *
+ * ldexp() multiplies x by 2**n.
+ *
+ * signbit(x) returns 1 if the sign bit of x is 1, else 0.
+ *
+ * These functions are part of the standard C run time library
+ * for many but not all C compilers.  The ones supplied are
+ * written in C for either DEC or IEEE arithmetic.  They should
+ * be used only if your compiler library does not already have
+ * them.
+ *
+ * The IEEE versions assume that denormal numbers are implemented
+ * in the arithmetic.  Some modifications will be required if
+ * the arithmetic has abrupt rather than gradual underflow.
+ */
+
+
+/*
+Cephes Math Library Release 2.3:  March, 1995
+Copyright 1984, 1995 by Stephen L. Moshier
+*/
+
+
+#include "mconf.h"
+
+#ifdef UNK
+/* ceil(), floor(), frexp(), ldexp() may need to be rewritten. */
+#undef UNK
+#if BIGENDIAN
+#define MIEEE 1
+#else
+#define IBMPC 1
+#endif
+#endif
+
+
+/* Return 1 if the sign bit of x is 1, else 0.  */
+
+int signbit(x)
+double x;
+{
+union
+	{
+	double d;
+	short s[4];
+	int i[2];
+	} u;
+
+u.d = x;
+
+if( sizeof(int) == 4 )
+	{
+#ifdef IBMPC
+	return( u.i[1] < 0 );
+#endif
+#ifdef DEC
+	return( u.s[3] < 0 );
+#endif
+#ifdef MIEEE
+	return( u.i[0] < 0 );
+#endif
+	}
+else
+	{
+#ifdef IBMPC
+	return( u.s[3] < 0 );
+#endif
+#ifdef DEC
+	return( u.s[3] < 0 );
+#endif
+#ifdef MIEEE
+	return( u.s[0] < 0 );
+#endif
+	}
+}
+
+
+/* Return 1 if x is a number that is Not a Number, else return 0.  */
+
+int isnan(x)
+double x;
+{
+#ifdef NANS
+union
+	{
+	double d;
+	unsigned short s[4];
+	unsigned int i[2];
+	} u;
+
+u.d = x;
+
+if( sizeof(int) == 4 )
+	{
+#ifdef IBMPC
+	if( ((u.i[1] & 0x7ff00000) == 0x7ff00000)
+	    && (((u.i[1] & 0x000fffff) != 0) || (u.i[0] != 0)))
+		return 1;
+#endif
+#ifdef DEC
+	if( (u.s[1] & 0x7fff) == 0)
+		{
+		if( (u.s[2] | u.s[1] | u.s[0]) != 0 )
+			return(1);
+		}
+#endif
+#ifdef MIEEE
+	if( ((u.i[0] & 0x7ff00000) == 0x7ff00000)
+	    && (((u.i[0] & 0x000fffff) != 0) || (u.i[1] != 0)))
+		return 1;
+#endif
+	return(0);
+	}
+else
+	{ /* size int not 4 */
+#ifdef IBMPC
+	if( (u.s[3] & 0x7ff0) == 0x7ff0)
+		{
+		if( ((u.s[3] & 0x000f) | u.s[2] | u.s[1] | u.s[0]) != 0 )
+			return(1);
+		}
+#endif
+#ifdef DEC
+	if( (u.s[3] & 0x7fff) == 0)
+		{
+		if( (u.s[2] | u.s[1] | u.s[0]) != 0 )
+			return(1);
+		}
+#endif
+#ifdef MIEEE
+	if( (u.s[0] & 0x7ff0) == 0x7ff0)
+		{
+		if( ((u.s[0] & 0x000f) | u.s[1] | u.s[2] | u.s[3]) != 0 )
+			return(1);
+		}
+#endif
+	return(0);
+	} /* size int not 4 */
+
+#else
+/* No NANS.  */
+return(0);
+#endif
+}
+
+
+/* Return 1 if x is not infinite and is not a NaN.  */
+
+int isfinite(x)
+double x;
+{
+#ifdef INFINITIES
+union
+	{
+	double d;
+	unsigned short s[4];
+	unsigned int i[2];
+	} u;
+
+u.d = x;
+
+if( sizeof(int) == 4 )
+	{
+#ifdef IBMPC
+	if( (u.i[1] & 0x7ff00000) != 0x7ff00000)
+		return 1;
+#endif
+#ifdef DEC
+	if( (u.s[3] & 0x7fff) != 0)
+		return 1;
+#endif
+#ifdef MIEEE
+	if( (u.i[0] & 0x7ff00000) != 0x7ff00000)
+		return 1;
+#endif
+	return(0);
+	}
+else
+	{
+#ifdef IBMPC
+	if( (u.s[3] & 0x7ff0) != 0x7ff0)
+		return 1;
+#endif
+#ifdef DEC
+	if( (u.s[3] & 0x7fff) != 0)
+		return 1;
+#endif
+#ifdef MIEEE
+	if( (u.s[0] & 0x7ff0) != 0x7ff0)
+		return 1;
+#endif
+	return(0);
+	}
+#else
+/* No INFINITY.  */
+return(1);
+#endif
+}

+ 341 - 0
components/external/espruino/libs/math/log.c

@@ -0,0 +1,341 @@
+/*							log.c
+ *
+ *	Natural logarithm
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, log();
+ *
+ * y = log( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns the base e (2.718...) logarithm of x.
+ *
+ * The argument is separated into its exponent and fractional
+ * parts.  If the exponent is between -1 and +1, the logarithm
+ * of the fraction is approximated by
+ *
+ *     log(1+x) = x - 0.5 x**2 + x**3 P(x)/Q(x).
+ *
+ * Otherwise, setting  z = 2(x-1)/x+1),
+ * 
+ *     log(x) = z + z**3 P(z)/Q(z).
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE      0.5, 2.0    150000      1.44e-16    5.06e-17
+ *    IEEE      +-MAXNUM    30000       1.20e-16    4.78e-17
+ *    DEC       0, 10       170000      1.8e-17     6.3e-18
+ *
+ * In the tests over the interval [+-MAXNUM], the logarithms
+ * of the random arguments were uniformly distributed over
+ * [0, MAXLOG].
+ *
+ * ERROR MESSAGES:
+ *
+ * log singularity:  x = 0; returns -INFINITY
+ * log domain:       x < 0; returns NAN
+ */
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+#include "mconf.h"
+static char fname[] = {"log"};
+
+/* Coefficients for log(1+x) = x - x**2/2 + x**3 P(x)/Q(x)
+ * 1/sqrt(2) <= x < sqrt(2)
+ */
+#ifdef UNK
+const static double P[] = {
+ 1.01875663804580931796E-4,
+ 4.97494994976747001425E-1,
+ 4.70579119878881725854E0,
+ 1.44989225341610930846E1,
+ 1.79368678507819816313E1,
+ 7.70838733755885391666E0,
+};
+const static double Q[] = {
+/* 1.00000000000000000000E0, */
+ 1.12873587189167450590E1,
+ 4.52279145837532221105E1,
+ 8.29875266912776603211E1,
+ 7.11544750618563894466E1,
+ 2.31251620126765340583E1,
+};
+#endif
+
+#ifdef DEC
+static unsigned short P[] = {
+0037777,0127270,0162547,0057274,
+0041001,0054665,0164317,0005341,
+0041451,0034104,0031640,0105773,
+0041677,0011276,0123617,0160135,
+0041701,0126603,0053215,0117250,
+0041420,0115777,0135206,0030232,
+};
+static unsigned short Q[] = {
+/*0040200,0000000,0000000,0000000,*/
+0041220,0144332,0045272,0174241,
+0041742,0164566,0035720,0130431,
+0042246,0126327,0166065,0116357,
+0042372,0033420,0157525,0124560,
+0042271,0167002,0066537,0172303,
+0041730,0164777,0113711,0044407,
+};
+#endif
+
+#ifdef IBMPC
+static unsigned short P[] = {
+0x1bb0,0x93c3,0xb4c2,0x3f1a,
+0x52f2,0x3f56,0xd6f5,0x3fdf,
+0x6911,0xed92,0xd2ba,0x4012,
+0xeb2e,0xc63e,0xff72,0x402c,
+0xc84d,0x924b,0xefd6,0x4031,
+0xdcf8,0x7d7e,0xd563,0x401e,
+};
+static unsigned short Q[] = {
+/*0x0000,0x0000,0x0000,0x3ff0,*/
+0xef8e,0xae97,0x9320,0x4026,
+0xc033,0x4e19,0x9d2c,0x4046,
+0xbdbd,0xa326,0xbf33,0x4054,
+0xae21,0xeb5e,0xc9e2,0x4051,
+0x25b2,0x9e1f,0x200a,0x4037,
+};
+#endif
+
+#ifdef MIEEE
+static unsigned short P[] = {
+0x3f1a,0xb4c2,0x93c3,0x1bb0,
+0x3fdf,0xd6f5,0x3f56,0x52f2,
+0x4012,0xd2ba,0xed92,0x6911,
+0x402c,0xff72,0xc63e,0xeb2e,
+0x4031,0xefd6,0x924b,0xc84d,
+0x401e,0xd563,0x7d7e,0xdcf8,
+};
+static unsigned short Q[] = {
+/*0x3ff0,0x0000,0x0000,0x0000,*/
+0x4026,0x9320,0xae97,0xef8e,
+0x4046,0x9d2c,0x4e19,0xc033,
+0x4054,0xbf33,0xa326,0xbdbd,
+0x4051,0xc9e2,0xeb5e,0xae21,
+0x4037,0x200a,0x9e1f,0x25b2,
+};
+#endif
+
+/* Coefficients for log(x) = z + z**3 P(z)/Q(z),
+ * where z = 2(x-1)/(x+1)
+ * 1/sqrt(2) <= x < sqrt(2)
+ */
+
+#ifdef UNK
+const static double R[3] = {
+-7.89580278884799154124E-1,
+ 1.63866645699558079767E1,
+-6.41409952958715622951E1,
+};
+const static double S[3] = {
+/* 1.00000000000000000000E0,*/
+-3.56722798256324312549E1,
+ 3.12093766372244180303E2,
+-7.69691943550460008604E2,
+};
+#endif
+#ifdef DEC
+static unsigned short R[12] = {
+0140112,0020756,0161540,0072035,
+0041203,0013743,0114023,0155527,
+0141600,0044060,0104421,0050400,
+};
+static unsigned short S[12] = {
+/*0040200,0000000,0000000,0000000,*/
+0141416,0130152,0017543,0064122,
+0042234,0006000,0104527,0020155,
+0142500,0066110,0146631,0174731,
+};
+#endif
+#ifdef IBMPC
+static unsigned short R[12] = {
+0x0e84,0xdc6c,0x443d,0xbfe9,
+0x7b6b,0x7302,0x62fc,0x4030,
+0x2a20,0x1122,0x0906,0xc050,
+};
+static unsigned short S[12] = {
+/*0x0000,0x0000,0x0000,0x3ff0,*/
+0x6d0a,0x43ec,0xd60d,0xc041,
+0xe40e,0x112a,0x8180,0x4073,
+0x3f3b,0x19b3,0x0d89,0xc088,
+};
+#endif
+#ifdef MIEEE
+static unsigned short R[12] = {
+0xbfe9,0x443d,0xdc6c,0x0e84,
+0x4030,0x62fc,0x7302,0x7b6b,
+0xc050,0x0906,0x1122,0x2a20,
+};
+static unsigned short S[12] = {
+/*0x3ff0,0x0000,0x0000,0x0000,*/
+0xc041,0xd60d,0x43ec,0x6d0a,
+0x4073,0x8180,0x112a,0xe40e,
+0xc088,0x0d89,0x19b3,0x3f3b,
+};
+#endif
+
+#ifdef ANSIPROT
+extern double frexp ( double, int * );
+extern double ldexp ( double, int );
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern int isnan ( double );
+extern int isfinite ( double );
+#else
+double frexp(), ldexp(), polevl(), p1evl();
+int isnan(), isfinite();
+#endif
+#define SQRTH 0.70710678118654752440
+extern double INFINITY, NAN;
+
+double log(x)
+double x;
+{
+int e;
+#ifdef DEC
+short *q;
+#endif
+double y, z;
+
+#ifdef NANS
+if( isnan(x) )
+	return(x);
+#endif
+#ifdef INFINITIES
+if( x == INFINITY )
+	return(x);
+#endif
+/* Test for domain */
+if( x <= 0.0 )
+	{
+	if( x == 0.0 )
+	        {
+		mtherr( fname, SING );
+		return( -INFINITY );
+	        }
+	else
+	        {
+		mtherr( fname, DOMAIN );
+		return( NAN );
+	        }
+	}
+
+/* separate mantissa from exponent */
+
+#ifdef DEC
+q = (short *)&x;
+e = *q;			/* short containing exponent */
+e = ((e >> 7) & 0377) - 0200;	/* the exponent */
+*q &= 0177;	/* strip exponent from x */
+*q |= 040000;	/* x now between 0.5 and 1 */
+#endif
+
+/* Note, frexp is used so that denormal numbers
+ * will be handled properly.
+ */
+#ifdef IBMPC
+x = frexp( x, &e );
+/*
+q = (short *)&x;
+q += 3;
+e = *q;
+e = ((e >> 4) & 0x0fff) - 0x3fe;
+*q &= 0x0f;
+*q |= 0x3fe0;
+*/
+#endif
+
+/* Equivalent C language standard library function: */
+#ifdef UNK
+x = frexp( x, &e );
+#endif
+
+#ifdef MIEEE
+x = frexp( x, &e );
+#endif
+
+
+
+/* logarithm using log(x) = z + z**3 P(z)/Q(z),
+ * where z = 2(x-1)/x+1)
+ */
+
+if( (e > 2) || (e < -2) )
+{
+if( x < SQRTH )
+	{ /* 2( 2x-1 )/( 2x+1 ) */
+	e -= 1;
+	z = x - 0.5;
+	y = 0.5 * z + 0.5;
+	}	
+else
+	{ /*  2 (x-1)/(x+1)   */
+	z = x - 0.5;
+	z -= 0.5;
+	y = 0.5 * x  + 0.5;
+	}
+
+x = z / y;
+
+
+/* rational form */
+z = x*x;
+z = x * ( z * polevl( z, R, 2 ) / p1evl( z, S, 3 ) );
+y = e;
+z = z - y * 2.121944400546905827679e-4;
+z = z + x;
+z = z + e * 0.693359375;
+goto ldone;
+}
+
+
+
+/* logarithm using log(1+x) = x - .5x**2 + x**3 P(x)/Q(x) */
+
+if( x < SQRTH )
+	{
+	e -= 1;
+	x = ldexp( x, 1 ) - 1.0; /*  2x - 1  */
+	}	
+else
+	{
+	x = x - 1.0;
+	}
+
+
+/* rational form */
+z = x*x;
+#if DEC
+y = x * ( z * polevl( x, P, 5 ) / p1evl( x, Q, 6 ) );
+#else
+y = x * ( z * polevl( x, P, 5 ) / p1evl( x, Q, 5 ) );
+#endif
+if( e )
+	y = y - e * 2.121944400546905827679e-4;
+y = y - ldexp( z, -1 );   /*  y - 0.5 * z  */
+z = x + y;
+if( e )
+	z = z + e * 0.693359375;
+
+ldone:
+
+return( z );
+}

+ 250 - 0
components/external/espruino/libs/math/log10.c

@@ -0,0 +1,250 @@
+/*							log10.c
+ *
+ *	Common logarithm
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, log10();
+ *
+ * y = log10( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns logarithm to the base 10 of x.
+ *
+ * The argument is separated into its exponent and fractional
+ * parts.  The logarithm of the fraction is approximated by
+ *
+ *     log(1+x) = x - 0.5 x**2 + x**3 P(x)/Q(x).
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE      0.5, 2.0     30000      1.5e-16     5.0e-17
+ *    IEEE      0, MAXNUM    30000      1.4e-16     4.8e-17
+ *    DEC       1, MAXNUM    50000      2.5e-17     6.0e-18
+ *
+ * In the tests over the interval [1, MAXNUM], the logarithms
+ * of the random arguments were uniformly distributed over
+ * [0, MAXLOG].
+ *
+ * ERROR MESSAGES:
+ *
+ * log10 singularity:  x = 0; returns -INFINITY
+ * log10 domain:       x < 0; returns NAN
+ */
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+#include "mconf.h"
+static char fname[] = {"log10"};
+
+/* Coefficients for log(1+x) = x - x**2/2 + x**3 P(x)/Q(x)
+ * 1/sqrt(2) <= x < sqrt(2)
+ */
+#ifdef UNK
+const static double P[] = {
+  4.58482948458143443514E-5,
+  4.98531067254050724270E-1,
+  6.56312093769992875930E0,
+  2.97877425097986925891E1,
+  6.06127134467767258030E1,
+  5.67349287391754285487E1,
+  1.98892446572874072159E1
+};
+const static double Q[] = {
+/* 1.00000000000000000000E0, */
+  1.50314182634250003249E1,
+  8.27410449222435217021E1,
+  2.20664384982121929218E2,
+  3.07254189979530058263E2,
+  2.14955586696422947765E2,
+  5.96677339718622216300E1
+};
+#endif
+
+#ifdef DEC
+static unsigned short P[] = {
+0034500,0046473,0051374,0135174,
+0037777,0037566,0145712,0150321,
+0040722,0002426,0031543,0123107,
+0041356,0046513,0170752,0004346,
+0041562,0071553,0023536,0163343,
+0041542,0170221,0024316,0114216,
+0041237,0016454,0046611,0104602
+};
+static unsigned short Q[] = {
+/*0040200,0000000,0000000,0000000,*/
+0041160,0100260,0067736,0102424,
+0041645,0075552,0036563,0147072,
+0042134,0125025,0021132,0025320,
+0042231,0120211,0046030,0103271,
+0042126,0172241,0052151,0120426,
+0041556,0125702,0072116,0047103
+};
+#endif
+
+#ifdef IBMPC
+static unsigned short P[] = {
+0x974f,0x6a5f,0x09a7,0x3f08,
+0x5a1a,0xd979,0xe7ee,0x3fdf,
+0x74c9,0xc66c,0x40a2,0x401a,
+0x411d,0x7e3d,0xc9a9,0x403d,
+0xdcdc,0x64eb,0x4e6d,0x404e,
+0xd312,0x2519,0x5e12,0x404c,
+0x3130,0x89b1,0xe3a5,0x4033
+};
+static unsigned short Q[] = {
+/*0x0000,0x0000,0x0000,0x3ff0,*/
+0xd0a2,0x0dfb,0x1016,0x402e,
+0x79c7,0x47ae,0xaf6d,0x4054,
+0x455a,0xa44b,0x9542,0x406b,
+0x10d7,0x2983,0x3411,0x4073,
+0x3423,0x2a8d,0xde94,0x406a,
+0xc9c8,0x4e89,0xd578,0x404d
+};
+#endif
+
+#ifdef MIEEE
+static unsigned short P[] = {
+0x3f08,0x09a7,0x6a5f,0x974f,
+0x3fdf,0xe7ee,0xd979,0x5a1a,
+0x401a,0x40a2,0xc66c,0x74c9,
+0x403d,0xc9a9,0x7e3d,0x411d,
+0x404e,0x4e6d,0x64eb,0xdcdc,
+0x404c,0x5e12,0x2519,0xd312,
+0x4033,0xe3a5,0x89b1,0x3130
+};
+static unsigned short Q[] = {
+0x402e,0x1016,0x0dfb,0xd0a2,
+0x4054,0xaf6d,0x47ae,0x79c7,
+0x406b,0x9542,0xa44b,0x455a,
+0x4073,0x3411,0x2983,0x10d7,
+0x406a,0xde94,0x2a8d,0x3423,
+0x404d,0xd578,0x4e89,0xc9c8
+};
+#endif
+
+#define SQRTH 0.70710678118654752440
+#define L102A 3.0078125E-1
+#define L102B 2.48745663981195213739E-4
+#define L10EA 4.3359375E-1
+#define L10EB 7.00731903251827651129E-4
+
+#ifdef ANSIPROT
+extern double frexp ( double, int * );
+extern double ldexp ( double, int );
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern int isnan ( double );
+extern int isfinite ( double );
+#else
+double frexp(), ldexp(), polevl(), p1evl();
+int isnan(), isfinite();
+#endif
+extern double LOGE2, SQRT2, INFINITY, NAN;
+
+double log10(x)
+double x;
+{
+VOLATILE double z;
+double y;
+#ifdef DEC
+short *q;
+#endif
+int e;
+
+#ifdef NANS
+if( isnan(x) )
+	return(x);
+#endif
+#ifdef INFINITIES
+if( x == INFINITY )
+	return(x);
+#endif
+/* Test for domain */
+if( x <= 0.0 )
+	{
+	if( x == 0.0 )
+	        {
+		mtherr( fname, SING );
+		return( -INFINITY );
+	        }
+	else
+	        {
+		mtherr( fname, DOMAIN );
+		return( NAN );
+	        }
+	}
+
+/* separate mantissa from exponent */
+
+#ifdef DEC
+q = (short *)&x;
+e = *q;			/* short containing exponent */
+e = ((e >> 7) & 0377) - 0200;	/* the exponent */
+*q &= 0177;	/* strip exponent from x */
+*q |= 040000;	/* x now between 0.5 and 1 */
+#endif
+
+#ifdef IBMPC
+x = frexp( x, &e );
+/*
+q = (short *)&x;
+q += 3;
+e = *q;
+e = ((e >> 4) & 0x0fff) - 0x3fe;
+*q &= 0x0f;
+*q |= 0x3fe0;
+*/
+#endif
+
+/* Equivalent C language standard library function: */
+#ifdef UNK
+x = frexp( x, &e );
+#endif
+
+#ifdef MIEEE
+x = frexp( x, &e );
+#endif
+
+/* logarithm using log(1+x) = x - .5x**2 + x**3 P(x)/Q(x) */
+
+if( x < SQRTH )
+	{
+	e -= 1;
+	x = ldexp( x, 1 ) - 1.0; /*  2x - 1  */
+	}	
+else
+	{
+	x = x - 1.0;
+	}
+
+
+/* rational form */
+z = x*x;
+y = x * ( z * polevl( x, P, 6 ) / p1evl( x, Q, 6 ) );
+y = y - ldexp( z, -1 );   /*  y - 0.5 * x**2  */
+
+/* multiply log of fraction by log10(e)
+ * and base 2 exponent by log10(2)
+ */
+z = (x + y) * L10EB;  /* accumulate terms in order of size */
+z += y * L10EA;
+z += x * L10EA;
+z += e * L102B;
+z += e * L102A;
+
+
+return( z );
+}

+ 348 - 0
components/external/espruino/libs/math/log2.c

@@ -0,0 +1,348 @@
+/*							log2.c
+ *
+ *	Base 2 logarithm
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, log2();
+ *
+ * y = log2( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns the base 2 logarithm of x.
+ *
+ * The argument is separated into its exponent and fractional
+ * parts.  If the exponent is between -1 and +1, the base e
+ * logarithm of the fraction is approximated by
+ *
+ *     log(1+x) = x - 0.5 x**2 + x**3 P(x)/Q(x).
+ *
+ * Otherwise, setting  z = 2(x-1)/x+1),
+ * 
+ *     log(x) = z + z**3 P(z)/Q(z).
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE      0.5, 2.0    30000       2.0e-16     5.5e-17
+ *    IEEE      exp(+-700)  40000       1.3e-16     4.6e-17
+ *
+ * In the tests over the interval [exp(+-700)], the logarithms
+ * of the random arguments were uniformly distributed.
+ *
+ * ERROR MESSAGES:
+ *
+ * log2 singularity:  x = 0; returns -INFINITY
+ * log2 domain:       x < 0; returns NAN
+ */
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+#include "mconf.h"
+static char fname[] = {"log2"};
+
+/* Coefficients for log(1+x) = x - x**2/2 + x**3 P(x)/Q(x)
+ * 1/sqrt(2) <= x < sqrt(2)
+ */
+#ifdef UNK
+const static double P[] = {
+ 1.01875663804580931796E-4,
+ 4.97494994976747001425E-1,
+ 4.70579119878881725854E0,
+ 1.44989225341610930846E1,
+ 1.79368678507819816313E1,
+ 7.70838733755885391666E0,
+};
+const static double Q[] = {
+/* 1.00000000000000000000E0, */
+ 1.12873587189167450590E1,
+ 4.52279145837532221105E1,
+ 8.29875266912776603211E1,
+ 7.11544750618563894466E1,
+ 2.31251620126765340583E1,
+};
+#define LOG2EA 0.44269504088896340735992
+#endif
+
+#ifdef DEC
+static unsigned short P[] = {
+0037777,0127270,0162547,0057274,
+0041001,0054665,0164317,0005341,
+0041451,0034104,0031640,0105773,
+0041677,0011276,0123617,0160135,
+0041701,0126603,0053215,0117250,
+0041420,0115777,0135206,0030232,
+};
+static unsigned short Q[] = {
+/*0040200,0000000,0000000,0000000,*/
+0041220,0144332,0045272,0174241,
+0041742,0164566,0035720,0130431,
+0042246,0126327,0166065,0116357,
+0042372,0033420,0157525,0124560,
+0042271,0167002,0066537,0172303,
+0041730,0164777,0113711,0044407,
+};
+static unsigned short L[5] = {0037742,0124354,0122560,0057703};
+#define LOG2EA (*(double *)(&L[0]))
+#endif
+
+#ifdef IBMPC
+static unsigned short P[] = {
+0x1bb0,0x93c3,0xb4c2,0x3f1a,
+0x52f2,0x3f56,0xd6f5,0x3fdf,
+0x6911,0xed92,0xd2ba,0x4012,
+0xeb2e,0xc63e,0xff72,0x402c,
+0xc84d,0x924b,0xefd6,0x4031,
+0xdcf8,0x7d7e,0xd563,0x401e,
+};
+static unsigned short Q[] = {
+/*0x0000,0x0000,0x0000,0x3ff0,*/
+0xef8e,0xae97,0x9320,0x4026,
+0xc033,0x4e19,0x9d2c,0x4046,
+0xbdbd,0xa326,0xbf33,0x4054,
+0xae21,0xeb5e,0xc9e2,0x4051,
+0x25b2,0x9e1f,0x200a,0x4037,
+};
+static unsigned short L[5] = {0x0bf8,0x94ae,0x551d,0x3fdc};
+#define LOG2EA (*(double *)(&L[0]))
+#endif
+
+#ifdef MIEEE
+static unsigned short P[] = {
+0x3f1a,0xb4c2,0x93c3,0x1bb0,
+0x3fdf,0xd6f5,0x3f56,0x52f2,
+0x4012,0xd2ba,0xed92,0x6911,
+0x402c,0xff72,0xc63e,0xeb2e,
+0x4031,0xefd6,0x924b,0xc84d,
+0x401e,0xd563,0x7d7e,0xdcf8,
+};
+static unsigned short Q[] = {
+/*0x3ff0,0x0000,0x0000,0x0000,*/
+0x4026,0x9320,0xae97,0xef8e,
+0x4046,0x9d2c,0x4e19,0xc033,
+0x4054,0xbf33,0xa326,0xbdbd,
+0x4051,0xc9e2,0xeb5e,0xae21,
+0x4037,0x200a,0x9e1f,0x25b2,
+};
+static unsigned short L[5] = {0x3fdc,0x551d,0x94ae,0x0bf8};
+#define LOG2EA (*(double *)(&L[0]))
+#endif
+
+/* Coefficients for log(x) = z + z**3 P(z)/Q(z),
+ * where z = 2(x-1)/(x+1)
+ * 1/sqrt(2) <= x < sqrt(2)
+ */
+
+#ifdef UNK
+const static double R[3] = {
+-7.89580278884799154124E-1,
+ 1.63866645699558079767E1,
+-6.41409952958715622951E1,
+};
+const static double S[3] = {
+/* 1.00000000000000000000E0,*/
+-3.56722798256324312549E1,
+ 3.12093766372244180303E2,
+-7.69691943550460008604E2,
+};
+/* log2(e) - 1 */
+#define LOG2EA 0.44269504088896340735992
+#endif
+#ifdef DEC
+static unsigned short R[12] = {
+0140112,0020756,0161540,0072035,
+0041203,0013743,0114023,0155527,
+0141600,0044060,0104421,0050400,
+};
+static unsigned short S[12] = {
+/*0040200,0000000,0000000,0000000,*/
+0141416,0130152,0017543,0064122,
+0042234,0006000,0104527,0020155,
+0142500,0066110,0146631,0174731,
+};
+/* log2(e) - 1 */
+#define LOG2EA 0.44269504088896340735992L
+#endif
+#ifdef IBMPC
+static unsigned short R[12] = {
+0x0e84,0xdc6c,0x443d,0xbfe9,
+0x7b6b,0x7302,0x62fc,0x4030,
+0x2a20,0x1122,0x0906,0xc050,
+};
+static unsigned short S[12] = {
+/*0x0000,0x0000,0x0000,0x3ff0,*/
+0x6d0a,0x43ec,0xd60d,0xc041,
+0xe40e,0x112a,0x8180,0x4073,
+0x3f3b,0x19b3,0x0d89,0xc088,
+};
+#endif
+#ifdef MIEEE
+static unsigned short R[12] = {
+0xbfe9,0x443d,0xdc6c,0x0e84,
+0x4030,0x62fc,0x7302,0x7b6b,
+0xc050,0x0906,0x1122,0x2a20,
+};
+static unsigned short S[12] = {
+/*0x3ff0,0x0000,0x0000,0x0000,*/
+0xc041,0xd60d,0x43ec,0x6d0a,
+0x4073,0x8180,0x112a,0xe40e,
+0xc088,0x0d89,0x19b3,0x3f3b,
+};
+#endif
+
+#ifdef ANSIPROT
+extern double frexp ( double, int * );
+extern double ldexp ( double, int );
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern int isnan ( double );
+extern int isfinite ( double );
+#else
+double frexp(), ldexp(), polevl(), p1evl();
+int isnan(), isfinite();
+#endif
+#define SQRTH 0.70710678118654752440
+extern double LOGE2, INFINITY, NAN;
+
+double log2(x)
+double x;
+{
+int e;
+double y;
+VOLATILE double z;
+#ifdef DEC
+short *q;
+#endif
+
+#ifdef NANS
+if( isnan(x) )
+	return(x);
+#endif
+#ifdef INFINITIES
+if( x == INFINITY )
+	return(x);
+#endif
+/* Test for domain */
+if( x <= 0.0 )
+	{
+	if( x == 0.0 )
+	        {
+		mtherr( fname, SING );
+		return( -INFINITY );
+	        }
+	else
+	        {
+		mtherr( fname, DOMAIN );
+		return( NAN );
+	        }
+	}
+
+/* separate mantissa from exponent */
+
+#ifdef DEC
+q = (short *)&x;
+e = *q;			/* short containing exponent */
+e = ((e >> 7) & 0377) - 0200;	/* the exponent */
+*q &= 0177;	/* strip exponent from x */
+*q |= 040000;	/* x now between 0.5 and 1 */
+#endif
+
+/* Note, frexp is used so that denormal numbers
+ * will be handled properly.
+ */
+#ifdef IBMPC
+x = frexp( x, &e );
+/*
+q = (short *)&x;
+q += 3;
+e = *q;
+e = ((e >> 4) & 0x0fff) - 0x3fe;
+*q &= 0x0f;
+*q |= 0x3fe0;
+*/
+#endif
+
+/* Equivalent C language standard library function: */
+#ifdef UNK
+x = frexp( x, &e );
+#endif
+
+#ifdef MIEEE
+x = frexp( x, &e );
+#endif
+
+
+/* logarithm using log(x) = z + z**3 P(z)/Q(z),
+ * where z = 2(x-1)/x+1)
+ */
+
+if( (e > 2) || (e < -2) )
+{
+if( x < SQRTH )
+	{ /* 2( 2x-1 )/( 2x+1 ) */
+	e -= 1;
+	z = x - 0.5;
+	y = 0.5 * z + 0.5;
+	}	
+else
+	{ /*  2 (x-1)/(x+1)   */
+	z = x - 0.5;
+	z -= 0.5;
+	y = 0.5 * x  + 0.5;
+	}
+
+x = z / y;
+z = x*x;
+y = x * ( z * polevl( z, R, 2 ) / p1evl( z, S, 3 ) );
+goto ldone;
+}
+
+
+
+/* logarithm using log(1+x) = x - .5x**2 + x**3 P(x)/Q(x) */
+
+if( x < SQRTH )
+	{
+	e -= 1;
+	x = ldexp( x, 1 ) - 1.0; /*  2x - 1  */
+	}	
+else
+	{
+	x = x - 1.0;
+	}
+
+z = x*x;
+#if DEC
+y = x * ( z * polevl( x, P, 5 ) / p1evl( x, Q, 6 ) ) - ldexp( z, -1 );
+#else
+y = x * ( z * polevl( x, P, 5 ) / p1evl( x, Q, 5 ) ) - ldexp( z, -1 );
+#endif
+
+ldone:
+
+/* Multiply log of fraction by log2(e)
+ * and base 2 exponent by 1
+ *
+ * ***CAUTION***
+ *
+ * This sequence of operations is critical and it may
+ * be horribly defeated by some compiler optimizers.
+ */
+z = y * LOG2EA;
+z += x * LOG2EA;
+z += y;
+z += x;
+z += e;
+return( z );
+}

+ 199 - 0
components/external/espruino/libs/math/mconf.h

@@ -0,0 +1,199 @@
+/*							mconf.h
+ *
+ *	Common include file for math routines
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * #include "mconf.h"
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * This file contains definitions for error codes that are
+ * passed to the common error handling routine mtherr()
+ * (which see).
+ *
+ * The file also includes a conditional assembly definition
+ * for the type of computer arithmetic (IEEE, DEC, Motorola
+ * IEEE, or UNKnown).
+ * 
+ * For Digital Equipment PDP-11 and VAX computers, certain
+ * IBM systems, and others that use numbers with a 56-bit
+ * significand, the symbol DEC should be defined.  In this
+ * mode, most floating point constants are given as arrays
+ * of octal integers to eliminate decimal to binary conversion
+ * errors that might be introduced by the compiler.
+ *
+ * For little-endian computers, such as IBM PC, that follow the
+ * IEEE Standard for Binary Floating Point Arithmetic (ANSI/IEEE
+ * Std 754-1985), the symbol IBMPC should be defined.  These
+ * numbers have 53-bit significands.  In this mode, constants
+ * are provided as arrays of hexadecimal 16 bit integers.
+ *
+ * Big-endian IEEE format is denoted MIEEE.  On some RISC
+ * systems such as Sun SPARC, double precision constants
+ * must be stored on 8-byte address boundaries.  Since integer
+ * arrays may be aligned differently, the MIEEE configuration
+ * may fail on such machines.
+ *
+ * To accommodate other types of computer arithmetic, all
+ * constants are also provided in a normal decimal radix
+ * which one can hope are correctly converted to a suitable
+ * format by the available C language compiler.  To invoke
+ * this mode, define the symbol UNK.
+ *
+ * An important difference among these modes is a predefined
+ * set of machine arithmetic constants for each.  The numbers
+ * MACHEP (the machine roundoff error), MAXNUM (largest number
+ * represented), and several other parameters are preset by
+ * the configuration symbol.  Check the file const.c to
+ * ensure that these values are correct for your computer.
+ *
+ * Configurations NANS, INFINITIES, MINUSZERO, and DENORMAL
+ * may fail on many systems.  Verify that they are supposed
+ * to work on your computer.
+ */
+/*
+Cephes Math Library Release 2.3:  June, 1995
+Copyright 1984, 1987, 1989, 1995 by Stephen L. Moshier
+*/
+
+
+/* Define if the `long double' type works.  */
+#define HAVE_LONG_DOUBLE 1
+
+/* Define as the return type of signal handlers (int or void).  */
+#define RETSIGTYPE void
+
+/* Define if you have the ANSI C header files.  */
+#define STDC_HEADERS 1
+
+/* Define if your processor stores words with the most significant
+   byte first (like Motorola and SPARC, unlike Intel and VAX).  */
+/* #undef WORDS_BIGENDIAN */
+
+/* Define if floating point words are bigendian.  */
+/* #undef FLOAT_WORDS_BIGENDIAN */
+
+/* The number of bytes in a int.  */
+#define SIZEOF_INT 4
+
+/* Define if you have the <string.h> header file.  */
+#define HAVE_STRING_H 1
+
+/* Name of package */
+#define PACKAGE "cephes"
+
+/* Version number of package */
+#define VERSION "2.7"
+
+/* Constant definitions for math error conditions
+ */
+
+#define DOMAIN		1	/* argument domain error */
+#define SING		2	/* argument singularity */
+#define OVERFLOW	3	/* overflow range error */
+#define UNDERFLOW	4	/* underflow range error */
+#define TLOSS		5	/* total loss of precision */
+#define PLOSS		6	/* partial loss of precision */
+
+#define EDOM		33
+#define ERANGE		34
+/* Complex numeral.  */
+typedef struct
+	{
+	double r;
+	double i;
+	} cmplx;
+
+#ifdef HAVE_LONG_DOUBLE
+/* Long double complex numeral.  */
+typedef struct
+	{
+	long double r;
+	long double i;
+	} cmplxl;
+#endif
+
+
+/* Type of computer arithmetic */
+
+/* PDP-11, Pro350, VAX:
+ */
+/* #define DEC 1 */
+
+/* Intel IEEE, low order words come first:
+ */
+/* #define IBMPC 1 */
+
+/* Motorola IEEE, high order words come first
+ * (Sun 680x0 workstation):
+ */
+/* #define MIEEE 1 */
+
+/* UNKnown arithmetic, invokes coefficients given in
+ * normal decimal format.  Beware of range boundary
+ * problems (MACHEP, MAXLOG, etc. in const.c) and
+ * roundoff problems in pow.c:
+ * (Sun SPARCstation)
+ */
+#define UNK 1
+
+/* If you define UNK, then be sure to set BIGENDIAN properly. */
+#ifdef FLOAT_WORDS_BIGENDIAN
+#define BIGENDIAN 1
+#else
+#define BIGENDIAN 0
+#endif
+/* Define this `volatile' if your compiler thinks
+ * that floating point arithmetic obeys the associative
+ * and distributive laws.  It will defeat some optimizations
+ * (but probably not enough of them).
+ *
+ * #define VOLATILE volatile
+ */
+#define VOLATILE
+
+/* For 12-byte long doubles on an i386, pad a 16-bit short 0
+ * to the end of real constants initialized by integer arrays.
+ *
+ * #define XPD 0,
+ *
+ * Otherwise, the type is 10 bytes long and XPD should be
+ * defined blank (e.g., Microsoft C).
+ *
+ * #define XPD
+ */
+#define XPD 0,
+
+/* Define to support tiny denormal numbers, else undefine. */
+#define DENORMAL 1
+
+/* Define to ask for infinity support, else undefine. */
+#define INFINITIES 1
+
+/* Define to ask for support of numbers that are Not-a-Number,
+   else undefine.  This may automatically define INFINITIES in some files. */
+#define NANS 1
+
+/* Define to distinguish between -0.0 and +0.0.  */
+#define MINUSZERO 1
+
+/* Define 1 for ANSI C atan2() function
+   See atan.c and clog.c. */
+#define ANSIC 1
+
+/* Get ANSI function prototypes, if you want them. */
+#if 1
+/* #ifdef __STDC__ */
+#define ANSIPROT 1
+int mtherr ( char *, int );
+#else
+int mtherr();
+#endif
+
+/* Variable for error reporting.  See mtherr.c.  */
+extern int merror;

+ 122 - 0
components/external/espruino/libs/math/mod2pi.c

@@ -0,0 +1,122 @@
+/* Program to test range reduction of trigonometry functions
+ *
+ * -- Steve Moshier
+ */
+
+#include "mconf.h"
+#ifdef ANSIPROT
+extern double floor ( double );
+extern double ldexp ( double, int );
+extern double sin ( double );
+#else
+double floor(), ldexp(), sin();
+#endif
+
+#define TPI 6.283185307179586476925
+
+main()
+{
+char s[40];
+double a, n, t, x, y, z;
+int lflg;
+
+x = TPI/4.0;
+t = 1.0;
+
+loop:
+
+t = 2.0 * t;
+
+/* Stop testing at a point beyond which the integer part of
+ * x/2pi cannot be represented exactly by a double precision number.
+ * The library trigonometry functions will probably give up long before
+ * this point is reached.
+ */
+if( t > 1.0e16 )
+	exit(0);
+
+/* Adjust the following to choose a nontrivial x
+ * where test function(x) has a slope of about 1 or more.
+ */
+x = TPI * t  + 0.5;
+
+z = x;
+lflg = 0;
+
+inlup:
+
+/* floor() returns the largest integer less than its argument.
+ * If you do not have this, or AINT(), then you may convert x/TPI
+ * to a long integer and then back to double; but in that case
+ * x will be limited to the largest value that will fit into a
+ * long integer.
+ */
+n = floor( z/TPI );
+
+/* Carefully subtract 2 pi n from x.
+ * This is done by subtracting n * 2**k in such a way that there
+ * is no arithmetic cancellation error at any step.  The k are the
+ * bits in the number 2 pi.
+ *
+ * If you do not have ldexp(), then you may multiply or
+ * divide n by an appropriate power of 2 after each step.
+ * For example:
+ *  a = z - 4*n;
+ *  a -= 2*n;
+ *  n /= 4;
+ *  a -= n;   n/4
+ *  n /= 8;
+ *  a -= n;   n/32
+ * etc.
+ * This will only work if division by a power of 2 is exact.
+ */
+
+a = z - ldexp(n, 2);	/* 4n */
+a -= ldexp( n, 1);	/* 2n */
+a -= ldexp( n, -2 );	/* n/4 */
+a -= ldexp( n, -5 );	/* n/32 */
+a -= ldexp( n, -9 );	/* n/512 */
+a += ldexp( n, -15 );	/* add n/32768 */
+a -= ldexp( n, -17 );	/* n/131072 */
+a -= ldexp( n, -18 );
+a -= ldexp( n, -20 );
+a -= ldexp( n, -22 );
+a -= ldexp( n, -24 );
+a -= ldexp( n, -28 );
+a -= ldexp( n, -32 );
+a -= ldexp( n, -37 );
+a -= ldexp( n, -39 );
+a -= ldexp( n, -40 );
+a -= ldexp( n, -42 );
+a -= ldexp( n, -46 );
+a -= ldexp( n, -47 );
+
+/* Subtract what is left of 2 pi n after all the above reductions.
+ */
+a -= 2.44929359829470635445e-16 * n;
+
+/* If the test is extended too far, it is possible
+ * to have chosen the wrong value of n.  The following
+ * will fix that, but at some reduction in accuracy.
+ */
+if( (a > TPI) || (a < -1e-11) )
+	{
+	z = a;
+	lflg += 1;
+	printf( "Warning! Reduction failed on first try.\n" );
+	goto inlup;
+	}
+if( a < 0.0 )
+	{
+	printf( "Warning! Reduced value < 0\n" );
+	a += TPI;
+	}
+
+/* Compute the test function at x and at a = x mod 2 pi.
+ */
+y = sin(x);
+z = sin(a);
+printf( "sin(%.15e) error = %.3e\n", x, y-z );
+goto loop;
+}
+

+ 103 - 0
components/external/espruino/libs/math/mtherr.c

@@ -0,0 +1,103 @@
+/*							mtherr.c
+ *
+ *	Library common error handling routine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * char *fctnam;
+ * int code;
+ * int mtherr();
+ *
+ * mtherr( fctnam, code );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * This routine may be called to report one of the following
+ * error conditions (in the include file mconf.h).
+ *  
+ *   Mnemonic        Value          Significance
+ *
+ *    DOMAIN            1       argument domain error
+ *    SING              2       function singularity
+ *    OVERFLOW          3       overflow range error
+ *    UNDERFLOW         4       underflow range error
+ *    TLOSS             5       total loss of precision
+ *    PLOSS             6       partial loss of precision
+ *    EDOM             33       Unix domain error code
+ *    ERANGE           34       Unix range error code
+ *
+ * The default version of the file prints the function name,
+ * passed to it by the pointer fctnam, followed by the
+ * error condition.  The display is directed to the standard
+ * output device.  The routine then returns to the calling
+ * program.  Users may wish to modify the program to abort by
+ * calling exit() under severe error conditions such as domain
+ * errors.
+ *
+ * Since all error conditions pass control to this function,
+ * the display may be easily changed, eliminated, or directed
+ * to an error logging device.
+ *
+ * SEE ALSO:
+ *
+ * mconf.h
+ *
+ */
+
+/*
+Cephes Math Library Release 2.0:  April, 1987
+Copyright 1984, 1987 by Stephen L. Moshier
+Direct inquiries to 30 Frost Street, Cambridge, MA 02140
+*/
+#if 0
+#include <stdio.h>
+#endif
+#include "mconf.h"
+
+int merror = 0;
+
+/* Notice: the order of appearance of the following
+ * messages is bound to the error codes defined
+ * in mconf.h.
+ */
+static char *ermsg[7] = {
+"unknown",      /* error code 0 */
+"domain",       /* error code 1 */
+"singularity",  /* et seq.      */
+"overflow",
+"underflow",
+"total loss of precision",
+"partial loss of precision"
+};
+
+
+int mtherr( name, code )
+char *name;
+int code;
+{
+#if 0
+/* Display string passed by calling program,
+ * which is supposed to be the name of the
+ * function in which the error occurred:
+ */
+printf( "\n%s ", name );
+
+/* Set global error message word */
+merror = code;
+
+/* Display error message defined
+ * by the code argument.
+ */
+if( (code <= 0) || (code >= 7) )
+	code = 0;
+printf( "%s error\n", ermsg[code] );
+#endif
+/* Return to calling
+ * program
+ */
+return( 0 );
+}

+ 518 - 0
components/external/espruino/libs/math/mtst.c

@@ -0,0 +1,518 @@
+/*   mtst.c
+ Consistency tests for math functions.
+ To get strict rounding rules on a 386 or 68000 computer,
+ define SETPREC to 1.
+
+ With NTRIALS=10000, the following are typical results for
+ IEEE double precision arithmetic.
+
+Consistency test of math functions.
+Max and rms relative errors for 10000 random arguments.
+x =   cbrt(   cube(x) ):  max = 0.00E+00   rms = 0.00E+00
+x =   atan(    tan(x) ):  max = 2.21E-16   rms = 3.27E-17
+x =    sin(   asin(x) ):  max = 2.13E-16   rms = 2.95E-17
+x =   sqrt( square(x) ):  max = 0.00E+00   rms = 0.00E+00
+x =    log(    exp(x) ):  max = 1.11E-16 A rms = 4.35E-18 A
+x =   tanh(  atanh(x) ):  max = 2.22E-16   rms = 2.43E-17
+x =  asinh(   sinh(x) ):  max = 2.05E-16   rms = 3.49E-18
+x =  acosh(   cosh(x) ):  max = 1.43E-15 A rms = 1.54E-17 A
+x =  log10(  exp10(x) ):  max = 5.55E-17 A rms = 1.27E-18 A
+x = pow( pow(x,a),1/a ):  max = 7.60E-14   rms = 1.05E-15
+x =    cos(   acos(x) ):  max = 2.22E-16 A rms = 6.90E-17 A
+*/
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1987, 1988, 2000 by Stephen L. Moshier
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "mconf.h"
+
+#ifndef NTRIALS
+#define NTRIALS 10000
+#endif
+
+/* C9X spells lgam lgamma.  */
+#define GLIBC2 0
+#define GLIBC2r1 0
+
+#define SETPREC 1
+#define STRTST 0
+
+#define WTRIALS (NTRIALS/5)
+
+#if GLIBC2
+double PI = 3.141592653589793238462643;
+double PIO2 = 3.141592653589793238462643 * 0.5;
+double MAXLOG = 7.09782712893383996732224E2;
+#else
+extern double PI;
+extern double PIO2;
+extern double MAXLOG;
+#endif
+
+extern double MINLOG;
+/*
+define MINLOG -170.0
+define MAXLOG +170.0
+define PI 3.14159265358979323846
+define PIO2 1.570796326794896619
+*/
+
+#ifdef ANSIPROT
+extern double fabs ( double );
+extern double sqrt ( double );
+extern double cbrt ( double );
+extern double exp ( double );
+extern double log ( double );
+extern double exp10 ( double );
+extern double log10 ( double );
+extern double tan ( double );
+extern double atan ( double );
+extern double sin ( double );
+extern double asin ( double );
+extern double cos ( double );
+extern double acos ( double );
+extern double pow ( double, double );
+extern double tanh ( double );
+extern double atanh ( double );
+extern double sinh ( double );
+extern double asinh ( double x );
+extern double cosh ( double );
+extern double acosh ( double );
+extern double gamma ( double );
+extern double lgam ( double );
+extern double jn ( int, double );
+extern double yn ( int, double );
+extern double ndtr ( double );
+extern double ndtri ( double );
+extern double stdtr ( int, double );
+extern double stdtri ( int, double );
+extern double ellpe ( double );
+extern double ellpk ( double );
+#else
+double fabs(), sqrt(), cbrt(), exp(), log();
+double exp10(), log10(), tan(), atan();
+double sin(), asin(), cos(), acos(), pow();
+double tanh(), atanh(), sinh(), asinh(), cosh(), acosh();
+double gamma(), lgam(), jn(), yn(), ndtrl(), ndtril();
+double stdtrl(), stdtril(), ellpel(), ellpkl();
+#endif
+
+#if GLIBC2
+extern double lgamma (double);
+extern double tgamma ( double );
+#endif
+
+#if SETPREC
+int dprec();
+#endif
+
+int drand();
+/* void exit(); */
+/* int printf(); */
+
+
+/* Provide inverses for square root and cube root: */
+double square(x)
+double x;
+{
+return( x * x );
+}
+
+double cube(x)
+double x;
+{
+return( x * x * x );
+}
+
+/* lookup table for each function */
+struct fundef
+	{
+	char *nam1;		/* the function */
+	double (*name )();
+	char *nam2;		/* its inverse  */
+	double (*inv )();
+	int nargs;		/* number of function arguments */
+	int tstyp;		/* type code of the function */
+	long ctrl;		/* relative error flag */
+	double arg1w;		/* width of domain for 1st arg */
+	double arg1l;		/* lower bound domain 1st arg */
+	long arg1f;		/* flags, e.g. integer arg */
+	double arg2w;		/* same info for args 2, 3, 4 */
+	double arg2l;
+	long arg2f;
+/*
+	double arg3w;
+	double arg3l;
+	long arg3f;
+	double arg4w;
+	double arg4l;
+	long arg4f;
+*/
+	};
+
+
+/* fundef.ctrl bits: */
+#define RELERR 1
+
+/* fundef.tstyp  test types: */
+#define POWER 1 
+#define ELLIP 2 
+#define GAMMA 3
+#define WRONK1 4
+#define WRONK2 5
+#define WRONK3 6
+#define STDTR 7
+
+/* fundef.argNf  argument flag bits: */
+#define INT 2
+#define EXPSCAL 4
+
+#if GLIBC2r1
+#define NTESTS 12
+#else
+#if GLIBC2
+#define NTESTS 13
+#else
+#define NTESTS 17
+#endif
+#endif
+
+struct fundef defs[NTESTS] = {
+{"  cube",   cube,   "  cbrt",   cbrt, 1, 0, 1, 2002.0, -1001.0, 0,
+0.0, 0.0, 0},
+{"   tan",    tan,   "  atan",   atan, 1, 0, 1,    0.0,     0.0,  0,
+0.0, 0.0, 0},
+{"  asin",   asin,   "   sin",    sin, 1, 0, 1,   2.0,      -1.0,  0,
+0.0, 0.0, 0},
+{"square", square,   "  sqrt",   sqrt, 1, 0, 1, 170.0,    -85.0, EXPSCAL,
+0.0, 0.0, 0},
+{"   exp",    exp,   "   log",    log, 1, 0, 0, 340.0,    -170.0,  0,
+0.0, 0.0, 0},
+{" atanh",  atanh,   "  tanh",   tanh, 1, 0, 1,    2.0,    -1.0,  0,
+0.0, 0.0, 0},
+{"  sinh",   sinh,   " asinh",  asinh, 1, 0, 1, 340.0,   0.0,  0,
+0.0, 0.0, 0},
+{"  cosh",   cosh,   " acosh",  acosh, 1, 0, 0, 340.0,      0.0,  0,
+0.0, 0.0, 0},
+#if !GLIBC2r1
+{" exp10",  exp10,   " log10",  log10, 1, 0, 0, 340.0,    -170.0,  0,
+0.0, 0.0, 0},
+#endif
+{"pow",       pow,      "pow",    pow, 2, POWER, 1, 21.0, 0.0,   0,
+42.0, -21.0, 0},
+{"  acos",   acos,   "   cos",    cos, 1, 0, 0,   2.0,      -1.0,  0,
+0.0, 0.0, 0},
+#if GLIBC2
+#if !GLIBC2r1
+{ "tgamma",  tgamma,  "lgamma", lgamma, 1, GAMMA, 0, 34.0, 0.0,   0,
+0.0, 0.0, 0},
+#endif
+#else
+{ "gamma",  gamma,     "lgam",   lgam, 1, GAMMA, 0, 34.0, 0.0,   0,
+0.0, 0.0, 0},
+#endif
+{ "  Jn",     jn,   "  Yn",     yn, 2, WRONK1, 0, 30.0,  0.1,  0,
+40.0, -20.0, INT},
+#if !GLIBC2
+{ "  ndtr",   ndtr,  " ndtri",  ndtri, 1, 0, 1,  10.0L,  -10.0L,  0,
+0.0, 0.0, 0},
+{ " ndtri",  ndtri,  "  ndtr",   ndtr, 1, 0, 1,  1.0L,  0.0L,  0,
+0.0, 0.0, 0},
+{" ellpe",  ellpe,   " ellpk",  ellpk, 1, ELLIP, 0,   1.0L, 0.0L,  0,
+0.0, 0.0, 0},
+{ "stdtr",  stdtr,   "stdtri", stdtri, 2, STDTR, 1, 4.0L, -2.0L,   0,
+30.0, 1.0, INT},
+#endif
+};
+
+static char *headrs[] = {
+"x = %s( %s(x) ): ",
+"x = %s( %s(x,a),1/a ): ",	/* power */
+"Legendre %s, %s: ",		/* ellip */
+"%s(x) = log(%s(x)): ",		/* gamma */
+"Wronksian of %s, %s: ",
+"Wronksian of %s, %s: ",
+"Wronksian of %s, %s: ",
+"x = %s(%s(k,x) ): ",	/* stdtr */
+};
+ 
+const static double yy1 = 0.0;
+const static double y2 = 0.0;
+const static double y3 = 0.0;
+const static double y4 = 0.0;
+const static double a = 0.0;
+const static double x = 0.0;
+const static double y = 0.0;
+const static double z = 0.0;
+const static double e = 0.0;
+const static double max = 0.0;
+const static double rmsa = 0.0;
+const static double rms = 0.0;
+const static double ave = 0.0;
+
+
+int main()
+{
+double (*fun )();
+double (*ifun )();
+struct fundef *d;
+int i, k, itst;
+int m, ntr;
+
+#if SETPREC
+dprec();  /* set coprocessor precision */
+#endif
+ntr = NTRIALS;
+printf( "Consistency test of math functions.\n" );
+printf( "Max and rms relative errors for %d random arguments.\n",
+	ntr );
+
+/* Initialize machine dependent parameters: */
+defs[1].arg1w = PI;
+defs[1].arg1l = -PI/2.0;
+/* Microsoft C has trouble with denormal numbers. */
+#if 0
+defs[3].arg1w = MAXLOG;
+defs[3].arg1l = -MAXLOG/2.0;
+defs[4].arg1w = 2*MAXLOG;
+defs[4].arg1l = -MAXLOG;
+#endif
+defs[6].arg1w = 2.0*MAXLOG;
+defs[6].arg1l = -MAXLOG;
+defs[7].arg1w = MAXLOG;
+defs[7].arg1l = 0.0;
+
+
+/* Outer loop, on the test number: */
+
+for( itst=STRTST; itst<NTESTS; itst++ )
+{
+d = &defs[itst];
+k = 0;
+m = 0;
+max = 0.0;
+rmsa = 0.0;
+ave = 0.0;
+fun = d->name;
+ifun = d->inv;
+
+/* Absolute error criterion starts with gamma function
+ * (put all such at end of table)
+ */
+#if 0
+if( d->tstyp == GAMMA )
+	printf( "Absolute error criterion (but relative if >1):\n" );
+#endif
+
+/* Smaller number of trials for Wronksians
+ * (put them at end of list)
+ */
+#if 0
+if( d->tstyp == WRONK1 )
+	{
+	ntr = WTRIALS;
+	printf( "Absolute error and only %d trials:\n", ntr );
+	}
+#endif
+if( d->tstyp == STDTR )
+	{
+	ntr = NTRIALS/10;
+	printf( "Relative error and only %d trials:\n", ntr );
+	}
+printf( headrs[d->tstyp], d->nam2, d->nam1 );
+
+for( i=0; i<ntr; i++ )
+{
+m++;
+
+/* make random number(s) in desired range(s) */
+switch( d->nargs )
+{
+
+default:
+goto illegn;
+	
+case 2:
+drand( &a );
+a = d->arg2w *  ( a - 1.0 )  +  d->arg2l;
+if( d->arg2f & EXPSCAL )
+	{
+	a = exp(a);
+	drand( &y2 );
+	a -= 1.0e-13 * a * y2;
+	}
+if( d->arg2f & INT )
+	{
+	k = a + 0.25;
+	a = k;
+	}
+
+case 1:
+drand( &x );
+x = d->arg1w *  ( x - 1.0 )  +  d->arg1l;
+if( d->arg1f & EXPSCAL )
+	{
+	x = exp(x);
+	drand( &a );
+	x += 1.0e-13 * x * a;
+	}
+}
+
+
+/* compute function under test */
+switch( d->nargs )
+	{
+	case 1:
+	switch( d->tstyp )
+		{
+		case ELLIP:
+		yy1 = ( *(fun) )(x);
+		y2 = ( *(fun) )(1.0-x);
+		y3 = ( *(ifun) )(x);
+		y4 = ( *(ifun) )(1.0-x);
+		break;
+
+		case GAMMA:
+#if GLIBC2
+		y = lgamma(x);
+		x = log( tgamma(x) );
+#else
+		y = lgam(x);
+		x = log( gamma(x) );
+#endif
+		break;
+
+		default:
+		z = ( *(fun) )(x);
+		y = ( *(ifun) )(z);
+		}
+	break;
+	
+	case 2:
+	if( d->arg2f & INT )
+		{
+		switch( d->tstyp )
+			{
+			case WRONK1:
+			yy1 = (*fun)( k, x ); /* jn */
+			y2 = (*fun)( k+1, x );
+			y3 = (*ifun)( k, x ); /* yn */
+			y4 = (*ifun)( k+1, x );	
+			break;
+
+			case WRONK2:
+			yy1 = (*fun)( a, x ); /* iv */
+			y2 = (*fun)( a+1.0, x );
+			y3 = (*ifun)( k, x ); /* kn */	
+			y4 = (*ifun)( k+1, x );	
+			break;
+
+			default:
+			z = (*fun)( k, x );
+			y = (*ifun)( k, z );
+			}
+		}
+	else
+		{
+		if( d->tstyp == POWER )
+			{
+			z = (*fun)( x, a );
+			y = (*ifun)( z, 1.0/a );
+			}
+		else
+			{
+			z = (*fun)( a, x );
+			y = (*ifun)( a, z );
+			}
+		}
+	break;
+
+
+	default:
+illegn:
+	printf( "Illegal nargs= %d", d->nargs );
+	exit(1);
+	}	
+
+switch( d->tstyp )
+	{
+	case WRONK1:
+	e = (y2*y3 - yy1*y4) - 2.0/(PI*x); /* Jn, Yn */
+	break;
+
+	case WRONK2:
+	e = (y2*y3 + yy1*y4) - 1.0/x; /* In, Kn */
+	break;
+	
+	case ELLIP:
+	e = (yy1-y3)*y4 + y3*y2 - PIO2;
+	break;
+
+	default:
+	e = y - x;
+	break;
+	}
+
+if( d->ctrl & RELERR )
+	e /= x;
+else
+	{
+	if( fabs(x) > 1.0 )
+		e /= x;
+	}
+
+ave += e;
+/* absolute value of error */
+if( e < 0 )
+	e = -e;
+
+/* peak detect the error */
+if( e > max )
+	{
+	max = e;
+
+	if( e > 1.0e-10 )
+		{
+		printf("x %.6E z %.6E y %.6E max %.4E\n",
+		 x, z, y, max);
+		if( d->tstyp == POWER )
+			{
+			printf( "a %.6E\n", a );
+			}
+		if( d->tstyp >= WRONK1 )
+			{
+		printf( "yy1 %.4E y2 %.4E y3 %.4E y4 %.4E k %d x %.4E\n",
+		 yy1, y2, y3, y4, k, x );
+			}
+		}
+
+/*
+	printf("%.8E %.8E %.4E %6ld \n", x, y, max, n);
+	printf("%d %.8E %.8E %.4E %6ld \n", k, x, y, max, n);
+	printf("%.6E %.6E %.6E %.4E %6ld \n", a, x, y, max, n);
+	printf("%.6E %.6E %.6E %.6E %.4E %6ld \n", a, b, x, y, max, n);
+	printf("%.4E %.4E %.4E %.4E %.4E %.4E %6ld \n",
+		a, b, c, x, y, max, n);
+*/
+	}
+
+/* accumulate rms error	*/
+e *= 1.0e16;	/* adjust range */
+rmsa += e * e;	/* accumulate the square of the error */
+}
+
+/* report after NTRIALS trials */
+rms = 1.0e-16 * sqrt( rmsa/m );
+if(d->ctrl & RELERR)
+	printf(" max = %.2E   rms = %.2E\n", max, rms );
+else
+	printf(" max = %.2E A rms = %.2E A\n", max, rms );
+} /* loop on itst */
+
+exit(0);
+}

+ 30 - 0
components/external/espruino/libs/math/mtst.opt

@@ -0,0 +1,30 @@
+acosh.obj,-
+asin.obj,-
+asinh.obj,-
+atan.obj,-
+atanh.obj,-
+cbrt.obj,-
+chbevl.obj,-
+const.obj,-
+cosh.obj,-
+drand.obj,-
+exp.obj,-
+exp10.obj,-
+fabs.obj,-
+floor.obj,-
+log.obj,-
+log10.obj,-
+polevl.obj,-
+pow.obj,-
+powi.obj,-
+round.obj,-
+sin.obj,-
+sinh.obj,-
+tan.obj,-
+tanh.obj,-
+unity.obj,-
+sqrt.obj,-
+floor.obj,-
+polevl.obj,-
+mtherr.obj,-
+sys$library:vaxcrtl/lib

+ 116 - 0
components/external/espruino/libs/math/polevl.asm

@@ -0,0 +1,116 @@
+;	Static Name Aliases
+;
+	TITLE   polevl
+
+_TEXT	SEGMENT  BYTE PUBLIC 'CODE'
+_TEXT	ENDS
+CONST	SEGMENT  WORD PUBLIC 'CONST'
+CONST	ENDS
+_BSS	SEGMENT  WORD PUBLIC 'BSS'
+_BSS	ENDS
+_DATA	SEGMENT  WORD PUBLIC 'DATA'
+_DATA	ENDS
+DGROUP	GROUP	CONST,	_BSS,	_DATA
+	ASSUME  CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP
+PUBLIC  _polevl
+PUBLIC  _p1evl
+_DATA	SEGMENT
+EXTRN	__chkstk:NEAR
+EXTRN	__fac:NEAR
+EXTRN	__fltused:NEAR
+$T20001		DQ	0000000000H    ;	.0000000000000000
+ans	DQ	0
+ctrlw	DW	0
+_DATA ENDS
+_TEXT      SEGMENT
+	PUBLIC	_polevl
+_polevl	PROC NEAR
+	push	bp
+	mov	bp,sp
+	mov	ax,12
+	call	__chkstk
+	push	si
+	mov	si,[bp+12]
+;	fstcw	ctrlw
+;	fwait
+;	mov	ax,ctrlw
+;	or	ax,00100h
+;	mov	ctrlw,ax
+;	fldcw	ctrlw
+
+	fldz
+	fwait	
+	mov	ax,[bp+14]
+	inc	ax
+	mov	[bp-12],ax
+$D15:
+	fmul	QWORD PTR [bp+4]
+	add	si,8
+	fwait
+	fadd	QWORD PTR [si-8]
+	fwait	
+	dec	WORD PTR [bp-12]
+	jne	$D15
+	fstp	ans
+;	fstcw	ctrlw
+;	fwait
+;	mov	ax,ctrlw
+;	and	ax,0feffh
+;	mov	ctrlw,ax
+;	fldcw	ctrlw
+
+	lea	ax, ans
+	fwait	
+	pop	si
+	mov	sp,bp
+	pop	bp
+	ret	
+_polevl	ENDP
+
+	PUBLIC	_p1evl
+_p1evl	PROC NEAR
+	push	bp
+	mov	bp,sp
+	mov	ax,12
+	call	__chkstk
+	push	si
+
+;	fstcw	ctrlw
+;	fwait
+;	mov	ax,ctrlw
+;	or	ax,00100h
+;	mov	ctrlw,ax
+;	fldcw	ctrlw
+
+	mov	si,[bp+12]
+	fld	QWORD PTR [bp+4]
+	add	si,8
+	fadd	QWORD PTR [si-8]
+	fwait	
+	mov	ax,[bp+14]
+	dec	ax
+	mov	[bp-12],ax
+$D26:
+	fmul	QWORD PTR [bp+4]
+	add	si,8
+	fadd	QWORD PTR [si-8]
+	fwait	
+	dec	WORD PTR [bp-12]
+	jne	$D26
+	fstp	ans
+	lea	ax, ans
+;	fstcw	ctrlw
+;	fwait
+;	mov	ax,ctrlw
+;	and	ax,0feffh
+;	mov	ctrlw,ax
+;	fldcw	ctrlw
+	fwait	
+	pop	si
+	mov	sp,bp
+	pop	bp
+	ret	
+_p1evl	ENDP
+
+_TEXT	ENDS
+END

+ 97 - 0
components/external/espruino/libs/math/polevl.c

@@ -0,0 +1,97 @@
+/*							polevl.c
+ *							p1evl.c
+ *
+ *	Evaluate polynomial
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * int N;
+ * double x, y, coef[N+1], polevl[];
+ *
+ * y = polevl( x, coef, N );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Evaluates polynomial of degree N:
+ *
+ *                     2          N
+ * y  =  C  + C x + C x  +...+ C x
+ *        0    1     2          N
+ *
+ * Coefficients are stored in reverse order:
+ *
+ * coef[0] = C  , ..., coef[N] = C  .
+ *            N                   0
+ *
+ *  The function p1evl() assumes that coef[N] = 1.0 and is
+ * omitted from the array.  Its calling arguments are
+ * otherwise the same as polevl().
+ *
+ *
+ * SPEED:
+ *
+ * In the interest of speed, there are no checks for out
+ * of bounds arithmetic.  This routine is used by most of
+ * the functions in the library.  Depending on available
+ * equipment features, the user may wish to rewrite the
+ * program in microcode or assembly language.
+ *
+ */
+
+
+/*
+Cephes Math Library Release 2.1:  December, 1988
+Copyright 1984, 1987, 1988 by Stephen L. Moshier
+Direct inquiries to 30 Frost Street, Cambridge, MA 02140
+*/
+
+
+double polevl( x, coef, N )
+double x;
+double coef[];
+int N;
+{
+double ans;
+int i;
+double *p;
+
+p = coef;
+ans = *p++;
+i = N;
+
+do
+	ans = ans * x  +  *p++;
+while( --i );
+
+return( ans );
+}
+
+/*							p1evl()	*/
+/*                                          N
+ * Evaluate polynomial when coefficient of x  is 1.0.
+ * Otherwise same as polevl.
+ */
+
+double p1evl( x, coef, N )
+double x;
+double coef[];
+int N;
+{
+double ans;
+double *p;
+int i;
+
+p = coef;
+ans = x + *p++;
+i = N-1;
+
+do
+	ans = ans * x  + *p++;
+while( --i );
+
+return( ans );
+}

+ 756 - 0
components/external/espruino/libs/math/pow.c

@@ -0,0 +1,756 @@
+/*							pow.c
+ *
+ *	Power function
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, z, pow();
+ *
+ * z = pow( x, y );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Computes x raised to the yth power.  Analytically,
+ *
+ *      x**y  =  exp( y log(x) ).
+ *
+ * Following Cody and Waite, this program uses a lookup table
+ * of 2**-i/16 and pseudo extended precision arithmetic to
+ * obtain an extra three bits of accuracy in both the logarithm
+ * and the exponential.
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE     -26,26       30000      4.2e-16      7.7e-17
+ *    DEC      -26,26       60000      4.8e-17      9.1e-18
+ * 1/26 < x < 26, with log(x) uniformly distributed.
+ * -26 < y < 26, y uniformly distributed.
+ *    IEEE     0,8700       30000      1.5e-14      2.1e-15
+ * 0.99 < x < 1.01, 0 < y < 8700, uniformly distributed.
+ *
+ *
+ * ERROR MESSAGES:
+ *
+ *   message         condition      value returned
+ * pow overflow     x**y > MAXNUM      INFINITY
+ * pow underflow   x**y < 1/MAXNUM       0.0
+ * pow domain      x<0 and y noninteger  0.0
+ *
+ */
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+
+#include "mconf.h"
+static char fname[] = {"pow"};
+
+#define SQRTH 0.70710678118654752440
+
+#ifdef UNK
+const static double P[] = {
+  4.97778295871696322025E-1,
+  3.73336776063286838734E0,
+  7.69994162726912503298E0,
+  4.66651806774358464979E0
+};
+const static double Q[] = {
+/* 1.00000000000000000000E0, */
+  9.33340916416696166113E0,
+  2.79999886606328401649E1,
+  3.35994905342304405431E1,
+  1.39995542032307539578E1
+};
+/* 2^(-i/16), IEEE precision */
+const static double A[] = {
+  1.00000000000000000000E0,
+  9.57603280698573700036E-1,
+  9.17004043204671215328E-1,
+  8.78126080186649726755E-1,
+  8.40896415253714502036E-1,
+  8.05245165974627141736E-1,
+  7.71105412703970372057E-1,
+  7.38413072969749673113E-1,
+  7.07106781186547572737E-1,
+  6.77127773468446325644E-1,
+  6.48419777325504820276E-1,
+  6.20928906036742001007E-1,
+  5.94603557501360513449E-1,
+  5.69394317378345782288E-1,
+  5.45253866332628844837E-1,
+  5.22136891213706877402E-1,
+  5.00000000000000000000E-1
+};
+const static double B[] = {
+ 0.00000000000000000000E0,
+ 1.64155361212281360176E-17,
+ 4.09950501029074826006E-17,
+ 3.97491740484881042808E-17,
+-4.83364665672645672553E-17,
+ 1.26912513974441574796E-17,
+ 1.99100761573282305549E-17,
+-1.52339103990623557348E-17,
+ 0.00000000000000000000E0
+};
+const static double R[] = {
+ 1.49664108433729301083E-5,
+ 1.54010762792771901396E-4,
+ 1.33335476964097721140E-3,
+ 9.61812908476554225149E-3,
+ 5.55041086645832347466E-2,
+ 2.40226506959099779976E-1,
+ 6.93147180559945308821E-1
+};
+
+#define douba(k) A[k]
+#define doubb(k) B[k]
+#define MEXP 16383.0
+#ifdef DENORMAL
+#define MNEXP -17183.0
+#else
+#define MNEXP -16383.0
+#endif
+#endif
+
+#ifdef DEC
+static unsigned short P[] = {
+0037776,0156313,0175332,0163602,
+0040556,0167577,0052366,0174245,
+0040766,0062753,0175707,0055564,
+0040625,0052035,0131344,0155636,
+};
+static unsigned short Q[] = {
+/*0040200,0000000,0000000,0000000,*/
+0041025,0052644,0154404,0105155,
+0041337,0177772,0007016,0047646,
+0041406,0062740,0154273,0020020,
+0041137,0177054,0106127,0044555,
+};
+static unsigned short A[] = {
+0040200,0000000,0000000,0000000,
+0040165,0022575,0012444,0103314,
+0040152,0140306,0163735,0022071,
+0040140,0146336,0166052,0112341,
+0040127,0042374,0145326,0116553,
+0040116,0022214,0012437,0102201,
+0040105,0063452,0010525,0003333,
+0040075,0004243,0117530,0006067,
+0040065,0002363,0031771,0157145,
+0040055,0054076,0165102,0120513,
+0040045,0177326,0124661,0050471,
+0040036,0172462,0060221,0120422,
+0040030,0033760,0050615,0134251,
+0040021,0141723,0071653,0010703,
+0040013,0112701,0161752,0105727,
+0040005,0125303,0063714,0044173,
+0040000,0000000,0000000,0000000
+};
+static unsigned short B[] = {
+0000000,0000000,0000000,0000000,
+0021473,0040265,0153315,0140671,
+0121074,0062627,0042146,0176454,
+0121413,0003524,0136332,0066212,
+0121767,0046404,0166231,0012553,
+0121257,0015024,0002357,0043574,
+0021736,0106532,0043060,0056206,
+0121310,0020334,0165705,0035326,
+0000000,0000000,0000000,0000000
+};
+
+static unsigned short R[] = {
+0034173,0014076,0137624,0115771,
+0035041,0076763,0003744,0111311,
+0035656,0141766,0041127,0074351,
+0036435,0112533,0073611,0116664,
+0037143,0054106,0134040,0152223,
+0037565,0176757,0176026,0025551,
+0040061,0071027,0173721,0147572
+};
+
+/*
+const static double R[] = {
+0.14928852680595608186e-4,
+0.15400290440989764601e-3,
+0.13333541313585784703e-2,
+0.96181290595172416964e-2,
+0.55504108664085595326e-1,
+0.24022650695909537056e0,
+0.69314718055994529629e0
+};
+*/
+#define douba(k) (*(double *)&A[(k)<<2])
+#define doubb(k) (*(double *)&B[(k)<<2])
+#define MEXP 2031.0
+#define MNEXP -2031.0
+#endif
+
+#ifdef IBMPC
+static unsigned short P[] = {
+0x5cf0,0x7f5b,0xdb99,0x3fdf,
+0xdf15,0xea9e,0xddef,0x400d,
+0xeb6f,0x7f78,0xccbd,0x401e,
+0x9b74,0xb65c,0xaa83,0x4012,
+};
+static unsigned short Q[] = {
+/*0x0000,0x0000,0x0000,0x3ff0,*/
+0x914e,0x9b20,0xaab4,0x4022,
+0xc9f5,0x41c1,0xffff,0x403b,
+0x6402,0x1b17,0xccbc,0x4040,
+0xe92e,0x918a,0xffc5,0x402b,
+};
+static unsigned short A[] = {
+0x0000,0x0000,0x0000,0x3ff0,
+0x90da,0xa2a4,0xa4af,0x3fee,
+0xa487,0xdcfb,0x5818,0x3fed,
+0x529c,0xdd85,0x199b,0x3fec,
+0xd3ad,0x995a,0xe89f,0x3fea,
+0xf090,0x82a3,0xc491,0x3fe9,
+0xa0db,0x422a,0xace5,0x3fe8,
+0x0187,0x73eb,0xa114,0x3fe7,
+0x3bcd,0x667f,0xa09e,0x3fe6,
+0x5429,0xdd48,0xab07,0x3fe5,
+0x2a27,0xd536,0xbfda,0x3fe4,
+0x3422,0x4c12,0xdea6,0x3fe3,
+0xb715,0x0a31,0x06fe,0x3fe3,
+0x6238,0x6e75,0x387a,0x3fe2,
+0x517b,0x3c7d,0x72b8,0x3fe1,
+0x890f,0x6cf9,0xb558,0x3fe0,
+0x0000,0x0000,0x0000,0x3fe0
+};
+static unsigned short B[] = {
+0x0000,0x0000,0x0000,0x0000,
+0x3707,0xd75b,0xed02,0x3c72,
+0xcc81,0x345d,0xa1cd,0x3c87,
+0x4b27,0x5686,0xe9f1,0x3c86,
+0x6456,0x13b2,0xdd34,0xbc8b,
+0x42e2,0xafec,0x4397,0x3c6d,
+0x82e4,0xd231,0xf46a,0x3c76,
+0x8a76,0xb9d7,0x9041,0xbc71,
+0x0000,0x0000,0x0000,0x0000
+};
+static unsigned short R[] = {
+0x937f,0xd7f2,0x6307,0x3eef,
+0x9259,0x60fc,0x2fbe,0x3f24,
+0xef1d,0xc84a,0xd87e,0x3f55,
+0x33b7,0x6ef1,0xb2ab,0x3f83,
+0x1a92,0xd704,0x6b08,0x3fac,
+0xc56d,0xff82,0xbfbd,0x3fce,
+0x39ef,0xfefa,0x2e42,0x3fe6
+};
+
+#define douba(k) (*(double *)&A[(k)<<2])
+#define doubb(k) (*(double *)&B[(k)<<2])
+#define MEXP 16383.0
+#ifdef DENORMAL
+#define MNEXP -17183.0
+#else
+#define MNEXP -16383.0
+#endif
+#endif
+
+#ifdef MIEEE
+static unsigned short P[] = {
+0x3fdf,0xdb99,0x7f5b,0x5cf0,
+0x400d,0xddef,0xea9e,0xdf15,
+0x401e,0xccbd,0x7f78,0xeb6f,
+0x4012,0xaa83,0xb65c,0x9b74
+};
+static unsigned short Q[] = {
+0x4022,0xaab4,0x9b20,0x914e,
+0x403b,0xffff,0x41c1,0xc9f5,
+0x4040,0xccbc,0x1b17,0x6402,
+0x402b,0xffc5,0x918a,0xe92e
+};
+static unsigned short A[] = {
+0x3ff0,0x0000,0x0000,0x0000,
+0x3fee,0xa4af,0xa2a4,0x90da,
+0x3fed,0x5818,0xdcfb,0xa487,
+0x3fec,0x199b,0xdd85,0x529c,
+0x3fea,0xe89f,0x995a,0xd3ad,
+0x3fe9,0xc491,0x82a3,0xf090,
+0x3fe8,0xace5,0x422a,0xa0db,
+0x3fe7,0xa114,0x73eb,0x0187,
+0x3fe6,0xa09e,0x667f,0x3bcd,
+0x3fe5,0xab07,0xdd48,0x5429,
+0x3fe4,0xbfda,0xd536,0x2a27,
+0x3fe3,0xdea6,0x4c12,0x3422,
+0x3fe3,0x06fe,0x0a31,0xb715,
+0x3fe2,0x387a,0x6e75,0x6238,
+0x3fe1,0x72b8,0x3c7d,0x517b,
+0x3fe0,0xb558,0x6cf9,0x890f,
+0x3fe0,0x0000,0x0000,0x0000
+};
+static unsigned short B[] = {
+0x0000,0x0000,0x0000,0x0000,
+0x3c72,0xed02,0xd75b,0x3707,
+0x3c87,0xa1cd,0x345d,0xcc81,
+0x3c86,0xe9f1,0x5686,0x4b27,
+0xbc8b,0xdd34,0x13b2,0x6456,
+0x3c6d,0x4397,0xafec,0x42e2,
+0x3c76,0xf46a,0xd231,0x82e4,
+0xbc71,0x9041,0xb9d7,0x8a76,
+0x0000,0x0000,0x0000,0x0000
+};
+static unsigned short R[] = {
+0x3eef,0x6307,0xd7f2,0x937f,
+0x3f24,0x2fbe,0x60fc,0x9259,
+0x3f55,0xd87e,0xc84a,0xef1d,
+0x3f83,0xb2ab,0x6ef1,0x33b7,
+0x3fac,0x6b08,0xd704,0x1a92,
+0x3fce,0xbfbd,0xff82,0xc56d,
+0x3fe6,0x2e42,0xfefa,0x39ef
+};
+
+#define douba(k) (*(double *)&A[(k)<<2])
+#define doubb(k) (*(double *)&B[(k)<<2])
+#define MEXP 16383.0
+#ifdef DENORMAL
+#define MNEXP -17183.0
+#else
+#define MNEXP -16383.0
+#endif
+#endif
+
+/* log2(e) - 1 */
+#define LOG2EA 0.44269504088896340736
+
+#define F W
+#define Fa Wa
+#define Fb Wb
+#define G W
+#define Ga Wa
+#define Gb u
+#define H W
+#define Ha Wb
+#define Hb Wb
+
+#ifdef ANSIPROT
+extern double floor ( double );
+extern double fabs ( double );
+extern double frexp ( double, int * );
+extern double ldexp ( double, int );
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern double powi ( double, int );
+extern int signbit ( double );
+extern int isnan ( double );
+extern int isfinite ( double );
+const static double reduc ( double );
+#else
+double floor(), fabs(), frexp(), ldexp();
+double polevl(), p1evl(), powi();
+int signbit(), isnan(), isfinite();
+const static double reduc();
+#endif
+extern double MAXNUM;
+#ifdef INFINITIES
+extern double INFINITY;
+#endif
+#ifdef NANS
+extern double NAN;
+#endif
+#ifdef MINUSZERO
+extern double NEGZERO;
+#endif
+
+double pow( x, y )
+double x, y;
+{
+double w, z, W, Wa, Wb, ya, yb, u;
+/* double F, Fa, Fb, G, Ga, Gb, H, Ha, Hb */
+double aw, ay, wy;
+int e, i, nflg, iyflg, yoddint;
+
+if( y == 0.0 )
+	return( 1.0 );
+#ifdef NANS
+if( isnan(x) )
+	return( x );
+if( isnan(y) )
+	return( y );
+#endif
+if( y == 1.0 )
+	return( x );
+
+
+#ifdef INFINITIES
+if( !isfinite(y) && (x == 1.0 || x == -1.0) )
+	{
+	mtherr( "pow", DOMAIN );
+#ifdef NANS
+	return( NAN );
+#else
+	return( INFINITY );
+#endif
+	}
+#endif
+
+if( x == 1.0 )
+	return( 1.0 );
+
+if( y >= MAXNUM )
+	{
+#ifdef INFINITIES
+	if( x > 1.0 )
+		return( INFINITY );
+#else
+	if( x > 1.0 )
+		return( MAXNUM );
+#endif
+	if( x > 0.0 && x < 1.0 )
+		return( 0.0);
+	if( x < -1.0 )
+		{
+#ifdef INFINITIES
+		return( INFINITY );
+#else
+		return( MAXNUM );
+#endif
+		}
+	if( x > -1.0 && x < 0.0 )
+		return( 0.0 );
+	}
+if( y <= -MAXNUM )
+	{
+	if( x > 1.0 )
+		return( 0.0 );
+#ifdef INFINITIES
+	if( x > 0.0 && x < 1.0 )
+		return( INFINITY );
+#else
+	if( x > 0.0 && x < 1.0 )
+		return( MAXNUM );
+#endif
+	if( x < -1.0 )
+		return( 0.0 );
+#ifdef INFINITIES
+	if( x > -1.0 && x < 0.0 )
+		return( INFINITY );
+#else
+	if( x > -1.0 && x < 0.0 )
+		return( MAXNUM );
+#endif
+	}
+if( x >= MAXNUM )
+	{
+#if INFINITIES
+	if( y > 0.0 )
+		return( INFINITY );
+#else
+	if( y > 0.0 )
+		return( MAXNUM );
+#endif
+	return(0.0);
+	}
+/* Set iyflg to 1 if y is an integer.  */
+iyflg = 0;
+w = floor(y);
+if( w == y )
+	iyflg = 1;
+
+/* Test for odd integer y.  */
+yoddint = 0;
+if( iyflg )
+	{
+	ya = fabs(y);
+	ya = floor(0.5 * ya);
+	yb = 0.5 * fabs(w);
+	if( ya != yb )
+		yoddint = 1;
+	}
+
+if( x <= -MAXNUM )
+	{
+	if( y > 0.0 )
+		{
+#ifdef INFINITIES
+		if( yoddint )
+			return( -INFINITY );
+		return( INFINITY );
+#else
+		if( yoddint )
+			return( -MAXNUM );
+		return( MAXNUM );
+#endif
+		}
+	if( y < 0.0 )
+		{
+#ifdef MINUSZERO
+		if( yoddint )
+			return( NEGZERO );
+#endif
+		return( 0.0 );
+		}
+ 	}
+
+nflg = 0;	/* flag = 1 if x<0 raised to integer power */
+if( x <= 0.0 )
+	{
+	if( x == 0.0 )
+		{
+		if( y < 0.0 )
+			{
+#ifdef MINUSZERO
+			if( signbit(x) && yoddint )
+				return( -INFINITY );
+#endif
+#ifdef INFINITIES
+			return( INFINITY );
+#else
+			return( MAXNUM );
+#endif
+			}
+		if( y > 0.0 )
+			{
+#ifdef MINUSZERO
+			if( signbit(x) && yoddint )
+				return( NEGZERO );
+#endif
+			return( 0.0 );
+			}
+		return( 1.0 );
+		}
+	else
+		{
+		if( iyflg == 0 )
+			{ /* noninteger power of negative number */
+			mtherr( fname, DOMAIN );
+#ifdef NANS
+			return(NAN);
+#else
+			return(0.0L);
+#endif
+			}
+		nflg = 1;
+		}
+	}
+
+/* Integer power of an integer.  */
+
+if( iyflg )
+	{
+	i = w;
+	w = floor(x);
+	if( (w == x) && (fabs(y) < 32768.0) )
+		{
+		w = powi( x, (int) y );
+		return( w );
+		}
+	}
+
+if( nflg )
+	x = fabs(x);
+
+/* For results close to 1, use a series expansion.  */
+w = x - 1.0;
+aw = fabs(w);
+ay = fabs(y);
+wy = w * y;
+ya = fabs(wy);
+if((aw <= 1.0e-3 && ay <= 1.0)
+   || (ya <= 1.0e-3 && ay >= 1.0))
+	{
+	z = (((((w*(y-5.)/720. + 1./120.)*w*(y-4.) + 1./24.)*w*(y-3.)
+		+ 1./6.)*w*(y-2.) + 0.5)*w*(y-1.) )*wy + wy + 1.;
+	goto done;
+	}
+/* These are probably too much trouble.  */
+#if 0
+w = y * log(x);
+if (aw > 1.0e-3 && fabs(w) < 1.0e-3)
+  {
+    z = ((((((
+    w/7. + 1.)*w/6. + 1.)*w/5. + 1.)*w/4. + 1.)*w/3. + 1.)*w/2. + 1.)*w + 1.;
+    goto done;
+  }
+
+if(ya <= 1.0e-3 && aw <= 1.0e-4)
+  {
+    z = (((((
+	     wy*1./720.
+	     + (-w*1./48. + 1./120.) )*wy
+	    + ((w*17./144. - 1./12.)*w + 1./24.) )*wy
+	   + (((-w*5./16. + 7./24.)*w - 1./4.)*w + 1./6.) )*wy
+	  + ((((w*137./360. - 5./12.)*w + 11./24.)*w - 1./2.)*w + 1./2.) )*wy
+	 + (((((-w*1./6. + 1./5.)*w - 1./4)*w + 1./3.)*w -1./2.)*w ) )*wy
+	   + wy + 1.0;
+    goto done;
+  }
+#endif
+
+/* separate significand from exponent */
+x = frexp( x, &e );
+
+#if 0
+/* For debugging, check for gross overflow. */
+if( (e * y)  > (MEXP + 1024) )
+	goto overflow;
+#endif
+
+/* Find significand of x in antilog table A[]. */
+i = 1;
+if( x <= douba(9) )
+	i = 9;
+if( x <= douba(i+4) )
+	i += 4;
+if( x <= douba(i+2) )
+	i += 2;
+if( x >= douba(1) )
+	i = -1;
+i += 1;
+
+
+/* Find (x - A[i])/A[i]
+ * in order to compute log(x/A[i]):
+ *
+ * log(x) = log( a x/a ) = log(a) + log(x/a)
+ *
+ * log(x/a) = log(1+v),  v = x/a - 1 = (x-a)/a
+ */
+x -= douba(i);
+x -= doubb(i/2);
+x /= douba(i);
+
+
+/* rational approximation for log(1+v):
+ *
+ * log(1+v)  =  v  -  v**2/2  +  v**3 P(v) / Q(v)
+ */
+z = x*x;
+w = x * ( z * polevl( x, P, 3 ) / p1evl( x, Q, 4 ) );
+w = w - ldexp( z, -1 );   /*  w - 0.5 * z  */
+
+/* Convert to base 2 logarithm:
+ * multiply by log2(e)
+ */
+w = w + LOG2EA * w;
+/* Note x was not yet added in
+ * to above rational approximation,
+ * so do it now, while multiplying
+ * by log2(e).
+ */
+z = w + LOG2EA * x;
+z = z + x;
+
+/* Compute exponent term of the base 2 logarithm. */
+w = -i;
+w = ldexp( w, -4 );	/* divide by 16 */
+w += e;
+/* Now base 2 log of x is w + z. */
+
+/* Multiply base 2 log by y, in extended precision. */
+
+/* separate y into large part ya
+ * and small part yb less than 1/16
+ */
+ya = reduc(y);
+yb = y - ya;
+
+
+F = z * y  +  w * yb;
+Fa = reduc(F);
+Fb = F - Fa;
+
+G = Fa + w * ya;
+Ga = reduc(G);
+Gb = G - Ga;
+
+H = Fb + Gb;
+Ha = reduc(H);
+w = ldexp( Ga+Ha, 4 );
+
+/* Test the power of 2 for overflow */
+if( w > MEXP )
+	{
+#ifndef INFINITIES
+	mtherr( fname, OVERFLOW );
+#endif
+#ifdef INFINITIES
+	if( nflg && yoddint )
+	  return( -INFINITY );
+	return( INFINITY );
+#else
+	if( nflg && yoddint )
+	  return( -MAXNUM );
+	return( MAXNUM );
+#endif
+	}
+
+if( w < (MNEXP - 1) )
+	{
+#ifndef DENORMAL
+	mtherr( fname, UNDERFLOW );
+#endif
+#ifdef MINUSZERO
+	if( nflg && yoddint )
+	  return( NEGZERO );
+#endif
+	return( 0.0 );
+	}
+
+e = w;
+Hb = H - Ha;
+
+if( Hb > 0.0 )
+	{
+	e += 1;
+	Hb -= 0.0625;
+	}
+
+/* Now the product y * log2(x)  =  Hb + e/16.0.
+ *
+ * Compute base 2 exponential of Hb,
+ * where -0.0625 <= Hb <= 0.
+ */
+z = Hb * polevl( Hb, R, 6 );  /*    z  =  2**Hb - 1    */
+
+/* Express e/16 as an integer plus a negative number of 16ths.
+ * Find lookup table entry for the fractional power of 2.
+ */
+if( e < 0 )
+	i = 0;
+else
+	i = 1;
+i = e/16 + i;
+e = 16*i - e;
+w = douba( e );
+z = w + w * z;      /*    2**-e * ( 1 + (2**Hb-1) )    */
+z = ldexp( z, i );  /* multiply by integer power of 2 */
+
+done:
+
+/* Negate if odd integer power of negative number */
+if( nflg && yoddint )
+	{
+#ifdef MINUSZERO
+	if( z == 0.0 )
+		z = NEGZERO;
+	else
+#endif
+		z = -z;
+	}
+return( z );
+}
+
+
+/* Find a multiple of 1/16 that is within 1/16 of x. */
+const static double reduc(x)
+double x;
+{
+double t;
+
+t = ldexp( x, 4 );
+t = floor( t );
+t = ldexp( t, -4 );
+return(t);
+}

+ 186 - 0
components/external/espruino/libs/math/powi.c

@@ -0,0 +1,186 @@
+/*							powi.c
+ *
+ *	Real raised to integer power
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, powi();
+ * int n;
+ *
+ * y = powi( x, n );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns argument x raised to the nth power.
+ * The routine efficiently decomposes n as a sum of powers of
+ * two. The desired power is a product of two-to-the-kth
+ * powers of x.  Thus to compute the 32767 power of x requires
+ * 28 multiplications instead of 32767 multiplications.
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *
+ *                      Relative error:
+ * arithmetic   x domain   n domain  # trials      peak         rms
+ *    DEC       .04,26     -26,26    100000       2.7e-16     4.3e-17
+ *    IEEE      .04,26     -26,26     50000       2.0e-15     3.8e-16
+ *    IEEE        1,2    -1022,1023   50000       8.6e-14     1.6e-14
+ *
+ * Returns MAXNUM on overflow, zero on underflow.
+ *
+ */
+
+/*							powi.c	*/
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+#include "mconf.h"
+#ifdef ANSIPROT
+extern double log ( double );
+extern double frexp ( double, int * );
+extern int signbit ( double );
+#else
+double log(), frexp();
+int signbit();
+#endif
+extern double NEGZERO, INFINITY, MAXNUM, MAXLOG, MINLOG, LOGE2;
+
+double powi( x, nn )
+double x;
+int nn;
+{
+int n, e, sign, asign, lx;
+double w, y, s;
+
+/* See pow.c for these tests.  */
+if( x == 0.0 )
+	{
+	if( nn == 0 )
+		return( 1.0 );
+	else if( nn < 0 )
+	    return( INFINITY );
+	else
+	  {
+	    if( nn & 1 )
+	      return( x );
+	    else
+	      return( 0.0 );
+	  }
+	}
+
+if( nn == 0 )
+	return( 1.0 );
+
+if( nn == -1 )
+	return( 1.0/x );
+
+if( x < 0.0 )
+	{
+	asign = -1;
+	x = -x;
+	}
+else
+	asign = 0;
+
+
+if( nn < 0 )
+	{
+	sign = -1;
+	n = -nn;
+	}
+else
+	{
+	sign = 1;
+	n = nn;
+	}
+
+/* Even power will be positive. */
+if( (n & 1) == 0 )
+	asign = 0;
+
+/* Overflow detection */
+
+/* Calculate approximate logarithm of answer */
+s = frexp( x, &lx );
+e = (lx - 1)*n;
+if( (e == 0) || (e > 64) || (e < -64) )
+	{
+	s = (s - 7.0710678118654752e-1) / (s +  7.0710678118654752e-1);
+	s = (2.9142135623730950 * s - 0.5 + lx) * nn * LOGE2;
+	}
+else
+	{
+	s = LOGE2 * e;
+	}
+
+if( s > MAXLOG )
+	{
+	mtherr( "powi", OVERFLOW );
+	y = INFINITY;
+	goto done;
+	}
+
+#if DENORMAL
+if( s < MINLOG )
+	{
+	y = 0.0;
+	goto done;
+	}
+
+/* Handle tiny denormal answer, but with less accuracy
+ * since roundoff error in 1.0/x will be amplified.
+ * The precise demarcation should be the gradual underflow threshold.
+ */
+if( (s < (-MAXLOG+2.0)) && (sign < 0) )
+	{
+	x = 1.0/x;
+	sign = -sign;
+	}
+#else
+/* do not produce denormal answer */
+if( s < -MAXLOG )
+	return(0.0);
+#endif
+
+
+/* First bit of the power */
+if( n & 1 )
+	y = x;
+		
+else
+	y = 1.0;
+
+w = x;
+n >>= 1;
+while( n )
+	{
+	w = w * w;	/* arg to the 2-to-the-kth power */
+	if( n & 1 )	/* if that bit is set, then include in product */
+		y *= w;
+	n >>= 1;
+	}
+
+if( sign < 0 )
+	y = 1.0/y;
+
+done:
+
+if( asign )
+	{
+	/* odd power of negative number */
+	if( y == 0.0 )
+		y = NEGZERO;
+	else
+		y = -y;
+	}
+return(y);
+}

+ 184 - 0
components/external/espruino/libs/math/protos.h

@@ -0,0 +1,184 @@
+/*
+ *   This file was automatically generated by version 1.7 of cextract.
+ *   Manual editing not recommended.
+ *
+ *   Created: Fri Mar 31 19:17:33 1995
+ */
+extern double acosh ( double x );
+extern int airy ( double, double *, double *, double *, double * );
+extern double asin ( double );
+extern double acos ( double );
+extern double asinh ( double x );
+extern double atan ( double );
+extern double atan2 ( double y, double x );
+extern double atanh ( double );
+extern double bdtrc ( int k, int n, double p );
+extern double bdtr ( int k, int n, double p );
+extern double bdtri ( int k, int n, double y );
+extern double beta ( double a, double b );
+extern double lbeta ( double a, double b );
+extern double btdtr ( double a, double b, double x );
+extern double cbrt ( double );
+extern double chbevl ( double, void *, int );
+extern double chdtrc ( double df, double x );
+extern double chdtr ( double df, double x );
+extern double chdtri ( double df, double y );
+//extern void clog ( cmplx *z, cmplx *w );
+//extern void cexp ( cmplx *z, cmplx *w );
+//extern void csin ( cmplx *z, cmplx *w );
+//extern void ccos ( cmplx *z, cmplx *w );
+//extern void ctan ( cmplx *z, cmplx *w );
+extern void ccot ( cmplx *z, cmplx *w );
+//extern void casin ( cmplx *z, cmplx *w );
+//extern void cacos ( cmplx *z, cmplx *w );
+//extern void catan ( cmplx *z, cmplx *w );
+extern void cadd ( cmplx *a, cmplx *b, cmplx *c );
+extern void csub ( cmplx *a, cmplx *b, cmplx *c );
+extern void cmul ( cmplx *a, cmplx *b, cmplx *c );
+extern void cdiv ( cmplx *a, cmplx *b, cmplx *c );
+extern void cmov ( void *a, void *b );
+extern void cneg ( cmplx *a );
+//extern double cabs ( cmplx *z );
+//extern void csqrt ( cmplx *z, cmplx *w );
+extern double hypot ( double, double );
+extern double cosh ( double );
+extern double dawsn ( double xx );
+extern void eigens ( double A[], double RR[], double E[], int N );
+extern double ellie ( double, double );
+extern double ellik ( double, double );
+extern double ellpe ( double );
+extern int ellpj ( double u, double m, double *sn, double *cn, double *dn, double *ph );
+extern double ellpk ( double );
+extern double exp10 ( double );
+extern double exp1m ( double );
+extern double exp2 ( double );
+extern double expn ( int n, double x );
+extern double fac ( int i );
+extern double fdtrc ( int ia, int ib, double x );
+extern double fdtr ( int ia, int ib, double x );
+extern double fdtri ( int ia, int ib, double y );
+extern int fftr ( double x[], int m0, double sine[] );
+extern double ceil ( double x );
+extern double fabs ( double );
+extern double floor ( double );
+extern double frexp ( double, int * );
+extern double ldexp ( double, int );
+extern int signbit ( double );
+extern int isnan ( double );
+extern int isfinite ( double );
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern double exp ( double );
+extern double log ( double );
+extern double sin ( double );
+extern double cos ( double );
+extern double sqrt ( double );
+extern int fresnl ( double xxa, double *ssa, double *cca );
+extern double gamma ( double );
+extern double lgam ( double );
+extern double gdtr ( double a, double b, double x );
+extern double gdtrc ( double a, double b, double x );
+extern int gels ( double A[], double R[], int M, double EPS, double AUX[] );
+extern double hyp2f1 ( double a, double b, double c, double x );
+extern double hyperg ( double, double, double );
+extern double hyp2f0 ( double a, double b, double x, int type, double *err );
+extern double i0 ( double );
+extern double i0e ( double x );
+extern double i1 ( double );
+extern double i1e ( double x );
+extern double igamc ( double, double );
+extern double igam ( double, double );
+extern double igami ( double, double );
+extern double incbet ( double, double, double );
+extern double incbi ( double, double, double );
+extern double iv ( double v, double x );
+extern double j0 ( double );
+extern double y0 ( double );
+extern double j1 ( double );
+extern double y1 ( double );
+extern double jn ( int n, double x );
+extern double jv ( double, double );
+extern double k0 ( double x );
+extern double k0e ( double x );
+extern double k1 ( double x );
+extern double k1e ( double x );
+extern double kn ( int nn, double x );
+extern int levnsn ( int n, double r[], double a[], double e[], double refl[] );
+extern double log10 ( double );
+extern double log2 ( double );
+extern long lrand ( void );
+extern long lsqrt ( long x );
+extern int minv ( double A[], double X[], int n, double B[], int IPS[] );
+extern int mmmpy ( int r, int c, double *A, double *B, double *Y );
+extern int mtherr ( char *name, int code );
+extern int mtransp ( int n, double *A, double *T );
+extern int mvmpy ( int r, int c, double *A, double *V, double *Y );
+extern double nbdtrc ( int k, int n, double p );
+extern double nbdtr ( int k, int n, double p );
+extern double nbdtri ( int k, int n, double p );
+extern double ndtr ( double a );
+extern double erfc ( double );
+extern double erf ( double );
+extern double ndtri ( double );
+extern double pdtrc ( int k, double m );
+extern double pdtr ( int k, double m );
+extern double pdtri ( int k, double y );
+extern double pow ( double, double );
+extern double powi ( double, int );
+extern double psi ( double );
+extern void revers ( double y[], double x[], int n );
+extern double rgamma ( double x );
+extern double round ( double );
+extern int sprec ( void );
+extern int dprec ( void );
+extern int ldprec ( void );
+extern int shichi ( double x, double *si, double *ci );
+extern int sici ( double x, double *si, double *ci );
+extern double simpsn ( double f[], double delta );
+extern int simq ( double A[], double B[], double X[], int n, int flag, int IPS[] );
+extern double radian ( double d, double m, double s );
+/*
+extern int sincos ( double x, double *s, double *c, int flg );
+*/
+extern double sindg ( double x );
+extern double cosdg ( double x );
+extern double sinh ( double );
+extern double spence ( double );
+extern double stdtr ( int k, double t );
+extern double stdtri ( int k, double p );
+extern double onef2 ( double a, double b, double c, double x, double *err );
+extern double threef0 ( double a, double b, double c, double x, double *err );
+extern double struve ( double v, double x );
+extern double tan ( double );
+extern double cot ( double );
+extern double tandg ( double x );
+extern double cotdg ( double x );
+extern double tanh ( double );
+extern double log1p ( double );
+extern double exmp1 ( double );
+extern double cosm1 ( double x );
+extern double yn ( int, double );
+extern double zeta ( double x, double q );
+extern double zetac ( double );
+extern int drand ( double *a );
+double smirnov ( int, double );
+double smirnovi ( int, double );
+double kolmogorov ( double );
+double kolmogi ( double );
+
+/* polyn.c */
+extern void polini ( int maxdeg );
+extern void polprt ( double a[], int na, int d );
+extern void polclr ( double *a, int n );
+extern void polmov ( double *a, int na, double *b );
+extern void polmul ( double a[], int na, double b[], int nb, double c[] );
+extern void poladd ( double a[], int na, double b[], int nb, double c[] );
+extern void polsub ( double a[], int na, double b[], int nb, double c[] );
+extern int poldiv ( double a[], int na, double b[], int nb, double c[] );
+extern void polsbt ( double a[], int na, double b[], int nb, double c[] );
+extern double poleva ( double a[], int na, double x );
+/* polmisc.c */
+extern void polatn ( double num[], double den[], double ans[], int nn );
+extern void polsqt ( double pol[], double ans[], int nn );
+extern void polsin ( double x[], double y[], int nn );
+extern void polcos ( double x[], double y[], int nn );

+ 70 - 0
components/external/espruino/libs/math/round.c

@@ -0,0 +1,70 @@
+/*							round.c
+ *
+ *	Round double to nearest or even integer valued double
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, round();
+ *
+ * y = round(x);
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns the nearest integer to x as a double precision
+ * floating point result.  If x ends in 0.5 exactly, the
+ * nearest even integer is chosen.
+ * 
+ *
+ *
+ * ACCURACY:
+ *
+ * If x is greater than 1/(2*MACHEP), its closest machine
+ * representation is already an integer, so rounding does
+ * not change it.
+ */
+
+/*
+Cephes Math Library Release 2.1:  January, 1989
+Copyright 1984, 1987, 1989 by Stephen L. Moshier
+Direct inquiries to 30 Frost Street, Cambridge, MA 02140
+*/
+#include "mconf.h"
+#ifdef ANSIPROT
+double floor ( double );
+#else
+double floor();
+#endif
+
+double round(x)
+double x;
+{
+double y, r;
+
+/* Largest integer <= x */
+y = floor(x);
+
+/* Fractional part */
+r = x - y;
+
+/* Round up to nearest. */
+if( r > 0.5 )
+	goto rndup;
+
+/* Round to even */
+if( r == 0.5 )
+	{
+	r = y - 2.0 * floor( 0.5 * y );
+	if( r == 1.0 )
+		{
+rndup:
+		y += 1.0;
+		}
+	}
+
+/* Else round down. */
+return(y);
+}

+ 207 - 0
components/external/espruino/libs/math/setprbor.asm

@@ -0,0 +1,207 @@
+;
+; Borland assembler header
+;
+; Microsoft MASM subroutines for setting coprocessor precision
+;
+.286
+.287
+PREC_TEXT	SEGMENT  BYTE PUBLIC 'CODE'
+PREC_TEXT	ENDS
+DGROUP	group	_DATA,_BSS
+	assume	cs:PREC_TEXT,ds:DGROUP
+_DATA	segment word public 'DATA'
+d@	label	byte
+d@w	label	word
+_DATA	ends
+_BSS	segment word public 'BSS'
+b@	label	byte
+b@w	label	word
+_BSS	ends
+
+; exception masks (1 = masked)
+;   1  invalid operation
+;   2  denormalized operand
+;   4  zero divide
+;   8  overflow
+;  10  underflow
+;  20  precision
+
+_DATA SEGMENT
+
+; double precision setting
+;;ctlwrd dw 01230h ; note this traps on denormal operands!
+;;ctld dw 0133fh   ; this doesn't trap
+ctld dw 01230h
+
+; single precision
+ctls dw 01030h
+
+; long double precision
+ctlld dw 01320h
+
+_DATA ENDS
+
+PREC_TEXT	segment byte public 'CODE'
+	assume	cs:PREC_TEXT,ds:DGROUP
+; Set coprocessor to single precision float
+	PUBLIC	_sprec
+_sprec	PROC FAR
+	fclex
+	fwait
+	finit
+	fwait
+	fldcw word ptr ctls
+	fwait
+	ret
+_sprec ENDP
+
+; set coprocessor to long double precision
+	PUBLIC	_ldprec
+_ldprec PROC far
+	fclex
+	fwait
+	finit
+	fwait
+	fldcw word ptr ctlld
+	fwait
+	ret
+_ldprec ENDP
+
+; set coprocessor to double precision
+	PUBLIC	_dprec
+_dprec PROC far
+	fclex
+	fwait
+	finit
+	fwait
+	fldcw word ptr ctld
+	fwait
+	ret
+_dprec ENDP
+
+
+; get a double promoted to long double size
+; getld( &doub, &ldoub );
+	PUBLIC	_getld
+_getld	PROC far
+	push bp
+	mov bp,sp
+	push bx
+	mov bx, word ptr [bp+4]
+;	fld st(0)
+	fld qword ptr [bx]
+	mov bx, word ptr [bp+6]
+	fstp tbyte ptr [bx]
+	mov bx, word ptr [bp+4]
+	fld qword ptr [bx]
+	mov bx, word ptr [bp+8]
+	fstp qword ptr [bx]
+	pop bx
+	pop bp
+	ret
+_getld	ENDP
+
+	PUBLIC	_getprec
+_getprec	PROC far
+	push bp
+	mov bp,sp
+	sub sp,4
+	fstcw [bp-4]
+	fwait
+	mov ax,[bp-4]
+	add sp,4
+	pop bp
+	ret
+_getprec	ENDP
+
+
+	PUBLIC	_fpclear
+_fpclear	PROC far
+	push bp
+	mov bp,sp
+	fnclex
+	fwait
+	pop bp
+	ret
+_fpclear	ENDP
+
+
+	PUBLIC	_noexcept
+_noexcept	PROC far
+	push bp
+	mov bp,sp
+	push ax
+	sub sp,4
+	fnclex
+	fwait
+	fstcw [bp-4]
+	fwait
+	mov ax,[bp-4]
+	and ax,0FFC0h
+	or ax,003fh
+	mov [bp-4],ax
+	fldcw [bp-4]
+	add sp,4
+	pop ax
+	pop bp
+	ret
+_noexcept	ENDP
+
+; single precision square root
+; assumes coprocessor precision already set up
+; return value in static __fac
+	PUBLIC	_sqrtf
+_sqrtf	PROC FAR
+	push bp
+	mov bp,sp
+	fld	DWORD PTR [bp+6]
+	fsqrt
+	fwait	
+	mov	sp,bp
+	pop bp
+	ret
+_sqrtf	ENDP
+
+; double precision square root
+; assumes coprocessor precision already set up
+; return value in static __fac
+	PUBLIC	_sqrt
+_sqrt	PROC FAR
+	push bp
+	mov bp,sp
+	fld	QWORD PTR [bp+6]
+	fsqrt
+	fwait	
+	mov	sp,bp
+	pop bp
+	ret
+_sqrt	ENDP
+
+
+; long double precision square root
+; assumes coprocessor precision already set up
+; return value in fp register
+	PUBLIC	_sqrtl
+_sqrtl	PROC FAR
+	push bp
+	mov bp,sp
+	fld tbyte ptr [bp+6]
+	fsqrt
+	fwait	
+	mov	sp,bp
+	pop bp
+	ret
+_sqrtl	ENDP
+
+
+PREC_TEXT	ends
+_DATA	segment word public 'DATA'
+s@	label	byte
+_DATA	ends
+PREC_TEXT	segment byte public 'CODE'
+PREC_TEXT	ends
+_s@	equ	s@
+	public _sprec
+	public _dprec
+	public _ldprec
+	end

+ 87 - 0
components/external/espruino/libs/math/setprec.387

@@ -0,0 +1,87 @@
+/* Set 80387 floating point hardware rounding precision */
+
+	.file	"setprec.387"
+.text
+	.align 2
+
+.globl _sprec
+_sprec:
+	pushl %ebp
+	movl %esp,%ebp
+	pushl %eax
+	subl $4,%esp
+	fstcw (%esp)
+	fwait
+	movl (%esp),%eax
+	andl $0xfcff,%eax
+	movl %eax,(%esp)
+	fldcw (%esp)
+	popl %eax
+	popl %eax
+	leave
+	ret
+
+	.align 2
+
+.globl _dprec
+_dprec:
+	pushl %ebp
+	movl %esp,%ebp
+	pushl %eax
+	subl $4,%esp
+	fstcw (%esp)
+	fwait
+	movl (%esp),%eax
+/*	andl $0xfcff,%eax */
+/* exception on overflow */
+	andl $0xfcf7,%eax
+	orl $0x200,%eax
+	movl %eax,(%esp)
+	fldcw (%esp)
+	popl %eax
+	popl %eax
+	leave
+	ret
+
+	.align 2
+
+.globl _ldprec
+_ldprec:
+	pushl %ebp
+	movl %esp,%ebp
+	pushl %eax
+	subl $4,%esp
+	fstcw (%esp)
+	fwait
+	movl (%esp),%eax
+	orl $0x300,%eax
+	movl %eax,(%esp)
+	fldcw (%esp)
+	popl %eax
+	popl %eax
+	leave
+	ret
+
+
+.globl _getprec
+_getprec:
+	pushl %ebp
+	movl %esp,%ebp
+	subl $4,%esp
+	fstcw (%esp)
+	fwait
+	movl (%esp),%eax
+	leave
+	ret
+
+.globl _setfpu
+_setfpu:
+        pushl %ebp
+        movl %esp,%ebp
+        movl 8(%ebp),%eax
+        pushl %eax
+        fldcw (%esp)
+        fwait
+        movl %ebp,%esp
+        popl %ebp
+        ret

+ 35 - 0
components/external/espruino/libs/math/setprec.688

@@ -0,0 +1,35 @@
+/* Set 68881/2 floating point rounding precision */
+/* Reference: MC68881/MC68882 Floating-Point Coprocessor */
+/* User's Manual, Motorola, Prentice-Hall, 1987 (First Edition) */
+/* Pages 1-14, 2-3, 4-68. */
+/* FPcr code $80 sets the 68882 coprocessor to */
+/*    rounding precision = 53 bits */
+/*    rounding mode = nearest or even */
+/*    all exceptions (bits 8-15) disabled */
+/* The instruction is */
+/*	FMOVE.L	#$80,Fcr */
+/* if the assembler will understand it. */
+
+	.align 2
+.text
+
+/* set to single precision */
+.globl _sprec
+_sprec
+	.word 0xf23c,0x9000,0x0000,0x0040
+	rts
+
+/* set to double precision */
+.globl _dprec
+
+_dprec:
+	.word	0xf23c,0x9000,0x0000,0x0080
+	rts
+
+/* set to extended (long double) precision */
+.globl _ldprec
+
+_ldprec:
+	.word	0xf23c,0x9000,0x0000,0x0000
+	rts
+

+ 208 - 0
components/external/espruino/libs/math/setprec.87

@@ -0,0 +1,208 @@
+;
+; Microsoft MASM subroutines for setting coprocessor precision
+;
+.286
+.287
+_TEXT	SEGMENT  BYTE PUBLIC 'CODE'
+_TEXT	ENDS
+CONST	SEGMENT  WORD PUBLIC 'CONST'
+CONST	ENDS
+_BSS	SEGMENT  WORD PUBLIC 'BSS'
+_BSS	ENDS
+_DATA	SEGMENT  WORD PUBLIC 'DATA'
+_DATA	ENDS
+DGROUP	GROUP	CONST,	_BSS,	_DATA
+	ASSUME  CS: _TEXT, DS: DGROUP, SS: DGROUP, ES: DGROUP
+EXTRN	__fac:QWORD
+
+_BSS      SEGMENT
+EXTRN	__fltused:NEAR
+_BSS      ENDS
+
+
+; exception masks (1 = masked)
+;   1  invalid operation
+;   2  denormalized operand
+;   4  zero divide
+;   8  overflow
+;  10  underflow
+;  20  precision
+
+_DATA SEGMENT
+
+; double precision setting
+;;ctlwrd dw 01230h ; note this traps on denormal operands!
+;;ctld dw 0133fh   ; this doesn't trap
+ctld dw 01230h
+
+; single precision
+ctls dw 01030h
+
+; long double precision
+ctlld dw 01320h
+
+_DATA ENDS
+
+	ASSUME  CS: _TEXT
+
+_TEXT SEGMENT
+; Set coprocessor to single precision float
+	PUBLIC	_sprec
+_sprec	PROC NEAR
+	fclex
+	fwait
+	finit
+	fwait
+	fldcw word ptr ctls
+	fwait
+	ret
+_sprec ENDP
+
+; set coprocessor to long double precision
+	PUBLIC	_ldprec
+_ldprec PROC NEAR
+	fclex
+	fwait
+	finit
+	fwait
+	fldcw word ptr ctlld
+	fwait
+	ret
+_ldprec ENDP
+
+; set coprocessor to double precision
+	PUBLIC	_dprec
+_dprec PROC NEAR
+	fclex
+	fwait
+	finit
+	fwait
+	fldcw word ptr ctld
+	fwait
+	ret
+_dprec ENDP
+
+
+; get a double promoted to long double size
+; getld( &doub, &ldoub );
+	PUBLIC	_getld
+_getld	PROC NEAR
+	push bp
+	mov bp,sp
+	push bx
+	mov bx, word ptr [bp+4]
+;	fld st(0)
+	fld qword ptr [bx]
+	mov bx, word ptr [bp+6]
+	fstp tbyte ptr [bx]
+	mov bx, word ptr [bp+4]
+	fld qword ptr [bx]
+	mov bx, word ptr [bp+8]
+	fstp qword ptr [bx]
+	pop bx
+	pop bp
+	ret
+_getld	ENDP
+
+	PUBLIC	_getprec
+_getprec	PROC NEAR
+	push bp
+	mov bp,sp
+	sub sp,4
+	fstcw [bp-4]
+	fwait
+	mov ax,[bp-4]
+	add sp,4
+	pop bp
+	ret
+_getprec	ENDP
+
+
+	PUBLIC	_fpclear
+_fpclear	PROC NEAR
+	push bp
+	mov bp,sp
+	fnclex
+	fwait
+	pop bp
+	ret
+_fpclear	ENDP
+
+
+	PUBLIC	_noexcept
+_noexcept	PROC NEAR
+	push bp
+	mov bp,sp
+	push ax
+	sub sp,4
+	fnclex
+	fwait
+	fstcw [bp-4]
+	fwait
+	mov ax,[bp-4]
+	and ax,0FFC0h
+	or ax,003fh
+	mov [bp-4],ax
+	fldcw [bp-4]
+	add sp,4
+	pop ax
+	pop bp
+	ret
+_noexcept	ENDP
+
+;; single precision square root
+;; assumes coprocessor precision already set up
+;; return value in static __fac
+;	PUBLIC	_sqrtf
+;_sqrtf	PROC NEAR
+;	push bp
+;	mov bp,sp
+;	fld	DWORD PTR [bp+4]
+;	fsqrt
+;	fwait	
+;	fstp	DWORD PTR __fac
+;	mov	ax,OFFSET __fac
+;	mov	sp,bp
+;	pop bp
+;	ret
+;_sqrtf	ENDP
+;
+;
+;; double precision square root
+;; assumes coprocessor precision already set up
+;; return value in static __fac
+;	PUBLIC	_sqrt
+;_sqrt	PROC NEAR
+;	push bp
+;	mov bp,sp
+;	fld	QWORD PTR [bp+4]
+;	fsqrt
+;	fwait	
+;	fstp	QWORD PTR __fac
+;	mov	ax,OFFSET __fac
+;	mov	sp,bp
+;	pop bp
+;	ret
+;_sqrt	ENDP
+;
+;
+;; long double precision square root
+;; assumes coprocessor precision already set up
+;; return value in fp register
+;	PUBLIC	_sqrtl
+;_sqrtl	PROC NEAR
+;	push bp
+;	mov bp,sp
+;	fld tbyte ptr [bp+4]
+;	fsqrt
+;	fwait	
+;	mov	sp,bp
+;	pop bp
+;	ret
+;_sqrtl	ENDP
+;
+
+_TEXT ENDS
+END
+
+

+ 10 - 0
components/external/espruino/libs/math/setprec.c

@@ -0,0 +1,10 @@
+/* Null stubs for coprocessor precision settings */
+
+int
+sprec() {return 0; }
+
+int
+dprec() {return 0; }
+
+int
+ldprec() {return 0; }

+ 83 - 0
components/external/espruino/libs/math/setprelf.387

@@ -0,0 +1,83 @@
+/* Set 80387 floating point hardware rounding precision */
+
+	.file	"setprec.387"
+	.version	"01.01"
+.text
+	.align 16
+
+.globl sprec
+sprec:
+	pushl %ebp
+	movl %esp,%ebp
+	pushl %eax
+	subl $4,%esp
+	fstcw (%esp)
+	fwait
+	movl (%esp),%eax
+	andl $0xfcff,%eax
+	movl %eax,(%esp)
+	fldcw (%esp)
+	popl %eax
+	popl %eax
+	leave
+	ret
+.Lfe1:
+	.size	sprec,.Lfe1-sprec
+	.align 16
+
+.globl dprec
+dprec:
+	pushl %ebp
+	movl %esp,%ebp
+	pushl %eax
+	subl $4,%esp
+	fstcw (%esp)
+	fwait
+	movl (%esp),%eax
+	andl $0xfcff,%eax
+/* trap on overflow */
+/*	andl $0xfcf7,%eax */
+	orl $0x200,%eax
+	movl %eax,(%esp)
+	fldcw (%esp)
+	popl %eax
+	popl %eax
+	leave
+	ret
+.Lfe2:
+	.size	dprec,.Lfe2-dprec
+	.align 16
+
+.globl ldprec
+ldprec:
+	pushl %ebp
+	movl %esp,%ebp
+	pushl %eax
+	subl $4,%esp
+	fstcw (%esp)
+	fwait
+	movl (%esp),%eax
+	orl $0x300,%eax
+	movl %eax,(%esp)
+	fldcw (%esp)
+	popl %eax
+	popl %eax
+	leave
+	ret
+.Lfe3:
+	.size	ldprec,.Lfe3-ldprec
+
+	.align 16
+.globl getprec
+getprec:
+	pushl %ebp
+	movl %esp,%ebp
+	subl $4,%esp
+	fstcw (%esp)
+	fwait
+	movl (%esp),%eax
+	leave
+	ret
+.Lfe4:
+	.size	getprec,.Lfe4-getprec
+

+ 387 - 0
components/external/espruino/libs/math/sin.c

@@ -0,0 +1,387 @@
+/*							sin.c
+ *
+ *	Circular sine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, sin();
+ *
+ * y = sin( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Range reduction is into intervals of pi/4.  The reduction
+ * error is nearly eliminated by contriving an extended precision
+ * modular arithmetic.
+ *
+ * Two polynomial approximating functions are employed.
+ * Between 0 and pi/4 the sine is approximated by
+ *      x  +  x**3 P(x**2).
+ * Between pi/4 and pi/2 the cosine is represented as
+ *      1  -  x**2 Q(x**2).
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain      # trials      peak         rms
+ *    DEC       0, 10       150000       3.0e-17     7.8e-18
+ *    IEEE -1.07e9,+1.07e9  130000       2.1e-16     5.4e-17
+ * 
+ * ERROR MESSAGES:
+ *
+ *   message           condition        value returned
+ * sin total loss   x > 1.073741824e9      0.0
+ *
+ * Partial loss of accuracy begins to occur at x = 2**30
+ * = 1.074e9.  The loss is not gradual, but jumps suddenly to
+ * about 1 part in 10e7.  Results may be meaningless for
+ * x > 2**49 = 5.6e14.  The routine as implemented flags a
+ * TLOSS error for x > 2**30 and returns 0.0.
+ */
+/*							cos.c
+ *
+ *	Circular cosine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, cos();
+ *
+ * y = cos( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Range reduction is into intervals of pi/4.  The reduction
+ * error is nearly eliminated by contriving an extended precision
+ * modular arithmetic.
+ *
+ * Two polynomial approximating functions are employed.
+ * Between 0 and pi/4 the cosine is approximated by
+ *      1  -  x**2 Q(x**2).
+ * Between pi/4 and pi/2 the sine is represented as
+ *      x  +  x**3 P(x**2).
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain      # trials      peak         rms
+ *    IEEE -1.07e9,+1.07e9  130000       2.1e-16     5.4e-17
+ *    DEC        0,+1.07e9   17000       3.0e-17     7.2e-18
+ */
+
+/*							sin.c	*/
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1985, 1995, 2000 by Stephen L. Moshier
+*/
+
+#include "mconf.h"
+
+#ifdef UNK
+const static double sincof[] = {
+ 1.58962301576546568060E-10,
+-2.50507477628578072866E-8,
+ 2.75573136213857245213E-6,
+-1.98412698295895385996E-4,
+ 8.33333333332211858878E-3,
+-1.66666666666666307295E-1,
+};
+const static double coscof[6] = {
+-1.13585365213876817300E-11,
+ 2.08757008419747316778E-9,
+-2.75573141792967388112E-7,
+ 2.48015872888517045348E-5,
+-1.38888888888730564116E-3,
+ 4.16666666666665929218E-2,
+};
+const static double DP1 =   7.85398125648498535156E-1;
+const static double DP2 =   3.77489470793079817668E-8;
+const static double DP3 =   2.69515142907905952645E-15;
+/* const static double lossth = 1.073741824e9; */
+#endif
+
+#ifdef DEC
+static unsigned short sincof[] = {
+0030056,0143750,0177214,0163153,
+0131727,0027455,0044510,0175352,
+0033470,0167432,0131752,0042414,
+0135120,0006400,0146776,0174027,
+0036410,0104210,0104207,0137202,
+0137452,0125252,0125252,0125103,
+};
+static unsigned short coscof[24] = {
+0127107,0151115,0002060,0152325,
+0031017,0072353,0155161,0174053,
+0132623,0171173,0172542,0057056,
+0034320,0006400,0147102,0023652,
+0135666,0005540,0133012,0076213,
+0037052,0125252,0125252,0125126,
+};
+/*  7.853981629014015197753906250000E-1 */
+static unsigned short P1[] = {0040111,0007732,0120000,0000000,};
+/*  4.960467869796758577649598009884E-10 */
+static unsigned short P2[] = {0030410,0055060,0100000,0000000,};
+/*  2.860594363054915898381331279295E-18 */
+static unsigned short P3[] = {0021523,0011431,0105056,0001560,};
+#define DP1 *(double *)P1
+#define DP2 *(double *)P2
+#define DP3 *(double *)P3
+#endif
+
+#ifdef IBMPC
+static unsigned short sincof[] = {
+0x9ccd,0x1fd1,0xd8fd,0x3de5,
+0x1f5d,0xa929,0xe5e5,0xbe5a,
+0x48a1,0x567d,0x1de3,0x3ec7,
+0xdf03,0x19bf,0x01a0,0xbf2a,
+0xf7d0,0x1110,0x1111,0x3f81,
+0x5548,0x5555,0x5555,0xbfc5,
+};
+static unsigned short coscof[24] = {
+0x1a9b,0xa086,0xfa49,0xbda8,
+0x3f05,0x7b4e,0xee9d,0x3e21,
+0x4bc6,0x7eac,0x7e4f,0xbe92,
+0x44f5,0x19c8,0x01a0,0x3efa,
+0x4f91,0x16c1,0xc16c,0xbf56,
+0x554b,0x5555,0x5555,0x3fa5,
+};
+/*
+  7.85398125648498535156E-1,
+  3.77489470793079817668E-8,
+  2.69515142907905952645E-15,
+*/
+static unsigned short P1[] = {0x0000,0x4000,0x21fb,0x3fe9};
+static unsigned short P2[] = {0x0000,0x0000,0x442d,0x3e64};
+static unsigned short P3[] = {0x5170,0x98cc,0x4698,0x3ce8};
+#define DP1 *(double *)P1
+#define DP2 *(double *)P2
+#define DP3 *(double *)P3
+#endif
+
+#ifdef MIEEE
+static unsigned short sincof[] = {
+0x3de5,0xd8fd,0x1fd1,0x9ccd,
+0xbe5a,0xe5e5,0xa929,0x1f5d,
+0x3ec7,0x1de3,0x567d,0x48a1,
+0xbf2a,0x01a0,0x19bf,0xdf03,
+0x3f81,0x1111,0x1110,0xf7d0,
+0xbfc5,0x5555,0x5555,0x5548,
+};
+static unsigned short coscof[24] = {
+0xbda8,0xfa49,0xa086,0x1a9b,
+0x3e21,0xee9d,0x7b4e,0x3f05,
+0xbe92,0x7e4f,0x7eac,0x4bc6,
+0x3efa,0x01a0,0x19c8,0x44f5,
+0xbf56,0xc16c,0x16c1,0x4f91,
+0x3fa5,0x5555,0x5555,0x554b,
+};
+static unsigned short P1[] = {0x3fe9,0x21fb,0x4000,0x0000};
+static unsigned short P2[] = {0x3e64,0x442d,0x0000,0x0000};
+static unsigned short P3[] = {0x3ce8,0x4698,0x98cc,0x5170};
+#define DP1 *(double *)P1
+#define DP2 *(double *)P2
+#define DP3 *(double *)P3
+#endif
+
+#ifdef ANSIPROT
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern double floor ( double );
+extern double ldexp ( double, int );
+extern int isnan ( double );
+extern int isfinite ( double );
+#else
+double polevl(), floor(), ldexp();
+int isnan(), isfinite();
+#endif
+extern double PIO4;
+const static double lossth = 1.073741824e9;
+#ifdef NANS
+extern double NAN;
+#endif
+#ifdef INFINITIES
+extern double INFINITY;
+#endif
+
+
+double sin(x)
+double x;
+{
+double y, z, zz;
+int j, sign;
+
+#ifdef MINUSZERO
+if( x == 0.0 )
+	return(x);
+#endif
+#ifdef NANS
+if( isnan(x) )
+	return(x);
+if( !isfinite(x) )
+	{
+	mtherr( "sin", DOMAIN );
+	return(NAN);
+	}
+#endif
+/* make argument positive but save the sign */
+sign = 1;
+if( x < 0 )
+	{
+	x = -x;
+	sign = -1;
+	}
+
+if( x > lossth )
+	{
+	mtherr( "sin", TLOSS );
+	return(0.0);
+	}
+
+y = floor( x/PIO4 ); /* integer part of x/PIO4 */
+
+/* strip high bits of integer part to prevent integer overflow */
+z = ldexp( y, -4 );
+z = floor(z);           /* integer part of y/8 */
+z = y - ldexp( z, 4 );  /* y - 16 * (y/16) */
+
+j = z; /* convert to integer for tests on the phase angle */
+/* map zeros to origin */
+if( j & 1 )
+	{
+	j += 1;
+	y += 1.0;
+	}
+j = j & 07; /* octant modulo 360 degrees */
+/* reflect in x axis */
+if( j > 3)
+	{
+	sign = -sign;
+	j -= 4;
+	}
+
+/* Extended precision modular arithmetic */
+z = ((x - y * DP1) - y * DP2) - y * DP3;
+
+zz = z * z;
+
+if( (j==1) || (j==2) )
+	{
+	y = 1.0 - ldexp(zz,-1) + zz * zz * polevl( zz, coscof, 5 );
+	}
+else
+	{
+/*	y = z  +  z * (zz * polevl( zz, sincof, 5 ));*/
+	y = z  +  z * z * z * polevl( zz, sincof, 5 );
+	}
+
+if(sign < 0)
+	y = -y;
+
+return(y);
+}
+
+
+
+
+
+double cos(x)
+double x;
+{
+double y, z, zz;
+long i;
+int j, sign;
+
+#ifdef NANS
+if( isnan(x) )
+	return(x);
+if( !isfinite(x) )
+	{
+	mtherr( "cos", DOMAIN );
+	return(NAN);
+	}
+#endif
+
+/* make argument positive */
+sign = 1;
+if( x < 0 )
+	x = -x;
+
+if( x > lossth )
+	{
+	mtherr( "cos", TLOSS );
+	return(0.0);
+	}
+
+y = floor( x/PIO4 );
+z = ldexp( y, -4 );
+z = floor(z);		/* integer part of y/8 */
+z = y - ldexp( z, 4 );  /* y - 16 * (y/16) */
+
+/* integer and fractional part modulo one octant */
+i = z;
+if( i & 1 )	/* map zeros to origin */
+	{
+	i += 1;
+	y += 1.0;
+	}
+j = i & 07;
+if( j > 3)
+	{
+	j -=4;
+	sign = -sign;
+	}
+
+if( j > 1 )
+	sign = -sign;
+
+/* Extended precision modular arithmetic */
+z = ((x - y * DP1) - y * DP2) - y * DP3;
+
+zz = z * z;
+
+if( (j==1) || (j==2) )
+	{
+/*	y = z  +  z * (zz * polevl( zz, sincof, 5 ));*/
+	y = z  +  z * z * z * polevl( zz, sincof, 5 );
+	}
+else
+	{
+	y = 1.0 - ldexp(zz,-1) + zz * zz * polevl( zz, coscof, 5 );
+	}
+
+if(sign < 0)
+	y = -y;
+
+return(y);
+}
+
+
+
+
+
+/* Degrees, minutes, seconds to radians: */
+
+/* 1 arc second, in radians = 4.8481368110953599358991410e-5 */
+#ifdef DEC
+static unsigned short P648[] = {034513,054170,0176773,0116043,};
+#define P64800 *(double *)P648
+#else
+const static double P64800 = 4.8481368110953599358991410e-5;
+#endif
+
+double radian(d,m,s)
+double d,m,s;
+{
+
+return( ((d*60.0 + m)*60.0 + s)*P64800 );
+}

+ 358 - 0
components/external/espruino/libs/math/sincos.c

@@ -0,0 +1,358 @@
+/*							sincos.c
+ *
+ *	Circular sine and cosine of argument in degrees
+ *	Table lookup and interpolation algorithm
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, sine, cosine, flg, sincos();
+ *
+ * sincos( x, &sine, &cosine, flg );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns both the sine and the cosine of the argument x.
+ * Several different compile time options and minimax
+ * approximations are supplied to permit tailoring the
+ * tradeoff between computation speed and accuracy.
+ * 
+ * Since range reduction is time consuming, the reduction
+ * of x modulo 360 degrees is also made optional.
+ *
+ * sin(i) is internally tabulated for 0 <= i <= 90 degrees.
+ * Approximation polynomials, ranging from linear interpolation
+ * to cubics in (x-i)**2, compute the sine and cosine
+ * of the residual x-i which is between -0.5 and +0.5 degree.
+ * In the case of the high accuracy options, the residual
+ * and the tabulated values are combined using the trigonometry
+ * formulas for sin(A+B) and cos(A+B).
+ *
+ * Compile time options are supplied for 5, 11, or 17 decimal
+ * relative accuracy (ACC5, ACC11, ACC17 respectively).
+ * A subroutine flag argument "flg" chooses betwen this
+ * accuracy and table lookup only (peak absolute error
+ * = 0.0087).
+ *
+ * If the argument flg = 1, then the tabulated value is
+ * returned for the nearest whole number of degrees. The
+ * approximation polynomials are not computed.  At
+ * x = 0.5 deg, the absolute error is then sin(0.5) = 0.0087.
+ *
+ * An intermediate speed and precision can be obtained using
+ * the compile time option LINTERP and flg = 1.  This yields
+ * a linear interpolation using a slope estimated from the sine
+ * or cosine at the nearest integer argument.  The peak absolute
+ * error with this option is 3.8e-5.  Relative error at small
+ * angles is about 1e-5.
+ *
+ * If flg = 0, then the approximation polynomials are computed
+ * and applied.
+ *
+ *
+ *
+ * SPEED:
+ *
+ * Relative speed comparisons follow for 6MHz IBM AT clone
+ * and Microsoft C version 4.0.  These figures include
+ * software overhead of do loop and function calls.
+ * Since system hardware and software vary widely, the
+ * numbers should be taken as representative only.
+ *
+ *			flg=0	flg=0	flg=1	flg=1
+ *			ACC11	ACC5	LINTERP	Lookup only
+ * In-line 8087 (/FPi)
+ * sin(), cos()		1.0	1.0	1.0	1.0
+ *
+ * In-line 8087 (/FPi)
+ * sincos()		1.1	1.4	1.9	3.0
+ *
+ * Software (/FPa)
+ * sin(), cos()		0.19	0.19	0.19	0.19
+ *
+ * Software (/FPa)
+ * sincos()		0.39	0.50	0.73	1.7
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ * The accurate approximations are designed with a relative error
+ * criterion.  The absolute error is greatest at x = 0.5 degree.
+ * It decreases from a local maximum at i+0.5 degrees to full
+ * machine precision at each integer i degrees.  With the
+ * ACC5 option, the relative error of 6.3e-6 is equivalent to
+ * an absolute angular error of 0.01 arc second in the argument
+ * at x = i+0.5 degrees.  For small angles < 0.5 deg, the ACC5
+ * accuracy is 6.3e-6 (.00063%) of reading; i.e., the absolute
+ * error decreases in proportion to the argument.  This is true
+ * for both the sine and cosine approximations, since the latter
+ * is for the function 1 - cos(x).
+ *
+ * If absolute error is of most concern, use the compile time
+ * option ABSERR to obtain an absolute error of 2.7e-8 for ACC5
+ * precision.  This is about half the absolute error of the
+ * relative precision option.  In this case the relative error
+ * for small angles will increase to 9.5e-6 -- a reasonable
+ * tradeoff.
+ */
+
+
+#include "mconf.h"
+
+/* Define one of the following to be 1:
+ */
+#define ACC5 1
+#define ACC11 0
+#define ACC17 0
+
+/* Option for linear interpolation when flg = 1
+ */
+#define LINTERP 1
+
+/* Option for absolute error criterion
+ */
+#define ABSERR 1
+
+/* Option to include modulo 360 function:
+ */
+#define MOD360 1
+
+/*
+Cephes Math Library Release 2.1
+Copyright 1987 by Stephen L. Moshier
+Direct inquiries to 30 Frost Street, Cambridge, MA 02140
+*/
+
+
+/* Table of sin(i degrees)
+ * for 0 <= i <= 90
+ */
+const static double sintbl[92] = {
+  0.00000000000000000000E0,
+  1.74524064372835128194E-2,
+  3.48994967025009716460E-2,
+  5.23359562429438327221E-2,
+  6.97564737441253007760E-2,
+  8.71557427476581735581E-2,
+  1.04528463267653471400E-1,
+  1.21869343405147481113E-1,
+  1.39173100960065444112E-1,
+  1.56434465040230869010E-1,
+  1.73648177666930348852E-1,
+  1.90808995376544812405E-1,
+  2.07911690817759337102E-1,
+  2.24951054343864998051E-1,
+  2.41921895599667722560E-1,
+  2.58819045102520762349E-1,
+  2.75637355816999185650E-1,
+  2.92371704722736728097E-1,
+  3.09016994374947424102E-1,
+  3.25568154457156668714E-1,
+  3.42020143325668733044E-1,
+  3.58367949545300273484E-1,
+  3.74606593415912035415E-1,
+  3.90731128489273755062E-1,
+  4.06736643075800207754E-1,
+  4.22618261740699436187E-1,
+  4.38371146789077417453E-1,
+  4.53990499739546791560E-1,
+  4.69471562785890775959E-1,
+  4.84809620246337029075E-1,
+  5.00000000000000000000E-1,
+  5.15038074910054210082E-1,
+  5.29919264233204954047E-1,
+  5.44639035015027082224E-1,
+  5.59192903470746830160E-1,
+  5.73576436351046096108E-1,
+  5.87785252292473129169E-1,
+  6.01815023152048279918E-1,
+  6.15661475325658279669E-1,
+  6.29320391049837452706E-1,
+  6.42787609686539326323E-1,
+  6.56059028990507284782E-1,
+  6.69130606358858213826E-1,
+  6.81998360062498500442E-1,
+  6.94658370458997286656E-1,
+  7.07106781186547524401E-1,
+  7.19339800338651139356E-1,
+  7.31353701619170483288E-1,
+  7.43144825477394235015E-1,
+  7.54709580222771997943E-1,
+  7.66044443118978035202E-1,
+  7.77145961456970879980E-1,
+  7.88010753606721956694E-1,
+  7.98635510047292846284E-1,
+  8.09016994374947424102E-1,
+  8.19152044288991789684E-1,
+  8.29037572555041692006E-1,
+  8.38670567945424029638E-1,
+  8.48048096156425970386E-1,
+  8.57167300702112287465E-1,
+  8.66025403784438646764E-1,
+  8.74619707139395800285E-1,
+  8.82947592858926942032E-1,
+  8.91006524188367862360E-1,
+  8.98794046299166992782E-1,
+  9.06307787036649963243E-1,
+  9.13545457642600895502E-1,
+  9.20504853452440327397E-1,
+  9.27183854566787400806E-1,
+  9.33580426497201748990E-1,
+  9.39692620785908384054E-1,
+  9.45518575599316810348E-1,
+  9.51056516295153572116E-1,
+  9.56304755963035481339E-1,
+  9.61261695938318861916E-1,
+  9.65925826289068286750E-1,
+  9.70295726275996472306E-1,
+  9.74370064785235228540E-1,
+  9.78147600733805637929E-1,
+  9.81627183447663953497E-1,
+  9.84807753012208059367E-1,
+  9.87688340595137726190E-1,
+  9.90268068741570315084E-1,
+  9.92546151641322034980E-1,
+  9.94521895368273336923E-1,
+  9.96194698091745532295E-1,
+  9.97564050259824247613E-1,
+  9.98629534754573873784E-1,
+  9.99390827019095730006E-1,
+  9.99847695156391239157E-1,
+  1.00000000000000000000E0,
+  9.99847695156391239157E-1,
+};
+
+#ifdef ANSIPROT
+double floor ( double );
+#else
+double floor();
+#endif
+
+int sincos(x, s, c, flg)
+double x;
+double *s, *c;
+int flg;
+{
+int ix, ssign, csign, xsign;
+double y, z, sx, sz, cx, cz;
+
+/* Make argument nonnegative.
+ */
+xsign = 1;
+if( x < 0.0 )
+	{
+	xsign = -1;
+	x = -x;
+	}
+
+
+#if MOD360
+x = x  -  360.0 * floor( x/360.0 );
+#endif
+
+/* Find nearest integer to x.
+ * Note there should be a domain error test here,
+ * but this is omitted to gain speed.
+ */
+ix = x + 0.5;
+z = x - ix;		/* the residual */
+
+/* Look up the sine and cosine of the integer.
+ */
+if( ix <= 180 )
+	{
+	ssign = 1;
+	csign = 1;
+	}
+else
+	{
+	ssign = -1;
+	csign = -1;
+	ix -= 180;
+	}
+
+if( ix > 90 )
+	{
+	csign = -csign;
+	ix = 180 - ix;
+	}
+
+sx = sintbl[ix];
+if( ssign < 0 )
+	sx = -sx;
+cx = sintbl[ 90-ix ];
+if( csign < 0 )
+	cx = -cx;
+
+/* If the flag argument is set, then just return
+ * the tabulated values for arg to the nearest whole degree.
+ */
+if( flg )
+	{
+#if LINTERP
+	y = sx + 1.74531263774940077459e-2 * z * cx;
+	cx -= 1.74531263774940077459e-2 * z * sx;
+	sx = y;
+#endif
+	if( xsign < 0 )
+		sx = -sx;
+	*s = sx;	/* sine */
+	*c = cx;	/* cosine */
+	return 0;
+	}
+
+/* Find sine and cosine
+ * of the residual angle between -0.5 and +0.5 degree.
+ */
+#if ACC5
+#if ABSERR
+/* absolute error = 2.769e-8: */
+sz = 1.74531263774940077459e-2 * z;
+/* absolute error = 4.146e-11: */
+cz = 1.0 - 1.52307909153324666207e-4 * z * z;
+#else
+/* relative error = 6.346e-6: */
+sz = 1.74531817576426662296e-2 * z;
+/* relative error = 3.173e-6: */
+cz = 1.0 - 1.52308226602566149927e-4 * z * z;
+#endif
+#else
+y = z * z;
+#endif
+
+
+#if ACC11
+sz = ( -8.86092781698004819918e-7 * y
+      + 1.74532925198378577601e-2     ) * z;
+
+cz = 1.0 - ( -3.86631403698859047896e-9 * y
+            + 1.52308709893047593702e-4     ) * y;
+#endif
+
+
+#if ACC17
+sz = ((  1.34959795251974073996e-11 * y
+       - 8.86096155697856783296e-7     ) * y
+       + 1.74532925199432957214e-2          ) * z;
+
+cz = 1.0 - ((  3.92582397764340914444e-14 * y
+             - 3.86632385155548605680e-9     ) * y
+             + 1.52308709893354299569e-4          ) * y;
+#endif
+
+
+/* Combine the tabulated part and the calculated part
+ * by trigonometry.
+ */
+y = sx * cz  +  cx * sz;
+if( xsign < 0 )
+	y = - y;
+*s = y; /* sine */
+
+*c = cx * cz  -  sx * sz; /* cosine */
+return 0;
+}

+ 308 - 0
components/external/espruino/libs/math/sindg.c

@@ -0,0 +1,308 @@
+/*							sindg.c
+ *
+ *	Circular sine of angle in degrees
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, sindg();
+ *
+ * y = sindg( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Range reduction is into intervals of 45 degrees.
+ *
+ * Two polynomial approximating functions are employed.
+ * Between 0 and pi/4 the sine is approximated by
+ *      x  +  x**3 P(x**2).
+ * Between pi/4 and pi/2 the cosine is represented as
+ *      1  -  x**2 P(x**2).
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain      # trials      peak         rms
+ *    DEC       +-1000        3100      3.3e-17      9.0e-18
+ *    IEEE      +-1000       30000      2.3e-16      5.6e-17
+ * 
+ * ERROR MESSAGES:
+ *
+ *   message           condition        value returned
+ * sindg total loss   x > 8.0e14 (DEC)      0.0
+ *                    x > 1.0e14 (IEEE)
+ *
+ */
+/*							cosdg.c
+ *
+ *	Circular cosine of angle in degrees
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, cosdg();
+ *
+ * y = cosdg( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Range reduction is into intervals of 45 degrees.
+ *
+ * Two polynomial approximating functions are employed.
+ * Between 0 and pi/4 the cosine is approximated by
+ *      1  -  x**2 P(x**2).
+ * Between pi/4 and pi/2 the sine is represented as
+ *      x  +  x**3 P(x**2).
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain      # trials      peak         rms
+ *    DEC      +-1000         3400       3.5e-17     9.1e-18
+ *    IEEE     +-1000        30000       2.1e-16     5.7e-17
+ *  See also sin().
+ *
+ */
+
+/* Cephes Math Library Release 2.0:  April, 1987
+ * Copyright 1985, 1987 by Stephen L. Moshier
+ * Direct inquiries to 30 Frost Street, Cambridge, MA 02140 */
+
+#include "mconf.h"
+
+#ifdef UNK
+const static double sincof[] = {
+ 1.58962301572218447952E-10,
+-2.50507477628503540135E-8,
+ 2.75573136213856773549E-6,
+-1.98412698295895384658E-4,
+ 8.33333333332211858862E-3,
+-1.66666666666666307295E-1
+};
+const static double coscof[] = {
+ 1.13678171382044553091E-11,
+-2.08758833757683644217E-9,
+ 2.75573155429816611547E-7,
+-2.48015872936186303776E-5,
+ 1.38888888888806666760E-3,
+-4.16666666666666348141E-2,
+ 4.99999999999999999798E-1
+};
+const static double PI180 = 1.74532925199432957692E-2; /* pi/180 */
+const static double lossth = 1.0e14;
+#endif
+
+#ifdef DEC
+static unsigned short sincof[] = {
+0030056,0143750,0177170,0073013,
+0131727,0027455,0044510,0132205,
+0033470,0167432,0131752,0042263,
+0135120,0006400,0146776,0174027,
+0036410,0104210,0104207,0137202,
+0137452,0125252,0125252,0125103
+};
+static unsigned short coscof[] = {
+0027107,0176030,0153315,0110312,
+0131017,0072476,0007450,0123243,
+0032623,0171174,0070066,0146445,
+0134320,0006400,0147355,0163313,
+0035666,0005540,0133012,0165067,
+0137052,0125252,0125252,0125206,
+0040000,0000000,0000000,0000000
+};
+static unsigned short P1[] = {0036616,0175065,0011224,0164711};
+#define PI180 *(double *)P1
+const static double lossth = 8.0e14;
+#endif
+
+#ifdef IBMPC
+static unsigned short sincof[] = {
+0x0ec1,0x1fcf,0xd8fd,0x3de5,
+0x1691,0xa929,0xe5e5,0xbe5a,
+0x4896,0x567d,0x1de3,0x3ec7,
+0xdf03,0x19bf,0x01a0,0xbf2a,
+0xf7d0,0x1110,0x1111,0x3f81,
+0x5548,0x5555,0x5555,0xbfc5
+};
+static unsigned short coscof[] = {
+0xb219,0x1ad9,0xff83,0x3da8,
+0x14d4,0xc1e5,0xeea7,0xbe21,
+0xd9a5,0x8e06,0x7e4f,0x3e92,
+0xbcd9,0x19dd,0x01a0,0xbefa,
+0x5d47,0x16c1,0xc16c,0x3f56,
+0x5551,0x5555,0x5555,0xbfa5,
+0x0000,0x0000,0x0000,0x3fe0
+};
+
+static unsigned short P1[] = {0x9d39,0xa252,0xdf46,0x3f91};
+#define PI180 *(double *)P1
+const static double lossth = 1.0e14;
+#endif
+
+#ifdef MIEEE
+static unsigned short sincof[] = {
+0x3de5,0xd8fd,0x1fcf,0x0ec1,
+0xbe5a,0xe5e5,0xa929,0x1691,
+0x3ec7,0x1de3,0x567d,0x4896,
+0xbf2a,0x01a0,0x19bf,0xdf03,
+0x3f81,0x1111,0x1110,0xf7d0,
+0xbfc5,0x5555,0x5555,0x5548
+};
+static unsigned short coscof[] = {
+0x3da8,0xff83,0x1ad9,0xb219,
+0xbe21,0xeea7,0xc1e5,0x14d4,
+0x3e92,0x7e4f,0x8e06,0xd9a5,
+0xbefa,0x01a0,0x19dd,0xbcd9,
+0x3f56,0xc16c,0x16c1,0x5d47,
+0xbfa5,0x5555,0x5555,0x5551,
+0x3fe0,0x0000,0x0000,0x0000
+};
+
+static unsigned short P1[] = {
+0x3f91,0xdf46,0xa252,0x9d39
+};
+#define PI180 *(double *)P1
+const static double lossth = 1.0e14;
+#endif
+
+#ifdef ANSIPROT
+extern double polevl ( double, void *, int );
+extern double floor ( double );
+extern double ldexp ( double, int );
+#else
+double polevl(), floor(), ldexp();
+#endif
+extern double PIO4;
+
+double sindg(x)
+double x;
+{
+double y, z, zz;
+int j, sign;
+
+/* make argument positive but save the sign */
+sign = 1;
+if( x < 0 )
+	{
+	x = -x;
+	sign = -1;
+	}
+
+if( x > lossth )
+	{
+	mtherr( "sindg", TLOSS );
+	return(0.0);
+	}
+
+y = floor( x/45.0 ); /* integer part of x/PIO4 */
+
+/* strip high bits of integer part to prevent integer overflow */
+z = ldexp( y, -4 );
+z = floor(z);           /* integer part of y/8 */
+z = y - ldexp( z, 4 );  /* y - 16 * (y/16) */
+
+j = z; /* convert to integer for tests on the phase angle */
+/* map zeros to origin */
+if( j & 1 )
+	{
+	j += 1;
+	y += 1.0;
+	}
+j = j & 07; /* octant modulo 360 degrees */
+/* reflect in x axis */
+if( j > 3)
+	{
+	sign = -sign;
+	j -= 4;
+	}
+
+z = x - y * 45.0; /* x mod 45 degrees */
+z *= PI180;	/* multiply by pi/180 to convert to radians */
+zz = z * z;
+
+if( (j==1) || (j==2) )
+	{
+	y = 1.0 - zz * polevl( zz, coscof, 6 );
+	}
+else
+	{
+	y = z  +  z * (zz * polevl( zz, sincof, 5 ));
+	}
+
+if(sign < 0)
+	y = -y;
+
+return(y);
+}
+
+
+
+
+
+double cosdg(x)
+double x;
+{
+double y, z, zz;
+int j, sign;
+
+/* make argument positive */
+sign = 1;
+if( x < 0 )
+	x = -x;
+
+if( x > lossth )
+	{
+	mtherr( "cosdg", TLOSS );
+	return(0.0);
+	}
+
+y = floor( x/45.0 );
+z = ldexp( y, -4 );
+z = floor(z);		/* integer part of y/8 */
+z = y - ldexp( z, 4 );  /* y - 16 * (y/16) */
+
+/* integer and fractional part modulo one octant */
+j = z;
+if( j & 1 )	/* map zeros to origin */
+	{
+	j += 1;
+	y += 1.0;
+	}
+j = j & 07;
+if( j > 3)
+	{
+	j -=4;
+	sign = -sign;
+	}
+
+if( j > 1 )
+	sign = -sign;
+
+z = x - y * 45.0; /* x mod 45 degrees */
+z *= PI180;	/* multiply by pi/180 to convert to radians */
+
+zz = z * z;
+
+if( (j==1) || (j==2) )
+	{
+	y = z  +  z * (zz * polevl( zz, sincof, 5 ));
+	}
+else
+	{
+	y = 1.0 - zz * polevl( zz, coscof, 6 );
+	}
+
+if(sign < 0)
+	y = -y;
+
+return(y);
+}

+ 148 - 0
components/external/espruino/libs/math/sinh.c

@@ -0,0 +1,148 @@
+/*							sinh.c
+ *
+ *	Hyperbolic sine
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, sinh();
+ *
+ * y = sinh( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns hyperbolic sine of argument in the range MINLOG to
+ * MAXLOG.
+ *
+ * The range is partitioned into two segments.  If |x| <= 1, a
+ * rational function of the form x + x**3 P(x)/Q(x) is employed.
+ * Otherwise the calculation is sinh(x) = ( exp(x) - exp(-x) )/2.
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC      +- 88        50000       4.0e-17     7.7e-18
+ *    IEEE     +-MAXLOG     30000       2.6e-16     5.7e-17
+ *
+ */
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+#include "mconf.h"
+
+#ifdef UNK
+const static double P[] = {
+-7.89474443963537015605E-1,
+-1.63725857525983828727E2,
+-1.15614435765005216044E4,
+-3.51754964808151394800E5
+};
+const static double Q[] = {
+/* 1.00000000000000000000E0,*/
+-2.77711081420602794433E2,
+ 3.61578279834431989373E4,
+-2.11052978884890840399E6
+};
+#endif
+
+#ifdef DEC
+static unsigned short P[] = {
+0140112,0015377,0042731,0163255,
+0142043,0134721,0146177,0123761,
+0143464,0122706,0034353,0006017,
+0144653,0140536,0157665,0054045
+};
+static unsigned short Q[] = {
+/*0040200,0000000,0000000,0000000,*/
+0142212,0155404,0133513,0022040,
+0044015,0036723,0173271,0011053,
+0145400,0150407,0023710,0001034
+};
+#endif
+
+#ifdef IBMPC
+static unsigned short P[] = {
+0x3cd6,0xe8bb,0x435f,0xbfe9,
+0xf4fe,0x398f,0x773a,0xc064,
+0x6182,0xc71d,0x94b8,0xc0c6,
+0xab05,0xdbf6,0x782b,0xc115
+};
+static unsigned short Q[] = {
+/*0x0000,0x0000,0x0000,0x3ff0,*/
+0x6484,0x96e9,0x5b60,0xc071,
+0x2245,0x7ed7,0xa7ba,0x40e1,
+0x0044,0xe4f9,0x1a20,0xc140
+};
+#endif
+
+#ifdef MIEEE
+static unsigned short P[] = {
+0xbfe9,0x435f,0xe8bb,0x3cd6,
+0xc064,0x773a,0x398f,0xf4fe,
+0xc0c6,0x94b8,0xc71d,0x6182,
+0xc115,0x782b,0xdbf6,0xab05
+};
+static unsigned short Q[] = {
+0xc071,0x5b60,0x96e9,0x6484,
+0x40e1,0xa7ba,0x7ed7,0x2245,
+0xc140,0x1a20,0xe4f9,0x0044
+};
+#endif
+
+#ifdef ANSIPROT
+extern double fabs ( double );
+extern double exp ( double );
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+#else
+double fabs(), exp(), polevl(), p1evl();
+#endif
+extern double INFINITY, MINLOG, MAXLOG, LOGE2;
+
+double sinh(x)
+double x;
+{
+double a;
+
+#ifdef MINUSZERO
+if( x == 0.0 )
+	return(x);
+#endif
+a = fabs(x);
+if( (x > (MAXLOG + LOGE2)) || (x > -(MINLOG-LOGE2) ) )
+	{
+	mtherr( "sinh", DOMAIN );
+	if( x > 0 )
+		return( INFINITY );
+	else
+		return( -INFINITY );
+	}
+if( a > 1.0 )
+	{
+	if( a >= (MAXLOG - LOGE2) )
+		{
+		a = exp(0.5*a);
+		a = (0.5 * a) * a;
+		if( x < 0 )
+			a = -a;
+		return(a);
+		}
+	a = exp(a);
+	a = 0.5*a - (0.5/a);
+	if( x < 0 )
+		a = -a;
+	return(a);
+	}
+
+a *= a;
+return( x + x * a * (polevl(a,P,3)/p1evl(a,Q,3)) );
+}

+ 178 - 0
components/external/espruino/libs/math/sqrt.c

@@ -0,0 +1,178 @@
+/*							sqrt.c
+ *
+ *	Square root
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, sqrt();
+ *
+ * y = sqrt( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns the square root of x.
+ *
+ * Range reduction involves isolating the power of two of the
+ * argument and using a polynomial approximation to obtain
+ * a rough value for the square root.  Then Heron's iteration
+ * is used three times to converge to an accurate value.
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       0, 10       60000       2.1e-17     7.9e-18
+ *    IEEE      0,1.7e308   30000       1.7e-16     6.3e-17
+ *
+ *
+ * ERROR MESSAGES:
+ *
+ *   message         condition      value returned
+ * sqrt domain        x < 0            0.0
+ *
+ */
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1987, 1988, 2000 by Stephen L. Moshier
+*/
+
+
+#include "mconf.h"
+#ifdef ANSIPROT
+extern double frexp ( double, int * );
+extern double ldexp ( double, int );
+#else
+double frexp(), ldexp();
+#endif
+extern double SQRT2;  /*  SQRT2 = 1.41421356237309504880 */
+
+double sqrt(x)
+double x;
+{
+int e;
+#ifndef UNK
+short *q;
+#endif
+double z, w;
+
+if( x <= 0.0 )
+	{
+	if( x < 0.0 )
+		mtherr( "sqrt", DOMAIN );
+	return( 0.0 );
+	}
+w = x;
+/* separate exponent and significand */
+#ifdef UNK
+z = frexp( x, &e );
+#endif
+#ifdef DEC
+q = (short *)&x;
+e = ((*q >> 7) & 0377) - 0200;
+*q &= 0177;
+*q |= 040000;
+z = x;
+#endif
+
+/* Note, frexp and ldexp are used in order to
+ * handle denormal numbers properly.
+ */
+#ifdef IBMPC
+z = frexp( x, &e );
+q = (short *)&x;
+q += 3;
+/*
+e = ((*q >> 4) & 0x0fff) - 0x3fe;
+*q &= 0x000f;
+*q |= 0x3fe0;
+z = x;
+*/
+#endif
+#ifdef MIEEE
+z = frexp( x, &e );
+q = (short *)&x;
+/*
+e = ((*q >> 4) & 0x0fff) - 0x3fe;
+*q &= 0x000f;
+*q |= 0x3fe0;
+z = x;
+*/
+#endif
+
+/* approximate square root of number between 0.5 and 1
+ * relative error of approximation = 7.47e-3
+ */
+x = 4.173075996388649989089E-1 + 5.9016206709064458299663E-1 * z;
+
+/* adjust for odd powers of 2 */
+if( (e & 1) != 0 )
+	x *= SQRT2;
+
+/* re-insert exponent */
+#ifdef UNK
+x = ldexp( x, (e >> 1) );
+#endif
+#ifdef DEC
+*q += ((e >> 1) & 0377) << 7;
+*q &= 077777;
+#endif
+#ifdef IBMPC
+x = ldexp( x, (e >> 1) );
+/*
+*q += ((e >>1) & 0x7ff) << 4;
+*q &= 077777;
+*/
+#endif
+#ifdef MIEEE
+x = ldexp( x, (e >> 1) );
+/*
+*q += ((e >>1) & 0x7ff) << 4;
+*q &= 077777;
+*/
+#endif
+
+/* Newton iterations: */
+#ifdef UNK
+x = 0.5*(x + w/x);
+x = 0.5*(x + w/x);
+x = 0.5*(x + w/x);
+#endif
+
+/* Note, assume the square root cannot be denormal,
+ * so it is safe to use integer exponent operations here.
+ */
+#ifdef DEC
+x += w/x;
+*q -= 0200;
+x += w/x;
+*q -= 0200;
+x += w/x;
+*q -= 0200;
+#endif
+#ifdef IBMPC
+x += w/x;
+*q -= 0x10;
+x += w/x;
+*q -= 0x10;
+x += w/x;
+*q -= 0x10;
+#endif
+#ifdef MIEEE
+x += w/x;
+*q -= 0x10;
+x += w/x;
+*q -= 0x10;
+x += w/x;
+*q -= 0x10;
+#endif
+
+return(x);
+}

+ 304 - 0
components/external/espruino/libs/math/tan.c

@@ -0,0 +1,304 @@
+/*							tan.c
+ *
+ *	Circular tangent
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, tan();
+ *
+ * y = tan( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns the circular tangent of the radian argument x.
+ *
+ * Range reduction is modulo pi/4.  A rational function
+ *       x + x**3 P(x**2)/Q(x**2)
+ * is employed in the basic interval [0, pi/4].
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC      +-1.07e9      44000      4.1e-17     1.0e-17
+ *    IEEE     +-1.07e9      30000      2.9e-16     8.1e-17
+ *
+ * ERROR MESSAGES:
+ *
+ *   message         condition          value returned
+ * tan total loss   x > 1.073741824e9     0.0
+ *
+ */
+/*							cot.c
+ *
+ *	Circular cotangent
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, cot();
+ *
+ * y = cot( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns the circular cotangent of the radian argument x.
+ *
+ * Range reduction is modulo pi/4.  A rational function
+ *       x + x**3 P(x**2)/Q(x**2)
+ * is employed in the basic interval [0, pi/4].
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE     +-1.07e9      30000      2.9e-16     8.2e-17
+ *
+ *
+ * ERROR MESSAGES:
+ *
+ *   message         condition          value returned
+ * cot total loss   x > 1.073741824e9       0.0
+ * cot singularity  x = 0                  INFINITY
+ *
+ */
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+yright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+#include "mconf.h"
+
+#ifdef UNK
+const static double P[] = {
+-1.30936939181383777646E4,
+ 1.15351664838587416140E6,
+-1.79565251976484877988E7
+};
+const static double Q[] = {
+/* 1.00000000000000000000E0,*/
+ 1.36812963470692954678E4,
+-1.32089234440210967447E6,
+ 2.50083801823357915839E7,
+-5.38695755929454629881E7
+};
+const static double DP1 = 7.853981554508209228515625E-1;
+const static double DP2 = 7.94662735614792836714E-9;
+const static double DP3 = 3.06161699786838294307E-17;
+const static double lossth = 1.073741824e9;
+#endif
+
+#ifdef DEC
+static unsigned short P[] = {
+0143514,0113306,0111171,0174674,
+0045214,0147545,0027744,0167346,
+0146210,0177526,0114514,0105660
+};
+static unsigned short Q[] = {
+/*0040200,0000000,0000000,0000000,*/
+0043525,0142457,0072633,0025617,
+0145241,0036742,0140525,0162256,
+0046276,0146176,0013526,0143573,
+0146515,0077401,0162762,0150607
+};
+/*  7.853981629014015197753906250000E-1 */
+static unsigned short P1[] = {0040111,0007732,0120000,0000000,};
+/*  4.960467869796758577649598009884E-10 */
+static unsigned short P2[] = {0030410,0055060,0100000,0000000,};
+/*  2.860594363054915898381331279295E-18 */
+static unsigned short P3[] = {0021523,0011431,0105056,0001560,};
+#define DP1 *(double *)P1
+#define DP2 *(double *)P2
+#define DP3 *(double *)P3
+const static double lossth = 1.073741824e9;
+#endif
+
+#ifdef IBMPC
+static unsigned short P[] = {
+0x3f38,0xd24f,0x92d8,0xc0c9,
+0x9ddd,0xa5fc,0x99ec,0x4131,
+0x9176,0xd329,0x1fea,0xc171
+};
+static unsigned short Q[] = {
+/*0x0000,0x0000,0x0000,0x3ff0,*/
+0x6572,0xeeb3,0xb8a5,0x40ca,
+0xbc96,0x582a,0x27bc,0xc134,
+0xd8ef,0xc2ea,0xd98f,0x4177,
+0x5a31,0x3cbe,0xafe0,0xc189
+};
+/*
+  7.85398125648498535156E-1,
+  3.77489470793079817668E-8,
+  2.69515142907905952645E-15,
+*/
+static unsigned short P1[] = {0x0000,0x4000,0x21fb,0x3fe9};
+static unsigned short P2[] = {0x0000,0x0000,0x442d,0x3e64};
+static unsigned short P3[] = {0x5170,0x98cc,0x4698,0x3ce8};
+#define DP1 *(double *)P1
+#define DP2 *(double *)P2
+#define DP3 *(double *)P3
+const static double lossth = 1.073741824e9;
+#endif
+
+#ifdef MIEEE
+static unsigned short P[] = {
+0xc0c9,0x92d8,0xd24f,0x3f38,
+0x4131,0x99ec,0xa5fc,0x9ddd,
+0xc171,0x1fea,0xd329,0x9176
+};
+static unsigned short Q[] = {
+0x40ca,0xb8a5,0xeeb3,0x6572,
+0xc134,0x27bc,0x582a,0xbc96,
+0x4177,0xd98f,0xc2ea,0xd8ef,
+0xc189,0xafe0,0x3cbe,0x5a31
+};
+static unsigned short P1[] = {
+0x3fe9,0x21fb,0x4000,0x0000
+};
+static unsigned short P2[] = {
+0x3e64,0x442d,0x0000,0x0000
+};
+static unsigned short P3[] = {
+0x3ce8,0x4698,0x98cc,0x5170,
+};
+#define DP1 *(double *)P1
+#define DP2 *(double *)P2
+#define DP3 *(double *)P3
+const static double lossth = 1.073741824e9;
+#endif
+
+#ifdef ANSIPROT
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern double floor ( double );
+extern double ldexp ( double, int );
+extern int isnan ( double );
+extern int isfinite ( double );
+const static double tancot(double, int);
+#else
+double polevl(), p1evl(), floor(), ldexp();
+const static double tancot();
+int isnan(), isfinite();
+#endif
+extern double PIO4;
+extern double INFINITY;
+extern double NAN;
+
+double tan(x)
+double x;
+{
+#ifdef MINUSZERO
+if( x == 0.0 )
+	return(x);
+#endif
+#ifdef NANS
+if( isnan(x) )
+	return(x);
+if( !isfinite(x) )
+	{
+	mtherr( "tan", DOMAIN );
+	return(NAN);
+	}
+#endif
+return( tancot(x,0) );
+}
+
+
+double cot(x)
+double x;
+{
+
+if( x == 0.0 )
+	{
+	mtherr( "cot", SING );
+	return( INFINITY );
+	}
+return( tancot(x,1) );
+}
+
+
+const static double tancot( xx, cotflg )
+double xx;
+int cotflg;
+{
+double x, y, z, zz;
+int j, sign;
+
+/* make argument positive but save the sign */
+if( xx < 0 )
+	{
+	x = -xx;
+	sign = -1;
+	}
+else
+	{
+	x = xx;
+	sign = 1;
+	}
+
+if( x > lossth )
+	{
+	if( cotflg )
+		mtherr( "cot", TLOSS );
+	else
+		mtherr( "tan", TLOSS );
+	return(0.0);
+	}
+
+/* compute x mod PIO4 */
+y = floor( x/PIO4 );
+
+/* strip high bits of integer part */
+z = ldexp( y, -3 );
+z = floor(z);		/* integer part of y/8 */
+z = y - ldexp( z, 3 );  /* y - 16 * (y/16) */
+
+/* integer and fractional part modulo one octant */
+j = z;
+
+/* map zeros and singularities to origin */
+if( j & 1 )
+	{
+	j += 1;
+	y += 1.0;
+	}
+
+z = ((x - y * DP1) - y * DP2) - y * DP3;
+
+zz = z * z;
+
+if( zz > 1.0e-14 )
+	y = z  +  z * (zz * polevl( zz, P, 2 )/p1evl(zz, Q, 4));
+else
+	y = z;
+	
+if( j & 2 )
+	{
+	if( cotflg )
+		y = -y;
+	else
+		y = -1.0/y;
+	}
+else
+	{
+	if( cotflg )
+		y = 1.0/y;
+	}
+
+if( sign < 0 )
+	y = -y;
+
+return( y );
+}

+ 267 - 0
components/external/espruino/libs/math/tandg.c

@@ -0,0 +1,267 @@
+/*							tandg.c
+ *
+ *	Circular tangent of argument in degrees
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, tandg();
+ *
+ * y = tandg( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns the circular tangent of the argument x in degrees.
+ *
+ * Range reduction is modulo pi/4.  A rational function
+ *       x + x**3 P(x**2)/Q(x**2)
+ * is employed in the basic interval [0, pi/4].
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC      0,10          8000      3.4e-17      1.2e-17
+ *    IEEE     0,10         30000      3.2e-16      8.4e-17
+ *
+ * ERROR MESSAGES:
+ *
+ *   message         condition          value returned
+ * tandg total loss   x > 8.0e14 (DEC)      0.0
+ *                    x > 1.0e14 (IEEE)
+ * tandg singularity  x = 180 k  +  90     MAXNUM
+ */
+/*							cotdg.c
+ *
+ *	Circular cotangent of argument in degrees
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, cotdg();
+ *
+ * y = cotdg( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns the circular cotangent of the argument x in degrees.
+ *
+ * Range reduction is modulo pi/4.  A rational function
+ *       x + x**3 P(x**2)/Q(x**2)
+ * is employed in the basic interval [0, pi/4].
+ *
+ *
+ * ERROR MESSAGES:
+ *
+ *   message         condition          value returned
+ * cotdg total loss   x > 8.0e14 (DEC)      0.0
+ *                    x > 1.0e14 (IEEE)
+ * cotdg singularity  x = 180 k            MAXNUM
+ */
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1987, 2000 by Stephen L. Moshier
+*/
+
+#include "mconf.h"
+
+#ifdef UNK
+const static double P[] = {
+-1.30936939181383777646E4,
+ 1.15351664838587416140E6,
+-1.79565251976484877988E7
+};
+const static double Q[] = {
+/* 1.00000000000000000000E0,*/
+ 1.36812963470692954678E4,
+-1.32089234440210967447E6,
+ 2.50083801823357915839E7,
+-5.38695755929454629881E7
+};
+const static double PI180 = 1.74532925199432957692E-2;
+const static double lossth = 1.0e14;
+#endif
+
+#ifdef DEC
+static unsigned short P[] = {
+0143514,0113306,0111171,0174674,
+0045214,0147545,0027744,0167346,
+0146210,0177526,0114514,0105660
+};
+static unsigned short Q[] = {
+/*0040200,0000000,0000000,0000000,*/
+0043525,0142457,0072633,0025617,
+0145241,0036742,0140525,0162256,
+0046276,0146176,0013526,0143573,
+0146515,0077401,0162762,0150607
+};
+static unsigned short P1[] = {0036616,0175065,0011224,0164711};
+#define PI180 *(double *)P1
+const static double lossth = 8.0e14;
+#endif
+
+#ifdef IBMPC
+static unsigned short P[] = {
+0x3f38,0xd24f,0x92d8,0xc0c9,
+0x9ddd,0xa5fc,0x99ec,0x4131,
+0x9176,0xd329,0x1fea,0xc171
+};
+static unsigned short Q[] = {
+/*0x0000,0x0000,0x0000,0x3ff0,*/
+0x6572,0xeeb3,0xb8a5,0x40ca,
+0xbc96,0x582a,0x27bc,0xc134,
+0xd8ef,0xc2ea,0xd98f,0x4177,
+0x5a31,0x3cbe,0xafe0,0xc189
+};
+static unsigned short P1[] = {0x9d39,0xa252,0xdf46,0x3f91};
+#define PI180 *(double *)P1
+const static double lossth = 1.0e14;
+#endif
+
+#ifdef MIEEE
+static unsigned short P[] = {
+0xc0c9,0x92d8,0xd24f,0x3f38,
+0x4131,0x99ec,0xa5fc,0x9ddd,
+0xc171,0x1fea,0xd329,0x9176
+};
+static unsigned short Q[] = {
+0x40ca,0xb8a5,0xeeb3,0x6572,
+0xc134,0x27bc,0x582a,0xbc96,
+0x4177,0xd98f,0xc2ea,0xd8ef,
+0xc189,0xafe0,0x3cbe,0x5a31
+};
+static unsigned short P1[] = {
+0x3f91,0xdf46,0xa252,0x9d39
+};
+#define PI180 *(double *)P1
+const static double lossth = 1.0e14;
+#endif
+
+#ifdef ANSIPROT
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern double floor ( double );
+extern double ldexp ( double, int );
+const static double tancot( double, int );
+#else
+double polevl(), p1evl(), floor(), ldexp();
+const static double tancot();
+#endif
+extern double MAXNUM;
+extern double PIO4;
+
+
+double tandg(x)
+double x;
+{
+
+return( tancot(x,0) );
+}
+
+
+double cotdg(x)
+double x;
+{
+
+return( tancot(x,1) );
+}
+
+
+const static double tancot( xx, cotflg )
+double xx;
+int cotflg;
+{
+double x, y, z, zz;
+int j, sign;
+
+/* make argument positive but save the sign */
+if( xx < 0 )
+	{
+	x = -xx;
+	sign = -1;
+	}
+else
+	{
+	x = xx;
+	sign = 1;
+	}
+
+if( x > lossth )
+	{
+	mtherr( "tandg", TLOSS );
+	return(0.0);
+	}
+
+/* compute x mod PIO4 */
+y = floor( x/45.0 );
+
+/* strip high bits of integer part */
+z = ldexp( y, -3 );
+z = floor(z);		/* integer part of y/8 */
+z = y - ldexp( z, 3 );  /* y - 16 * (y/16) */
+
+/* integer and fractional part modulo one octant */
+j = z;
+
+/* map zeros and singularities to origin */
+if( j & 1 )
+	{
+	j += 1;
+	y += 1.0;
+	}
+
+z = x - y * 45.0;
+z *= PI180;
+
+zz = z * z;
+
+if( zz > 1.0e-14 )
+	y = z  +  z * (zz * polevl( zz, P, 2 )/p1evl(zz, Q, 4));
+else
+	y = z;
+	
+if( j & 2 )
+	{
+	if( cotflg )
+		y = -y;
+	else
+		{
+		if( y != 0.0 )
+			{
+			y = -1.0/y;
+			}
+		else
+			{
+			mtherr( "tandg", SING );
+			y = MAXNUM;
+			}
+		}
+	}
+else
+	{
+	if( cotflg )
+		{
+		if( y != 0.0 )
+			y = 1.0/y;
+		else
+			{
+			mtherr( "cotdg", SING );
+			y = MAXNUM;
+			}
+		}
+	}
+
+if( sign < 0 )
+	y = -y;
+
+return( y );
+}

+ 141 - 0
components/external/espruino/libs/math/tanh.c

@@ -0,0 +1,141 @@
+/*							tanh.c
+ *
+ *	Hyperbolic tangent
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, tanh();
+ *
+ * y = tanh( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns hyperbolic tangent of argument in the range MINLOG to
+ * MAXLOG.
+ *
+ * A rational function is used for |x| < 0.625.  The form
+ * x + x**3 P(x)/Q(x) of Cody _& Waite is employed.
+ * Otherwise,
+ *    tanh(x) = sinh(x)/cosh(x) = 1  -  2/(exp(2x) + 1).
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       -2,2        50000       3.3e-17     6.4e-18
+ *    IEEE      -2,2        30000       2.5e-16     5.8e-17
+ *
+ */
+
+/*
+Cephes Math Library Release 2.8:  June, 2000
+Copyright 1984, 1995, 2000 by Stephen L. Moshier
+*/
+
+#include "mconf.h"
+
+#ifdef UNK
+const static double P[] = {
+-9.64399179425052238628E-1,
+-9.92877231001918586564E1,
+-1.61468768441708447952E3
+};
+const static double Q[] = {
+/* 1.00000000000000000000E0,*/
+ 1.12811678491632931402E2,
+ 2.23548839060100448583E3,
+ 4.84406305325125486048E3
+};
+#endif
+#ifdef DEC
+static unsigned short P[] = {
+0140166,0161335,0053753,0075126,
+0141706,0111520,0070463,0040552,
+0142711,0153001,0101300,0025430
+};
+static unsigned short Q[] = {
+/*0040200,0000000,0000000,0000000,*/
+0041741,0117624,0051300,0156060,
+0043013,0133720,0071251,0127717,
+0043227,0060201,0021020,0020136
+};
+#endif
+
+#ifdef IBMPC
+static unsigned short P[] = {
+0x6f4b,0xaafd,0xdc5b,0xbfee,
+0x682d,0x0e26,0xd26a,0xc058,
+0x0563,0x3058,0x3ac0,0xc099
+};
+static unsigned short Q[] = {
+/*0x0000,0x0000,0x0000,0x3ff0,*/
+0x1b86,0x8a58,0x33f2,0x405c,
+0x35fa,0x0e55,0x76fa,0x40a1,
+0x040c,0x2442,0xec10,0x40b2
+};
+#endif
+
+#ifdef MIEEE
+static unsigned short P[] = {
+0xbfee,0xdc5b,0xaafd,0x6f4b,
+0xc058,0xd26a,0x0e26,0x682d,
+0xc099,0x3ac0,0x3058,0x0563
+};
+static unsigned short Q[] = {
+0x405c,0x33f2,0x8a58,0x1b86,
+0x40a1,0x76fa,0x0e55,0x35fa,
+0x40b2,0xec10,0x2442,0x040c
+};
+#endif
+
+#ifdef ANSIPROT
+extern double fabs ( double );
+extern double exp ( double );
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+#else
+double fabs(), exp(), polevl(), p1evl();
+#endif
+extern double MAXLOG;
+
+double tanh(x)
+double x;
+{
+double s, z;
+
+#ifdef MINUSZERO
+if( x == 0.0 )
+	return(x);
+#endif
+z = fabs(x);
+if( z > 0.5 * MAXLOG )
+	{
+	if( x > 0 )
+		return( 1.0 );
+	else
+		return( -1.0 );
+	}
+if( z >= 0.625 )
+	{
+	s = exp(2.0*z);
+	z =  1.0  - 2.0/(s + 1.0);
+	if( x < 0 )
+		z = -z;
+	}
+else
+	{
+	if( x == 0.0 )
+	  return(x);
+	s = x * x;
+	z = polevl( s, P, 2 )/p1evl(s, Q, 3);
+	z = x * s * z;
+	z = x + z;
+	}
+return( z );
+}

+ 138 - 0
components/external/espruino/libs/math/unity.c

@@ -0,0 +1,138 @@
+/*							unity.c
+ *
+ * Relative error approximations for function arguments near
+ * unity.
+ *
+ *    log1p(x) = log(1+x)
+ *    expm1(x) = exp(x) - 1
+ *    cosm1(x) = cos(x) - 1
+ *
+ */
+
+#include "mconf.h"
+
+#ifdef ANSIPROT
+extern int isnan (double);
+extern int isfinite (double);
+extern double log ( double );
+extern double polevl ( double, void *, int );
+extern double p1evl ( double, void *, int );
+extern double exp ( double );
+extern double cos ( double );
+#else
+double log(), polevl(), p1evl(), exp(), cos();
+int isnan(), isfinite();
+#endif
+extern double INFINITY;
+
+/* log1p(x) = log(1 + x)  */
+
+/* Coefficients for log(1+x) = x - x**2/2 + x**3 P(x)/Q(x)
+ * 1/sqrt(2) <= x < sqrt(2)
+ * Theoretical peak relative error = 2.32e-20
+ */
+const static double LP[] = {
+ 4.5270000862445199635215E-5,
+ 4.9854102823193375972212E-1,
+ 6.5787325942061044846969E0,
+ 2.9911919328553073277375E1,
+ 6.0949667980987787057556E1,
+ 5.7112963590585538103336E1,
+ 2.0039553499201281259648E1,
+};
+const static double LQ[] = {
+/* 1.0000000000000000000000E0,*/
+ 1.5062909083469192043167E1,
+ 8.3047565967967209469434E1,
+ 2.2176239823732856465394E2,
+ 3.0909872225312059774938E2,
+ 2.1642788614495947685003E2,
+ 6.0118660497603843919306E1,
+};
+
+#define SQRTH 0.70710678118654752440
+#define SQRT2 1.41421356237309504880
+
+double log1p(x)
+double x;
+{
+double z;
+
+z = 1.0 + x;
+if( (z < SQRTH) || (z > SQRT2) )
+	return( log(z) );
+z = x*x;
+z = -0.5 * z + x * ( z * polevl( x, LP, 6 ) / p1evl( x, LQ, 6 ) );
+return (x + z);
+}
+
+
+
+/* expm1(x) = exp(x) - 1  */
+
+/*  e^x =  1 + 2x P(x^2)/( Q(x^2) - P(x^2) )
+ * -0.5 <= x <= 0.5
+ */
+
+const static double EP[3] = {
+ 1.2617719307481059087798E-4,
+ 3.0299440770744196129956E-2,
+ 9.9999999999999999991025E-1,
+};
+const static double EQ[4] = {
+ 3.0019850513866445504159E-6,
+ 2.5244834034968410419224E-3,
+ 2.2726554820815502876593E-1,
+ 2.0000000000000000000897E0,
+};
+
+double expm1(x)
+double x;
+{
+double r, xx;
+
+#ifdef NANS
+if( isnan(x) )
+	return(x);
+#endif
+#ifdef INFINITIES
+if( x == INFINITY )
+	return(INFINITY);
+if( x == -INFINITY )
+	return(-1.0);
+#endif
+if( (x < -0.5) || (x > 0.5) )
+	return( exp(x) - 1.0 );
+xx = x * x;
+r = x * polevl( xx, EP, 2 );
+r = r/( polevl( xx, EQ, 3 ) - r );
+return (r + r);
+}
+
+
+
+/* cosm1(x) = cos(x) - 1  */
+
+const static double coscof[7] = {
+ 4.7377507964246204691685E-14,
+-1.1470284843425359765671E-11,
+ 2.0876754287081521758361E-9,
+-2.7557319214999787979814E-7,
+ 2.4801587301570552304991E-5,
+-1.3888888888888872993737E-3,
+ 4.1666666666666666609054E-2,
+};
+
+extern double PIO4;
+
+double cosm1(x)
+double x;
+{
+double xx;
+
+if( (x < -PIO4) || (x > PIO4) )
+	return( cos(x) - 1.0 );
+xx = x * x;
+xx = -0.5*xx + xx * xx * polevl( xx, coscof, 6 );
+return xx;
+}

+ 109 - 0
components/external/espruino/libs/math/unix.mak

@@ -0,0 +1,109 @@
+# Double precision Cephes library
+# Makefile for unix or GCC
+
+CC = gcc
+CFLAGS = -g -O2 -Wall -fno-builtin
+AR = ar
+RANLIB = ranlib
+INCS = mconf.h
+AS = as
+
+OBJS = acosh.o airy.o asin.o asinh.o atan.o atanh.o bdtr.o beta.o \
+btdtr.o cbrt.o chbevl.o chdtr.o clog.o cmplx.o const.o \
+cosh.o dawsn.o drand.o ellie.o ellik.o ellpe.o ellpj.o ellpk.o \
+exp.o exp10.o exp2.o expn.o fabs.o fac.o fdtr.o \
+fresnl.o gamma.o gdtr.o hyp2f1.o hyperg.o i0.o i1.o igami.o \
+incbet.o incbi.o igam.o isnan.o iv.o j0.o j1.o jn.o jv.o k0.o k1.o \
+kn.o log.o log2.o log10.o lrand.o nbdtr.o ndtr.o ndtri.o pdtr.o \
+polevl.o polmisc.o polyn.o pow.o powi.o psi.o rgamma.o round.o \
+shichi.o sici.o sin.o sindg.o sinh.o spence.o stdtr.o struve.o \
+tan.o tandg.o tanh.o unity.o yn.o zeta.o zetac.o \
+sqrt.o floor.o setprec.o mtherr.o
+
+all: libmd.a mtst dtestvec dcalc paranoia # stamp-timing
+
+stamp-timing: libmd.a mtst time-it
+	time-it "mtst > /dev/null"
+	touch stamp-timing
+
+time-it: time-it.o
+	$(CC) -o time-it time-it.o
+
+time-it.o: time-it.c
+	$(CC) -O2 -c time-it.c
+
+dcalc: dcalc.o libmd.a
+	$(CC) -o dcalc dcalc.o libmd.a
+#	aout2exe mtst
+
+mtst: mtst.o libmd.a
+	$(CC) -v -o mtst mtst.o libmd.a
+#	gcc -Wl,-verbose -b i486-linuxaout -v -o mtst mtst.o libmd.a
+#	coff2exe mtst
+
+mtst.o: mtst.c
+	$(CC) -O2 -Wall -c mtst.c
+
+dtestvec: dtestvec.o libmd.a
+	$(CC) -o dtestvec dtestvec.o libmd.a
+
+dtestvec.o: dtestvec.c
+	$(CC) -g -c dtestvec.c
+
+paranoia: paranoia.o setprec.o libmd.a
+	$(CC) -o paranoia paranoia.o setprec.o libmd.a
+
+paranoia.o: paranoia.c
+	$(CC) $(CFLAGS) -c paranoia.c
+
+libmd.a: $(OBJS) $(INCS)
+# for real Unix:
+	$(AR) rv libmd.a $(OBJS)
+# for djgcc MSDOS:
+#	>libmd.rf -rv libmd.a $(OBJS)
+#	$(AR) @libmd.rf
+	$(RANLIB) libmd.a
+
+# If the following are all commented out, the C versions
+# will be used by default.
+
+# IBM PC:
+#sqrt.o: sqrt.387
+#	$(AS) -o sqrt.o sqrt.387
+#
+#floor.o: floor.387
+#	$(AS) -o floor.o floor.387
+#
+#setprec.o: setprec.387
+#	$(AS) -o setprec.o setprec.387
+
+# ELF versions for linux (no underscores)
+sqrt.o: sqrtelf.387
+	$(AS) -o sqrt.o sqrtelf.387
+
+#floor.o: floorelf.387
+#	$(AS) -o floor.o floorelf.387
+
+setprec.o: setprelf.387
+	$(AS) -o setprec.o setprelf.387
+
+# Motorola 68881. Caution, subroutine return conventions vary.
+#sqrt.o: sqrt.688
+#	$(AS) -o sqrt.o sqrt.688
+#
+#setprec.o: setprec.688
+#	$(AS) -o setprec.o setprec.688
+
+# SPARC:
+#sqrt.o: sqrt.spa
+#	$(AS) -o sqrt.o sqrt.spa
+
+clean:
+	rm -f *.o
+	rm -f mtst
+	rm -f paranoia
+	rm -f dcalc
+	rm -f libmd.a
+	rm -f time-it
+	rm -f dtestvec
+

+ 336 - 0
components/external/espruino/src/jsdevices.c

@@ -0,0 +1,336 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Common low-level device handling (Events, IO buffers)
+ * ----------------------------------------------------------------------------
+ */
+#include "jsdevices.h"
+#include "jsparse.h"
+#include "jsinteractive.h"
+
+#ifdef LINUX
+ #include <signal.h>
+#endif//LINUX
+#ifdef USE_TRIGGER
+#include "trigger.h"
+#endif
+#ifdef USE_CC3000
+#include "board_spi.h"
+#endif
+// ----------------------------------------------------------------------------
+//                                                                     BUFFERS
+
+// ----------------------------------------------------------------------------
+//                                                         DATA TRANSMIT BUFFER
+typedef struct {
+  IOEventFlags flags; // Where this data should be transmitted
+  unsigned char data;         // data to transmit
+} PACKED_FLAGS TxBufferItem;
+
+TxBufferItem txBuffer[TXBUFFERMASK+1];
+volatile unsigned char txHead=0, txTail=0;
+// ----------------------------------------------------------------------------
+
+// Queue a character for transmission
+void jshTransmit(IOEventFlags device, unsigned char data) {
+#ifdef RT_USING_JS
+    rt_device_write(rt_console_get_device(), 0, &data, 1);
+#else
+#ifndef LINUX
+#ifdef USB
+  if (device==EV_USBSERIAL && !jshIsUSBSERIALConnected()) {
+    jshTransmitClearDevice(EV_USBSERIAL); // clear out stuff already waiting
+    return;
+  }
+#endif
+  if (device==EV_NONE) return;
+  unsigned char txHeadNext = (txHead+1)&TXBUFFERMASK;
+  if (txHeadNext==txTail) {
+    jsiSetBusy(BUSY_TRANSMIT, true);
+    while (txHeadNext==txTail) {
+      // wait for send to finish as buffer is about to overflow
+#ifdef USB
+      // just in case USB was unplugged while we were waiting!
+      if (!jshIsUSBSERIALConnected()) jshTransmitClearDevice(EV_USBSERIAL);
+#endif
+    }
+    jsiSetBusy(BUSY_TRANSMIT, false);
+  }
+  txBuffer[txHead].flags = device;
+  txBuffer[txHead].data = (char)data;
+  txHead = txHeadNext;
+
+  jshUSARTKick(device); // set up interrupts if required
+
+#else // if PC, just put to stdout
+  if (device==DEFAULT_CONSOLE_DEVICE) {
+    fputc(data, stdout);
+    fflush(stdout);
+  }
+#endif
+#endif
+}
+
+// Try and get a character for transmission - could just return -1 if nothing
+int jshGetCharToTransmit(IOEventFlags device) {
+  unsigned char ptr = txTail;
+  while (txHead != ptr) {
+    if (IOEVENTFLAGS_GETTYPE(txBuffer[ptr].flags) == device) {
+      unsigned char data = txBuffer[ptr].data;
+      if (ptr != txTail) { // so we weren't right at the back of the queue
+        // we need to work back from ptr (until we hit tail), shifting everything forwards
+        unsigned char this = ptr;
+        unsigned char last = (unsigned char)((this+TXBUFFERMASK)&TXBUFFERMASK);
+        while (this!=txTail) { // if this==txTail, then last is before it, so stop here
+          txBuffer[this] = txBuffer[last];
+          this = last;
+          last = (unsigned char)((this+TXBUFFERMASK)&TXBUFFERMASK);
+        }
+      }
+      txTail = (unsigned char)((txTail+1)&TXBUFFERMASK); // advance the tail
+      return data; // return data
+    }
+    ptr = (unsigned char)((ptr+1)&TXBUFFERMASK);
+  }
+  return -1; // no data :(
+}
+
+void jshTransmitFlush() {
+  jsiSetBusy(BUSY_TRANSMIT, true);
+  while (jshHasTransmitData()) ; // wait for send to finish
+  jsiSetBusy(BUSY_TRANSMIT, false);
+}
+
+// Clear everything from a device
+void jshTransmitClearDevice(IOEventFlags device) {
+  while (jshGetCharToTransmit(device)>=0);
+}
+
+bool jshHasTransmitData() {
+#ifndef LINUX
+  return txHead != txTail;
+#else
+  return false;
+#endif
+}
+
+// ----------------------------------------------------------------------------
+//                                                              IO EVENT BUFFER
+IOEvent ioBuffer[IOBUFFERMASK+1];
+volatile unsigned char ioHead=0, ioTail=0;
+// ----------------------------------------------------------------------------
+
+
+void jshIOEventOverflowed() {
+  // Error here - just light LED as we can't do much else right now
+}
+
+
+void jshPushIOCharEvent(IOEventFlags channel, char charData) {
+  if (charData==3 && channel==jsiGetConsoleDevice()) {
+    // Ctrl-C - force interrupt
+#ifdef LINUX
+    raise(SIGINT);
+#endif
+    jspSetInterrupted(true);
+    return;
+  }
+  if (DEVICE_IS_USART(channel) && jshGetEventsUsed() > IOBUFFER_XOFF)
+    jshSetFlowControlXON(channel, false);
+  // Check for existing buffer (we must have at least 2 in the queue to avoid dropping chars though!)
+  unsigned char nextTail = (unsigned char)((ioTail+1) & IOBUFFERMASK);
+  if (ioHead!=ioTail && ioHead!=nextTail) {
+    // we can do this because we only read in main loop, and we're in an interrupt here
+    unsigned char lastHead = (unsigned char)((ioHead+IOBUFFERMASK) & IOBUFFERMASK); // one behind head
+    if (IOEVENTFLAGS_GETTYPE(ioBuffer[lastHead].flags) == channel &&
+        IOEVENTFLAGS_GETCHARS(ioBuffer[lastHead].flags) < IOEVENT_MAXCHARS) {
+      // last event was for this event type, and it has chars left
+      unsigned char c = (unsigned char)IOEVENTFLAGS_GETCHARS(ioBuffer[lastHead].flags);
+      ioBuffer[lastHead].data.chars[c] = charData;
+      IOEVENTFLAGS_SETCHARS(ioBuffer[lastHead].flags, c+1);
+      return;
+    }
+  }
+  // Make new buffer
+  unsigned char nextHead = (unsigned char)((ioHead+1) & IOBUFFERMASK);
+  if (ioTail == nextHead) {
+    jshIOEventOverflowed();
+    return; // queue full - dump this event!
+  }
+  ioBuffer[ioHead].flags = channel;
+  IOEVENTFLAGS_SETCHARS(ioBuffer[ioHead].flags, 1);
+  ioBuffer[ioHead].data.chars[0] = charData;
+  ioHead = nextHead;
+}
+
+void jshPushIOWatchEvent(IOEventFlags channel) {
+ JsSysTime time = jshGetSystemTime();
+ bool state = jshGetWatchedPinState(channel);
+
+/* // This is some simple debounce code - however it requires that the event
+   // is not taken out of ioBuffer by the main thread, which will require
+   // a bit of fiddling in jsinteractive.c. In fact it might be worth just
+   // doing debounce outside of the interrupt
+   if (true) { // debounce
+   // scan back and see if we have an event for this pin
+   unsigned char prevHead = ioHead;
+   while (prevHead!=ioTail && (IOEVENTFLAGS_GETTYPE(ioBuffer[prevHead].flags)!=channel))
+     prevHead = (unsigned char)((prevHead+IOBUFFERMASK) & IOBUFFERMASK); // step back
+   // if we have an event
+   if (prevHead!=ioTail  && (IOEVENTFLAGS_GETTYPE(ioBuffer[prevHead].flags)==channel)) {
+     // just use it (with the same timestamp)...
+     ioBuffer[prevHead].flags = channel | (state?EV_EXTI_IS_HIGH:0);
+     return;
+   }
+ }*/
+
+#ifdef USE_TRIGGER
+  if (trigHandleEXTI(channel | (state?EV_EXTI_IS_HIGH:0), time))
+    return;
+#endif
+#ifdef USE_CC3000
+  IOEvent event;
+  event.flags = channel;
+  if (!state && jshIsEventForPin(&event, WLAN_IRQ_PIN)) {
+    cc3000_irq_handler();
+  }
+#endif
+ // Otherwise add this event
+ jshPushIOEvent(channel | (state?EV_EXTI_IS_HIGH:0), time);
+}
+
+void jshPushIOEvent(IOEventFlags channel, JsSysTime time) {
+
+  unsigned char nextHead = (unsigned char)((ioHead+1) & IOBUFFERMASK);
+  if (ioTail == nextHead) {
+    jshIOEventOverflowed();
+    return; // queue full - dump this event!
+  }
+  ioBuffer[ioHead].flags = channel;
+  ioBuffer[ioHead].data.time = time;
+  ioHead = nextHead;
+}
+
+// returns true on success
+bool jshPopIOEvent(IOEvent *result) {
+  if (ioHead==ioTail) return false;
+  *result = ioBuffer[ioTail];
+  ioTail = (unsigned char)((ioTail+1) & IOBUFFERMASK);
+  return true;
+}
+
+bool jshHasEvents() {
+  return ioHead!=ioTail;
+}
+
+int jshGetEventsUsed() {
+  int spaceUsed = (ioHead >= ioTail) ? ((int)ioHead-(int)ioTail) : /*or rolled*/((int)ioHead+IOBUFFERMASK+1-(int)ioTail);
+  return spaceUsed;
+}
+
+bool jshHasEventSpaceForChars(int n) {
+  int spacesNeeded = 4 + (n/IOEVENT_MAXCHARS); // be sensible - leave a little spare
+  int spaceUsed = jshGetEventsUsed();
+  int spaceLeft = IOBUFFERMASK+1-spaceUsed;
+  return spaceLeft > spacesNeeded;
+}
+
+// ----------------------------------------------------------------------------
+//                                                                      DEVICES
+const char *jshGetDeviceString(IOEventFlags device) {
+  switch (device) {
+#ifdef USB
+  case EV_USBSERIAL: return "USB";
+#endif
+  case EV_SERIAL1: return "Serial1";
+  case EV_SERIAL2: return "Serial2";
+  case EV_SERIAL3: return "Serial3";
+#if USARTS>=4
+  case EV_SERIAL4: return "Serial4";
+#endif
+#if USARTS>=5
+  case EV_SERIAL5: return "Serial5";
+#endif
+#if USARTS>=6
+  case EV_SERIAL6: return "Serial6";
+#endif
+#if SPIS>=1
+  case EV_SPI1: return "SPI1";
+#endif
+#if SPIS>=2
+  case EV_SPI2: return "SPI2";
+#endif
+#if SPIS>=3
+  case EV_SPI3: return "SPI3";
+#endif
+#if I2CS>=1
+  case EV_I2C1: return "I2C1";
+#endif
+#if I2CS>=2
+  case EV_I2C2: return "I2C2";
+#endif
+#if I2CS>=3
+  case EV_I2C3: return "I2C3";
+#endif
+  default: return "";
+  }
+}
+
+IOEventFlags jshFromDeviceString(const char *device) {
+  if (device[0]=='U') {
+#ifdef USB
+    if (strcmp(device, "USB")==0) return EV_USBSERIAL;
+#endif
+  }
+  else if (device[0]=='S') {
+    if (device[1]=='e' && device[2]=='r' && device[3]=='i' && device[4]=='a' && device[5]=='l') {
+      if (strcmp(device, "Serial1")==0) return EV_SERIAL1;
+      if (strcmp(device, "Serial2")==0) return EV_SERIAL2;
+      if (strcmp(device, "Serial3")==0) return EV_SERIAL3;
+#if USARTS>=4
+      if (strcmp(device, "Serial4")==0) return EV_SERIAL4;
+#endif
+#if USARTS>=5
+      if (strcmp(device, "Serial5")==0) return EV_SERIAL5;
+#endif
+#if USARTS>=6
+      if (strcmp(device, "Serial6")==0) return EV_SERIAL6;
+#endif
+    }
+    if (device[1]=='P' && device[2]=='I') {
+#if SPIS>=1
+      if (strcmp(device, "SPI1")==0) return EV_SPI1;
+#endif
+#if SPIS>=2
+      if (strcmp(device, "SPI2")==0) return EV_SPI2;
+#endif
+#if SPIS>=3
+      if (strcmp(device, "SPI3")==0) return EV_SPI3;
+#endif
+    }
+  }
+  else if (device[0]=='I' && device[1]=='2' && device[2]=='C') {
+#if I2CS>=1
+    if (strcmp(device, "I2C1")==0) return EV_I2C1;
+#endif
+#if I2CS>=2
+    if (strcmp(device, "I2C2")==0) return EV_I2C2;
+#endif
+#if I2CS>=3
+    if (strcmp(device, "I2C3")==0) return EV_I2C3;
+#endif
+  }
+  return EV_NONE;
+}
+
+/// Set whether the host should transmit or not
+void jshSetFlowControlXON(IOEventFlags device, bool hostShouldTransmit) {
+}
+

+ 127 - 0
components/external/espruino/src/jsdevices.h

@@ -0,0 +1,127 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Common low-level device handling (Events, IO buffers)
+ * ----------------------------------------------------------------------------
+ */
+#ifndef JSDEVICES_H_
+#define JSDEVICES_H_
+
+#include "jsutils.h"
+#include "platform_config.h"
+
+typedef enum {
+ // device type
+ EV_NONE,
+ EV_EXTI0,
+ EV_EXTI1,
+ EV_EXTI2,
+ EV_EXTI3,
+ EV_EXTI4,
+ EV_EXTI5,
+ EV_EXTI6,
+ EV_EXTI7,
+ EV_EXTI8,
+ EV_EXTI9,
+ EV_EXTI10,
+ EV_EXTI11,
+ EV_EXTI12,
+ EV_EXTI13,
+ EV_EXTI14,
+ EV_EXTI15,
+ EV_EXTI_MAX = EV_EXTI15,
+ EV_USBSERIAL,
+ EV_SERIAL1,
+ EV_SERIAL2,
+ EV_SERIAL3,
+ EV_SERIAL4,
+ EV_SERIAL5,
+ EV_SERIAL6,
+ EV_SERIAL_MAX = EV_SERIAL6,
+ EV_SPI1,
+ EV_SPI2,
+ EV_SPI3,
+ EV_SPI_MAX = EV_SPI3,
+ EV_I2C1,
+ EV_I2C2,
+ EV_I2C3,
+ EV_I2C_MAX = EV_I2C3,
+ EV_DEVICE_MAX = EV_I2C_MAX,
+ // EV_DEVICE_MAX should not be >64 - see DEVICE_INITIALISED_FLAGS
+ // Also helps if we're under 32 so we can fit IOEventFlags into a byte
+ EV_TYPE_MASK = NEXT_POWER_2(EV_DEVICE_MAX) - 1,
+ EV_CHARS_MASK = 7 * NEXT_POWER_2(EV_DEVICE_MAX),
+ // -----------------------------------------
+ // if the pin we're watching is high, the handler sets this
+ EV_EXTI_IS_HIGH = NEXT_POWER_2(EV_DEVICE_MAX),
+} PACKED_FLAGS IOEventFlags;
+
+#define DEVICE_IS_USART(X) (((X)>=EV_USBSERIAL)&& ((X)<=EV_SERIAL_MAX))
+#define DEVICE_IS_SPI(X) (((X)>=EV_SPI1) || ((X)<=EV_SPI_MAX))
+#define DEVICE_IS_I2C(X) (((X)>=EV_I2C1) || ((X)<=EV_I2C_MAX))
+#define DEVICE_IS_EXTI(X) (((X)>=EV_EXTI0) || ((X)<=EV_EXTI_MAX))
+
+#define IOEVENTFLAGS_GETTYPE(X) ((X)&EV_TYPE_MASK)
+#define IOEVENTFLAGS_GETCHARS(X) ((((X)&EV_CHARS_MASK)>>5)+1)
+#define IOEVENTFLAGS_SETCHARS(X,CHARS) ((X)=(((X)&(IOEventFlags)~EV_CHARS_MASK) | (((CHARS)-1)<<5)))
+#define IOEVENT_MAXCHARS 8
+
+typedef union {
+  JsSysTime time; // time event occurred
+  char chars[IOEVENT_MAXCHARS];
+} PACKED_FLAGS IOEventData;
+
+// IO Events - these happen when a pin changes
+typedef struct IOEvent {
+  IOEventFlags flags; // where this came from, and # of chars in it
+  IOEventData data;
+} PACKED_FLAGS IOEvent;
+
+void jshPushIOEvent(IOEventFlags channel, JsSysTime time);
+void jshPushIOWatchEvent(IOEventFlags channel); // push an even when a pin changes state
+/// Push a single character event (for example USART RX)
+void jshPushIOCharEvent(IOEventFlags channel, char charData);
+/// Push many character events at once (for example USB RX)
+static inline void jshPushIOCharEvents(IOEventFlags channel, char *data, unsigned int count) {
+  // TODO: optimise me!
+  unsigned int i;
+  for (i=0;i<count;i++) jshPushIOCharEvent(channel, data[i]);
+}
+bool jshPopIOEvent(IOEvent *result); ///< returns true on success
+/// Do we have any events pending? Will jshPopIOEvent return true?
+bool jshHasEvents();
+
+/// How many event blocks are left? compare this to IOBUFFERMASK
+int jshGetEventsUsed();
+
+/// Do we have enough space for N characters?
+bool jshHasEventSpaceForChars(int n);
+
+const char *jshGetDeviceString(IOEventFlags device);
+IOEventFlags jshFromDeviceString(const char *device);
+
+// ----------------------------------------------------------------------------
+//                                                         DATA TRANSMIT BUFFER
+/// Queue a character for transmission
+void jshTransmit(IOEventFlags device, unsigned char data);
+/// Wait for transmit to finish
+void jshTransmitFlush();
+/// Clear everything from a device
+void jshTransmitClearDevice(IOEventFlags device);
+/// Do we have anything we need to send?
+bool jshHasTransmitData();
+/// Try and get a character for transmission - could just return -1 if nothing
+int jshGetCharToTransmit(IOEventFlags device);
+
+
+/// Set whether the host should transmit or not
+void jshSetFlowControlXON(IOEventFlags device, bool hostShouldTransmit);
+
+#endif /* JSDEVICES_H_ */

+ 223 - 0
components/external/espruino/src/jshardware.h

@@ -0,0 +1,223 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Hardware interface Layer
+ * NOTE: The definitions of these functions are inside:
+ *                                         targets/{target}/jshardware.c
+ * ----------------------------------------------------------------------------
+ */
+
+#ifndef JSHARDWARE_H_
+#define JSHARDWARE_H_
+
+#include "jsutils.h"
+#include "jsvar.h"
+#include "jsdevices.h"
+#include "jspin.h"
+#ifndef LINUX
+#include "jspininfo.h"
+#else
+#include <inttypes.h>
+#endif
+
+void jshInit();
+void jshKill();
+void jshIdle(); // stuff to do on idle
+
+/// Get this IC's serial number. Passed max # of chars and a pointer to write to. Returns # of chars
+int jshGetSerialNumber(unsigned char *data, int maxChars);
+
+bool jshIsUSBSERIALConnected(); // is the serial device connected?
+
+/// Get the system time (in ticks)
+JsSysTime jshGetSystemTime();
+/// Convert a time in Milliseconds to one in ticks
+JsSysTime jshGetTimeFromMilliseconds(JsVarFloat ms);
+/// Convert ticks to a time in Milliseconds
+JsVarFloat jshGetMillisecondsFromTime(JsSysTime time);
+
+// software IO functions...
+void jshInterruptOff();
+void jshInterruptOn();
+void jshDelayMicroseconds(int microsec);
+void jshPinSetValue(Pin pin, bool value);
+bool jshPinGetValue(Pin pin);
+// ------
+
+typedef enum {
+  JSHPINSTATE_UNDEFINED,
+  JSHPINSTATE_GPIO_OUT,
+  JSHPINSTATE_GPIO_OUT_OPENDRAIN,
+  JSHPINSTATE_GPIO_IN,
+  JSHPINSTATE_GPIO_IN_PULLUP,
+  JSHPINSTATE_GPIO_IN_PULLDOWN,
+  JSHPINSTATE_ADC_IN,
+  JSHPINSTATE_AF_OUT,
+  JSHPINSTATE_USART_IN,
+  JSHPINSTATE_USART_OUT,
+  JSHPINSTATE_DAC_OUT,
+  JSHPINSTATE_I2C,
+  JSHPINSTATE_MASK = NEXT_POWER_2(JSHPINSTATE_I2C)-1,
+
+  JSHPINSTATE_PIN_IS_ON = JSHPINSTATE_MASK+1,
+} PACKED_FLAGS JshPinState;
+
+#define JSHPINSTATE_IS_OUTPUT(state) ( \
+             state==JSHPINSTATE_GPIO_OUT ||               \
+             state==JSHPINSTATE_GPIO_OUT_OPENDRAIN ||     \
+             state==JSHPINSTATE_AF_OUT ||                 \
+             state==JSHPINSTATE_USART_OUT ||              \
+             state==JSHPINSTATE_DAC_OUT ||                \
+             state==JSHPINSTATE_I2C                       \
+)
+
+/// Is the pin state manual (has the user asked us explicitly to change it?)
+bool jshGetPinStateIsManual(Pin pin);
+/// Is the pin state manual (has the user asked us explicitly to change it?)
+void jshSetPinStateIsManual(Pin pin, bool manual);
+/// Set the pin state
+void jshPinSetState(Pin pin, JshPinState state);
+/** Get the pin state (only accurate for simple IO - won't return JSHPINSTATE_USART_OUT for instance).
+ * Note that you should use JSHPINSTATE_MASK as other flags may have been added */
+JshPinState jshPinGetState(Pin pin);
+
+
+bool jshPinInput(Pin pin);
+JsVarFloat jshPinAnalog(Pin pin);
+void jshPinOutput(Pin pin, bool value);
+void jshPinAnalogOutput(Pin pin, JsVarFloat value, JsVarFloat freq); // if freq<=0, the default is used
+void jshPinPulse(Pin pin, bool value, JsVarFloat time);
+void jshPinWatch(Pin pin, bool shouldWatch);
+/// returns false if timer queue was full...
+bool jshPinOutputAtTime(JsSysTime time, Pin pin, bool value);
+
+/** Check the pin associated with this EXTI - return true if it is a 1 */
+bool jshGetWatchedPinState(IOEventFlags device);
+
+bool jshIsEventForPin(IOEvent *event, Pin pin);
+
+/** Is the given device initialised? */
+bool jshIsDeviceInitialised(IOEventFlags device);
+
+
+#define DEFAULT_BAUD_RATE               9600
+#define DEFAULT_BYTESIZE                8
+#define DEFAULT_PARITY                  0
+#define DEFAULT_STOPBITS                1
+
+typedef struct {
+  int baudRate; // FIXME uint32_t ???
+  Pin pinRX;
+  Pin pinTX;
+  unsigned char bytesize;
+  unsigned char parity;
+  unsigned char stopbits;
+} PACKED_FLAGS JshUSARTInfo;
+
+static inline void jshUSARTInitInfo(JshUSARTInfo *inf) {
+  inf->baudRate = DEFAULT_BAUD_RATE;
+  inf->pinRX    = PIN_UNDEFINED;
+  inf->pinTX    = PIN_UNDEFINED;
+  inf->bytesize = DEFAULT_BYTESIZE;
+  inf->parity   = DEFAULT_PARITY; // PARITY_NONE = 0, PARITY_ODD = 1, PARITY_EVEN = 2 FIXME: enum?
+  inf->stopbits = DEFAULT_STOPBITS;
+}
+
+/** Set up a UART, if pins are -1 they will be guessed */
+void jshUSARTSetup(IOEventFlags device, JshUSARTInfo *inf);
+/** Kick a device into action (if required). For instance we may need
+ * to set up interrupts */
+void jshUSARTKick(IOEventFlags device);
+
+typedef enum {
+  SPIF_CPHA = 1,
+  SPIF_CPOL = 2,
+  SPIF_SPI_MODE_0 = 0,
+  SPIF_SPI_MODE_1 = SPIF_CPHA,
+  SPIF_SPI_MODE_2 = SPIF_CPOL,
+  SPIF_SPI_MODE_3 = SPIF_CPHA | SPIF_CPOL,
+  /* Mode   CPOL  CPHA
+        0   0     0
+        1   0     1
+        2   1     0
+        3   1     1
+    */
+
+} PACKED_FLAGS JshSPIFlags;
+
+typedef struct {
+  int baudRate;
+  Pin pinSCK;
+  Pin pinMISO;
+  Pin pinMOSI;
+  unsigned char spiMode;
+} PACKED_FLAGS JshSPIInfo;
+static inline void jshSPIInitInfo(JshSPIInfo *inf) {
+  inf->baudRate = 1000000;
+  inf->pinSCK = PIN_UNDEFINED;
+  inf->pinMISO = PIN_UNDEFINED;
+  inf->pinMOSI = PIN_UNDEFINED;
+  inf->spiMode = SPIF_SPI_MODE_0;
+}
+
+/** Set up SPI, if pins are -1 they will be guessed */
+void jshSPISetup(IOEventFlags device, JshSPIInfo *inf);
+/** Send data through the given SPI device (if data>=0), and return the result
+ * of the previous send (or -1). If data<0, no data is sent and the function
+ * waits for data to be returned */
+int jshSPISend(IOEventFlags device, int data);
+/** Send 16 bit data through the given SPI device. */
+void jshSPISend16(IOEventFlags device, int data);
+/** Set whether to send 16 bits or 8 over SPI */
+void jshSPISet16(IOEventFlags device, bool is16);
+
+typedef struct {
+  Pin pinSCL;
+  Pin pinSDA;
+  char slaveAddr; // or -1 if it is master!
+  // speed? 100khz std
+  // timeout?
+} PACKED_FLAGS JshI2CInfo;
+static inline void jshI2CInitInfo(JshI2CInfo *inf) {
+  inf->pinSCL = PIN_UNDEFINED;
+  inf->pinSDA = PIN_UNDEFINED;
+  inf->slaveAddr = (char)-1; // master
+}
+/** Set up I2C, if pins are -1 they will be guessed */
+void jshI2CSetup(IOEventFlags device, JshI2CInfo *inf);
+/** Addresses are 7 bit - that is, between 0 and 0x7F */
+void jshI2CWrite(IOEventFlags device, unsigned char address, int nBytes, const unsigned char *data);
+void jshI2CRead(IOEventFlags device, unsigned char address, int nBytes, unsigned char *data);
+
+
+/// Save contents of JsVars into Flash
+void jshSaveToFlash();
+/// Load contents of JsVars from Flash
+void jshLoadFromFlash();
+/// Returns true if flash contains something useful
+bool jshFlashContainsCode();
+
+/// Enter simple sleep mode (can be woken up by interrupts). Returns true on success
+bool jshSleep(JsSysTime timeUntilWake);
+
+// ---------------------------------------------- LOW LEVEL
+#ifdef ARM
+// ----------------------------------------------------------------------------
+//                                                                      SYSTICK
+// On SYSTick interrupt, call this
+void jshDoSysTick();
+#ifdef USB
+// Kick the USB SysTick watchdog - we need this to see if we have disconnected or not
+void jshKickUSBWatchdog();
+#endif
+
+#endif // ARM
+
+#endif /* JSHARDWARE_H_ */

+ 1686 - 0
components/external/espruino/src/jsinteractive.c

@@ -0,0 +1,1686 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Interactive Shell implementation
+ * ----------------------------------------------------------------------------
+ */
+#include "jsutils.h"
+#include "jsinteractive.h"
+#include "jshardware.h"
+#include "jswrapper.h"
+#include "jswrap_json.h"
+#include "jswrap_io.h"
+#ifdef USE_NET
+#include "httpserver.h"
+#endif
+#ifdef USE_LCD_FSMC
+#include "graphics.h"
+#include "lcd_fsmc.h"
+#endif
+
+#ifdef ARM
+#define CHAR_DELETE_SEND 0x08
+#else
+#define CHAR_DELETE_SEND '\b'
+#endif
+
+// ----------------------------------------------------------------------------
+typedef struct TimerState {
+  JsSysTime time;
+  JsSysTime interval;
+  bool recurring;
+  JsVarRef callback; // a calback, or 0
+} TimerState;
+
+typedef enum {
+ IS_NONE,
+ IS_HAD_R,
+ IS_HAD_27,
+ IS_HAD_27_79,
+ IS_HAD_27_91,
+ IS_HAD_27_91_49,
+ IS_HAD_27_91_51,
+ IS_HAD_27_91_52,
+ IS_HAD_27_91_53,
+ IS_HAD_27_91_54,
+} InputState;
+
+TODOFlags todo = TODO_NOTHING;
+JsVar *events = 0; // Array of events to execute
+JsVarRef timerArray = 0; // Linked List of timers to check and run
+JsVarRef watchArray = 0; // Linked List of input watches to check and run
+// ----------------------------------------------------------------------------
+IOEventFlags consoleDevice = DEFAULT_CONSOLE_DEVICE; ///< The console device for user interaction
+Pin pinBusyIndicator = DEFAULT_BUSY_PIN_INDICATOR;
+Pin pinSleepIndicator = DEFAULT_SLEEP_PIN_INDICATOR;
+bool echo;                  ///< do we provide any user feedback?
+bool allowDeepSleep;
+// ----------------------------------------------------------------------------
+JsParse p; ///< The parser we're using for interactiveness
+JsVar *inputLine = 0; ///< The current input line
+bool inputLineRemoved = false;
+int inputCursorPos = 0; ///< The position of the cursor in the input line
+InputState inputState = 0; ///< state for dealing with cursor keys
+bool hasUsedHistory = false; ///< Used to speed up - if we were cycling through history and then edit, we need to copy the string
+unsigned char loopsIdling; ///< How many times around the loop have we been entirely idle?
+bool interruptedDuringEvent; ///< Were we interrupted while executing an event? If so may want to clear timers
+// ----------------------------------------------------------------------------
+
+IOEventFlags jsiGetDeviceFromClass(JsVar *class) {
+  // Built-in classes have their object data set to the device name
+  return jshFromDeviceString(class->varData.str);
+}
+
+JsVar *jsiGetClassNameFromDevice(IOEventFlags device) {
+  const char *deviceName = jshGetDeviceString(device);
+  return jsvFindChildFromString(p.root, deviceName, false);
+}
+
+static inline bool jsiShowInputLine() {
+  return echo && !inputLineRemoved;
+}
+
+/// Change the console to a new location
+void jsiSetConsoleDevice(IOEventFlags device) {
+  if (device == consoleDevice) return;
+
+  if (!jshIsDeviceInitialised(device)) {
+    JshUSARTInfo inf;
+    jshUSARTInitInfo(&inf);
+    jshUSARTSetup(device, &inf);
+  }
+
+  jsiConsoleRemoveInputLine();
+  if (echo) { // intentionally not using jsiShowInputLine()
+    jsiConsolePrint("Console Moved to ");
+    jsiConsolePrint(jshGetDeviceString(device));
+    jsiConsolePrint("\n");
+  }
+  IOEventFlags oldDevice = consoleDevice;
+  consoleDevice = device;
+  if (echo) { // intentionally not using jsiShowInputLine()
+    jsiConsolePrint("Console Moved from ");
+    jsiConsolePrint(jshGetDeviceString(oldDevice));
+    jsiConsolePrint("\n");
+  }
+}
+
+/// Get the device that the console is currently on
+IOEventFlags jsiGetConsoleDevice() {
+  return consoleDevice;
+}
+
+void jsiConsolePrintChar(char data) {
+  jshTransmit(consoleDevice, (unsigned char)data);
+}
+
+void jsiConsolePrint(const char *str) {
+  while (*str) {
+       if (*str == '\n') jsiConsolePrintChar('\r');
+       jsiConsolePrintChar(*(str++));
+  }
+}
+
+void jsiConsolePrintf(const char *fmt, ...) {
+  va_list argp;
+  va_start(argp, fmt);
+  vcbprintf((vcbprintf_callback)jsiConsolePrint,0, fmt, argp);
+  va_end(argp);
+}
+
+
+void jsiConsolePrintInt(JsVarInt d) {
+    char buf[32];
+    itoa(d, buf, 10);
+    jsiConsolePrint(buf);
+}
+
+
+/// Print the contents of a string var from a character position until end of line (adding an extra ' ' to delete a character if there was one)
+void jsiConsolePrintStringVarUntilEOL(JsVar *v, int fromCharacter, bool andBackup) {
+  int chars = 0;
+  JsvStringIterator it;
+  jsvStringIteratorNew(&it, v, fromCharacter);
+  while (jsvStringIteratorHasChar(&it)) {
+    char ch = jsvStringIteratorGetChar(&it);
+    if (ch == '\n') break;
+    jsiConsolePrintChar(ch);
+    chars++;
+    jsvStringIteratorNext(&it);
+  }
+  jsvStringIteratorFree(&it);
+  if (andBackup) {
+    jsiConsolePrintChar(' ');chars++;
+    while (chars--) jsiConsolePrintChar(0x08); //delete
+  }
+}
+
+/** Print the contents of a string var - directly - starting from the given character, and
+ * using newLineCh to prefix new lines (if it is not 0). */
+void jsiConsolePrintStringVarWithNewLineChar(JsVar *v, int fromCharacter, char newLineCh) {
+  JsvStringIterator it;
+  jsvStringIteratorNew(&it, v, fromCharacter);
+  while (jsvStringIteratorHasChar(&it)) {
+    char ch = jsvStringIteratorGetChar(&it);
+    if (ch == '\n') jsiConsolePrintChar('\r');
+    jsiConsolePrintChar(ch);
+    if (ch == '\n' && newLineCh) jsiConsolePrintChar(newLineCh);
+    jsvStringIteratorNext(&it);
+  }
+  jsvStringIteratorFree(&it);
+}
+
+/// Print the contents of a string var - directly
+void jsiConsolePrintStringVar(JsVar *v) {
+  jsiConsolePrintStringVarWithNewLineChar(v,0,0);
+}
+
+/** Assuming that we are at the end of the string, this backs up
+ * and deletes it */
+void jsiConsoleEraseStringVarBackwards(JsVar *v) {
+  assert(jsvHasCharacterData(v));
+
+  int line, lines = jsvGetLinesInString(v);
+  for (line=lines;line>0;line--) {
+    int i,chars = jsvGetCharsOnLine(v, line);
+    if (line==lines) {
+      for (i=0;i<chars;i++) jsiConsolePrintChar(0x08); // move cursor back
+    }
+    for (i=0;i<chars;i++) jsiConsolePrintChar(' '); // move cursor forwards and wipe out
+    for (i=0;i<chars;i++) jsiConsolePrintChar(0x08); // move cursor back
+    if (line>1) { 
+      // clear the character before - this would have had a colon
+      jsiConsolePrintChar(0x08);
+      jsiConsolePrintChar(' ');
+      // move cursor up      
+      jsiConsolePrintChar(27);
+      jsiConsolePrintChar(91); 
+      jsiConsolePrintChar(65);
+    }
+  }
+}
+
+/** Assuming that we are at fromCharacter position in the string var,
+ * erase everything that comes AFTER and return the cursor to 'fromCharacter'
+ * On newlines, if erasePrevCharacter, we remove the character before too. */
+void jsiConsoleEraseStringVarFrom(JsVar *v, int fromCharacter, bool erasePrevCharacter) {
+  assert(jsvHasCharacterData(v));
+  int cursorLine, cursorCol;
+  jsvGetLineAndCol(v, fromCharacter, &cursorLine, &cursorCol);
+  // delete contents of current line
+  int i, chars = jsvGetCharsOnLine(v, cursorLine);
+  for (i=cursorCol;i<=chars;i++) jsiConsolePrintChar(' ');
+  for (i=0;i<chars;i++) jsiConsolePrintChar(0x08); // move cursor back
+
+  int line, lines = jsvGetLinesInString(v);
+  for (line=cursorLine+1;line<=lines;line++) {
+    jsiConsolePrintChar(27);
+    jsiConsolePrintChar(91);
+    jsiConsolePrintChar(66); // move down
+    chars = jsvGetCharsOnLine(v, line);
+    for (i=0;i<chars;i++) jsiConsolePrintChar(' '); // move cursor forwards and wipe out
+    for (i=0;i<chars;i++) jsiConsolePrintChar(0x08); // move cursor back
+    if (erasePrevCharacter) {
+      jsiConsolePrintChar(0x08); // move cursor back
+      jsiConsolePrintChar(' ');
+    }
+  }
+  // move the cursor back up
+  for (line=cursorLine+1;line<=lines;line++) {
+    jsiConsolePrintChar(27);
+    jsiConsolePrintChar(91);
+    jsiConsolePrintChar(65);
+  }
+  // move the cursor forwards
+  for (i=1;i<cursorCol;i++) {
+    jsiConsolePrintChar(27);
+    jsiConsolePrintChar(91);
+    jsiConsolePrintChar(67);
+  }
+}
+
+void jsiMoveCursor(int oldX, int oldY, int newX, int newY) {
+  // see http://www.termsys.demon.co.uk/vtansi.htm - we could do this better
+  // move cursor
+  while (oldX < newX) {
+    jsiConsolePrintChar(27);
+    jsiConsolePrintChar(91);
+    jsiConsolePrintChar(67);
+    oldX++;
+  }
+  while (oldX > newX) {
+    jsiConsolePrintChar(27);
+    jsiConsolePrintChar(91);
+    jsiConsolePrintChar(68);
+    oldX--;
+  }
+  while (oldY < newY) {
+    jsiConsolePrintChar(27);
+    jsiConsolePrintChar(91);
+    jsiConsolePrintChar(66);
+    oldY++;
+  }
+  while (oldY > newY) {
+    jsiConsolePrintChar(27);
+    jsiConsolePrintChar(91);
+    jsiConsolePrintChar(65);
+    oldY--;
+  }
+}
+
+void jsiMoveCursorChar(JsVar *v, int fromCharacter, int toCharacter) {
+  if (fromCharacter==toCharacter) return;
+  int oldX, oldY;
+  jsvGetLineAndCol(v, fromCharacter, &oldY, &oldX);
+  int newX, newY;
+  jsvGetLineAndCol(v, toCharacter, &newY, &newX);
+  jsiMoveCursor(oldX, oldY, newX, newY);
+}
+
+/// If the input line was shown in the console, remove it
+void jsiConsoleRemoveInputLine() {
+  if (!inputLineRemoved) {
+    inputLineRemoved = true;
+    if (echo && inputLine) { // intentionally not using jsiShowInputLine()
+      jsiMoveCursorChar(inputLine, inputCursorPos, 0);
+      jsiConsoleEraseStringVarFrom(inputLine, 0, true);
+      jsiConsolePrintChar(0x08); // go back to start of line
+    }
+  }
+}
+
+/// If the input line has been removed, return it
+void jsiReturnInputLine() {
+  if (inputLineRemoved) {
+    inputLineRemoved = false;
+    if (echo) { // intentionally not using jsiShowInputLine()
+      jsiConsolePrintChar('\r'); // carriage return
+      jsiConsolePrintChar('>');
+      jsiConsolePrintStringVarWithNewLineChar(inputLine, 0, ':');
+      jsiMoveCursorChar(inputLine, (int)jsvGetStringLength(inputLine), inputCursorPos);
+    }
+  }
+}
+
+void jsiConsolePrintPosition(struct JsLex *lex, int tokenPos) {
+  int line,col;
+  jsvGetLineAndCol(lex->sourceVar, tokenPos, &line, &col);
+  jsiConsolePrintf("line %d col %d\n",line,col);
+}
+
+void jsiConsolePrintTokenLineMarker(struct JsLex *lex, int tokenPos) {
+  int line = 1,col = 1;
+  jsvGetLineAndCol(lex->sourceVar, tokenPos, &line, &col);
+  int startOfLine = jsvGetIndexFromLineAndCol(lex->sourceVar, line, 1);
+  jsiConsolePrintStringVarUntilEOL(lex->sourceVar, startOfLine, false);
+  jsiConsolePrint("\n");
+  while (col-- > 1) jsiConsolePrintChar(' ');
+  jsiConsolePrintChar('^');
+  jsiConsolePrint("\n");
+}
+
+
+/// Print the contents of a string var to a device - directly
+void jsiTransmitStringVar(IOEventFlags device, JsVar *v) {
+  JsvStringIterator it;
+  jsvStringIteratorNew(&it, v, 0);
+  while (jsvStringIteratorHasChar(&it)) {
+    char ch = jsvStringIteratorGetChar(&it);
+    jshTransmit(device, (unsigned char)ch);
+    jsvStringIteratorNext(&it);
+  }
+  jsvStringIteratorFree(&it);
+}
+
+void jsiSetBusy(JsiBusyDevice device, bool isBusy) {
+  static JsiBusyDevice business = 0;
+
+  if (isBusy)
+    business |= device;
+  else
+    business &= (JsiBusyDevice)~device;
+
+  if (pinBusyIndicator != PIN_UNDEFINED)
+    jshPinOutput(pinBusyIndicator, business!=0);
+}
+
+void jsiSetSleep(bool isSleep) {
+  if (pinSleepIndicator != PIN_UNDEFINED)
+    jshPinOutput(pinSleepIndicator, isSleep);
+}
+
+static JsVarRef _jsiInitNamedArray(const char *name) {
+  JsVar *arrayName = jsvFindChildFromString(p.root, name, true);
+  if (!arrayName) return 0; // out of memory
+  if (!arrayName->firstChild) {
+    JsVar *array = jsvNewWithFlags(JSV_ARRAY);
+    if (!array) { // out of memory
+      jsvUnLock(arrayName);
+      return 0;
+    }
+
+    arrayName->firstChild = jsvGetRef(jsvRef(array));
+    jsvUnLock(array);
+  }
+  JsVarRef arrayRef = jsvRefRef(arrayName->firstChild);
+  jsvUnLock(arrayName);
+  return arrayRef;
+}
+
+// Used when recovering after being flashed
+// 'claim' anything we are using
+void jsiSoftInit() {
+#ifdef USE_NET
+  httpInit();
+#endif
+#ifdef USE_LCD_FSMC
+  JsVar *parent = jspNewObject(jsiGetParser(), "LCD", "Graphics");
+  if (parent) {
+    JsVar *parentObj = jsvSkipName(parent);
+    JsGraphics gfx;
+    graphicsStructInit(&gfx);
+    gfx.data.type = JSGRAPHICSTYPE_FSMC;
+    gfx.graphicsVar = parentObj;
+    gfx.data.width = 320;
+    gfx.data.height = 240;
+    gfx.data.bpp = 16;
+    lcdInit_FSMC(&gfx);
+    lcdSetCallbacks_FSMC(&gfx);
+    graphicsSplash(&gfx);
+    graphicsSetVar(&gfx);
+    jsvUnLock(parentObj);
+    jsvUnLock(parent);
+  }
+#endif
+
+
+  events = jsvNewWithFlags(JSV_ARRAY);
+  inputLine = jsvNewFromEmptyString();
+  inputCursorPos = 0;
+  allowDeepSleep = false;
+
+  // Load timer/watch arrays
+  timerArray = _jsiInitNamedArray(JSI_TIMERS_NAME);
+  watchArray = _jsiInitNamedArray(JSI_WATCHES_NAME);
+
+  // Now run initialisation code
+  JsVar *initName = jsvFindChildFromString(p.root, JSI_INIT_CODE_NAME, false);
+  if (initName && initName->firstChild) {
+    //jsiConsolePrint("Running initialisation code...\n");
+    JsVar *initCode = jsvLock(initName->firstChild);
+    jsvUnLock(jspEvaluateVar(&p, initCode, 0));
+    jsvUnLock(initCode);
+    jsvRemoveChild(p.root, initName);
+  }
+  jsvUnLock(initName);
+
+  // Check any existing watches and set up interrupts for them
+  if (watchArray) {
+    JsVar *watchArrayPtr = jsvLock(watchArray);
+    JsVarRef watch = watchArrayPtr->firstChild;
+    while (watch) {
+      JsVar *watchNamePtr = jsvLock(watch);
+      JsVar *watchPin = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
+      jshPinWatch(jshGetPinFromVar(watchPin), true);
+      jsvUnLock(watchPin);
+      watch = watchNamePtr->nextSibling;
+      jsvUnLock(watchNamePtr);
+    }
+    jsvUnLock(watchArrayPtr);
+  }
+
+  // Check any existing timers and try and set time correctly
+  if (timerArray) {
+    JsSysTime currentTime = jshGetSystemTime();
+    JsVar *timerArrayPtr = jsvLock(timerArray);
+    JsVarRef timer = timerArrayPtr->firstChild;
+    while (timer) {
+      JsVar *timerNamePtr = jsvLock(timer);
+      JsVar *timerTime = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "time", false));
+      JsVarFloat interval = jsvGetFloatAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "interval", false)));
+      jsvSetInteger(timerTime, currentTime + jshGetTimeFromMilliseconds(interval));
+      jsvUnLock(timerTime);
+      timer = timerNamePtr->nextSibling;
+      jsvUnLock(timerNamePtr);
+    }
+    jsvUnLock(timerArrayPtr);
+  }
+  // And look for onInit function
+  JsVar *onInit = jsvFindChildFromString(p.root, JSI_ONINIT_NAME, false);
+  if (onInit && onInit->firstChild) {
+    if (echo) jsiConsolePrint("Running onInit()...\n");
+    JsVar *func = jsvSkipName(onInit);
+    if (jsvIsFunction(func))
+      jspExecuteFunction(&p, func, 0, 0, (JsVar**)0);
+    else if (jsvIsString(func))
+      jsvUnLock(jspEvaluateVar(&p, func, 0));
+    else
+      jsError("onInit is not a Function or a String");
+  }
+  jsvUnLock(onInit);
+}
+
+/** Append the code required to initialise a serial port to this string */
+void jsiAppendSerialInitialisation(JsVar *str, const char *serialName, bool addCallbacks) {
+  JsVar *serialVar = jsvObjectGetChild(p.root, serialName, 0);
+  if (serialVar) {
+    if (addCallbacks) {
+      JsVar *onData = jsvSkipOneNameAndUnLock(jsvFindChildFromString(serialVar, USART_CALLBACK_NAME, false));
+      if (onData) {
+        JsVar *onDataStr = jsvAsString(onData, true/*unlock*/);
+        jsvAppendString(str, serialName);
+        jsvAppendString(str, ".onData(");
+        jsvAppendStringVarComplete(str, onDataStr);
+        jsvAppendString(str, ");\n");
+        jsvUnLock(onDataStr);
+      }
+    }
+    JsVar *baud = jsvObjectGetChild(serialVar, USART_BAUDRATE_NAME, 0);
+    JsVar *options = jsvObjectGetChild(serialVar, DEVICE_OPTIONS_NAME, 0);
+    if (baud || options) {
+      JsVarInt baudrate = jsvGetInteger(baud);
+      if (baudrate <= 0) baudrate = DEFAULT_BAUD_RATE;
+      jsvAppendPrintf(str, "%s.setup(%d", serialName, baudrate);
+      if (jsvIsObject(options)) {
+        jsvAppendString(str, ", ");
+        jsfGetJSON(options, str);
+      }
+      jsvAppendString(str, ");\n");
+    }
+    jsvUnLock(baud);
+    jsvUnLock(options);
+    jsvUnLock(serialVar);
+  }
+}
+
+/** Append the code required to initialise a SPI port to this string */
+void jsiAppendDeviceInitialisation(JsVar *str, const char *deviceName) {
+  JsVar *deviceVar = jsvObjectGetChild(p.root, deviceName, 0);
+  if (deviceVar) {
+    JsVar *options = jsvObjectGetChild(deviceVar, DEVICE_OPTIONS_NAME, 0);
+    if (options) {
+      jsvAppendString(str, deviceName);
+      jsvAppendString(str, ".setup(");
+      if (jsvIsObject(options)) {
+        jsfGetJSON(options, str);
+      }
+      jsvAppendString(str, ");\n");
+    }
+    jsvUnLock(options);
+    jsvUnLock(deviceVar);
+  }
+}
+
+/** Append all the code required to initialise hardware to this string */
+void jsiAppendHardwareInitialisation(JsVar *str, bool addCallbacks) {
+  if (!echo) jsvAppendString(str, "echo(0);");
+  if (pinBusyIndicator != DEFAULT_BUSY_PIN_INDICATOR) {
+    jsvAppendPrintf(str, "setBusyIndicator(%p);\n", pinBusyIndicator);
+  }
+  if (pinSleepIndicator != DEFAULT_BUSY_PIN_INDICATOR) {
+    jsvAppendPrintf(str, "setSleepIndicator(%p);\n", pinSleepIndicator);
+  }
+  if (allowDeepSleep) {
+      jsvAppendPrintf(str, "setDeepSleep(1);\n");
+    }
+
+  jsiAppendSerialInitialisation(str, "USB", addCallbacks);
+  int i;
+  for (i=0;i<USARTS;i++)
+    jsiAppendSerialInitialisation(str, jshGetDeviceString(EV_SERIAL1+i), addCallbacks);
+  for (i=0;i<SPIS;i++)
+    jsiAppendDeviceInitialisation(str, jshGetDeviceString(EV_SPI1+i));
+  for (i=0;i<I2CS;i++)
+    jsiAppendDeviceInitialisation(str, jshGetDeviceString(EV_I2C1+i));
+  // pins
+  Pin pin;
+  for (pin=0;jshIsPinValid(pin) && pin<255;pin++) {
+    if (IS_PIN_USED_INTERNALLY(pin)) continue;
+    JshPinState state = jshPinGetState(pin);
+    JshPinState statem = state&JSHPINSTATE_MASK;
+    if (statem == JSHPINSTATE_GPIO_OUT) {
+      bool isOn = (state&JSHPINSTATE_PIN_IS_ON)!=0;
+      if (!isOn && IS_PIN_A_LED(pin)) continue;
+      jsvAppendPrintf(str, "digitalWrite(%p,%d);\n",pin,isOn?1:0);
+    } else if (/*statem == JSHPINSTATE_GPIO_IN ||*/statem == JSHPINSTATE_GPIO_IN_PULLUP || statem == JSHPINSTATE_GPIO_IN_PULLDOWN) {
+      // don't bother with normal inputs, as they come up in this state (ish) anyway
+      const char *s = "";
+      if (statem == JSHPINSTATE_GPIO_IN_PULLUP) s="_pullup";
+      if (statem == JSHPINSTATE_GPIO_IN_PULLDOWN) s="_pulldown";
+      jsvAppendPrintf(str, "pinMode(%p,input%s);\n",pin,s);
+    }
+  }
+}
+
+// Used when shutting down before flashing
+// 'release' anything we are using, but ensure that it doesn't get freed
+void jsiSoftKill() {
+  jsvUnLock(inputLine);
+  inputLine=0;
+  inputCursorPos = 0;
+
+  // Unref Watches/etc
+  if (events) {
+    jsvUnLock(events);
+    events=0;
+  }
+  if (timerArray) {
+    jsvUnRefRef(timerArray);
+    timerArray=0;
+  }
+  if (watchArray) {
+    // Check any existing watches and disable interrupts for them
+    JsVar *watchArrayPtr = jsvLock(watchArray);
+    JsVarRef watch = watchArrayPtr->firstChild;
+    while (watch) {
+      JsVar *watchNamePtr = jsvLock(watch);
+      JsVar *watchPin = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
+      jshPinWatch(jshGetPinFromVar(watchPin), false);
+      jsvUnLock(watchPin);
+      watch = watchNamePtr->nextSibling;
+      jsvUnLock(watchNamePtr);
+    }
+    jsvUnLock(watchArrayPtr);
+
+    jsvUnRefRef(watchArray);
+    watchArray=0;
+  }
+  // Save initialisation information
+  JsVar *initName = jsvFindChildFromString(p.root, JSI_INIT_CODE_NAME, true);
+  if (initName->firstChild) {
+    jsvUnRefRef(initName->firstChild); 
+    initName->firstChild = 0;
+  }
+  JsVar *initCode = jsvNewFromEmptyString();
+  if (initCode) { // out of memory
+    initName->firstChild = jsvGetRef(jsvRef(initCode));
+    jsiAppendHardwareInitialisation(initCode, false);
+    jsvUnLock(initCode);
+  }
+  jsvUnLock(initName);
+
+#ifdef USE_NET
+  httpKill();
+#endif
+}
+
+void jsiInit(bool autoLoad) {
+  jsvInit();
+  jspInit(&p);
+
+#ifdef USE_LCD
+ lcdInit_Main(&LCD);
+#ifdef USE_LCD_ILI9325
+  lcdInit_FSMC_ILI9325(&LCD, LCD_WIDTH, LCD_HEIGHT);
+#endif
+#ifdef USE_LCD_SDL
+  lcdInit_SDL(&LCD, LCD_WIDTH, LCD_HEIGHT);
+#endif
+  lcdClear(0);
+  lcdSplash();
+#endif
+
+
+
+  /*for (i=0;i<IOPINS;i++)
+     ioPinState[i].callbacks = 0;*/
+
+  // Set state
+  interruptedDuringEvent = false;
+  // Set defaults
+  echo = true;
+  consoleDevice = DEFAULT_CONSOLE_DEVICE;
+  pinBusyIndicator = DEFAULT_BUSY_PIN_INDICATOR;
+  if (jshIsUSBSERIALConnected())
+    consoleDevice = EV_USBSERIAL;
+
+  /* If flash contains any code, then we should
+     Try and load from it... */
+  if (autoLoad && jshFlashContainsCode()) {
+    jspSoftKill(&p);
+    jsvSoftKill();
+    jshLoadFromFlash();
+    jsvSoftInit();
+    jspSoftInit(&p);
+  }
+  //jsvTrace(jsvGetRef(jsiGetParser()->root), 0)
+
+  // Softinit may run initialisation code that will overwrite defaults
+  jsiSoftInit();
+
+  if (echo) { // intentionally not using jsiShowInputLine()
+#ifndef LINUX
+    // set up terminal to avoid word wrap
+    jsiConsolePrint("\e[?7l");
+#endif
+    // rectangles @ http://www.network-science.de/ascii/
+    jsiConsolePrint("\n"
+              "   _____                 _ \n"
+              "  |   __|___ ___ ___ _ _|_|___ ___ \n"
+              "  |   __|_ -| . |  _| | | |   | . |\n"
+              "  |_____|___|  _|_| |___|_|_|_|___|");
+    jsiConsolePrint("\n"
+              "            |_|   http://www.espruino.com\n"
+              "  "JS_VERSION" Copyright 2013 Gordon Williams\n"
+              "-------------------------------------------\n"
+              "                       KickStarter Version\n"
+              "-------------------------------------------\n");
+    jsiConsolePrint("\n>");
+  }
+}
+
+
+
+void jsiKill() {
+  jsiSoftKill();
+
+  jspKill(&p);
+  jsvKill();
+}
+
+int jsiCountBracketsInInput() {
+  int brackets = 0;
+
+  JsLex lex;
+  jslInit(&lex, inputLine);
+  while (lex.tk!=LEX_EOF && lex.tk!=LEX_UNFINISHED_COMMENT) {
+    if (lex.tk=='{' || lex.tk=='[' || lex.tk=='(') brackets++;
+    if (lex.tk=='}' || lex.tk==']' || lex.tk==')') brackets--;
+    if (brackets<0) break; // closing bracket before opening!
+    jslGetNextToken(&lex);
+  }
+  if (lex.tk==LEX_UNFINISHED_COMMENT)
+    brackets=1000; // if there's an unfinished comment, we're in the middle of something
+  jslKill(&lex);
+
+  return brackets;
+} 
+
+/// Tries to get rid of some memory (by clearing command history). Returns true if it got rid of something, false if it didn't.
+bool jsiFreeMoreMemory() {
+  JsVar *history = jsvObjectGetChild(p.root, JSI_HISTORY_NAME, 0);
+  if (!history) return 0;
+  JsVar *item = jsvArrayPopFirst(history);
+  bool freed = item!=0;
+  jsvUnLock(item);
+  jsvUnLock(history);
+  // TODO: could also free the array structure?
+  return freed;
+}
+
+// Add a new line to the command history
+void jsiHistoryAddLine(JsVar *newLine) {
+  if (!newLine || jsvGetStringLength(newLine)==0) return;
+  JsVar *history = jsvFindChildFromString(p.root, JSI_HISTORY_NAME, true);
+  if (!history) return; // out of memory
+  // ensure we actually have the history array
+  if (!history->firstChild) {
+    JsVar *arr = jsvNewWithFlags(JSV_ARRAY);
+    if (!arr) {// out of memory
+      jsvUnLock(history);
+      return;
+    }
+    history->firstChild = jsvGetRef(jsvRef(arr));
+    jsvUnLock(arr);
+  }
+  history = jsvSkipNameAndUnLock(history);
+  // if it was already in history, remove it - we'll put it back in front
+  JsVar *alreadyInHistory = jsvGetArrayIndexOf(history, newLine, false/*not exact*/);
+  if (alreadyInHistory) {
+    jsvRemoveChild(history, alreadyInHistory);
+    jsvUnLock(alreadyInHistory);
+  }
+  // put it back in front
+  jsvArrayPush(history, newLine);
+  jsvUnLock(history);
+}
+
+JsVar *jsiGetHistoryLine(bool previous /* next if false */) {
+  JsVar *history = jsvObjectGetChild(p.root, JSI_HISTORY_NAME, 0);
+  JsVar *historyLine = 0;
+  if (history) {
+    JsVar *idx = jsvGetArrayIndexOf(history, inputLine, true/*exact*/); // get index of current line
+    if (idx) {
+      if (previous && idx->prevSibling) {
+        historyLine = jsvSkipNameAndUnLock(jsvLock(idx->prevSibling));
+      } else if (!previous && idx->nextSibling) {
+        historyLine = jsvSkipNameAndUnLock(jsvLock(idx->nextSibling));
+      }
+      jsvUnLock(idx);
+    } else {
+      if (previous) historyLine = jsvSkipNameAndUnLock(jsvArrayGetLast(history));
+      // if next, we weren't using history so couldn't go forwards
+    }
+    
+    jsvUnLock(history);
+  }
+  return historyLine;
+}
+
+bool jsiIsInHistory(JsVar *line) {
+  JsVar *history = jsvObjectGetChild(p.root, JSI_HISTORY_NAME, 0);
+  if (!history) return false;
+  JsVar *historyFound = jsvGetArrayIndexOf(history, line, true/*exact*/);
+  bool inHistory = historyFound!=0;
+  jsvUnLock(historyFound);
+  jsvUnLock(history);
+  return inHistory;
+}
+
+void jsiReplaceInputLine(JsVar *newLine) {
+  if (jsiShowInputLine()) {
+    int oldLen =  (int)jsvGetStringLength(inputLine);
+    jsiMoveCursorChar(inputLine, inputCursorPos, oldLen); // move cursor to end
+    jsiConsoleEraseStringVarBackwards(inputLine);
+    jsiConsolePrintStringVarWithNewLineChar(newLine,0,':');
+  }
+  jsvUnLock(inputLine);
+  inputLine = jsvLockAgain(newLine);
+  inputCursorPos = (int)jsvGetStringLength(inputLine);
+}
+
+void jsiChangeToHistory(bool previous) {
+  JsVar *nextHistory = jsiGetHistoryLine(previous);
+  if (nextHistory) {
+    jsiReplaceInputLine(nextHistory);
+    jsvUnLock(nextHistory);
+    hasUsedHistory = true;
+  } else if (!previous) { // if next, but we have something, just clear the line
+    if (jsiShowInputLine()) {
+      jsiConsoleEraseStringVarBackwards(inputLine);
+    }
+    jsvUnLock(inputLine);
+    inputLine = jsvNewFromEmptyString();
+    inputCursorPos = 0;
+  }
+}
+
+void jsiIsAboutToEditInputLine() {
+  // we probably plan to do something with the line now - check it wasn't in history
+  // and if it was, duplicate it
+  if (hasUsedHistory) {
+    hasUsedHistory = false;
+    if (jsiIsInHistory(inputLine)) {
+      JsVar *newLine = jsvCopy(inputLine);
+      if (newLine) { // could have been out of memory!
+        jsvUnLock(inputLine);
+        inputLine = newLine;
+      }
+    }
+  }
+}
+
+void jsiHandleDelete(bool isBackspace) {
+  int l = (int)jsvGetStringLength(inputLine);
+  if (isBackspace && inputCursorPos<=0) return; // at beginning of line
+  if (!isBackspace && inputCursorPos>=l) return; // at end of line
+  // work out if we are deleting a newline
+  bool deleteNewline = (isBackspace && jsvGetCharInString(inputLine,inputCursorPos-1)=='\n') ||
+                       (!isBackspace && jsvGetCharInString(inputLine,inputCursorPos)=='\n');
+  // If we mod this to keep the string, use jsiIsAboutToEditInputLine
+  if (deleteNewline && jsiShowInputLine()) {
+    jsiConsoleEraseStringVarFrom(inputLine, inputCursorPos, true/*before newline*/); // erase all in front
+    if (isBackspace) {
+      // delete newline char
+      jsiConsolePrintChar(0x08);
+      jsiConsolePrintChar(' ');
+      jsiMoveCursorChar(inputLine, inputCursorPos, inputCursorPos-1); // move cursor back
+    }
+  }
+
+  JsVar *v = jsvNewFromEmptyString();
+  int p = inputCursorPos;
+  if (isBackspace) p--;
+  if (p>0) jsvAppendStringVar(v, inputLine, 0, p); // add before cursor (delete)
+  if (p+1<l) jsvAppendStringVar(v, inputLine, p+1, JSVAPPENDSTRINGVAR_MAXLENGTH); // add the rest
+  jsvUnLock(inputLine);
+  inputLine=v;
+  if (isBackspace)
+    inputCursorPos--; // move cursor back
+
+  // update the console
+  if (jsiShowInputLine()) {
+    if (deleteNewline) {
+      // we already removed everything, so just put it back
+      jsiConsolePrintStringVarWithNewLineChar(inputLine, inputCursorPos, ':');
+      jsiMoveCursorChar(inputLine, (int)jsvGetStringLength(inputLine), inputCursorPos); // move cursor back
+    } else {
+      // clear the character and move line back
+      if (isBackspace) jsiConsolePrintChar(0x08);
+      jsiConsolePrintStringVarUntilEOL(inputLine, inputCursorPos, true/*and backup*/);
+    }
+  }
+}
+
+void jsiHandleHome() {
+  while (inputCursorPos>0 && jsvGetCharInString(inputLine,inputCursorPos-1)!='\n') {
+    if (jsiShowInputLine()) jsiConsolePrintChar(0x08);
+    inputCursorPos--;
+  }
+}
+
+void jsiHandleEnd() {
+  int l = (int)jsvGetStringLength(inputLine);
+  while (inputCursorPos<l && jsvGetCharInString(inputLine,inputCursorPos)!='\n') {
+    if (jsiShowInputLine())
+      jsiConsolePrintChar(jsvGetCharInString(inputLine,inputCursorPos));
+    inputCursorPos++;
+  }
+}
+
+/** Page up/down move cursor to beginnint or end */
+void jsiHandlePageUpDown(bool isDown) {
+  int x,y;
+  jsvGetLineAndCol(inputLine, inputCursorPos, &y, &x);
+  if (!isDown) { // up
+    inputCursorPos = 0;
+  } else { // down
+    inputCursorPos = (int)jsvGetStringLength(inputLine);
+  }
+  int newX=x,newY=y;
+  jsvGetLineAndCol(inputLine, inputCursorPos, &newY, &newX);
+  jsiMoveCursor(x,y,newX,newY);
+}
+
+void jsiHandleMoveUpDown(int direction) {
+  int x,y, lines=jsvGetLinesInString(inputLine);
+  jsvGetLineAndCol(inputLine, inputCursorPos, &y, &x);
+  int newX=x,newY=y;
+  newY+=direction;
+  if (newY<1) newY=1;
+  if (newY>lines) newY=lines;
+  // work out cursor pos and feed back through - we might not be able to get right to the same place
+  // if we move up
+  inputCursorPos = jsvGetIndexFromLineAndCol(inputLine, newY, newX);
+  jsvGetLineAndCol(inputLine, inputCursorPos, &newY, &newX);
+  if (jsiShowInputLine()) {
+    jsiMoveCursor(x,y,newX,newY);
+  }
+}
+
+bool jsiAtEndOfInputLine() {
+  int i = inputCursorPos, l = (int)jsvGetStringLength(inputLine);
+  while (i < l) {
+    if (!isWhitespace(jsvGetCharInString(inputLine, i)))
+        return false;
+    i++;
+  }
+  return true;
+}
+
+void jsiHandleChar(char ch) {
+  //jsiConsolePrintf("[%d:%d]\n", inputState, ch);
+  //
+  // special stuff
+  // 27 then 91 then 68 - left
+  // 27 then 91 then 67 - right
+  // 27 then 91 then 65 - up
+  // 27 then 91 then 66 - down
+  // 27 then 91 then 51 then 126 - backwards delete
+  // 27 then 91 then 52 then 126 - numpad end
+  // 27 then 91 then 49 then 126 - numpad home
+  // 27 then 91 then 53 then 126 - pgup
+  // 27 then 91 then 54 then 126 - pgdn
+  // 27 then 79 then 70 - home
+  // 27 then 79 then 72 - end
+
+  if (ch == 0) {
+    inputState = IS_NONE; // ignore 0 - it's scary
+  } else if (ch == 27) {
+    inputState = IS_HAD_27;
+  } else if (inputState==IS_HAD_27) {
+    inputState = IS_NONE;
+    if (ch == 79)
+      inputState = IS_HAD_27_79;
+    else if (ch == 91)
+      inputState = IS_HAD_27_91;
+  } else if (inputState==IS_HAD_27_79) { // Numpad
+    inputState = IS_NONE;
+    if (ch == 70) jsiHandleEnd();
+    else if (ch == 72) jsiHandleHome();
+    else if (ch == 111) jsiHandleChar('/');
+    else if (ch == 106) jsiHandleChar('*');
+    else if (ch == 109) jsiHandleChar('-');
+    else if (ch == 107) jsiHandleChar('+');
+    else if (ch == 77) jsiHandleChar('\r');
+  } else if (inputState==IS_HAD_27_91) {
+    inputState = IS_NONE;
+    if (ch==68) { // left
+      if (inputCursorPos>0 && jsvGetCharInString(inputLine,inputCursorPos-1)!='\n') {
+        inputCursorPos--;
+        if (jsiShowInputLine()) {
+          jsiConsolePrintChar(27);
+          jsiConsolePrintChar(91);
+          jsiConsolePrintChar(68);
+        }
+      }
+    } else if (ch==67) { // right
+      if (inputCursorPos<(int)jsvGetStringLength(inputLine) && jsvGetCharInString(inputLine,inputCursorPos)!='\n') {
+        inputCursorPos++;
+        if (jsiShowInputLine()) {
+          jsiConsolePrintChar(27);
+          jsiConsolePrintChar(91);
+          jsiConsolePrintChar(67);
+        }
+      }
+    } else if (ch==65) { // up
+      int l = (int)jsvGetStringLength(inputLine);
+      if ((l==0 || jsiIsInHistory(inputLine)) && inputCursorPos==l)
+        jsiChangeToHistory(true); // if at end of line
+      else
+        jsiHandleMoveUpDown(-1);
+    } else if (ch==66) { // down
+      int l = (int)jsvGetStringLength(inputLine);
+      if ((l==0 || jsiIsInHistory(inputLine)) && inputCursorPos==l)
+        jsiChangeToHistory(false); // if at end of line
+      else
+        jsiHandleMoveUpDown(1);
+    } else if (ch==49) {
+      inputState=IS_HAD_27_91_49;
+    } else if (ch==51) {
+      inputState=IS_HAD_27_91_51;
+    } else if (ch==52) {
+      inputState=IS_HAD_27_91_52;
+    } else if (ch==53) {
+      inputState=IS_HAD_27_91_53;
+    } else if (ch==54) {
+      inputState=IS_HAD_27_91_54;
+    }
+  } else if (inputState==IS_HAD_27_91_49) {
+    inputState = IS_NONE;
+    if (ch==126) { // Numpad Home
+      jsiHandleHome();
+    }
+  } else if (inputState==IS_HAD_27_91_51) {
+    inputState = IS_NONE;
+    if (ch==126) { // Numpad (forwards) Delete
+      jsiHandleDelete(false/*not backspace*/);
+    }
+  } else if (inputState==IS_HAD_27_91_52) {
+    inputState = IS_NONE;
+    if (ch==126) { // Numpad End
+      jsiHandleEnd();
+    }
+  } else if (inputState==IS_HAD_27_91_53) {
+      inputState = IS_NONE;
+      if (ch==126) { // Page Up
+        jsiHandlePageUpDown(0);
+      }
+  } else if (inputState==IS_HAD_27_91_54) {
+      inputState = IS_NONE;
+      if (ch==126) { // Page Down
+        jsiHandlePageUpDown(1);
+      }
+  } else {  
+    inputState = IS_NONE;
+    if (ch == 0x08 || ch == 0x7F /*delete*/) {
+      jsiHandleDelete(true /*backspace*/);
+    } else if (ch == '\n' && inputState == IS_HAD_R) {
+      inputState = IS_NONE; //  ignore \ r\n - we already handled it all on \r
+    } else if (ch == '\r' || ch == '\n') { 
+      if (jsiAtEndOfInputLine()) { // at EOL so we need to figure out if we can execute or not
+        if (ch == '\r') inputState = IS_HAD_R;
+        if (jsiCountBracketsInInput()<=0) { // actually execute!
+          if (jsiShowInputLine()) {
+            jsiConsolePrintChar('\r');
+            jsiConsolePrintChar('\n');
+          }
+          inputLineRemoved = true;
+
+          // Get line to execute, and reset inputLine
+          JsVar *lineToExecute = jsvStringTrimRight(inputLine);
+          jsvUnLock(inputLine);
+          inputLine = jsvNewFromEmptyString();
+          inputCursorPos = 0;
+          // execute!
+          JsVar *v = jspEvaluateVar(&p, lineToExecute, 0);
+          // add input line to history
+          jsiHistoryAddLine(lineToExecute);
+          jsvUnLock(lineToExecute);
+          // print result
+          if (echo) { // intentionally not using jsiShowInputLine()
+            jsiConsolePrintChar('=');
+            jsfPrintJSON(v);
+            jsiConsolePrint("\n");
+          }
+          jsvUnLock(v);
+          // console will be returned next time around the input loop
+        } else {
+          // Brackets aren't all closed, so we're going to append a newline
+          // without executing
+          if (jsiShowInputLine()) jsiConsolePrint("\n:");
+          jsiIsAboutToEditInputLine();
+          jsvAppendCharacter(inputLine, '\n');
+          inputCursorPos++; 
+        }
+      } else { // new line - but not at end of line!
+        jsiIsAboutToEditInputLine();
+        if (jsiShowInputLine()) jsiConsoleEraseStringVarFrom(inputLine, inputCursorPos, false/*no need to erase the char before*/); // erase all in front
+        JsVar *v = jsvNewFromEmptyString();
+        if (inputCursorPos>0) jsvAppendStringVar(v, inputLine, 0, inputCursorPos);
+        jsvAppendCharacter(v, '\n');
+        jsvAppendStringVar(v, inputLine, inputCursorPos, JSVAPPENDSTRINGVAR_MAXLENGTH); // add the rest
+        jsvUnLock(inputLine);
+        inputLine=v;
+        if (jsiShowInputLine()) { // now print the rest
+          jsiConsolePrintStringVarWithNewLineChar(inputLine, inputCursorPos, ':');
+          jsiMoveCursorChar(inputLine, (int)jsvGetStringLength(inputLine), inputCursorPos+1); // move cursor back
+        }
+        inputCursorPos++;
+      }
+    } else if (ch>=32 || ch=='\t') {
+      // Add the character to our input line
+      jsiIsAboutToEditInputLine();
+      int l = (int)jsvGetStringLength(inputLine);
+      bool hasTab = ch=='\t';
+      if (inputCursorPos>=l) {
+        if (hasTab) jsvAppendString(inputLine, "    ");
+        else jsvAppendCharacter(inputLine, ch);
+      } else {
+        JsVar *v = jsvNewFromEmptyString();
+        if (inputCursorPos>0) jsvAppendStringVar(v, inputLine, 0, inputCursorPos);
+        if (hasTab) jsvAppendString(v, "    ");
+        else jsvAppendCharacter(v, ch);
+        jsvAppendStringVar(v, inputLine, inputCursorPos, JSVAPPENDSTRINGVAR_MAXLENGTH); // add the rest
+        jsvUnLock(inputLine);
+        inputLine=v;
+        if (jsiShowInputLine()) jsiConsolePrintStringVarUntilEOL(inputLine, inputCursorPos, true/*and backup*/);
+      }
+      inputCursorPos += hasTab ? 4 : 1;
+      if (jsiShowInputLine()) {
+        if (hasTab) jsiConsolePrint("    ");
+        else jsiConsolePrintChar(ch);
+      }
+    }
+  }
+}
+
+void jsiQueueEvents(JsVarRef callbacks, JsVar *arg0, JsVar *arg1) { // array of functions or single function
+  if (!callbacks) return;
+
+  JsVar *callbackVar = jsvLock(callbacks);
+  // if it is a single callback, just add it
+  if (jsvIsFunction(callbackVar) || jsvIsString(callbackVar)) {
+    JsVar *event = jsvNewWithFlags(JSV_OBJECT);
+    if (event) { // Could be out of memory error!
+      jsvUnLock(jsvAddNamedChild(event, callbackVar, "func"));
+      if (arg0) jsvUnLock(jsvAddNamedChild(event, arg0, "arg0"));
+      if (arg1) jsvUnLock(jsvAddNamedChild(event, arg1, "arg1"));
+      jsvArrayPushAndUnLock(events, event);
+    }
+    jsvUnLock(callbackVar);
+  } else {
+    assert(jsvIsArray(callbackVar));
+    // go through all callbacks
+    JsVarRef next = callbackVar->firstChild;
+    jsvUnLock(callbackVar);
+    while (next) {
+      //jsPrint("Queue Event\n");
+      JsVar *child = jsvLock(next);
+      
+      // for each callback...
+      JsVar *event = jsvNewWithFlags(JSV_OBJECT);
+      if (event) { // Could be out of memory error!
+        jsvUnLock(jsvAddNamedChild(event, child, "func"));
+        if (arg0) jsvUnLock(jsvAddNamedChild(event, arg0, "arg0"));
+        if (arg1) jsvUnLock(jsvAddNamedChild(event, arg1, "arg1"));
+        // add event to the events list
+        jsvArrayPushAndUnLock(events, event);
+        // go to next callback
+      }
+      next = child->nextSibling;
+      jsvUnLock(child);
+    }
+  }
+}
+
+void jsiQueueObjectCallbacks(JsVar *object, const char *callbackName, JsVar *arg0, JsVar *arg1) {
+  JsVar *callback = jsvObjectGetChild(object, callbackName, 0);
+  if (!callback) return;
+  jsiQueueEvents(jsvGetRef(callback), arg0, arg1);
+  jsvUnLock(callback);
+}
+
+void jsiExecuteEvents() {
+  bool hasEvents = !jsvArrayIsEmpty(events);
+  bool wasInterrupted = jspIsInterrupted();
+  if (hasEvents) jsiSetBusy(BUSY_INTERACTIVE, true);
+  while (!jsvArrayIsEmpty(events)) {
+    JsVar *event = jsvSkipNameAndUnLock(jsvArrayPopFirst(events));
+    // Get function to execute
+    JsVar *func = jsvObjectGetChild(event, "func", 0);
+    JsVar *args[2];
+    args[0] = jsvObjectGetChild(event, "arg0", 0);
+    args[1] = jsvObjectGetChild(event, "arg1", 0);
+    // free
+    jsvUnLock(event);
+
+
+    // now run..
+    if (func) {
+      if (jsvIsFunction(func))
+        jspExecuteFunction(&p, func, 0, 2, args);
+      else if (jsvIsString(func))
+        jsvUnLock(jspEvaluateVar(&p, func, 0));
+      else 
+        jsError("Unknown type of callback in Event Queue");
+    }
+    //jsPrint("Event Done\n");
+    jsvUnLock(func);
+    jsvUnLock(args[0]);
+    jsvUnLock(args[1]);
+  }
+  if (hasEvents) {
+    jsiSetBusy(BUSY_INTERACTIVE, false);
+    if (!wasInterrupted && jspIsInterrupted())
+      interruptedDuringEvent = true;
+  }
+}
+
+void jsiExecuteEventCallback(JsVar *callbackVar, JsVar *arg0, JsVar *arg1) { // array of functions or single function
+  bool wasInterrupted = jspIsInterrupted();
+  JsVar *callbackNoNames = jsvSkipName(callbackVar);
+
+  if (callbackNoNames) {
+    if (jsvIsArray(callbackNoNames)) {
+      JsVarRef next = callbackNoNames->firstChild;
+      while (next) {
+        JsVar *child = jsvLock(next);
+        jsiExecuteEventCallback(child, arg0, arg1);
+        next = child->nextSibling;
+        jsvUnLock(child);
+      }
+    } else if (jsvIsFunction(callbackNoNames)) {
+       JsVar *args[2] = { arg0, arg1 };
+       JsVar *parent = 0;
+       jspExecuteFunction(&p, callbackNoNames, parent, 2, args);
+    } else if (jsvIsString(callbackNoNames))
+      jsvUnLock(jspEvaluateVar(&p, callbackNoNames, 0));
+    else 
+      jsError("Unknown type of callback in Event Queue");
+    jsvUnLock(callbackNoNames);
+  }
+  if (!wasInterrupted && jspIsInterrupted())
+    interruptedDuringEvent = true;
+}
+
+bool jsiHasTimers() {
+  if (!timerArray) return false;
+  JsVar *timerArrayPtr = jsvLock(timerArray);
+  JsVarInt c = jsvGetArrayLength(timerArrayPtr);
+  jsvUnLock(timerArrayPtr);
+  return c>0;
+}
+
+void jsiPrintUnregisteredMessage(const char *desc) {
+  jsiConsolePrint("\n You must have registered Espruino in order to ");
+  jsiConsolePrint(desc);
+  jsiConsolePrint(".\n\n Please type register() for more information.\n\n");
+}
+
+void jsiIdle() {
+  // This is how many times we have been here and not done anything.
+  // It will be zeroed if we do stuff later
+  if (loopsIdling<255) loopsIdling++;
+
+  // Handle hardware-related idle stuff (like checking for pin events)
+  bool wasBusy = false;
+  IOEvent event;
+  while (jshPopIOEvent(&event)) {
+    jsiSetBusy(BUSY_INTERACTIVE, true);
+    wasBusy = true;
+
+    IOEventFlags eventType = IOEVENTFLAGS_GETTYPE(event.flags);
+
+    loopsIdling = 0; // because we're not idling
+    if (eventType == consoleDevice) {
+      int i, c = IOEVENTFLAGS_GETCHARS(event.flags);
+      jsiSetBusy(BUSY_INTERACTIVE, true);
+      for (i=0;i<c;i++) jsiHandleChar(event.data.chars[i]);
+      jsiSetBusy(BUSY_INTERACTIVE, false);
+    }
+
+
+
+    if (DEVICE_IS_USART(eventType)) {
+      // ------------------------------------------------------------------------ SERIAL CALLBACK
+      JsVar *usartClass = jsvSkipNameAndUnLock(jsiGetClassNameFromDevice(IOEVENTFLAGS_GETTYPE(event.flags)));
+      if (usartClass) {
+        JsVar *callback = jsvFindChildFromString(usartClass, USART_CALLBACK_NAME, false);
+
+        if (callback) {
+          int i, c = IOEVENTFLAGS_GETCHARS(event.flags);
+
+// Part of hackish solution to 7 bit support on STM32
+#ifdef STM32
+          unsigned char bytesize = 8;
+          unsigned char parity   = 0;
+          JsVar *options    = jsvObjectGetChild(usartClass, DEVICE_OPTIONS_NAME, 0);
+
+          if(jsvIsObject(options)) {
+            bytesize = (unsigned char)jsvGetIntegerAndUnLock(jsvObjectGetChild(options, "bytesize", 0));
+            JsVar *v = jsvObjectGetChild(options, "parity", 0);
+
+            if(jsvIsString(v)) {
+              parity = 0xFF;
+              char s[8] = "";
+
+              jsvGetString(v, s, sizeof(s) - 1);
+
+              if(!strcmp(s, "o") || !strcmp(s, "odd")) {
+                parity = 1;
+              }
+              else if(!strcmp(s, "e") || !strcmp(s, "even")) {
+                parity = 2;
+              }
+            }
+            else if(jsvIsInt(v)) {
+              parity = (unsigned char)jsvGetInteger(v);
+            }
+
+            jsvUnLock(v);
+          }
+
+          jsvUnLock(options);
+#endif
+
+          for (i=0; i<c; i++) {
+            JsVar *data = jsvNewWithFlags(JSV_OBJECT);
+
+            if (data) {
+              JsVar *dataTime = jsvNewFromString("X");
+
+#ifdef STM32 
+              if(bytesize == 7 && parity > 0) {
+                dataTime->varData.str[0] = event.data.chars[i] & 0x7F;
+              }
+              else if(bytesize == 8 && parity > 0) {
+                dataTime->varData.str[0] = event.data.chars[i] & 0xFF;
+              }
+              else {
+                dataTime->varData.str[0] = event.data.chars[i];
+              }
+#else
+              dataTime->varData.str[0] = event.data.chars[i];
+#endif
+
+              if (dataTime) jsvUnLock(jsvAddNamedChild(data, dataTime, "data"));
+              jsvUnLock(dataTime);
+            }
+            
+            jsiExecuteEventCallback(callback, data, 0);
+            jsvUnLock(data);
+          }
+        }
+        jsvUnLock(callback);
+        jsvUnLock(usartClass);
+      }
+    } else if (DEVICE_IS_EXTI(eventType)) { // ---------------------------------------------------------------- PIN WATCH
+      // we have an event... find out what it was for...
+      // Check everything in our Watch array
+      JsVar *watchArrayPtr = jsvLock(watchArray);
+      JsVarRef watch = watchArrayPtr->firstChild;
+      while (watch) {
+        JsVar *watchNamePtr = jsvLock(watch); // effectively the array index
+        JsVar *watchPin = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
+        Pin pin = jshGetPinFromVar(watchPin); // TODO: could be faster?
+        jsvUnLock(watchPin);
+
+        if (jshIsEventForPin(&event, pin)) {
+          bool pinIsHigh = (event.flags&EV_EXTI_IS_HIGH)!=0;
+          int watchEdge = (int)jsvGetIntegerAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "edge", false)));
+          if (watchEdge==0 || (pinIsHigh && watchEdge>0) || (!pinIsHigh && watchEdge<0)) { // edge triggering
+            JsVar *watchCallback = jsvFindChildFromStringRef(watchNamePtr->firstChild, "callback", false);
+            bool watchRecurring = jsvGetBoolAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "recur", false)));
+            JsVar *data = jsvNewWithFlags(JSV_OBJECT);
+            if (data) {
+              JsVar *dataTime = jsvNewFromFloat(jshGetMillisecondsFromTime(event.data.time)/1000);
+              if (dataTime) jsvUnLock(jsvAddNamedChild(data, dataTime, "time"));
+              jsvUnLock(dataTime);
+              JsVar *dataState = jsvNewFromBool(pinIsHigh);
+              if (dataState) jsvUnLock(jsvAddNamedChild(data, dataState, "state"));
+              jsvUnLock(dataState);
+            }
+            jsiExecuteEventCallback(watchCallback, data, 0);
+            jsvUnLock(data);
+            if (!watchRecurring) {
+              // free all
+              jsvRemoveChild(watchArrayPtr, watchNamePtr);
+            }
+            jsvUnLock(watchCallback);
+          }
+        }
+        watch = watchNamePtr->nextSibling;
+        jsvUnLock(watchNamePtr);
+      }
+      jsvUnLock(watchArrayPtr);
+    }
+  }
+
+  // Reset Flow control if it was set...
+  if (jshGetEventsUsed() < IOBUFFER_XON) { 
+    int i;
+    for (i=0;i<USARTS;i++)
+      jshSetFlowControlXON(EV_SERIAL1+i, true);
+  }
+
+  // Check timers
+  JsSysTime minTimeUntilNext = JSSYSTIME_MAX;
+  JsSysTime time = jshGetSystemTime();
+
+  JsVar *timerArrayPtr = jsvLock(timerArray);
+  JsVarRef timer = timerArrayPtr->firstChild;
+  while (timer) {
+    JsVar *timerNamePtr = jsvLock(timer);
+    timer = timerNamePtr->nextSibling; // ptr to next
+    JsVar *timerTime = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "time", false));
+    JsSysTime timeUntilNext = jsvGetInteger(timerTime) - time;
+    if (timeUntilNext < minTimeUntilNext)
+      minTimeUntilNext = timeUntilNext;
+    if (timerTime && timeUntilNext<=0) {
+      // we're now doing work
+      jsiSetBusy(BUSY_INTERACTIVE, true);
+      wasBusy = true;
+
+      JsVar *timerCallback = jsvFindChildFromStringRef(timerNamePtr->firstChild, "callback", false);
+      JsVar *timerRecurring = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "recur", false));
+      JsVar *data = jsvNewWithFlags(JSV_OBJECT);
+      if (data) {
+        JsVar *dataTime = jsvNewFromFloat(jshGetMillisecondsFromTime(jsvGetInteger(timerTime))/1000);
+        if (dataTime) jsvUnLock(jsvAddNamedChild(data, dataTime, "time"));
+        jsvUnLock(dataTime);
+      }
+      jsiExecuteEventCallback(timerCallback, data, 0);
+      jsvUnLock(data);
+      if (jsvGetBool(timerRecurring)) {
+        JsVarFloat interval = jsvGetFloatAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "interval", false)));
+        if (interval<=0)
+          jsvSetInteger(timerTime, time); // just set to current system time
+        else
+          jsvSetInteger(timerTime, jsvGetInteger(timerTime)+jshGetTimeFromMilliseconds(interval));
+      } else {
+        // free all
+        JsVar *foundChild = jsvFindChildFromVar(timerArrayPtr, timerNamePtr, false);
+        if (foundChild) {
+          // check it exists - could have been removed during jsiExecuteEventCallback!
+          jsvRemoveChild(timerArrayPtr, timerNamePtr);
+          jsvUnLock(foundChild);
+        }
+      }
+      jsvUnLock(timerCallback);
+      jsvUnLock(timerRecurring);
+
+    }
+    jsvUnLock(timerTime);
+    jsvUnLock(timerNamePtr);
+  }
+  jsvUnLock(timerArrayPtr);
+
+
+  // Check for network events
+#ifdef USE_NET
+  httpIdle();
+#endif
+
+  // Just in case we got any events to do and didn't clear loopsIdling before
+  if (wasBusy || !jsvArrayIsEmpty(events) )
+    loopsIdling = 0;
+
+  if (wasBusy)
+    jsiSetBusy(BUSY_INTERACTIVE, false);
+
+
+  // TODO: could now sort events by time?
+  // execute any outstanding events
+  if (!jspIsInterrupted()) {
+    jsiExecuteEvents();
+  }
+  if (interruptedDuringEvent) {
+    jspSetInterrupted(false);
+    interruptedDuringEvent = false;
+    jsiConsolePrint("Execution Interrupted during event processing - clearing all timers and watches.\n");
+    jswrap_interface_clearInterval(0);
+    jswrap_interface_clearWatch(0);
+  }
+
+  // check for TODOs
+  if (todo) {
+    jsiSetBusy(BUSY_INTERACTIVE, true);
+    if (todo & TODO_RESET) {
+      todo &= (TODOFlags)~TODO_RESET;
+      // shut down everything and start up again
+      jsiKill();
+      jsiInit(false); // don't autoload
+    }
+    if (todo & TODO_FLASH_SAVE) {
+      todo &= (TODOFlags)~TODO_FLASH_SAVE;
+
+      jsvGarbageCollect(); // nice to have everything all tidy!
+      jsiSoftKill();
+      jspSoftKill(&p);
+      jsvSoftKill();
+      jshSaveToFlash();
+      jsvSoftInit();
+      jspSoftInit(&p);
+      jsiSoftInit();
+    }
+    if (todo & TODO_FLASH_LOAD) {
+      todo &= (TODOFlags)~TODO_FLASH_LOAD;
+
+      jsiSoftKill();
+      jspSoftKill(&p);
+      jsvSoftKill();
+      jshLoadFromFlash();
+      jsvSoftInit();
+      jspSoftInit(&p);
+      jsiSoftInit();
+    }
+    jsiSetBusy(BUSY_INTERACTIVE, false);
+  }
+
+  /* if we've been around this loop, there is nothing to do, and
+   * we have a spare 10ms then let's do some Garbage Collection
+   * just in case. */
+  if (loopsIdling==1 &&
+      minTimeUntilNext > jshGetTimeFromMilliseconds(10)) {
+    jsiSetBusy(BUSY_INTERACTIVE, true);
+    jsvGarbageCollect();
+    jsiSetBusy(BUSY_INTERACTIVE, false);
+  }
+  // Go to sleep!
+  if (loopsIdling>1 && // once around the idle loop without having done any work already (just in case)
+#ifdef USB
+      !jshIsUSBSERIALConnected() && // if USB is on, no point sleeping (later, sleep might be more drastic)
+#endif
+      !jshHasEvents() && //no events have arrived in the mean time
+      !jshHasTransmitData()/* && //nothing left to send over serial?
+      minTimeUntilNext > SYSTICK_RANGE*5/4*/) { // we are sure we won't miss anything - leave a little leeway (SysTick will wake us up!)
+    jshSleep(minTimeUntilNext);
+  }
+}
+
+void jsiLoop() {
+  // idle stuff for hardware
+  jshIdle();
+  // Do general idle stuff
+  jsiIdle();
+  // Idle LCD
+#ifdef USE_LCD
+  graphicsIdle();
+#endif
+  
+  if (jspIsInterrupted()) {
+    jspSetInterrupted(false);
+    jsiConsoleRemoveInputLine();
+    // clear input line
+    jsvUnLock(inputLine);
+    inputLine = jsvNewFromEmptyString();
+  }
+
+  // return console (if it was gone!)
+  jsiReturnInputLine();
+}
+
+/** Output extra functions defined in an object such that they can be copied to a new device */
+void jsiDumpObjectState(JsVar *parentName, JsVar *parent) {
+  JsVarRef childRef = parent->firstChild;
+  while (childRef) {
+    JsVar *child = jsvLock(childRef);
+    JsVar *data = jsvSkipName(child);
+    if (jsvIsStringEqual(child, JSPARSE_PROTOTYPE_VAR)) {
+      JsVarRef protoRef = data->firstChild;
+      while (protoRef) {
+         JsVar *proto = jsvLock(protoRef);
+         jsiConsolePrintStringVar(parentName);
+         jsiConsolePrint(".prototype.");
+         jsiConsolePrintStringVar(proto);
+         jsiConsolePrint(" = ");
+         JsVar *protoData = jsvSkipName(proto);
+         jsfPrintJSON(protoData);
+         jsvUnLock(protoData);
+         jsiConsolePrint(";\n");
+         protoRef = proto->nextSibling;
+         jsvUnLock(proto);
+       }
+    } else {
+      jsiConsolePrintStringVar(parentName);
+      jsiConsolePrint(".");
+      jsiConsolePrintStringVar(child);
+      jsiConsolePrint(" = ");
+      jsfPrintJSON(data);
+      jsiConsolePrint(";\n");
+
+    }
+    jsvUnLock(data);
+    childRef = child->nextSibling;
+    jsvUnLock(child);
+  }
+}
+
+/** Output current interpreter state such that it can be copied to a new device */
+void jsiDumpState() {
+  JsVarRef childRef = p.root->firstChild;
+  while (childRef) {
+    JsVar *child = jsvLock(childRef);
+    char childName[JSLEX_MAX_TOKEN_LENGTH];
+    jsvGetString(child, childName, JSLEX_MAX_TOKEN_LENGTH);
+
+    JsVar *data = jsvSkipName(child);
+    if (jspIsCreatedObject(&p, data) || jswIsBuiltInObject(childName)) {
+      jsiDumpObjectState(child, data);
+    } else if (jsvIsStringEqual(child, JSI_TIMERS_NAME)) {
+      // skip - done later
+    } else if (jsvIsStringEqual(child, JSI_WATCHES_NAME)) {
+      // skip - done later
+    } else if (child->varData.str[0]==JS_HIDDEN_CHAR ||
+               jshFromDeviceString(childName)!=EV_NONE) {
+      // skip - don't care about this stuff
+    } else if (!jsvIsNative(data)) { // just a variable/function!
+      if (jsvIsFunction(data)) {
+        // function-specific output
+        jsiConsolePrint("function ");
+        jsiConsolePrintStringVar(child);
+        jsfPrintJSONForFunction(data);
+        jsiConsolePrint("\n");
+        // print any prototypes we had
+        JsVar *proto = jsvObjectGetChild(data, JSPARSE_PROTOTYPE_VAR, 0);
+        if (proto) {
+          JsVarRef protoRef = proto->firstChild;
+          jsvUnLock(proto);
+          while (protoRef) {
+            JsVar *protoName = jsvLock(protoRef);
+            JsVar *protoData = jsvSkipName(protoName);
+            jsiConsolePrintStringVar(child);
+            jsiConsolePrint(".prototype.");
+            jsiConsolePrintStringVar(protoName);
+            jsiConsolePrint(" = ");
+            jsfPrintJSON(protoData);
+            jsiConsolePrint(";\n");
+            jsvUnLock(protoData);
+            protoRef = protoName->nextSibling;
+            jsvUnLock(protoName);
+          }
+        }
+      } else {
+        // normal variable definition
+        jsiConsolePrint("var ");
+        jsiConsolePrintStringVar(child);
+        jsiConsolePrint(" = ");
+        jsfPrintJSON(data);
+        jsiConsolePrint(";\n");
+      }
+    }
+    jsvUnLock(data);
+    childRef = child->nextSibling;
+    jsvUnLock(child);
+  }
+  // Now do timers
+  JsVar *timerArrayPtr = jsvLock(timerArray);
+  JsVarRef timerRef = timerArrayPtr->firstChild;
+  jsvUnLock(timerArrayPtr);
+  while (timerRef) {
+    JsVar *timerNamePtr = jsvLock(timerRef);
+    JsVar *timerCallback = jsvSkipOneNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "callback", false));
+    bool recur = jsvGetBoolAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "recur", false)));
+    JsVar *timerInterval = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(timerNamePtr->firstChild, "interval", false));
+    jsiConsolePrint(recur ? "setInterval(" : "setTimeout(");
+    jsfPrintJSON(timerCallback);
+    jsiConsolePrint(", ");
+    jsfPrintJSON(timerInterval);
+    jsiConsolePrint(");\n");
+    jsvUnLock(timerInterval);
+    jsvUnLock(timerCallback);
+    // next
+    timerRef = timerNamePtr->nextSibling;
+    jsvUnLock(timerNamePtr);
+  }
+  // Now do watches
+  {
+   JsVar *watchArrayPtr = jsvLock(watchArray);
+   JsVarRef watchRef = watchArrayPtr->firstChild;
+   jsvUnLock(watchArrayPtr);
+   while (watchRef) {
+     JsVar *watchNamePtr = jsvLock(watchRef);
+     JsVar *watchCallback = jsvSkipOneNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "callback", false));
+     bool watchRecur = jsvGetBoolAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "recur", false)));
+     int watchEdge = (int)jsvGetIntegerAndUnLock(jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "edge", false)));
+     JsVar *watchPin = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
+     jsiConsolePrint("setWatch(");
+     jsfPrintJSON(watchCallback);
+     jsiConsolePrint(", ");
+     jsfPrintJSON(watchPin);
+     jsiConsolePrint(", { repeat:");
+     jsiConsolePrint(watchRecur?"true":"false");
+     jsiConsolePrint(", edge:");
+     if (watchEdge<0) jsiConsolePrint("'falling'");
+     else if (watchEdge>0) jsiConsolePrint("'rising'");
+     else jsiConsolePrint("'both'");
+     jsiConsolePrint(" });\n");
+     jsvUnLock(watchPin);
+     jsvUnLock(watchCallback);
+     // next
+     watchRef = watchNamePtr->nextSibling;
+     jsvUnLock(watchNamePtr);
+   }
+  }
+  // and now serial
+  JsVar *str = jsvNewFromEmptyString();
+  jsiAppendHardwareInitialisation(str, true);
+  jsiConsolePrintStringVar(str);
+  jsvUnLock(str);
+}
+
+void jsiSetTodo(TODOFlags newTodo) {
+  todo = newTodo;
+}
+
+JsParse *jsiGetParser() {
+  return &p;
+}

+ 105 - 0
components/external/espruino/src/jsinteractive.h

@@ -0,0 +1,105 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Interactive Shell implementation
+ * ----------------------------------------------------------------------------
+ */
+#ifndef JSINTERACTIVE_H_
+#define JSINTERACTIVE_H_
+
+#include "jsparse.h"
+#include "jshardware.h"
+
+#define JSI_WATCHES_NAME ">watches"
+#define JSI_TIMERS_NAME ">timers"
+#define JSI_HISTORY_NAME ">history"
+#define JSI_INIT_CODE_NAME ">init"
+#define JSI_ONINIT_NAME "onInit"
+
+/// autoLoad = do we load the current state if it exists?
+void jsiInit(bool autoLoad);
+void jsiKill();
+
+void jsiLoop();
+
+/// Tries to get rid of some memory (by clearing command history). Returns true if it got rid of something, false if it didn't.
+bool jsiFreeMoreMemory();
+
+bool jsiHasTimers(); // are there timers still left to run?
+JsParse *jsiGetParser();
+
+/// Queue up callbacks for other things (touchscreen? network?)
+void jsiQueueObjectCallbacks(JsVar *object, const char *callbackName, JsVar *arg0, JsVar *arg1);
+
+
+IOEventFlags jsiGetDeviceFromClass(JsVar *deviceClass);
+JsVar *jsiGetClassNameFromDevice(IOEventFlags device);
+
+/// Change the console to a new location
+void jsiSetConsoleDevice(IOEventFlags device);
+/// Get the device that the console is currently on
+IOEventFlags jsiGetConsoleDevice();
+/// Transmit a byte
+void jsiConsolePrintChar(char data);
+/// Transmit a string
+void jsiConsolePrint(const char *str);
+/// Write the formatted string to the console (see vcbprintf)
+void jsiConsolePrintf(const char *fmt, ...);
+/// Print the contents of a string var - directly
+void jsiConsolePrintStringVar(JsVar *v);
+/// Transmit an integer
+void jsiConsolePrintInt(JsVarInt d);
+/// Transmit a position in the lexer (for reporting errors)
+void jsiConsolePrintPosition(struct JsLex *lex, int tokenPos);
+/// Transmit the current line, along with a marker of where the error was (for reporting errors)
+void jsiConsolePrintTokenLineMarker(struct JsLex *lex, int tokenPos);
+/// Print the contents of a string var to a device - directly
+void jsiTransmitStringVar(IOEventFlags device, JsVar *v);
+/// If the input line was shown in the console, remove it
+void jsiConsoleRemoveInputLine();
+/// Change what is in the inputline into something else (and update the console)
+void jsiReplaceInputLine(JsVar *newLine);
+
+/// Flags for jsiSetBusy - THESE SHOULD BE 2^N
+typedef enum {
+  BUSY_INTERACTIVE = 1,
+  BUSY_TRANSMIT    = 2,
+  // ???           = 4
+} JsiBusyDevice;
+/// Shows a busy indicator, if one is set up
+void jsiSetBusy(JsiBusyDevice device, bool isBusy);
+/// Shows a sleep indicator, if one is set up
+void jsiSetSleep(bool isSleep);
+
+
+// for jswrap_interactive/io.c ----------------------------------------------------
+typedef enum {
+ TODO_NOTHING = 0,
+ TODO_FLASH_SAVE = 1,
+ TODO_FLASH_LOAD = 2,
+ TODO_RESET = 4,
+} TODOFlags;
+#define USART_CALLBACK_NAME "_callback"
+#define USART_BAUDRATE_NAME "_baudrate"
+#define DEVICE_OPTIONS_NAME "_options"
+
+extern Pin pinBusyIndicator;
+extern Pin pinSleepIndicator;
+extern bool echo;
+extern bool allowDeepSleep;
+void jsiDumpState();
+void jsiSetTodo(TODOFlags newTodo);
+#define TIMER_MIN_INTERVAL 0.1 // in milliseconds
+extern JsVarRef timerArray; // Linked List of timers to check and run
+extern JsVarRef watchArray; // Linked List of input watches to check and run
+// end for jswrap_interactive/io.c ------------------------------------------------
+
+
+#endif /* JSINTERACTIVE_H_ */

+ 501 - 0
components/external/espruino/src/jslex.c

@@ -0,0 +1,501 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Lexer (convert JsVar strings into a series of tokens)
+ * ----------------------------------------------------------------------------
+ */
+#include "jslex.h"
+
+
+void jslSeek(JsLex *lex, JslCharPos seekToChar) {
+  jsvStringIteratorFree(&lex->it);
+  jsvStringIteratorNew(&lex->it, lex->sourceVar, seekToChar);
+}
+
+void NO_INLINE jslGetNextCh(JsLex *lex) {
+  lex->currCh = lex->nextCh;
+  lex->nextCh = jsvStringIteratorGetChar(&lex->it);
+  jsvStringIteratorNextInline(&lex->it);
+}
+
+static inline void jslTokenAppendChar(JsLex *lex, char ch) {
+  /* Add character to buffer but check it isn't too big.
+   * Also Leave ONE character at the end for null termination */
+  if (lex->tokenl < JSLEX_MAX_TOKEN_LENGTH-1) {
+    lex->token[lex->tokenl++] = ch;
+  }
+#ifdef DEBUG
+  else {
+    jsWarnAt("Token name is too long! skipping character", lex, lex->tokenStart);
+  }
+#endif
+}
+
+static bool jslIsToken(JsLex *lex, const char *token, int startOffset) {
+  int i;
+  for (i=startOffset;i<lex->tokenl;i++) {
+    if (lex->token[i]!=token[i]) return false;
+    // if token is smaller than lex->token, there will be a null char
+    // which will be different from the token
+  }
+  return token[lex->tokenl] == 0; // only match if token ends now
+}
+
+void jslGetNextToken(JsLex *lex) {
+  lex->tk = LEX_EOF;
+  lex->tokenl = 0; // clear token string
+  if (lex->tokenValue) {
+    jsvUnLock(lex->tokenValue);
+    lex->tokenValue = 0;
+  }
+  while (lex->currCh && isWhitespace(lex->currCh)) jslGetNextCh(lex);
+  // newline comments
+  if (lex->currCh=='/' && lex->nextCh=='/') {
+      while (lex->currCh && lex->currCh!='\n') jslGetNextCh(lex);
+      jslGetNextCh(lex);
+      jslGetNextToken(lex);
+      return;
+  }
+  // block comments
+  if (lex->currCh=='/' && lex->nextCh=='*') {
+      while (lex->currCh && !(lex->currCh=='*' && lex->nextCh=='/'))
+        jslGetNextCh(lex);
+      if (!lex->currCh) {
+        lex->tk = LEX_UNFINISHED_COMMENT;
+        return; /* an unfinished multi-line comment. When in interactive console,
+                   detect this and make sure we accept new lines */
+      }
+      jslGetNextCh(lex);
+      jslGetNextCh(lex);
+      jslGetNextToken(lex);
+      return;
+  }
+  // record beginning of this token
+  lex->tokenLastStart = lex->tokenStart;
+  lex->tokenStart = (JslCharPos)(lex->it.index-2);
+  // tokens
+  if (isAlpha(lex->currCh) || lex->currCh=='$') { //  IDs
+      while (isAlpha(lex->currCh) || isNumeric(lex->currCh) || lex->currCh=='$') {
+          jslTokenAppendChar(lex, lex->currCh);
+          jslGetNextCh(lex);
+      }
+      lex->tk = LEX_ID;
+      // We do fancy stuff here to reduce number of compares (hopefully GCC creates a jump table)
+      switch (lex->token[0]) {
+      case 'b': if (jslIsToken(lex,"break", 1)) lex->tk = LEX_R_BREAK;
+                break;
+      case 'c': if (jslIsToken(lex,"case", 1)) lex->tk = LEX_R_CASE;
+                else if (jslIsToken(lex,"continue", 1)) lex->tk = LEX_R_CONTINUE;
+                break;
+      case 'd': if (jslIsToken(lex,"default", 1)) lex->tk = LEX_R_DEFAULT;
+                else if (jslIsToken(lex,"do", 1)) lex->tk = LEX_R_DO;
+                break;
+      case 'e': if (jslIsToken(lex,"else", 1)) lex->tk = LEX_R_ELSE;
+                break;
+      case 'f': if (jslIsToken(lex,"false", 1)) lex->tk = LEX_R_FALSE;
+                else if (jslIsToken(lex,"for", 1)) lex->tk = LEX_R_FOR;
+                else if (jslIsToken(lex,"function", 1)) lex->tk = LEX_R_FUNCTION;
+                break;
+      case 'i': if (jslIsToken(lex,"if", 1)) lex->tk = LEX_R_IF;
+                else if (jslIsToken(lex,"in", 1)) lex->tk = LEX_R_IN;
+                else if (jslIsToken(lex,"instanceof", 1)) lex->tk = LEX_R_INSTANCEOF;
+                break;
+      case 'n': if (jslIsToken(lex,"new", 1)) lex->tk = LEX_R_NEW;
+                else if (jslIsToken(lex,"null", 1)) lex->tk = LEX_R_NULL;
+                break;
+      case 'r': if (jslIsToken(lex,"return", 1)) lex->tk = LEX_R_RETURN;
+                break;
+      case 's': if (jslIsToken(lex,"switch", 1)) lex->tk = LEX_R_SWITCH;
+                break;
+      case 't': if (jslIsToken(lex,"this", 1)) lex->tk = LEX_R_THIS;
+                else if (jslIsToken(lex,"true", 1)) lex->tk = LEX_R_TRUE;
+                else if (jslIsToken(lex,"typeof", 1)) lex->tk = LEX_R_TYPEOF;
+                break;
+      case 'u': if (jslIsToken(lex,"undefined", 1)) lex->tk = LEX_R_UNDEFINED;
+                break;
+      case 'w': if (jslIsToken(lex,"while", 1)) lex->tk = LEX_R_WHILE;
+                break;
+      case 'v': if (jslIsToken(lex,"var", 1)) lex->tk = LEX_R_VAR;
+                else if (jslIsToken(lex,"void", 1)) lex->tk = LEX_R_VOID;
+                break;
+      default: break;
+      }
+  } else if (isNumeric(lex->currCh)) { // Numbers
+      // TODO: check numbers aren't the wrong format
+      bool canBeFloating = true;
+      if (lex->currCh=='0') {
+        jslTokenAppendChar(lex, lex->currCh);
+        jslGetNextCh(lex);
+      }
+
+      if ((lex->currCh=='x' || lex->currCh=='X') ||
+          (lex->currCh=='b' || lex->currCh=='B') ||
+          (lex->currCh=='o' || lex->currCh=='O')) {
+        canBeFloating = false;
+        jslTokenAppendChar(lex, lex->currCh); jslGetNextCh(lex);
+      }
+      lex->tk = LEX_INT;
+      while (isNumeric(lex->currCh) || (!canBeFloating && isHexadecimal(lex->currCh))) {
+        jslTokenAppendChar(lex, lex->currCh);
+        jslGetNextCh(lex);
+      }
+      if (canBeFloating && lex->currCh=='.') {
+          lex->tk = LEX_FLOAT;
+          jslTokenAppendChar(lex, '.');
+          jslGetNextCh(lex);
+          while (isNumeric(lex->currCh)) {
+            jslTokenAppendChar(lex, lex->currCh);
+            jslGetNextCh(lex);
+          }
+      }
+      // do fancy e-style floating point
+      if (canBeFloating && (lex->currCh=='e'||lex->currCh=='E')) {
+        lex->tk = LEX_FLOAT;
+        jslTokenAppendChar(lex, lex->currCh); jslGetNextCh(lex);
+        if (lex->currCh=='-' || lex->currCh=='+') { jslTokenAppendChar(lex, lex->currCh); jslGetNextCh(lex); }
+        while (isNumeric(lex->currCh)) {
+          jslTokenAppendChar(lex, lex->currCh); jslGetNextCh(lex);
+        }
+      }
+  } else if (lex->currCh=='"' || lex->currCh=='\'') {
+      char delim = lex->currCh;
+      lex->tokenValue = jsvNewFromEmptyString();
+      // strings...
+      jslGetNextCh(lex);
+      while (lex->currCh && lex->currCh!=delim) {
+          if (lex->currCh == '\\') {
+              jslGetNextCh(lex);
+              char ch = lex->currCh;
+              switch (lex->currCh) {
+              case 'n'  : ch = '\n'; jslGetNextCh(lex); break;
+              case 'a'  : ch = '\a'; jslGetNextCh(lex); break;
+              case 'r'  : ch = '\r'; jslGetNextCh(lex); break;
+              case 't'  : ch = '\t'; jslGetNextCh(lex); break;
+              case 'x' : { // hex digits
+                            char buf[5] = "0x??";
+                            jslGetNextCh(lex);
+                            buf[2] = lex->currCh; jslGetNextCh(lex);
+                            buf[3] = lex->currCh; jslGetNextCh(lex);
+                            ch = (char)stringToInt(buf);
+                         } break;
+              default:
+                       if (lex->currCh>='0' && lex->currCh<='7') {
+                         // octal digits
+                         char buf[5] = "0";
+                         buf[1] = lex->currCh;
+                         int n=2;
+                         jslGetNextCh(lex);
+                         if (lex->currCh>='0' && lex->currCh<='7') {
+                           buf[n++] = lex->currCh; jslGetNextCh(lex);
+                           if (lex->currCh>='0' && lex->currCh<='7') {
+                             buf[n++] = lex->currCh; jslGetNextCh(lex);
+                           }
+                         }
+                         buf[n]=0;
+                         ch = (char)stringToInt(buf);
+                       } else {
+                         // for anything else, just push the character through
+                         jslGetNextCh(lex);
+                       }
+                       break;
+              }
+              if (lex->tokenValue) {
+                jslTokenAppendChar(lex, ch);
+                jsvAppendCharacter(lex->tokenValue, ch);
+              }
+          } else {
+            if (lex->tokenValue) {
+              jslTokenAppendChar(lex, lex->currCh);
+              jsvAppendCharacter(lex->tokenValue, lex->currCh);
+            }
+            jslGetNextCh(lex);
+          }
+      }
+      jslGetNextCh(lex);
+      lex->tk = LEX_STR;
+  } else {
+      // single chars
+      lex->tk = lex->currCh;
+      jslGetNextCh(lex);
+      if (lex->tk=='=' && lex->currCh=='=') { // ==
+          lex->tk = LEX_EQUAL;
+          jslGetNextCh(lex);
+          if (lex->currCh=='=') { // ===
+            lex->tk = LEX_TYPEEQUAL;
+            jslGetNextCh(lex);
+          }
+      } else if (lex->tk=='!' && lex->currCh=='=') { // !=
+          lex->tk = LEX_NEQUAL;
+          jslGetNextCh(lex);
+          if (lex->currCh=='=') { // !==
+            lex->tk = LEX_NTYPEEQUAL;
+            jslGetNextCh(lex);
+          }
+      } else if (lex->tk=='<') {
+        if (lex->currCh=='=') {
+          lex->tk = LEX_LEQUAL;
+          jslGetNextCh(lex);
+        } else if (lex->currCh=='<') {
+            lex->tk = LEX_LSHIFT;
+            jslGetNextCh(lex);
+            if (lex->currCh=='=') { // <<=
+              lex->tk = LEX_LSHIFTEQUAL;
+              jslGetNextCh(lex);
+            }
+        }
+      } else if (lex->tk=='>') {
+        if (lex->currCh=='=') {
+          lex->tk = LEX_GEQUAL;
+          jslGetNextCh(lex);
+        } else if (lex->currCh=='>') {
+          lex->tk = LEX_RSHIFT;
+          jslGetNextCh(lex);
+          if (lex->currCh=='=') { // >>=
+            lex->tk = LEX_RSHIFTEQUAL;
+            jslGetNextCh(lex);
+          } else if (lex->currCh=='>') { // >>>
+            jslGetNextCh(lex);
+            if (lex->currCh=='=') { // >>>=
+              lex->tk = LEX_RSHIFTUNSIGNEDEQUAL;
+              jslGetNextCh(lex);
+            } else {
+              lex->tk = LEX_RSHIFTUNSIGNED;
+            }
+          }
+        }
+      }  else if (lex->tk=='+') {
+          if (lex->currCh=='=') {
+            lex->tk = LEX_PLUSEQUAL;
+            jslGetNextCh(lex);
+          } else if (lex->currCh=='+') {
+            lex->tk = LEX_PLUSPLUS;
+            jslGetNextCh(lex);
+          }
+      }  else if (lex->tk=='-') {
+          if (lex->currCh=='=') {
+            lex->tk = LEX_MINUSEQUAL;
+            jslGetNextCh(lex);
+          } else if (lex->currCh=='-') {
+            lex->tk = LEX_MINUSMINUS;
+            jslGetNextCh(lex);
+          }
+      } else if (lex->tk=='&') {
+          if (lex->currCh=='=') {
+            lex->tk = LEX_ANDEQUAL;
+            jslGetNextCh(lex);
+          } else if (lex->currCh=='&') {
+            lex->tk = LEX_ANDAND;
+            jslGetNextCh(lex);
+          }
+      } else if (lex->tk=='|') {
+          if (lex->currCh=='=') {
+            lex->tk = LEX_OREQUAL;
+            jslGetNextCh(lex);
+          } else if (lex->tk=='|' && lex->currCh=='|') {
+            lex->tk = LEX_OROR;
+            jslGetNextCh(lex);
+          }
+      } else if (lex->tk=='^' && lex->currCh=='=') {
+          lex->tk = LEX_XOREQUAL;
+          jslGetNextCh(lex);
+      } else if (lex->tk=='*' && lex->currCh=='=') {
+          lex->tk = LEX_MULEQUAL;
+          jslGetNextCh(lex);
+      } else if (lex->tk=='/' && lex->currCh=='=') {
+          lex->tk = LEX_DIVEQUAL;
+          jslGetNextCh(lex);
+      } else if (lex->tk=='%' && lex->currCh=='=') {
+          lex->tk = LEX_MODEQUAL;
+          jslGetNextCh(lex);
+      }
+  }
+  /* This isn't quite right yet */
+  lex->tokenLastEnd = lex->tokenEnd;
+  lex->tokenEnd = (JslCharPos)(lex->it.index-3)/*because of nextCh/currCh/etc */;
+}
+
+static inline void jslPreload(JsLex *lex) {
+  // set up..
+  jslGetNextCh(lex);
+  jslGetNextCh(lex);
+  jslGetNextToken(lex);
+}
+
+void jslInit(JsLex *lex, JsVar *var) {
+  lex->sourceVar = jsvLockAgain(var);
+  // reset stuff
+  lex->tk = 0;
+  lex->tokenStart = 0;
+  lex->tokenEnd = 0;
+  lex->tokenLastStart = 0;
+  lex->tokenLastEnd = 0;
+  lex->tokenl = 0;
+  lex->tokenValue = 0;
+  // set up iterator
+  jsvStringIteratorNew(&lex->it, lex->sourceVar, 0);
+  jslPreload(lex);
+}
+
+void jslKill(JsLex *lex) {
+  lex->tk = LEX_EOF; // safety ;)
+  jsvStringIteratorFree(&lex->it);
+  if (lex->tokenValue) {
+    jsvUnLock(lex->tokenValue);
+    lex->tokenValue = 0;
+  }
+  jsvUnLock(lex->sourceVar);
+}
+
+void jslSeekTo(JsLex *lex, JslCharPos seekToChar) {
+  jslSeek(lex, seekToChar);
+  jslPreload(lex);
+}
+
+void jslReset(JsLex *lex) {
+  jslSeekTo(lex, 0);
+}
+
+void jslTokenAsString(int token, char *str, size_t len) {
+  // see JS_ERROR_TOKEN_BUF_SIZE
+  if (token>32 && token<128) {
+      assert(len>=4);
+      str[0] = '\'';
+      str[1] = (char)token;
+      str[2] = '\'';
+      str[3] = 0;
+      return;
+  }
+
+  switch (token) {
+      case LEX_EOF : strncpy(str, "EOF", len); return;
+      case LEX_ID : strncpy(str, "ID", len); return;
+      case LEX_INT : strncpy(str, "INT", len); return;
+      case LEX_FLOAT : strncpy(str, "FLOAT", len); return;
+      case LEX_STR : strncpy(str, "STRING", len); return;
+  }
+  if (token>=LEX_EQUAL && token<LEX_R_LIST_END) {
+    const char tokenNames[] =
+      /* LEX_EQUAL      :   */ "==\0"
+      /* LEX_TYPEEQUAL  :   */ "===\0"
+      /* LEX_NEQUAL     :   */ "!=\0"
+      /* LEX_NTYPEEQUAL :   */ "!==\0"
+      /* LEX_LEQUAL    :    */ "<=\0"
+      /* LEX_LSHIFT     :   */ "<<\0"
+      /* LEX_LSHIFTEQUAL :  */ "<<=\0"
+      /* LEX_GEQUAL      :  */ ">=\0"
+      /* LEX_RSHIFT      :  */ ">>\0"
+      /* LEX_RSHIFTUNSIGNED */ ">>>\0"
+      /* LEX_RSHIFTEQUAL :  */ ">>=\0"
+      /* LEX_RSHIFTUNSIGNEDEQUAL */ ">>>=\0"
+      /* LEX_PLUSEQUAL   :  */ "+=\0"
+      /* LEX_MINUSEQUAL  :  */ "-=\0"
+      /* LEX_PLUSPLUS :     */ "++\0"
+      /* LEX_MINUSMINUS     */ "--\0"
+      /* LEX_MULEQUAL :     */ "*=\0"
+      /* LEX_DIVEQUAL :     */ "/=\0"
+      /* LEX_MODEQUAL :     */ "%=\0"
+      /* LEX_ANDEQUAL :     */ "&=\0"
+      /* LEX_ANDAND :       */ "&&\0"
+      /* LEX_OREQUAL :      */ "|=\0"
+      /* LEX_OROR :         */ "||\0"
+      /* LEX_XOREQUAL :     */ "^=\0"
+
+      // reserved words
+      /*LEX_R_IF :       */ "if\0"
+      /*LEX_R_ELSE :     */ "else\0"
+      /*LEX_R_DO :       */ "do\0"
+      /*LEX_R_WHILE :    */ "while\0"
+      /*LEX_R_FOR :      */ "for\0"
+      /*LEX_R_BREAK :    */ "return\0"
+      /*LEX_R_CONTINUE   */ "continue\0"
+      /*LEX_R_FUNCTION   */ "function\0"
+      /*LEX_R_RETURN     */ "return\0"
+      /*LEX_R_VAR :      */ "var\0"
+      /*LEX_R_THIS :     */ "this\0"
+      /*LEX_R_TRUE :     */ "true\0"
+      /*LEX_R_FALSE :    */ "false\0"
+      /*LEX_R_NULL :     */ "null\0"
+      /*LEX_R_UNDEFINED  */ "undefined\0"
+      /*LEX_R_NEW :      */ "new\0"
+      /*LEX_R_IN :       */ "in\0"
+      /*LEX_R_INSTANCEOF */ "instanceof\0"
+      /*LEX_R_SWITCH */     "switch\0"
+      /*LEX_R_CASE */       "case\0"
+      /*LEX_R_DEFAULT */    "default\0"
+      /*LEX_R_TYPEOF :   */ "typeof\0"
+      /*LEX_R_VOID :     */ "void\0"
+        ;
+    unsigned int p = 0;
+    int n = token-LEX_EQUAL;
+    while (n>0 && p<sizeof(tokenNames)) {
+      while (tokenNames[p] && p<sizeof(tokenNames)) p++;
+      p++; // skip the zero
+      n--; // next token
+    }
+    assert(n==0);
+    strncpy(str, &tokenNames[p], len);
+    return;
+  }
+
+  assert(len>=10);
+  strncpy(str, "?[",len);
+  itoa(token, &str[2], 10);
+  strncat(str, "]",len);
+}
+
+void jslGetTokenString(JsLex *lex, char *str, size_t len) {
+  if (lex->tk == LEX_ID) {
+    strncpy(str, "ID:", len);
+    strncat(str, jslGetTokenValueAsString(lex), len);
+  } else if (lex->tk == LEX_STR) {
+    strncpy(str, "String:'", len);
+    strncat(str, jslGetTokenValueAsString(lex), len);
+    strncat(str, "'", len);
+  } else
+    jslTokenAsString(lex->tk, str, len);
+}
+
+char *jslGetTokenValueAsString(JsLex *lex) {
+  assert(lex->tokenl < JSLEX_MAX_TOKEN_LENGTH);
+  lex->token[lex->tokenl]  = 0; // add final null
+  return lex->token;
+}
+
+JsVar *jslGetTokenValueAsVar(JsLex *lex) {
+  if (lex->tokenValue) {
+    return jsvLockAgain(lex->tokenValue);
+  } else {
+    assert(lex->tokenl < JSLEX_MAX_TOKEN_LENGTH);
+    lex->token[lex->tokenl]  = 0; // add final null
+    return jsvNewFromString(lex->token);
+  }
+}
+
+/// Match, and return true on success, false on failure
+bool jslMatch(JsLex *lex, int expected_tk) {
+  if (lex->tk!=expected_tk) {
+      char buf[JS_ERROR_BUF_SIZE];
+      size_t bufpos = 0;
+      strncpy(&buf[bufpos], "Got ", JS_ERROR_BUF_SIZE-bufpos);
+      bufpos = strlen(buf);
+      jslGetTokenString(lex, &buf[bufpos], JS_ERROR_BUF_SIZE-bufpos);
+      bufpos = strlen(buf);
+      strncpy(&buf[bufpos], " expected ", JS_ERROR_BUF_SIZE-bufpos);
+      bufpos = strlen(buf);
+      jslTokenAsString(expected_tk, &buf[bufpos], JS_ERROR_BUF_SIZE-bufpos);
+      jsErrorAt(buf, lex, lex->tokenStart);
+      // Sod it, skip this token anyway - stops us looping
+      jslGetNextToken(lex);
+      return false;
+  }
+  jslGetNextToken(lex);
+  return true;
+}
+

+ 59 - 0
components/external/espruino/src/jslex.h

@@ -0,0 +1,59 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Lexer (convert JsVar strings into a series of tokens)
+ * ----------------------------------------------------------------------------
+ */
+#ifndef JSLEX_H_
+#define JSLEX_H_
+
+#include "jsutils.h"
+#include "jsvar.h"
+
+typedef struct JsLex
+{
+    // Actual Lexing related stuff
+    char currCh, nextCh;
+    short tk; ///< The type of the token that we have
+    JslCharPos tokenStart; ///< Position in the data at the beginning of the token we have here
+    JslCharPos tokenEnd; ///< Position in the data at the last character of the token we have here
+    JslCharPos tokenLastStart; ///< Position in the data of the first character of the last token
+    JslCharPos tokenLastEnd; ///< Position in the data of the last character of the last token
+    char token[JSLEX_MAX_TOKEN_LENGTH]; ///< Data contained in the token we have here
+    JsVar *tokenValue; ///< JsVar containing the current token - used only for strings
+    unsigned char tokenl; ///< the current length of token
+
+    /* Where we get our data from...
+     *
+     * This is a bit more tricky than normal because the data comes from JsVars,
+     * which only have fixed length strings. If we go past this, we have to go
+     * to the next jsVar...
+     */
+    JsVar *sourceVar; // the actual string var
+    JsvStringIterator it; // Iterator for the string
+} JsLex;
+
+void jslInit(JsLex *lex, JsVar *var);
+void jslKill(JsLex *lex);
+void jslReset(JsLex *lex);
+void jslSeekTo(JsLex *lex, JslCharPos seekToChar); // like jslSeek, but pre-fills characters
+
+bool jslMatch(JsLex *lex, int expected_tk); ///< Match, and return true on success, false on failure
+void jslTokenAsString(int token, char *str, size_t len); ///< output the given token as a string - for debugging
+void jslGetTokenString(JsLex *lex, char *str, size_t len);
+char *jslGetTokenValueAsString(JsLex *lex);
+JsVar *jslGetTokenValueAsVar(JsLex *lex);
+
+// Only for more 'internal' use
+void jslSeek(JsLex *lex, JslCharPos seekToChar); // like jslSeekTo, but doesn't pre-fill characters
+void jslGetNextCh(JsLex *lex);
+void jslGetNextToken(JsLex *lex); ///< Get the text token from our text string
+
+#endif /* JSLEX_H_ */

+ 2213 - 0
components/external/espruino/src/jsparse.c

@@ -0,0 +1,2213 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Recursive descent parser for code execution
+ * ----------------------------------------------------------------------------
+ */
+#include "jsparse.h"
+#include "jsinteractive.h"
+#include "jswrapper.h"
+
+/* Info about execution when Parsing - this saves passing it on the stack
+ * for each call */
+JsExecInfo execInfo;
+
+// ----------------------------------------------- Forward decls
+JsVar *jspeBase();
+JsVar *jspeBaseWithComma();
+JsVar *jspeBlock();
+JsVar *jspeStatement();
+// ----------------------------------------------- Utils
+#define JSP_MATCH_WITH_CLEANUP_AND_RETURN(TOKEN, CLEANUP_CODE, RETURN_VAL) { if (!jslMatch(execInfo.lex,(TOKEN))) { jspSetError(); CLEANUP_CODE; return RETURN_VAL; } }
+#define JSP_MATCH_WITH_RETURN(TOKEN, RETURN_VAL) JSP_MATCH_WITH_CLEANUP_AND_RETURN(TOKEN, , RETURN_VAL)
+#define JSP_MATCH(TOKEN) JSP_MATCH_WITH_CLEANUP_AND_RETURN(TOKEN, , 0)
+#define JSP_SHOULD_EXECUTE (((execInfo.execute)&EXEC_RUN_MASK)==EXEC_YES)
+#define JSP_SAVE_EXECUTE() JsExecFlags oldExecute = execInfo.execute
+#define JSP_RESTORE_EXECUTE() execInfo.execute = (execInfo.execute&(JsExecFlags)(~EXEC_SAVE_RESTORE_MASK)) | (oldExecute&EXEC_SAVE_RESTORE_MASK);
+#define JSP_HAS_ERROR (((execInfo.execute)&EXEC_ERROR_MASK)!=0)
+
+/// if interrupting execution, this is set
+bool jspIsInterrupted() {
+  return (execInfo.execute & EXEC_INTERRUPTED)!=0;
+}
+
+/// if interrupting execution, this is set
+void jspSetInterrupted(bool interrupt) {
+  if (interrupt)
+    execInfo.execute = execInfo.execute | EXEC_INTERRUPTED;
+  else
+    execInfo.execute = execInfo.execute & (JsExecFlags)~EXEC_INTERRUPTED;
+}
+
+static inline void jspSetError() {
+  execInfo.execute = (execInfo.execute & (JsExecFlags)~EXEC_YES) | EXEC_ERROR;
+}
+
+bool jspHasError() {
+  return JSP_HAS_ERROR;
+}
+
+///< Same as jsvSetValueOfName, but nice error message
+void jspReplaceWith(JsVar *dst, JsVar *src) {
+  // If this is an index in an array buffer, write directly into the array buffer
+  if (jsvIsArrayBufferName(dst)) {
+    JsVarInt idx = jsvGetInteger(dst);
+    JsVar *arrayBuffer = jsvLock(dst->firstChild);
+    jsvArrayBufferSet(arrayBuffer, idx, src);
+    jsvUnLock(arrayBuffer);
+    return;
+  }
+  // if destination isn't there, isn't a 'name', or is used, give an error
+  if (!jsvIsName(dst)) {
+    jsErrorAt("Unable to assign value to non-reference", execInfo.lex, execInfo.lex->tokenLastEnd);
+    jspSetError();
+    return;
+  }
+  jsvSetValueOfName(dst, src);
+}
+
+void jspeiInit(JsParse *parse, JsLex *lex) {
+  execInfo.parse = parse;
+  execInfo.lex = lex;
+  execInfo.scopeCount = 0;
+  execInfo.execute = EXEC_YES;
+}
+
+void jspeiKill() {
+  execInfo.parse = 0;
+  execInfo.lex = 0;
+  assert(execInfo.scopeCount==0);
+}
+
+bool jspeiAddScope(JsVarRef scope) {
+  if (execInfo.scopeCount >= JSPARSE_MAX_SCOPES) {
+    jsError("Maximum number of scopes exceeded");
+    jspSetError();
+    return false;
+  }
+  execInfo.scopes[execInfo.scopeCount++] = jsvRefRef(scope);
+  return true;
+}
+
+void jspeiRemoveScope() {
+  if (execInfo.scopeCount <= 0) {
+    jsErrorInternal("Too many scopes removed");
+    jspSetError();
+    return;
+  }
+  jsvUnRefRef(execInfo.scopes[--execInfo.scopeCount]);
+}
+
+JsVar *jspeiFindInScopes(const char *name) {
+  int i;
+  for (i=execInfo.scopeCount-1;i>=0;i--) {
+    JsVar *ref = jsvFindChildFromStringRef(execInfo.scopes[i], name, false);
+    if (ref) return ref;
+  }
+  return jsvFindChildFromString(execInfo.parse->root, name, false);
+}
+
+JsVar *jspeiFindOnTop(const char *name, bool createIfNotFound) {
+  if (execInfo.scopeCount>0)
+    return jsvFindChildFromStringRef(execInfo.scopes[execInfo.scopeCount-1], name, createIfNotFound);
+  return jsvFindChildFromString(execInfo.parse->root, name, createIfNotFound);
+}
+JsVar *jspeiFindNameOnTop(JsVar *childName, bool createIfNotFound) {
+  if (execInfo.scopeCount>0)
+    return jsvFindChildFromVarRef(execInfo.scopes[execInfo.scopeCount-1], childName, createIfNotFound);
+  return jsvFindChildFromVar(execInfo.parse->root, childName, createIfNotFound);
+}
+
+/** Here we assume that we have already looked in the parent itself -
+ * and are now going down looking at the stuff it inherited */
+JsVar *jspeiFindChildFromStringInParents(JsVar *parent, const char *name) {
+  if (jsvIsObject(parent)) {
+    // If an object, look for an 'inherits' var
+    JsVar *inheritsFrom = jsvObjectGetChild(parent, JSPARSE_INHERITS_VAR, 0);
+
+    // if there's no inheritsFrom, just default to 'Object.prototype'
+    if (!inheritsFrom) {
+      JsVar *obj = jsvObjectGetChild(execInfo.parse->root, "Object", 0);
+      if (obj) {
+        inheritsFrom = jsvObjectGetChild(obj, JSPARSE_PROTOTYPE_VAR, 0);
+        jsvUnLock(obj);
+      }
+    }
+
+    if (inheritsFrom && inheritsFrom!=parent) {
+      // we have what it inherits from (this is ACTUALLY the prototype var)
+      // https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/proto
+      JsVar *child = jsvFindChildFromString(inheritsFrom, name, false);
+      if (!child)
+        child = jspeiFindChildFromStringInParents(inheritsFrom, name);
+      jsvUnLock(inheritsFrom);
+      if (child) return child;
+    }
+  } else { // Not actually an object - but might be an array/string/etc
+    const char *objectName = jswGetBasicObjectName(parent);
+    while (objectName) {
+      JsVar *objName = jsvFindChildFromString(execInfo.parse->root, objectName, false);
+      if (objName) {
+        JsVar *result = 0;
+        JsVar *obj = jsvSkipNameAndUnLock(objName);
+        if (obj) {
+          // We have found an object with this name - search for the prototype var
+          JsVar *proto = jsvObjectGetChild(obj, JSPARSE_PROTOTYPE_VAR, 0);
+          if (proto) {
+            result = jsvFindChildFromString(proto, name, false);
+            jsvUnLock(proto);
+          }
+          jsvUnLock(obj);
+        }
+        if (result) return result;
+      }
+      /* We haven't found anything in the actual object, we should check the 'Object' itself
+        eg, we tried 'String', so now we should try 'Object'. Built-in types don't have room for
+        a prototype field, so we hard-code it */
+      objectName = jswGetBasicObjectPrototypeName(objectName);
+    }
+  }
+
+  // no luck!
+  return 0;
+}
+
+JsVar *jspeiGetScopesAsVar() {
+  if (execInfo.scopeCount==0) return 0;
+  JsVar *arr = jsvNewWithFlags(JSV_ARRAY);
+  int i;
+  for (i=0;i<execInfo.scopeCount;i++) {
+      //printf("%d %d\n",i,execInfo.scopes[i]);
+      JsVar *scope = jsvLock(execInfo.scopes[i]);
+      JsVar *idx = jsvMakeIntoVariableName(jsvNewFromInteger(i), scope);
+      jsvUnLock(scope);
+      if (!idx) { // out of memort
+        jspSetError();
+        return arr;
+      }
+      jsvAddName(arr, idx);
+      jsvUnLock(idx);
+  }
+  //printf("%d\n",arr->firstChild);
+  return arr;
+}
+
+void jspeiLoadScopesFromVar(JsVar *arr) {
+    execInfo.scopeCount = 0;
+    //printf("%d\n",arr->firstChild);
+    JsVarRef childref = arr->firstChild;
+    while (childref) {
+      JsVar *child = jsvLock(childref);
+      //printf("%d %d %d %d\n",execInfo.scopeCount,childref,child, child->firstChild);
+      execInfo.scopes[execInfo.scopeCount] = jsvRefRef(child->firstChild);
+      execInfo.scopeCount++;
+      childref = child->nextSibling;
+      jsvUnLock(child);
+    }
+}
+// -----------------------------------------------
+#ifdef ARM
+extern int _end;
+#endif
+bool jspCheckStackPosition() {
+#ifdef ARM
+  void *frame = __builtin_frame_address(0);
+  if ((char*)frame < ((char*)&_end)+1024/*so many bytes leeway*/) {
+//   jsiConsolePrintf("frame: %d,end:%d\n",(int)frame,(int)&_end);
+    jsErrorAt("Too much recursion - the stack is about to overflow", execInfo.lex, execInfo.lex->tokenStart );
+    jspSetInterrupted(true);
+    return false;
+  }
+#endif
+  return true;
+}
+
+
+// Set execFlags such that we are not executing
+void jspSetNoExecute() {
+  execInfo.execute = (execInfo.execute & (JsExecFlags)(int)~EXEC_RUN_MASK) | EXEC_NO;
+}
+
+// parse single variable name
+bool jspParseVariableName() {
+  JSP_MATCH(LEX_ID);
+  return true;
+}
+
+// parse function with no arguments
+bool jspParseEmptyFunction() {
+  JSP_MATCH(LEX_ID);
+  JSP_MATCH('(');
+  if (execInfo.lex->tk != ')')
+    jsvUnLock(jspeBase());
+  // throw away extra params
+  while (!JSP_HAS_ERROR && execInfo.lex->tk != ')') {
+    JSP_MATCH(',');
+    jsvUnLock(jspeBase());
+  }
+  JSP_MATCH(')');
+  return true;
+}
+
+// parse function with a single argument, return its value (no names!)
+JsVar *jspParseSingleFunction() {
+  JsVar *v = 0;
+  JSP_MATCH(LEX_ID);
+  JSP_MATCH('(');
+  if (execInfo.lex->tk != ')')
+    v = jsvSkipNameAndUnLock(jspeBase());
+  // throw away extra params
+  while (!JSP_HAS_ERROR && execInfo.lex->tk != ')') {
+    JSP_MATCH_WITH_RETURN(',', v);
+    jsvUnLock(jspeBase());
+  }
+  JSP_MATCH_WITH_RETURN(')', v);
+  return v;
+}
+
+/// parse function with max 4 arguments (can set arg to 0 to avoid parse). Usually first arg will be 0, but if we DON'T want to skip names on an arg stuff, we can say
+bool jspParseFunction(JspSkipFlags skipName, JsVar **a, JsVar **b, JsVar **c, JsVar **d) {
+  if (a) *a = 0;
+  if (b) *b = 0;
+  if (c) *c = 0; 
+  if (d) *d = 0;
+  JSP_MATCH(LEX_ID);
+  JSP_MATCH('(');
+  if (a && execInfo.lex->tk != ')') {
+    *a = jspeBase();
+    if (!(skipName&JSP_NOSKIP_A)) *a = jsvSkipNameAndUnLock(*a);
+  }
+  if (b && execInfo.lex->tk != ')') {
+    JSP_MATCH(',');
+    *b = jspeBase();
+    if (!(skipName&JSP_NOSKIP_B)) *b = jsvSkipNameAndUnLock(*b);
+  }
+  if (c && execInfo.lex->tk != ')') {
+    JSP_MATCH(',');
+    *c = jspeBase();
+    if (!(skipName&JSP_NOSKIP_C)) *c = jsvSkipNameAndUnLock(*c);
+  }
+  if (d && execInfo.lex->tk != ')') {
+    JSP_MATCH(',');
+    *d = jspeBase();
+    if (!(skipName&JSP_NOSKIP_D)) *d = jsvSkipNameAndUnLock(*d);
+  }
+  // throw away extra params
+  while (!JSP_HAS_ERROR && execInfo.lex->tk != ')') {
+    JSP_MATCH(',');
+    jsvUnLock(jspeBase());
+  }
+  JSP_MATCH(')');
+  return true;
+}
+
+/// parse function with max 8 arguments (can set arg to 0 to avoid parse). Usually first arg will be 0, but if we DON'T want to skip names on an arg stuff, we can say
+bool jspParseFunction8(JspSkipFlags skipName, JsVar **a, JsVar **b, JsVar **c, JsVar **d, JsVar **e, JsVar **f, JsVar **g, JsVar **h) {
+  if (a) *a = 0;
+  if (b) *b = 0;
+  if (c) *c = 0;
+  if (d) *d = 0;
+  if (e) *e = 0;
+  if (f) *f = 0;
+  if (g) *g = 0;
+  if (h) *h = 0;
+  JSP_MATCH(LEX_ID);
+  JSP_MATCH('(');
+  if (a && execInfo.lex->tk != ')') {
+    *a = jspeBase();
+    if (!(skipName&JSP_NOSKIP_A)) *a = jsvSkipNameAndUnLock(*a);
+  }
+  if (b && execInfo.lex->tk != ')') {
+    JSP_MATCH(',');
+    *b = jspeBase();
+    if (!(skipName&JSP_NOSKIP_B)) *b = jsvSkipNameAndUnLock(*b);
+  }
+  if (c && execInfo.lex->tk != ')') {
+    JSP_MATCH(',');
+    *c = jspeBase();
+    if (!(skipName&JSP_NOSKIP_C)) *c = jsvSkipNameAndUnLock(*c);
+  }
+  if (d && execInfo.lex->tk != ')') {
+    JSP_MATCH(',');
+    *d = jspeBase();
+    if (!(skipName&JSP_NOSKIP_D)) *d = jsvSkipNameAndUnLock(*d);
+  }
+  if (e && execInfo.lex->tk != ')') {
+    JSP_MATCH(',');
+    *e = jspeBase();
+    if (!(skipName&JSP_NOSKIP_E)) *e = jsvSkipNameAndUnLock(*e);
+  }
+  if (f && execInfo.lex->tk != ')') {
+    JSP_MATCH(',');
+    *f = jspeBase();
+    if (!(skipName&JSP_NOSKIP_F)) *f = jsvSkipNameAndUnLock(*f);
+  }
+  if (g && execInfo.lex->tk != ')') {
+    JSP_MATCH(',');
+    *g = jspeBase();
+    if (!(skipName&JSP_NOSKIP_G)) *g = jsvSkipNameAndUnLock(*g);
+  }
+  if (h && execInfo.lex->tk != ')') {
+    JSP_MATCH(',');
+    *h = jspeBase();
+    if (!(skipName&JSP_NOSKIP_H)) *h = jsvSkipNameAndUnLock(*h);
+  }
+  // throw away extra params
+  while (!JSP_HAS_ERROR && execInfo.lex->tk != ')') {
+    JSP_MATCH(',');
+    jsvUnLock(jspeBase());
+  }
+  JSP_MATCH(')');
+  return true;
+}
+
+/// parse a function with any number of argument, and return an array of de-named aruments
+JsVar *jspParseFunctionAsArray() {
+  JSP_MATCH(LEX_ID);
+  JsVar *arr = jsvNewWithFlags(JSV_ARRAY);
+  if (!arr) return 0; // out of memory
+  JSP_MATCH_WITH_CLEANUP_AND_RETURN('(', jsvUnLock(arr), 0);
+  while (!JSP_HAS_ERROR && execInfo.lex->tk!=')' && execInfo.lex->tk!=LEX_EOF) {
+    JsVar *arg = jsvSkipNameAndUnLock(jspeBase());
+    jsvArrayPushAndUnLock(arr, arg); // even if undefined
+    if (execInfo.lex->tk!=')') JSP_MATCH_WITH_CLEANUP_AND_RETURN(',', jsvUnLock(arr), 0);
+  }
+  JSP_MATCH_WITH_CLEANUP_AND_RETURN(')', jsvUnLock(arr), 0);
+  return arr;
+}
+
+// ----------------------------------------------
+
+// we return a value so that JSP_MATCH can return 0 if it fails (if we pass 0, we just parse all args)
+bool jspeFunctionArguments(JsVar *funcVar) {
+  JSP_MATCH('(');
+  while (execInfo.lex->tk!=')') {
+      if (funcVar) {
+        JsVar *param = jsvAddNamedChild(funcVar, 0, jslGetTokenValueAsString(execInfo.lex));
+        if (!param) { // out of memory
+          jspSetError();
+          return false;
+        }
+        param->flags |= JSV_FUNCTION_PARAMETER; // force this to be called a function parameter
+        jsvUnLock(param);
+      }
+      JSP_MATCH(LEX_ID);
+      if (execInfo.lex->tk!=')') JSP_MATCH(',');
+  }
+  JSP_MATCH(')');
+  return true;
+}
+
+bool jspeParseNativeFunction(JsCallback callbackPtr) {
+    char funcName[JSLEX_MAX_TOKEN_LENGTH];
+    JsVar *funcVar;
+    JsVar *base = jsvLockAgain(execInfo.parse->root);
+    JSP_MATCH(LEX_R_FUNCTION);
+    // not too bothered about speed/memory here as only called on init :)
+    strncpy(funcName, jslGetTokenValueAsString(execInfo.lex), JSLEX_MAX_TOKEN_LENGTH);
+    JSP_MATCH(LEX_ID);
+    /* Check for dots, we might want to do something like function 'String.substring' ... */
+    while (execInfo.lex->tk == '.') {
+      JsVar *link;
+      JSP_MATCH('.');
+      link = jsvFindChildFromString(base, funcName, false);
+      // if it doesn't exist, make a new object class
+      if (!link) {
+        JsVar *obj = jsvNewWithFlags(JSV_OBJECT);
+        link = jsvAddNamedChild(base, obj, funcName);
+        jsvUnLock(obj);
+      }
+      // set base to the object (not the name)
+      jsvUnLock(base);
+      base = jsvSkipNameAndUnLock(link);
+      // Look for another name
+      strncpy(funcName, jslGetTokenValueAsString(execInfo.lex), JSLEX_MAX_TOKEN_LENGTH);
+      JSP_MATCH(LEX_ID);
+    }
+    // So now, base points to an object where we want our function
+    funcVar = jsvNewWithFlags(JSV_FUNCTION | JSV_NATIVE);
+    if (!funcVar) {
+      jsvUnLock(base);
+      jspSetError();
+      return false; // Out of memory
+    }
+    funcVar->varData.callback = callbackPtr;
+    jspeFunctionArguments(funcVar);
+
+    if (JSP_HAS_ERROR) { // probably out of memory while parsing
+      jsvUnLock(base);
+      jsvUnLock(funcVar);
+      return false;
+    }
+    // Add the function with its name
+    JsVar *funcNameVar = jsvFindChildFromString(base, funcName, true);
+    if (funcNameVar) // could be out of memory
+      jsvUnLock(jsvSetValueOfName(funcNameVar, funcVar)); // unlocks funcNameVar
+    jsvUnLock(base);
+    jsvUnLock(funcVar);
+    return true;
+}
+
+bool jspAddNativeFunction(JsParse *parse, const char *funcDesc, JsCallback callbackPtr) {
+    JsVar *fncode = jsvNewFromString(funcDesc);
+    if (!fncode) return false; // out of memory!
+
+    JSP_SAVE_EXECUTE();
+    JsExecInfo oldExecInfo = execInfo;
+
+    // Set up Lexer
+
+    JsLex lex;
+    jslInit(&lex, fncode);
+    jsvUnLock(fncode);
+
+    
+    jspeiInit(parse, &lex);
+
+    // Parse
+    bool success = jspeParseNativeFunction(callbackPtr);
+    if (!success) {
+      jsError("Parsing Native Function failed!");
+      jspSetError();
+    }
+
+
+    // cleanup
+    jspeiKill();
+    jslKill(&lex);
+    JSP_RESTORE_EXECUTE();
+    oldExecInfo.execute = execInfo.execute; // JSP_RESTORE_EXECUTE has made this ok.
+    execInfo = oldExecInfo;
+
+    return success;
+}
+
+JsVar *jspeFunctionDefinition() {
+  // actually parse a function... We assume that the LEX_FUNCTION and name
+  // have already been parsed
+  JsVar *funcVar = 0;
+  if (JSP_SHOULD_EXECUTE)
+    funcVar = jsvNewWithFlags(JSV_FUNCTION);
+  // Get arguments save them to the structure
+  if (!jspeFunctionArguments(funcVar)) {
+    jsvUnLock(funcVar);
+    // parse failed
+    return 0;
+  }
+  // Get the code - first parse it so we know where it stops
+  JslCharPos funcBegin = execInfo.lex->tokenStart;
+  JSP_SAVE_EXECUTE();
+  jspSetNoExecute();
+  jsvUnLock(jspeBlock());
+  JSP_RESTORE_EXECUTE();
+  // Then create var and set
+  if (JSP_SHOULD_EXECUTE) {
+    // code var
+    JsVar *funcCodeVar = jsvNewFromLexer(execInfo.lex, funcBegin, (JslCharPos)(execInfo.lex->tokenLastEnd+1));
+    jsvUnLock(jsvAddNamedChild(funcVar, funcCodeVar, JSPARSE_FUNCTION_CODE_NAME));
+    jsvUnLock(funcCodeVar);
+    // scope var
+    JsVar *funcScopeVar = jspeiGetScopesAsVar();
+    if (funcScopeVar) {
+      jsvUnLock(jsvAddNamedChild(funcVar, funcScopeVar, JSPARSE_FUNCTION_SCOPE_NAME));
+      jsvUnLock(funcScopeVar);
+    }
+  }
+  return funcVar;
+}
+
+/* Parse just the brackets of a function - and throw
+ * everything away */
+bool jspeParseFunctionCallBrackets() {
+  JSP_MATCH('(');
+  while (!JSP_HAS_ERROR && execInfo.lex->tk != ')') {
+    jsvUnLock(jspeBase());
+    if (execInfo.lex->tk!=')') JSP_MATCH(',');
+  }
+  if (!JSP_HAS_ERROR) JSP_MATCH(')');
+  return 0;
+}
+
+/** Handle a function call (assumes we've parsed the function name and we're
+ * on the start bracket). 'thisArg' is the value of the 'this' variable when the
+ * function is executed (it's usually the parent object)
+ *
+ * If !isParsing and arg0!=0, argument 0 is set to what is supplied (same with arg1)
+ *
+ * functionName is used only for error reporting - and can be 0
+ */
+JsVar *jspeFunctionCall(JsVar *function, JsVar *functionName, JsVar *thisArg, bool isParsing, int argCount, JsVar **argPtr) {
+  if (JSP_SHOULD_EXECUTE && !function) {
+      jsErrorAt("Function not found! Skipping.", execInfo.lex, execInfo.lex->tokenLastStart );
+      jspSetError();
+  }
+
+  if (JSP_SHOULD_EXECUTE) jspCheckStackPosition(); // try and ensure that we won't overflow our stack
+
+  if (JSP_SHOULD_EXECUTE && function) {
+    JsVar *functionRoot;
+    JsVar *functionCode = 0;
+    JsVar *returnVarName;
+    JsVar *returnVar;
+    JsVarRef v;
+    if (!jsvIsFunction(function)) {
+        char buf[JS_ERROR_BUF_SIZE];
+        strncpy(buf, "Expecting a function to call", JS_ERROR_BUF_SIZE);
+        const char *name = jswGetBasicObjectName(function);
+        if (name) {
+          strncat(buf, ", got a ", JS_ERROR_BUF_SIZE);
+          strncat(buf, name, JS_ERROR_BUF_SIZE);
+        }
+        jsErrorAt(buf, execInfo.lex, execInfo.lex->tokenLastStart );
+        jspSetError();
+        return 0;
+    }
+
+    /** Special case - we're parsing and we hit an already-defined function
+     * that has no 'code'. This means that we should use jswHandleFunctionCall
+     * to try and parse it */
+    if (!jsvIsNative(function)) {
+      functionCode = jsvFindChildFromString(function, JSPARSE_FUNCTION_CODE_NAME, false);
+      if (isParsing && !functionCode) {
+        char buf[32];
+        jsvGetString(functionName, buf, sizeof(buf));
+        JslCharPos pos = execInfo.lex->tokenStart;
+        jslSeekTo(execInfo.lex, execInfo.lex->tokenLastStart); // NASTY! because jswHandleFunctionCall expects to parse IDs
+        JsVar *res = jswHandleFunctionCall(0, 0, buf);
+        // but we didn't find anything - so just carry on...
+        if (res!=JSW_HANDLEFUNCTIONCALL_UNHANDLED)
+          return res;
+        jslSeekTo(execInfo.lex, pos); // NASTY!
+      }
+    }
+
+    if (isParsing) JSP_MATCH('(');
+    // create a new symbol table entry for execution of this function
+    // OPT: can we cache this function execution environment + param variables?
+    // OPT: Probably when calling a function ONCE, use it, otherwise when recursing, make new?
+    functionRoot = jsvNewWithFlags(JSV_FUNCTION);
+    if (!functionRoot) { // out of memory
+      jspSetError();
+      return 0;
+    }
+    JsVar *oldThisVar = execInfo.thisVar;
+    execInfo.thisVar = thisArg;
+    if (isParsing) {
+      int hadParams = 0;
+      // grab in all parameters. We go around this loop until we've run out
+      // of named parameters AND we've parsed all the supplied arguments
+      v = function->firstChild;
+      while (!JSP_HAS_ERROR && (v || execInfo.lex->tk!=')')) {
+        JsVar *param = 0;
+        if (v) param = jsvLock(v);
+        bool paramDefined = jsvIsFunctionParameter(param);
+        if (execInfo.lex->tk!=')' || paramDefined) {
+          hadParams++;
+          JsVar *value = 0;
+          // ONLY parse this if it was supplied, otherwise leave 0 (undefined)
+          if (execInfo.lex->tk!=')')
+            value = jspeBase();
+          // and if execute, copy it over
+          if (JSP_SHOULD_EXECUTE) {
+            value = jsvSkipNameAndUnLock(value);
+            JsVar *paramName = paramDefined ? jsvCopy(param) : jsvNewFromEmptyString();
+            paramName->flags |= JSV_FUNCTION_PARAMETER; // force this to be called a function parameter
+            JsVar *newValueName = jsvMakeIntoVariableName(paramName, value);
+            if (newValueName) { // could be out of memory
+              jsvAddName(functionRoot, newValueName);
+            } else
+              jspSetError();
+            jsvUnLock(newValueName);
+          }
+          jsvUnLock(value);
+          if (execInfo.lex->tk!=')') JSP_MATCH(',');
+        }
+        if (param) {
+          v = param->nextSibling;
+          jsvUnLock(param);
+        }
+      }
+      JSP_MATCH(')');
+    } else if (JSP_SHOULD_EXECUTE && argCount>0) {  // and NOT isParsing
+      int args = 0;
+      v = function->firstChild;
+      while (args<argCount) {
+        JsVar *param = v ? jsvLock(v) : 0;
+        bool paramDefined = jsvIsFunctionParameter(param);
+        JsVar *paramName = paramDefined ? jsvCopy(param) : jsvNewFromEmptyString();
+        paramName->flags |= JSV_FUNCTION_PARAMETER; // force this to be called a function parameter
+        JsVar *newValueName = jsvMakeIntoVariableName(paramName, argPtr[args]);
+        if (newValueName) // could be out of memory - or maybe just not supplied!
+          jsvAddName(functionRoot, newValueName);
+        jsvUnLock(newValueName);
+        args++;
+        if (param) {
+          v = param->nextSibling;
+          jsvUnLock(param);
+        }
+      }
+    } 
+    // setup a return variable
+    returnVarName = jsvAddNamedChild(functionRoot, 0, JSPARSE_RETURN_VAR);
+    if (!returnVarName) // out of memory
+      jspSetError();
+    //jsvTrace(jsvGetRef(functionRoot), 5); // debugging
+
+    if (!JSP_HAS_ERROR) {
+      if (jsvIsNative(function)) {
+        assert(function->varData.callback);
+        if (function->varData.callback)
+          function->varData.callback(jsvGetRef(functionRoot));
+      } else {
+        // save old scopes
+        JsVarRef oldScopes[JSPARSE_MAX_SCOPES];
+        int oldScopeCount;
+        int i;
+        oldScopeCount = execInfo.scopeCount;
+        for (i=0;i<execInfo.scopeCount;i++)
+          oldScopes[i] = execInfo.scopes[i];
+        // if we have a scope var, load it up. We may not have one if there were no scopes apart from root
+        JsVar *functionScope = jsvFindChildFromString(function, JSPARSE_FUNCTION_SCOPE_NAME, false);
+        if (functionScope) {
+            JsVar *functionScopeVar = jsvLock(functionScope->firstChild);
+            //jsvTrace(jsvGetRef(functionScopeVar),5);
+            jspeiLoadScopesFromVar(functionScopeVar);
+            jsvUnLock(functionScopeVar);
+            jsvUnLock(functionScope);
+        } else {
+            // no scope var defined? We have no scopes at all!
+            execInfo.scopeCount = 0;
+        }
+        // add the function's execute space to the symbol table so we can recurse
+        if (jspeiAddScope(jsvGetRef(functionRoot))) {
+          /* Adding scope may have failed - we may have descended too deep - so be sure
+           * not to pull somebody else's scope off
+           */
+
+          /* 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... */
+          if (functionCode) {
+            JsLex *oldLex;
+            JsVar* functionCodeVar = jsvSkipNameAndUnLock(functionCode);
+            JsLex newLex;
+            jslInit(&newLex, functionCodeVar);
+            jsvUnLock(functionCodeVar);
+
+            oldLex = execInfo.lex;
+            execInfo.lex = &newLex;
+            JSP_SAVE_EXECUTE();
+            jspeBlock();
+            bool hasError = JSP_HAS_ERROR;
+            JSP_RESTORE_EXECUTE(); // because return will probably have set execute to false
+            jslKill(&newLex);
+            execInfo.lex = oldLex;
+            if (hasError) {
+              jsiConsolePrint("in function ");
+              if (jsvIsString(functionName)) {
+                jsiConsolePrint("\"");
+                jsiConsolePrintStringVar(functionName);
+                jsiConsolePrint("\" ");
+              }
+              jsiConsolePrint("called from ");
+              if (execInfo.lex)
+                jsiConsolePrintPosition(execInfo.lex, execInfo.lex->tokenLastEnd);
+              else
+                jsiConsolePrint("system\n");
+              jspSetError();
+            }
+          }
+
+          jspeiRemoveScope();
+        }
+
+        // Unref old scopes
+        for (i=0;i<execInfo.scopeCount;i++)
+            jsvUnRefRef(execInfo.scopes[i]);
+        // restore function scopes
+        for (i=0;i<oldScopeCount;i++)
+            execInfo.scopes[i] = oldScopes[i];
+        execInfo.scopeCount = oldScopeCount;
+      }
+    }
+
+    /* Return to old 'this' var. No need to unlock as we never locked before */
+    execInfo.thisVar = oldThisVar;
+
+    /* get the real return var before we remove it from our function */
+    returnVar = jsvSkipNameAndUnLock(returnVarName);
+    if (returnVarName) // could have failed with out of memory
+      jsvSetValueOfName(returnVarName, 0); // remove return value (which helps stops circular references)
+    jsvUnLock(functionRoot);
+    if (returnVar)
+      return returnVar;
+    else
+      return 0;
+  } else if (isParsing) { // ---------------------------------- function, but not executing - just parse args and be done
+    jspeParseFunctionCallBrackets();
+    /* Do not return function, as it will be unlocked! */
+    return 0;
+  } else return 0;
+}
+
+JsVar *jspeFactorSingleId() {
+  JsVar *a = JSP_SHOULD_EXECUTE ? jspeiFindInScopes(jslGetTokenValueAsString(execInfo.lex)) : 0;
+  if (JSP_SHOULD_EXECUTE && !a) {
+    const char *tokenName = jslGetTokenValueAsString(execInfo.lex); // BEWARE - this won't hang around forever!
+    /* Special case! We haven't found the variable, so check out
+     * and see if it's one of our builtins...  */
+    if (jswIsBuiltInObject(tokenName)) {
+      JsVar *obj = jspNewBuiltin(tokenName);
+      if (obj) { // not out of memory
+        a = jsvAddNamedChild(execInfo.parse->root, obj, tokenName);
+        jsvUnLock(obj);
+      }
+    } else {
+      a = jswHandleFunctionCall(0, 0, tokenName);
+      if (a != JSW_HANDLEFUNCTIONCALL_UNHANDLED)
+        return 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 = jsvMakeIntoVariableName(jslGetTokenValueAsVar(execInfo.lex), 0);
+    }
+  }
+  JSP_MATCH_WITH_RETURN(LEX_ID, a);
+
+  return a;
+}
+
+JsVar *jspeFactorMember(JsVar *a, JsVar **parentResult) {
+  /* The parent if we're executing a method call */
+  JsVar *parent = 0;
+
+  while (execInfo.lex->tk=='.' || execInfo.lex->tk=='[') {
+      if (execInfo.lex->tk == '.') { // ------------------------------------- Record Access
+          JSP_MATCH('.');
+          if (JSP_SHOULD_EXECUTE) {
+            // Note: name will go away when we oarse something else!
+            const char *name = jslGetTokenValueAsString(execInfo.lex);
+
+            JsVar *aVar = jsvSkipName(a);
+            JsVar *child = 0;
+            if (aVar && jswGetBasicObjectName(aVar)) {
+              // if we're an object (or pretending to be one)
+              if (jsvHasChildren(aVar))
+                child = jsvFindChildFromString(aVar, name, false);
+
+              if (!child)
+                child = jspeiFindChildFromStringInParents(aVar, name);
+
+
+              if (child) {
+                // it was found - no need for name ptr now, so match!
+                JSP_MATCH_WITH_CLEANUP_AND_RETURN(LEX_ID, jsvUnLock(parent);jsvUnLock(a);, child);
+              } else { // NOT FOUND...
+                /* Check for builtins via separate function
+                 * This way we save on RAM for built-ins because all comes out of program code.
+                 *
+                 * We don't check for prototype vars, so people can overload the built
+                 * in functions (eg. Person.prototype.toString). HOWEVER if we did
+                 * this for 'this' then we couldn't say 'this.toString()'
+                 * */
+                if (!jsvIsString(a) || (!jsvIsStringEqual(a, JSPARSE_PROTOTYPE_VAR))) // don't try and use builtins on the prototype var!
+                  child = jswHandleFunctionCall(aVar, a/*name*/, name);
+                else
+                  child = JSW_HANDLEFUNCTIONCALL_UNHANDLED;
+                if (child == JSW_HANDLEFUNCTIONCALL_UNHANDLED) {
+                  child = 0;
+                  // It wasn't handled... We already know this is an object so just add a new child
+                  if (jsvIsObject(aVar) || jsvIsFunction(aVar) || jsvIsArray(aVar)) {
+                    JsVar *value = 0;
+                    if (jsvIsFunction(aVar) && strcmp(name, JSPARSE_PROTOTYPE_VAR)==0)
+                      value = jsvNewWithFlags(JSV_OBJECT); // prototype is supposed to be an object
+                    child = jsvAddNamedChild(aVar, value, name);
+                    jsvUnLock(value);
+                  } else {
+                    // could have been a string...
+                    jsErrorAt("Field or method does not already exist, and can't create it on a non-object", execInfo.lex, execInfo.lex->tokenLastEnd);
+                    jspSetError();
+                  }
+                  JSP_MATCH_WITH_CLEANUP_AND_RETURN(LEX_ID, jsvUnLock(parent);jsvUnLock(a);, child);
+                }
+              }
+            } else {
+                jsErrorAt("Using '.' operator on non-object", execInfo.lex, execInfo.lex->tokenLastEnd);
+                jspSetError();
+                JSP_MATCH_WITH_CLEANUP_AND_RETURN(LEX_ID, jsvUnLock(parent);jsvUnLock(a);, child);
+            }
+            jsvUnLock(parent);
+            parent = aVar;
+            jsvUnLock(a);
+            a = child;
+          } else {
+            // Not executing, just match
+            JSP_MATCH_WITH_RETURN(LEX_ID, a);
+          }
+      } else if (execInfo.lex->tk == '[') { // ------------------------------------- Array Access
+          JsVar *index;
+          JSP_MATCH('[');
+          index = jspeBase();
+          JSP_MATCH_WITH_CLEANUP_AND_RETURN(']', jsvUnLock(parent);jsvUnLock(index);, a);
+          if (JSP_SHOULD_EXECUTE) {
+            /* Index filtering (bug #19) - if we have an array index A that is:
+             is_string(A) && int_to_string(string_to_int(A)) = =A
+             then convert it to an integer. Should be too nasty for performance
+             as we only do this when accessing an array with a string */
+            if (jsvIsString(index) && jsvIsStringNumericStrict(index)) {
+              JsVar *v = jsvNewFromInteger(jsvGetInteger(index));
+              jsvUnLock(index);
+              index = v;
+            }
+
+            JsVar *aVar = jsvSkipName(a);
+            if (aVar && (jsvIsArrayBuffer(aVar))) {
+              // for array buffers, we actually create a NAME, and hand that back - then when we assign (or use SkipName) we pull out the correct data
+              JsVar *indexValue = jsvSkipName(index);
+              jsvUnLock(a);
+              a = jsvMakeIntoVariableName(jsvNewFromInteger(jsvGetInteger(indexValue)), aVar);
+              jsvUnLock(indexValue);
+              if (a) // turn into an 'array buffer name'
+                a->flags = (a->flags & ~(JSV_NAME|JSV_VARTYPEMASK)) | JSV_ARRAYBUFFERNAME;
+            } else if (aVar && (jsvIsArray(aVar) || jsvIsObject(aVar) || jsvIsFunction(aVar))) {
+                // TODO: If we set to undefined, maybe we should remove the name?
+                JsVar *indexValue = jsvSkipName(index);
+                if (!jsvIsString(indexValue) && !jsvIsNumeric(indexValue))
+                  indexValue = jsvAsString(indexValue, true);
+                JsVar *child = jsvFindChildFromVar(aVar, indexValue, true);
+                jsvUnLock(indexValue);
+
+                jsvUnLock(parent);
+                parent = jsvLockAgain(aVar);
+                jsvUnLock(a);
+                a = child;
+            } else if (aVar && (jsvIsString(aVar))) {
+                JsVarInt idx = jsvGetIntegerAndUnLock(jsvSkipName(index));
+                JsVar *child = 0;
+                if (idx>=0 && idx<(JsVarInt)jsvGetStringLength(aVar)) {
+                  char ch = jsvGetCharInString(aVar, (int)idx);
+                  child = jsvNewFromEmptyString();
+                  if (child) jsvAppendStringBuf(child, &ch, 1);
+                }
+                jsvUnLock(parent);
+                parent = jsvLockAgain(aVar);
+                jsvUnLock(a);
+                a = child;
+            } else {
+                jsWarnAt("Variable is not an Array or Object", execInfo.lex, execInfo.lex->tokenLastEnd);
+                jsvUnLock(parent);
+                parent = 0;
+                jsvUnLock(a);
+                a = 0;
+            }
+            jsvUnLock(aVar);
+          }
+          jsvUnLock(index);
+      } else {
+        assert(0);
+      }
+  }
+
+  if (parentResult) *parentResult = parent;
+  else jsvUnLock(parent);
+  return a;
+}
+
+JsVar *jspeFactor();
+void jspEnsureIsPrototype(JsVar *prototypeName);
+
+JsVar *jspeConstruct(JsVar *func, JsVar *funcName, bool hasArgs) {
+  assert(JSP_SHOULD_EXECUTE);
+  if (!jsvIsFunction(func)) {
+    jsErrorAt("Constructor should be a function", execInfo.lex, execInfo.lex->tokenLastEnd);
+    jspSetError();
+    return 0;
+  }
+
+  JsVar *thisObj = jsvNewWithFlags(JSV_OBJECT);
+  // Make sure the function has a 'prototype' var
+  JsVar *prototypeName = jsvFindChildFromString(func, JSPARSE_PROTOTYPE_VAR, true);
+  jspEnsureIsPrototype(prototypeName); // make sure it's an object
+  jsvUnLock(jsvAddNamedChild(thisObj, prototypeName, JSPARSE_INHERITS_VAR));
+  jsvUnLock(prototypeName);
+
+  JsVar *a = jspeFunctionCall(func, funcName, thisObj, hasArgs, 0, 0);
+
+  if (a) {
+    jsvUnLock(thisObj);
+    thisObj = a;
+  } else {
+    jsvUnLock(a);
+    JsVar *constructor = jsvFindChildFromString(thisObj, JSPARSE_CONSTRUCTOR_VAR, true);
+    if (constructor) {
+      jsvSetValueOfName(constructor, funcName);
+      jsvUnLock(constructor);
+    }
+  }
+  return thisObj;
+}
+
+JsVar *jspeFactorFunctionCall() {
+  /* The parent if we're executing a method call */
+  bool isConstructor = false;
+  if (execInfo.lex->tk==LEX_R_NEW) {
+    JSP_MATCH(LEX_R_NEW);
+    isConstructor = true;
+
+    if (execInfo.lex->tk==LEX_R_NEW) {
+      jsError("Nesting 'new' operators is unsupported");
+      jspSetError();
+      return 0;
+    }
+  }
+
+  JsVar *parent = 0;
+  JsVar *a = jspeFactorMember(jspeFactor(), &parent);
+
+  while (execInfo.lex->tk=='(' || (isConstructor && JSP_SHOULD_EXECUTE)) {
+    JsVar *funcName = a;
+    JsVar *func = jsvSkipName(funcName);
+
+    /* The constructor function doesn't change parsing, so if we're
+     * not executing, just short-cut it. */
+    if (isConstructor && JSP_SHOULD_EXECUTE) {
+      // If we have '(' parse an argument list, otherwise don't look for any args
+      bool parseArgs = execInfo.lex->tk=='(';
+      a = jspeConstruct(func, funcName, parseArgs);
+      isConstructor = false; // don't treat subsequent brackets as constructors
+    } else
+      a = jspeFunctionCall(func, funcName, parent, true, 0, 0);
+
+    jsvUnLock(funcName);
+    jsvUnLock(func);
+
+    jsvUnLock(parent); parent=0;
+    a = jspeFactorMember(a, &parent);
+  }
+
+  jsvUnLock(parent);
+  return a;
+}
+
+JsVar *jspeFactorId() {
+  return jspeFactorSingleId();
+}
+
+
+JsVar *jspeFactorObject() {
+  if (JSP_SHOULD_EXECUTE) {
+    JsVar *contents = jsvNewWithFlags(JSV_OBJECT);
+    if (!contents) { // out of memory
+      jspSetError();
+      return 0;
+    }
+    /* JSON-style object definition */
+    JSP_MATCH_WITH_RETURN('{', contents);
+    while (!JSP_HAS_ERROR && execInfo.lex->tk != '}') {
+      JsVar *varName = 0;
+      if (JSP_SHOULD_EXECUTE) {
+        varName = jslGetTokenValueAsVar(execInfo.lex);
+        if (!varName) { // out of memory
+          return contents;
+        }
+      }
+      // we only allow strings or IDs on the left hand side of an initialisation
+      if (execInfo.lex->tk==LEX_STR) {
+        JSP_MATCH_WITH_CLEANUP_AND_RETURN(LEX_STR, jsvUnLock(varName), contents);
+      } else {
+        JSP_MATCH_WITH_CLEANUP_AND_RETURN(LEX_ID, jsvUnLock(varName), contents);
+      }
+      JSP_MATCH_WITH_CLEANUP_AND_RETURN(':', jsvUnLock(varName), contents);
+      if (JSP_SHOULD_EXECUTE) {
+        JsVar *valueVar;
+        JsVar *value = jspeBase(); // value can be 0 (could be undefined!)
+        valueVar = jsvSkipNameAndUnLock(value);
+        varName = jsvMakeIntoVariableName(varName, valueVar);
+        jsvAddName(contents, varName);
+        jsvUnLock(valueVar);
+      }
+      jsvUnLock(varName);
+      // no need to clean here, as it will definitely be used
+      if (execInfo.lex->tk != '}') JSP_MATCH_WITH_RETURN(',', contents);
+    }
+    JSP_MATCH_WITH_RETURN('}', contents);
+    return contents;
+  } else {
+    // Not executing so do fast skip
+    return jspeBlock();
+  }
+}
+
+JsVar *jspeFactorArray() {
+  int idx = 0;
+  JsVar *contents = 0;
+  if (JSP_SHOULD_EXECUTE) {
+    contents = jsvNewWithFlags(JSV_ARRAY);
+    if (!contents) { // out of memory
+      jspSetError();
+      return 0;
+    }
+  }
+  /* JSON-style array */
+  JSP_MATCH_WITH_RETURN('[', contents);
+  while (!JSP_HAS_ERROR && execInfo.lex->tk != ']') {
+    if (JSP_SHOULD_EXECUTE) {
+      // OPT: Store array indices as actual ints
+      JsVar *a;
+      JsVar *aVar;
+      JsVar *indexName;
+      a = jspeBase();
+      aVar = jsvSkipNameAndUnLock(a);
+      indexName = jsvMakeIntoVariableName(jsvNewFromInteger(idx),  aVar);
+      if (indexName) { // could be out of memory
+        jsvAddName(contents, indexName);
+        jsvUnLock(indexName);
+      }
+      jsvUnLock(aVar);
+    } else {
+      jsvUnLock(jspeBase());
+    }
+    // no need to clean here, as it will definitely be used
+    if (execInfo.lex->tk != ']') JSP_MATCH_WITH_RETURN(',', contents);
+    idx++;
+  }
+  JSP_MATCH_WITH_RETURN(']', contents);
+  return contents;
+}
+
+void jspEnsureIsPrototype(JsVar *prototypeName) {
+  if (!prototypeName) return;
+  JsVar *prototypeVar = jsvSkipName(prototypeName);
+  if (!jsvIsObject(prototypeVar)) {
+    if (!jsvIsUndefined(prototypeVar))
+      jsWarn("Prototype is not an Object, so setting it to {}");    
+    jsvUnLock(prototypeVar);
+    prototypeVar = jsvNewWithFlags(JSV_OBJECT); // prototype is supposed to be an object
+    JsVar *lastName = jsvSkipToLastName(prototypeName);
+    jsvSetValueOfName(lastName, prototypeVar);
+    jsvUnLock(lastName);
+  }
+  jsvUnLock(prototypeVar);
+}
+
+JsVar *jspeFactorTypeOf() {
+  JSP_MATCH(LEX_R_TYPEOF);
+  JsVar *a = jspeBase();
+  JsVar *result = 0;
+  if (JSP_SHOULD_EXECUTE) {
+    a = jsvSkipNameAndUnLock(a);
+    result=jsvNewFromString(jsvGetTypeOf(a));
+  }
+  jsvUnLock(a);
+  return result;
+}
+
+JsVar *jspeFactor() {
+    if (execInfo.lex->tk=='(') {
+        JsVar *a = 0;
+        JSP_MATCH('(');
+        if (jspCheckStackPosition())
+          a = jspeBaseWithComma();
+        if (!JSP_HAS_ERROR) JSP_MATCH_WITH_RETURN(')',a);
+        return a;
+    } else if (execInfo.lex->tk==LEX_R_TRUE) {
+        JSP_MATCH(LEX_R_TRUE);
+        return JSP_SHOULD_EXECUTE ? jsvNewFromBool(true) : 0;
+    } else if (execInfo.lex->tk==LEX_R_FALSE) {
+        JSP_MATCH(LEX_R_FALSE);
+        return JSP_SHOULD_EXECUTE ? jsvNewFromBool(false) : 0;
+    } else if (execInfo.lex->tk==LEX_R_NULL) {
+        JSP_MATCH(LEX_R_NULL);
+        return JSP_SHOULD_EXECUTE ? jsvNewWithFlags(JSV_NULL) : 0;
+    } else if (execInfo.lex->tk==LEX_R_UNDEFINED) {
+        JSP_MATCH(LEX_R_UNDEFINED);
+        return 0;
+    } else if (execInfo.lex->tk==LEX_ID) {
+      return jspeFactorId();
+    } else if (execInfo.lex->tk==LEX_INT) {
+        // atol works only on decimals
+        // strtol handles 0x12345 as well
+        //JsVarInt v = (JsVarInt)atol(jslGetTokenValueAsString(execInfo.lex));
+        //JsVarInt v = (JsVarInt)strtol(jslGetTokenValueAsString(execInfo.lex),0,0); // broken on PIC
+        if (JSP_SHOULD_EXECUTE) {
+          JsVarInt v = stringToInt(jslGetTokenValueAsString(execInfo.lex));
+          JSP_MATCH(LEX_INT);
+          return jsvNewFromInteger(v);
+        } else {
+          JSP_MATCH(LEX_INT);
+          return 0;
+        }
+    } else if (execInfo.lex->tk==LEX_FLOAT) {
+      if (JSP_SHOULD_EXECUTE) {
+        JsVarFloat v = stringToFloat(jslGetTokenValueAsString(execInfo.lex));
+        JSP_MATCH(LEX_FLOAT);
+        return jsvNewFromFloat(v);
+      } else {
+        JSP_MATCH(LEX_FLOAT);
+        return 0;
+      }
+    } else if (execInfo.lex->tk==LEX_STR) {
+      if (JSP_SHOULD_EXECUTE) {
+        JsVar *a = jslGetTokenValueAsVar(execInfo.lex);
+        JSP_MATCH_WITH_RETURN(LEX_STR, a);
+        return a;
+      } else {
+        JSP_MATCH(LEX_STR);
+        return 0;
+      }
+    } else if (execInfo.lex->tk=='{') {
+      return jspeFactorObject();
+    } else if (execInfo.lex->tk=='[') {
+        return jspeFactorArray();
+    } else if (execInfo.lex->tk==LEX_R_FUNCTION) {
+      JSP_MATCH(LEX_R_FUNCTION);
+      return jspeFunctionDefinition();
+    } else if (execInfo.lex->tk==LEX_R_THIS) {
+      JSP_MATCH(LEX_R_THIS);
+      return jsvLockAgain( execInfo.thisVar ? execInfo.thisVar : execInfo.parse->root );
+    } else if (execInfo.lex->tk==LEX_R_TYPEOF) {
+      return jspeFactorTypeOf();
+    } else if (execInfo.lex->tk==LEX_R_VOID) {
+      JSP_MATCH(LEX_R_VOID);
+      jsvUnLock(jspeFactor());
+      return 0;
+    }
+    // Nothing we can do here... just hope it's the end...
+    JSP_MATCH(LEX_EOF);
+    return 0;
+}
+
+__attribute((noinline)) JsVar *__jspePostfix(JsVar *a) {
+  while (execInfo.lex->tk==LEX_PLUSPLUS || execInfo.lex->tk==LEX_MINUSMINUS) {
+    int op = execInfo.lex->tk;
+    JSP_MATCH(execInfo.lex->tk);
+    if (JSP_SHOULD_EXECUTE) {
+        JsVar *one = jsvNewFromInteger(1);
+        JsVar *res = jsvMathsOpSkipNames(a, one, op==LEX_PLUSPLUS ? '+' : '-');
+        JsVar *oldValue;
+        jsvUnLock(one);
+        oldValue = jsvSkipName(a); // keep the old value
+        // in-place add/subtract
+        jspReplaceWith(a, res);
+        jsvUnLock(res);
+        // but then use the old value
+        jsvUnLock(a);
+        a = oldValue;
+    }
+  }
+  return a;
+}
+
+JsVar *jspePostfix() {
+  JsVar *a;
+  if (execInfo.lex->tk==LEX_PLUSPLUS || execInfo.lex->tk==LEX_MINUSMINUS) {
+      int op = execInfo.lex->tk;
+      JSP_MATCH(execInfo.lex->tk);
+      a = jspePostfix();
+      if (JSP_SHOULD_EXECUTE) {
+          JsVar *one = jsvNewFromInteger(1);
+          JsVar *res = jsvMathsOpSkipNames(a, one, op==LEX_PLUSPLUS ? '+' : '-');
+          jsvUnLock(one);
+          // in-place add/subtract
+          jspReplaceWith(a, res);
+          jsvUnLock(res);
+      }
+  } else
+    a = jspeFactorFunctionCall();
+  return __jspePostfix(a);
+}
+
+JsVar *jspeUnary() {
+    if (execInfo.lex->tk=='!' || execInfo.lex->tk=='~' || execInfo.lex->tk=='-' || execInfo.lex->tk=='+') {
+      short tk = execInfo.lex->tk;
+      JSP_MATCH(execInfo.lex->tk);
+      if (!JSP_SHOULD_EXECUTE) {
+        return jspePostfix();
+      }
+      if (tk=='!') { // logical not
+        return jsvNewFromBool(!jsvGetBoolAndUnLock(jsvSkipNameAndUnLock(jspeUnary())));
+      } else if (tk=='~') { // bitwise not
+        return jsvNewFromInteger(~jsvGetIntegerAndUnLock(jsvSkipNameAndUnLock(jspeUnary())));
+      } else if (tk=='-') { // unary minus
+        return jsvNegateAndUnLock(jspeUnary()); // names already skipped
+      }  else if (tk=='+') { // unary plus (convert to number)
+        return jsvAsNumber(jspeUnary()); // names already skipped
+      }
+      assert(0);
+      return 0;
+    } else
+      return jspePostfix();
+}
+
+__attribute((noinline)) JsVar *__jspeTerm(JsVar *a) {
+    while (execInfo.lex->tk=='*' || execInfo.lex->tk=='/' || execInfo.lex->tk=='%') {
+        JsVar *b;
+        int op = execInfo.lex->tk;
+        JSP_MATCH(execInfo.lex->tk);
+        b = jspeUnary();
+        if (JSP_SHOULD_EXECUTE) {
+          JsVar *res = jsvMathsOpSkipNames(a, b, op);
+          jsvUnLock(a); a = res;
+        }
+        jsvUnLock(b);
+    }
+    return a;
+}
+
+JsVar *jspeTerm() {
+    return __jspeTerm(jspeUnary());
+}
+
+__attribute((noinline)) JsVar *__jspeExpression(JsVar *a) {
+  while (execInfo.lex->tk=='+' || execInfo.lex->tk=='-') {
+      int op = execInfo.lex->tk;
+      JSP_MATCH(execInfo.lex->tk);
+      JsVar *b = jspeTerm();
+      if (JSP_SHOULD_EXECUTE) {
+          // not in-place, so just replace
+        JsVar *res = jsvMathsOpSkipNames(a, b, op);
+        jsvUnLock(a); a = res;
+      }
+      jsvUnLock(b);
+  }
+  return a;
+}
+
+
+JsVar *jspeExpression() {
+    return __jspeExpression(jspeTerm());
+}
+
+__attribute((noinline)) JsVar *__jspeShift(JsVar *a) {
+  if (execInfo.lex->tk==LEX_LSHIFT || execInfo.lex->tk==LEX_RSHIFT || execInfo.lex->tk==LEX_RSHIFTUNSIGNED) {
+    JsVar *b;
+    int op = execInfo.lex->tk;
+    JSP_MATCH(op);
+    b = jspeExpression();
+    if (JSP_SHOULD_EXECUTE) {
+      JsVar *res = jsvMathsOpSkipNames(a, b, op);
+      jsvUnLock(a); a = res;
+    }
+    jsvUnLock(b);
+  }
+  return a;
+}
+
+JsVar *jspeShift() {
+  return __jspeShift(jspeExpression());
+}
+
+__attribute((noinline)) JsVar *__jspeCondition(JsVar *a) {
+    JsVar *b;
+    while (execInfo.lex->tk==LEX_EQUAL || execInfo.lex->tk==LEX_NEQUAL ||
+           execInfo.lex->tk==LEX_TYPEEQUAL || execInfo.lex->tk==LEX_NTYPEEQUAL ||
+           execInfo.lex->tk==LEX_LEQUAL || execInfo.lex->tk==LEX_GEQUAL ||
+           execInfo.lex->tk=='<' || execInfo.lex->tk=='>' ||
+           execInfo.lex->tk==LEX_R_INSTANCEOF ||
+           (execInfo.lex->tk==LEX_R_IN && !(execInfo.execute&EXEC_FOR_INIT))) {
+        int op = execInfo.lex->tk;
+        JSP_MATCH(execInfo.lex->tk);
+        b = jspeShift();
+        if (JSP_SHOULD_EXECUTE) {
+          JsVar *res = 0;
+          if (op==LEX_R_IN) {
+            JsVar *av = jsvSkipName(a);
+            JsVar *bv = jsvSkipName(b);
+            if (jsvIsArray(bv) || jsvIsObject(bv)) {
+              JsVar *varFound = jsvGetArrayIndexOf(bv, av, false/*not exact*/); // ArrayIndexOf will return 0 if not found
+              res = jsvNewFromBool(varFound!=0);
+              jsvUnLock(varFound);
+            } // else it will be undefined
+            jsvUnLock(av);
+            jsvUnLock(bv);
+          } else if (op==LEX_R_INSTANCEOF) {
+            bool inst = false;
+            JsVar *av = jsvSkipName(a);
+            JsVar *bv = jsvSkipName(b);
+            if (!jsvIsFunction(bv)) {
+              jsErrorAt("Expecting a function on RHS in instanceof check", execInfo.lex, execInfo.lex->tokenLastEnd);
+              jspSetError();
+            } else {
+              if (jsvIsObject(av)) {
+                JsVar *constructor = jsvObjectGetChild(av, JSPARSE_CONSTRUCTOR_VAR, 0);
+                if (constructor==bv) inst=true;
+                else inst = jspIsConstructor(bv,"Object");
+                jsvUnLock(constructor);
+              } else {
+                const char *name = jswGetBasicObjectName(av);
+                if (name) {
+                  inst = jspIsConstructor(bv, name);
+                }
+              }
+            }
+            jsvUnLock(av);
+            jsvUnLock(bv);
+            res = jsvNewFromBool(inst);
+          } else {
+            res = jsvMathsOpSkipNames(a, b, op);
+
+          }
+          jsvUnLock(a); a = res;
+        }
+        jsvUnLock(b);
+    }
+    return a;
+}
+
+JsVar *jspeCondition() {
+  return __jspeCondition(jspeShift());
+}
+
+__attribute((noinline)) JsVar *__jspeLogic(JsVar *a) {
+    JsVar *b = 0;
+    while (execInfo.lex->tk=='&' || execInfo.lex->tk=='|' || execInfo.lex->tk=='^' || execInfo.lex->tk==LEX_ANDAND || execInfo.lex->tk==LEX_OROR) {
+        bool shortCircuit = false;
+        bool boolean = false;
+        int op = execInfo.lex->tk;
+        JSP_MATCH(execInfo.lex->tk);
+        
+        // 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 = !jsvGetBoolAndUnLock(jsvSkipName(a));
+            boolean = true;
+        } else if (op==LEX_OROR) {
+            op = '|';
+            shortCircuit = jsvGetBoolAndUnLock(jsvSkipName(a));
+            boolean = true;
+        }
+        
+        JSP_SAVE_EXECUTE();
+        if (shortCircuit) jspSetNoExecute(); 
+        b = jspeCondition();
+        if (shortCircuit) JSP_RESTORE_EXECUTE();
+        if (JSP_SHOULD_EXECUTE && !shortCircuit) {
+            JsVar *res;
+            if (boolean) {
+              JsVar *newa = jsvNewFromBool(jsvGetBoolAndUnLock(jsvSkipName(a)));
+              JsVar *newb = jsvNewFromBool(jsvGetBoolAndUnLock(jsvSkipName(b)));
+              jsvUnLock(a); a = newa;
+              jsvUnLock(b); b = newb;
+            }
+            res = jsvMathsOpSkipNames(a, b, op);
+            jsvUnLock(a); a = res;
+        }
+        jsvUnLock(b);
+    }
+    return a;
+}
+
+JsVar *jspeLogic() {
+  return __jspeLogic(jspeCondition());
+}
+
+__attribute((noinline)) JsVar *__jspeTernary(JsVar *lhs) {
+  if (execInfo.lex->tk=='?') {
+    JSP_MATCH('?');
+    if (!JSP_SHOULD_EXECUTE) {
+      // just let lhs pass through
+      jsvUnLock(jspeBase());
+      JSP_MATCH(':');
+      jsvUnLock(jspeBase());
+    } else {
+      bool first = jsvGetBoolAndUnLock(jsvSkipName(lhs));
+      jsvUnLock(lhs);
+      if (first) {
+        lhs = jspeBase();
+        JSP_MATCH(':');
+        JSP_SAVE_EXECUTE();
+        jspSetNoExecute();
+        jsvUnLock(jspeBase());
+        JSP_RESTORE_EXECUTE();
+      } else {
+        JSP_SAVE_EXECUTE();
+        jspSetNoExecute();
+        jsvUnLock(jspeBase());
+        JSP_RESTORE_EXECUTE();
+        JSP_MATCH(':');
+        lhs = jspeBase();
+      }
+    }
+  }
+
+  return lhs;
+}
+
+JsVar *jspeTernary() {
+  return __jspeTernary(jspeLogic());
+}
+
+__attribute((noinline)) JsVar *__jspeBase(JsVar *lhs) {
+    if (execInfo.lex->tk=='=' || execInfo.lex->tk==LEX_PLUSEQUAL || execInfo.lex->tk==LEX_MINUSEQUAL ||
+                                 execInfo.lex->tk==LEX_MULEQUAL || execInfo.lex->tk==LEX_DIVEQUAL || execInfo.lex->tk==LEX_MODEQUAL ||
+                                 execInfo.lex->tk==LEX_ANDEQUAL || execInfo.lex->tk==LEX_OREQUAL ||
+                                 execInfo.lex->tk==LEX_XOREQUAL || execInfo.lex->tk==LEX_RSHIFTEQUAL ||
+                                 execInfo.lex->tk==LEX_LSHIFTEQUAL || execInfo.lex->tk==LEX_RSHIFTUNSIGNEDEQUAL) {
+        JsVar *rhs;
+        /* If we're assigning to this and we don't have a parent,
+         * add it to the symbol table root as per JavaScript. */
+        if (JSP_SHOULD_EXECUTE && lhs && !lhs->refs) {
+          if (jsvIsName(lhs)/* && jsvGetStringLength(lhs)>0*/) {
+            if (!jsvIsArrayBufferName(lhs))
+              jsvAddName(execInfo.parse->root, lhs);
+          } else // TODO: Why was this here? can it happen?
+            jsWarnAt("Trying to assign to an un-named type\n", execInfo.lex, execInfo.lex->tokenLastEnd);
+        }
+
+        int op = execInfo.lex->tk;
+        JSP_MATCH(execInfo.lex->tk);
+        rhs = jspeBase();
+        rhs = jsvSkipNameAndUnLock(rhs); // ensure we get rid of any references on the RHS
+        if (JSP_SHOULD_EXECUTE && lhs) {
+            if (op=='=') {
+                jspReplaceWith(lhs, rhs);
+            } else {
+                if (op==LEX_PLUSEQUAL) op='+';
+                else if (op==LEX_MINUSEQUAL) op='-';
+                else if (op==LEX_MULEQUAL) op='*';
+                else if (op==LEX_DIVEQUAL) op='/';
+                else if (op==LEX_MODEQUAL) op='%';
+                else if (op==LEX_ANDEQUAL) op='&';
+                else if (op==LEX_OREQUAL) op='|';
+                else if (op==LEX_XOREQUAL) op='^';
+                else if (op==LEX_RSHIFTEQUAL) op=LEX_RSHIFT;
+                else if (op==LEX_LSHIFTEQUAL) op=LEX_LSHIFT;
+                else if (op==LEX_RSHIFTUNSIGNEDEQUAL) op=LEX_RSHIFTUNSIGNED;
+                if (op=='+' && jsvIsName(lhs)) {
+                  JsVar *currentValue = jsvSkipName(lhs);
+                  if (jsvIsString(currentValue) && currentValue->refs==1) {
+                    /* A special case for string += where this is the only use of the string,
+                     * as we may be able to do a simple append (rather than clone + append)*/
+                    JsVar *str = jsvAsString(rhs, false);
+                    jsvAppendStringVarComplete(currentValue, str);
+                    jsvUnLock(str);
+                    op = 0;
+                  }
+                  jsvUnLock(currentValue);
+                }
+                if (op) {
+                  /* Fallback which does a proper add */
+                  JsVar *res = jsvMathsOpSkipNames(lhs,rhs,op);
+                  jspReplaceWith(lhs, res);
+                  jsvUnLock(res);
+                }
+            }
+        }
+        jsvUnLock(rhs);
+    }
+    return lhs;
+}
+
+
+JsVar *jspeBase() {
+  return __jspeBase(jspeTernary());
+}
+
+// jspeBase where ',' is allowed to add multiple expressions
+JsVar *jspeBaseWithComma() {
+  while (!JSP_HAS_ERROR) {
+    JsVar *a = jspeBase();
+    if (execInfo.lex->tk!=',') return a;
+    // if we get a comma, we just forget this data and parse the next bit...
+    jsvUnLock(a);
+    JSP_MATCH(',');
+  }
+  return 0;
+}
+
+JsVar *jspeBlock() {
+    JSP_MATCH('{');
+    if (JSP_SHOULD_EXECUTE) {
+      while (execInfo.lex->tk && execInfo.lex->tk!='}') {
+        jsvUnLock(jspeStatement());
+        if (JSP_HAS_ERROR) {
+          if (execInfo.lex && !(execInfo.execute&EXEC_ERROR_LINE_REPORTED)) {
+            execInfo.execute = (JsExecFlags)(execInfo.execute | EXEC_ERROR_LINE_REPORTED);
+            jsiConsolePrint("at ");
+            jsiConsolePrintPosition(execInfo.lex, execInfo.lex->tokenLastEnd);
+            jsiConsolePrintTokenLineMarker(execInfo.lex, execInfo.lex->tokenLastEnd);
+          }
+          return 0;
+        }
+      }
+      JSP_MATCH('}');
+    } else {
+      // fast skip of blocks
+      int brackets = 1;
+      while (execInfo.lex->tk && brackets) {
+        if (execInfo.lex->tk == '{') brackets++;
+        if (execInfo.lex->tk == '}') brackets--;
+        JSP_MATCH(execInfo.lex->tk);
+      }
+    }
+    return 0;
+}
+
+JsVar *jspeBlockOrStatement() {
+    if (execInfo.lex->tk=='{') 
+       return jspeBlock();
+    else {
+       JsVar *v = jspeStatement();
+       if (execInfo.lex->tk==';') JSP_MATCH(';');
+       return v;
+    }
+}
+
+JsVar *jspeStatementVar() {
+  JsVar *lastDefined = 0;
+   /* 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.*/
+   JSP_MATCH(LEX_R_VAR);
+   bool hasComma = true; // for first time in loop
+   while (hasComma && execInfo.lex->tk == LEX_ID) {
+     JsVar *a = 0;
+     if (JSP_SHOULD_EXECUTE) {
+       a = jspeiFindOnTop(jslGetTokenValueAsString(execInfo.lex), true);
+       if (!a) { // out of memory
+         jspSetError();
+         return lastDefined;
+       }
+     }
+     JSP_MATCH_WITH_CLEANUP_AND_RETURN(LEX_ID, jsvUnLock(a), lastDefined);
+     // now do stuff defined with dots
+     while (execInfo.lex->tk == '.') {
+         JSP_MATCH_WITH_CLEANUP_AND_RETURN('.', jsvUnLock(a), lastDefined);
+         if (JSP_SHOULD_EXECUTE) {
+             JsVar *lastA = a;
+             a = jsvFindChildFromString(lastA, jslGetTokenValueAsString(execInfo.lex), true);
+             jsvUnLock(lastA);
+         }
+         JSP_MATCH_WITH_CLEANUP_AND_RETURN(LEX_ID, jsvUnLock(a), lastDefined);
+     }
+     // sort out initialiser
+     if (execInfo.lex->tk == '=') {
+         JsVar *var;
+         JSP_MATCH_WITH_CLEANUP_AND_RETURN('=', jsvUnLock(a), lastDefined);
+         var = jsvSkipNameAndUnLock(jspeBase());
+         if (JSP_SHOULD_EXECUTE)
+             jspReplaceWith(a, var);
+         jsvUnLock(var);
+     }
+     jsvUnLock(lastDefined);
+     lastDefined = a;
+     hasComma = execInfo.lex->tk == ',';
+     if (hasComma) JSP_MATCH_WITH_RETURN(',', lastDefined);
+   }
+   return lastDefined;
+}
+
+JsVar *jspeStatementIf() {
+  bool cond;
+  JsVar *var;
+  JSP_MATCH(LEX_R_IF);
+  JSP_MATCH('(');
+  var = jspeBase();
+  JSP_MATCH(')');
+  cond = JSP_SHOULD_EXECUTE && jsvGetBoolAndUnLock(jsvSkipName(var));
+  jsvUnLock(var);
+
+  JSP_SAVE_EXECUTE();
+  if (!cond) jspSetNoExecute();
+  jsvUnLock(jspeBlockOrStatement());
+  if (!cond) JSP_RESTORE_EXECUTE();
+  if (execInfo.lex->tk==LEX_R_ELSE) {
+      //JSP_MATCH(';'); ???
+      JSP_MATCH(LEX_R_ELSE);
+      JSP_SAVE_EXECUTE();
+      if (cond) jspSetNoExecute();
+      jsvUnLock(jspeBlockOrStatement());
+      if (cond) JSP_RESTORE_EXECUTE();
+  }
+  return 0;
+}
+
+JsVar *jspeStatementSwitch() {
+  JSP_MATCH(LEX_R_SWITCH);
+  JSP_MATCH('(');
+  JsVar *switchOn = jspeBase();
+  JSP_MATCH_WITH_CLEANUP_AND_RETURN(')', jsvUnLock(switchOn), 0);
+  JSP_MATCH_WITH_CLEANUP_AND_RETURN('{', jsvUnLock(switchOn), 0);
+  JSP_SAVE_EXECUTE();
+  bool execute = JSP_SHOULD_EXECUTE;
+  bool hasExecuted = false;
+  if (execute) execInfo.execute=EXEC_NO|EXEC_IN_SWITCH;
+  while (execInfo.lex->tk==LEX_R_CASE) {
+    JSP_MATCH_WITH_CLEANUP_AND_RETURN(LEX_R_CASE, jsvUnLock(switchOn), 0);
+    JsExecFlags oldFlags = execInfo.execute;
+    if (execute) execInfo.execute=EXEC_YES|EXEC_IN_SWITCH;
+    JsVar *test = jspeBase();
+    execInfo.execute = oldFlags|EXEC_IN_SWITCH;;
+    JSP_MATCH_WITH_CLEANUP_AND_RETURN(':', jsvUnLock(switchOn);jsvUnLock(test), 0);
+    bool cond = false;
+    if (execute)
+      cond = jsvGetBoolAndUnLock(jsvMathsOpSkipNames(switchOn, test, LEX_EQUAL));
+    if (cond) hasExecuted = true;
+    jsvUnLock(test);
+    if (cond && (execInfo.execute&EXEC_RUN_MASK)==EXEC_NO)
+      execInfo.execute=EXEC_YES|EXEC_IN_SWITCH;
+    while (!JSP_HAS_ERROR && execInfo.lex->tk!=LEX_EOF && execInfo.lex->tk!=LEX_R_CASE && execInfo.lex->tk!=LEX_R_DEFAULT && execInfo.lex->tk!='}')
+      jsvUnLock(jspeBlockOrStatement());
+  }
+  jsvUnLock(switchOn);
+  if (execute && (execInfo.execute&EXEC_RUN_MASK)==EXEC_BREAK)
+    execInfo.execute=EXEC_YES|EXEC_IN_SWITCH;
+  JSP_RESTORE_EXECUTE();
+
+  if (execInfo.lex->tk==LEX_R_DEFAULT) {
+    JSP_MATCH(LEX_R_DEFAULT);
+    JSP_MATCH(':');
+    JSP_SAVE_EXECUTE();
+    if (hasExecuted) jspSetNoExecute();
+    while (!JSP_HAS_ERROR && execInfo.lex->tk!=LEX_EOF && execInfo.lex->tk!='}')
+      jsvUnLock(jspeBlockOrStatement());
+    JSP_RESTORE_EXECUTE();
+  }
+  JSP_MATCH('}');
+  return 0;
+}
+
+JsVar *jspeStatementWhile() {
+#ifdef JSPARSE_MAX_LOOP_ITERATIONS
+  int loopCount = JSPARSE_MAX_LOOP_ITERATIONS;
+#endif
+  JsVar *cond;
+  bool loopCond;
+  bool hasHadBreak = false;
+  // We do repetition by pulling out the string representing our statement
+  // there's definitely some opportunity for optimisation here
+  JSP_MATCH(LEX_R_WHILE);
+  JSP_MATCH('(');
+  JslCharPos whileCondStart = execInfo.lex->tokenStart;
+  cond = jspeBase();
+  loopCond = JSP_SHOULD_EXECUTE && jsvGetBoolAndUnLock(jsvSkipName(cond));
+  jsvUnLock(cond);
+  JSP_MATCH(')');
+  JslCharPos whileBodyStart = execInfo.lex->tokenStart;
+  JSP_SAVE_EXECUTE();
+  // actually try and execute first bit of while loop (we'll do the rest in the actual loop later)
+  if (!loopCond) jspSetNoExecute();
+  execInfo.execute |= EXEC_IN_LOOP;
+  jsvUnLock(jspeBlockOrStatement());
+  JslCharPos whileBodyEnd = execInfo.lex->tokenStart;
+  execInfo.execute &= (JsExecFlags)~EXEC_IN_LOOP;
+  if (execInfo.execute == EXEC_CONTINUE)
+    execInfo.execute = EXEC_YES;
+  if (execInfo.execute == EXEC_BREAK) {
+    execInfo.execute = EXEC_YES;
+    hasHadBreak = true; // fail loop condition, so we exit
+  }
+  if (!loopCond) JSP_RESTORE_EXECUTE();
+
+  while (!hasHadBreak && loopCond
+#ifdef JSPARSE_MAX_LOOP_ITERATIONS
+         && loopCount-->0
+#endif
+         ) {
+      jslSeekTo(execInfo.lex, whileCondStart);
+      cond = jspeBase();
+      loopCond = JSP_SHOULD_EXECUTE && jsvGetBoolAndUnLock(jsvSkipName(cond));
+      jsvUnLock(cond);
+      if (loopCond) {
+          jslSeekTo(execInfo.lex, whileBodyStart);
+          execInfo.execute |= EXEC_IN_LOOP;
+          jsvUnLock(jspeBlockOrStatement());
+          execInfo.execute &= (JsExecFlags)~EXEC_IN_LOOP;
+          if (execInfo.execute == EXEC_CONTINUE)
+            execInfo.execute = EXEC_YES;
+          if (execInfo.execute == EXEC_BREAK) {
+            execInfo.execute = EXEC_YES;
+            hasHadBreak = true;
+          }
+      }
+  }
+  jslSeekTo(execInfo.lex, whileBodyEnd);
+#ifdef JSPARSE_MAX_LOOP_ITERATIONS
+  if (loopCount<=0) {
+    jsErrorAt("WHILE Loop exceeded the maximum number of iterations (" STRINGIFY(JSPARSE_MAX_LOOP_ITERATIONS) ")", execInfo.lex, execInfo.lex->tokenLastEnd);
+    jspSetError();
+  }
+#endif
+  return 0;
+}
+
+JsVar *jspeStatementFor() {
+  JSP_MATCH(LEX_R_FOR);
+  JSP_MATCH('(');
+  execInfo.execute |= EXEC_FOR_INIT;
+  // initialisation
+  JsVar *forStatement = 0;
+  // we could have 'for (;;)' - so don't munch up our semicolon if that's all we have
+  if (execInfo.lex->tk != ';') 
+    forStatement = jspeStatement(); 
+  execInfo.execute &= (JsExecFlags)~EXEC_FOR_INIT;
+  if (execInfo.lex->tk == LEX_R_IN) {
+    // for (i in array)
+    // where i = jsvUnLock(forStatement);
+    if (!jsvIsName(forStatement)) {
+      jsvUnLock(forStatement);
+      jsErrorAt("FOR a IN b - 'a' must be a variable name", execInfo.lex, execInfo.lex->tokenLastEnd);
+      jspSetError();
+      return 0;
+    }
+    bool addedIteratorToScope = false;
+    if (JSP_SHOULD_EXECUTE && !forStatement->refs) {
+      // if the variable did not exist, add it to the scope
+      addedIteratorToScope = true;
+      jsvAddName(execInfo.parse->root, forStatement);
+    }
+    JSP_MATCH_WITH_CLEANUP_AND_RETURN(LEX_R_IN, jsvUnLock(forStatement), 0);
+    JsVar *array = jsvSkipNameAndUnLock(jspeExpression());
+    JSP_MATCH_WITH_CLEANUP_AND_RETURN(')', jsvUnLock(forStatement);jsvUnLock(array), 0);
+    JslCharPos forBodyStart = execInfo.lex->tokenStart;
+    JSP_SAVE_EXECUTE();
+    jspSetNoExecute();
+    execInfo.execute |= EXEC_IN_LOOP;
+    jsvUnLock(jspeBlockOrStatement());
+    JslCharPos forBodyEnd = execInfo.lex->tokenStart;
+    execInfo.execute &= (JsExecFlags)~EXEC_IN_LOOP;
+    JSP_RESTORE_EXECUTE();
+
+    if (jsvIsIterable(array)) {
+      bool (*checkerFunction)(JsVar*) = 0;
+      if (jsvIsFunction(array)) checkerFunction = jsvIsInternalFunctionKey;
+      else if (jsvIsObject(array)) checkerFunction = jsvIsInternalObjectKey;
+      JsvIterator it;
+      jsvIteratorNew(&it, array);
+      bool hasHadBreak = false;
+      while (JSP_SHOULD_EXECUTE && jsvIteratorHasElement(&it) && !hasHadBreak) {
+          JsVar *loopIndexVar = jsvIteratorGetKey(&it);
+          bool ignore = false;
+          if (checkerFunction && checkerFunction(loopIndexVar))
+            ignore = true;
+          if (!ignore) {
+            JsVar *indexValue = jsvIsName(loopIndexVar) ?
+                                  jsvCopyNameOnly(loopIndexVar, false/*no copy children*/, false/*not a name*/) :
+                                  loopIndexVar;
+            if (indexValue) { // could be out of memory
+              assert(!jsvIsName(indexValue) && indexValue->refs==0);
+              jsvSetValueOfName(forStatement, indexValue);
+              if (indexValue!=loopIndexVar) jsvUnLock(indexValue);
+  
+              jsvIteratorNext(&it);
+ 
+              jslSeekTo(execInfo.lex, forBodyStart);
+              execInfo.execute |= EXEC_IN_LOOP;
+              jsvUnLock(jspeBlockOrStatement());
+              execInfo.execute &= (JsExecFlags)~EXEC_IN_LOOP;
+
+              if (execInfo.execute == EXEC_CONTINUE)
+                execInfo.execute = EXEC_YES;
+              if (execInfo.execute == EXEC_BREAK) {
+                execInfo.execute = EXEC_YES;
+                hasHadBreak = true;
+              }
+            }
+          } else
+            jsvIteratorNext(&it);
+          jsvUnLock(loopIndexVar);
+      }
+      jsvIteratorFree(&it);
+    } else {
+      jsErrorAt("FOR loop can only iterate over Arrays, Strings or Objects", execInfo.lex, execInfo.lex->tokenLastEnd);
+      jspSetError();
+    }
+
+    jslSeekTo(execInfo.lex, forBodyEnd);
+
+    if (addedIteratorToScope) {
+      jsvRemoveChild(execInfo.parse->root, forStatement);
+    }
+    jsvUnLock(forStatement);
+    jsvUnLock(array);
+  } else { // NORMAL FOR LOOP
+#ifdef JSPARSE_MAX_LOOP_ITERATIONS
+    int loopCount = JSPARSE_MAX_LOOP_ITERATIONS;
+#endif
+    bool loopCond = true;
+    bool hasHadBreak = false;
+
+    jsvUnLock(forStatement);
+    JSP_MATCH(';');
+    JslCharPos forCondStart = execInfo.lex->tokenStart;
+    if (execInfo.lex->tk != ';') {
+      JsVar *cond = jspeBase(); // condition
+      loopCond = JSP_SHOULD_EXECUTE && jsvGetBoolAndUnLock(jsvSkipName(cond));
+      jsvUnLock(cond);
+    }
+    JSP_MATCH(';');
+    JslCharPos forIterStart = execInfo.lex->tokenStart;
+    if (execInfo.lex->tk != ')')  { // we could have 'for (;;)'
+      JSP_SAVE_EXECUTE();
+      jspSetNoExecute();
+      jsvUnLock(jspeBase()); // iterator
+      JSP_RESTORE_EXECUTE();
+    }
+    JSP_MATCH(')');
+
+    JslCharPos forBodyStart = execInfo.lex->tokenStart; // actual for body
+    JSP_SAVE_EXECUTE();
+    if (!loopCond) jspSetNoExecute();
+    execInfo.execute |= EXEC_IN_LOOP;
+    jsvUnLock(jspeBlockOrStatement());
+    JslCharPos forBodyEnd = execInfo.lex->tokenStart;
+    execInfo.execute &= (JsExecFlags)~EXEC_IN_LOOP;
+    if (execInfo.execute == EXEC_CONTINUE)
+      execInfo.execute = EXEC_YES;
+    if (execInfo.execute == EXEC_BREAK) {
+      execInfo.execute = EXEC_YES;
+      hasHadBreak = true;
+    }
+    if (!loopCond) JSP_RESTORE_EXECUTE();
+    if (loopCond) {
+        jslSeekTo(execInfo.lex, forIterStart);
+        if (execInfo.lex->tk != ')') jsvUnLock(jspeBase());
+    }
+    while (!hasHadBreak && JSP_SHOULD_EXECUTE && loopCond
+#ifdef JSPARSE_MAX_LOOP_ITERATIONS
+           && loopCount-->0
+#endif
+           ) {
+        jslSeekTo(execInfo.lex, forCondStart);
+        ;
+        if (execInfo.lex->tk == ';') {
+          loopCond = true;
+        } else {
+          JsVar *cond = jspeBase();
+          loopCond = jsvGetBoolAndUnLock(jsvSkipName(cond));
+          jsvUnLock(cond);
+        }
+        if (JSP_SHOULD_EXECUTE && loopCond) {
+            jslSeekTo(execInfo.lex, forBodyStart);
+            execInfo.execute |= EXEC_IN_LOOP;
+            jsvUnLock(jspeBlockOrStatement());
+            execInfo.execute &= (JsExecFlags)~EXEC_IN_LOOP;
+            if (execInfo.execute == EXEC_CONTINUE)
+              execInfo.execute = EXEC_YES;
+            if (execInfo.execute == EXEC_BREAK) {
+              execInfo.execute = EXEC_YES;
+              hasHadBreak = true;
+            }
+        }
+        if (JSP_SHOULD_EXECUTE && loopCond) {
+            jslSeekTo(execInfo.lex, forIterStart);
+            if (execInfo.lex->tk != ')') jsvUnLock(jspeBase());
+        }
+    }
+    jslSeekTo(execInfo.lex, forBodyEnd);
+#ifdef JSPARSE_MAX_LOOP_ITERATIONS
+    if (loopCount<=0) {
+        jsErrorAt("FOR Loop exceeded the maximum number of iterations ("STRINGIFY(JSPARSE_MAX_LOOP_ITERATIONS)")", execInfo.lex, execInfo.lex->tokenLastEnd);
+        jspSetError();
+    }
+#endif
+  }
+  return 0;
+}
+
+JsVar *jspeStatementReturn() {
+  JsVar *result = 0;
+  JSP_MATCH(LEX_R_RETURN);
+  if (execInfo.lex->tk != ';') {
+    // we only want the value, so skip the name if there was one
+    result = jsvSkipNameAndUnLock(jspeBaseWithComma());
+  }
+  if (JSP_SHOULD_EXECUTE) {
+    JsVar *resultVar = jspeiFindOnTop(JSPARSE_RETURN_VAR, false);
+    if (resultVar) {
+      jspReplaceWith(resultVar, result);
+      jsvUnLock(resultVar);
+    } else {
+      jsErrorAt("RETURN statement, but not in a function.\n", execInfo.lex, execInfo.lex->tokenLastEnd);
+      jspSetError();
+    }
+    jspSetNoExecute(); // Stop anything else in this function executing
+  }
+  jsvUnLock(result);
+  return 0;
+}
+
+JsVar *jspeStatementFunctionDecl() {
+  JsVar *funcName = 0;
+  JsVar *funcVar;
+  JSP_MATCH(LEX_R_FUNCTION);
+  if (JSP_SHOULD_EXECUTE)
+    funcName = jsvMakeIntoVariableName(jsvNewFromString(jslGetTokenValueAsString(execInfo.lex)), 0);
+  if (!funcName) { // out of memory
+    jspSetError();
+    return 0;
+  }
+  JSP_MATCH(LEX_ID);
+  funcVar = jspeFunctionDefinition();
+  if (JSP_SHOULD_EXECUTE) {
+    // find a function with the same name (or make one)
+    // OPT: can Find* use just a JsVar that is a 'name'?
+    JsVar *existingFunc = jspeiFindNameOnTop(funcName, true);
+    // replace it
+    jspReplaceWith(existingFunc, funcVar);
+    jsvUnLock(funcName);
+    funcName = existingFunc;
+  }
+  jsvUnLock(funcVar);
+  return funcName;
+}
+
+JsVar *jspeStatement() {
+    if (execInfo.lex->tk==LEX_ID ||
+        execInfo.lex->tk==LEX_INT ||
+        execInfo.lex->tk==LEX_FLOAT ||
+        execInfo.lex->tk==LEX_STR ||
+        execInfo.lex->tk==LEX_R_NEW ||
+        execInfo.lex->tk==LEX_R_NULL ||
+        execInfo.lex->tk==LEX_R_UNDEFINED ||
+        execInfo.lex->tk==LEX_R_TRUE ||
+        execInfo.lex->tk==LEX_R_FALSE ||
+        execInfo.lex->tk==LEX_R_THIS ||
+        execInfo.lex->tk==LEX_R_TYPEOF ||
+        execInfo.lex->tk==LEX_R_VOID ||
+        execInfo.lex->tk==LEX_PLUSPLUS ||
+        execInfo.lex->tk==LEX_MINUSMINUS ||
+        execInfo.lex->tk=='!' ||
+        execInfo.lex->tk=='-' ||
+        execInfo.lex->tk=='+' ||
+        execInfo.lex->tk=='~' ||
+        execInfo.lex->tk=='[' ||
+        execInfo.lex->tk=='(') {
+        /* Execute a simple statement that only contains basic arithmetic... */
+      return jspeBaseWithComma();
+    } else if (execInfo.lex->tk=='{') {
+        /* A block of code */
+        return jspeBlock();
+    } else if (execInfo.lex->tk==';') {
+        /* Empty statement - to allow things like ;;; */
+        JSP_MATCH(';');
+        return 0;
+    } else if (execInfo.lex->tk==LEX_R_VAR) {
+        return jspeStatementVar();
+    } else if (execInfo.lex->tk==LEX_R_IF) {
+        return jspeStatementIf();
+    } else if (execInfo.lex->tk==LEX_R_WHILE) {
+        return jspeStatementWhile();
+    } else if (execInfo.lex->tk==LEX_R_FOR) {
+        return jspeStatementFor();
+    } else if (execInfo.lex->tk==LEX_R_RETURN) {
+        return jspeStatementReturn();
+    } else if (execInfo.lex->tk==LEX_R_FUNCTION) {
+        return jspeStatementFunctionDecl();
+    } else if (execInfo.lex->tk==LEX_R_CONTINUE) {
+      JSP_MATCH(LEX_R_CONTINUE);
+      if (JSP_SHOULD_EXECUTE) {
+        if (!(execInfo.execute & EXEC_IN_LOOP))
+          jsErrorAt("CONTINUE statement outside of FOR or WHILE loop", execInfo.lex, execInfo.lex->tokenLastEnd);
+        else
+          execInfo.execute = (execInfo.execute & (JsExecFlags)~EXEC_RUN_MASK) |  EXEC_CONTINUE;
+      }
+    } else if (execInfo.lex->tk==LEX_R_BREAK) {
+      JSP_MATCH(LEX_R_BREAK);
+      if (JSP_SHOULD_EXECUTE) {
+        if (!(execInfo.execute & (EXEC_IN_LOOP|EXEC_IN_SWITCH)))
+          jsErrorAt("BREAK statement outside of SWITCH, FOR or WHILE loop", execInfo.lex, execInfo.lex->tokenLastEnd);
+        else
+          execInfo.execute = (execInfo.execute & (JsExecFlags)~EXEC_RUN_MASK) | EXEC_BREAK;
+      }
+    } else if (execInfo.lex->tk==LEX_R_SWITCH) {
+      return jspeStatementSwitch();
+    } else JSP_MATCH(LEX_EOF);
+    return 0;
+}
+
+// -----------------------------------------------------------------------------
+/// Create a new built-in object that jswrapper can use to check for built-in functions
+JsVar *jspNewBuiltin(const char *instanceOf) {
+  JsVar *objFunc = jsvNewWithFlags(JSV_FUNCTION);
+  if (!objFunc) return 0; // out of memory
+  // set object data to be object name
+  if (strlen(instanceOf)==sizeof(objFunc->varData))
+    memcpy(objFunc->varData.str, instanceOf, sizeof(objFunc->varData)); // no trailing zero!
+  else
+    strncpy(objFunc->varData.str, instanceOf, sizeof(objFunc->varData));
+  return objFunc;
+}
+
+
+JsVar *jspNewObject(JsParse *parse, const char *name, const char *instanceOf) {
+  JsVar *objFuncName = jsvFindChildFromString(parse->root, instanceOf, true);
+  if (!objFuncName) // out of memory
+    return 0;
+
+  JsVar *objFunc = jsvSkipName(objFuncName);
+  if (!objFunc) {
+    objFunc = jspNewBuiltin(instanceOf);
+    if (!objFunc) { // out of memory
+      jsvUnLock(objFuncName);
+      return 0;
+    }
+
+    // set up name
+    jsvSetValueOfName(objFuncName, objFunc);
+  }
+
+  JsVar *prototypeName = jsvFindChildFromString(objFunc, JSPARSE_PROTOTYPE_VAR, true);
+  jspEnsureIsPrototype(prototypeName); // make sure it's an object
+  jsvUnLock(objFunc);
+  if (!prototypeName) { // out of memory
+    jsvUnLock(objFuncName);
+    return 0;
+  }
+
+  JsVar *obj = jsvNewWithFlags(JSV_OBJECT);
+  if (!obj) { // out of memory
+    jsvUnLock(objFuncName);
+    jsvUnLock(prototypeName);
+    return 0;
+  }
+  if (name) {
+    // set object data to be object name
+    strncpy(obj->varData.str, name, sizeof(obj->varData));
+  }
+  // add inherits/constructor/etc
+  jsvUnLock(jsvAddNamedChild(obj, prototypeName, JSPARSE_INHERITS_VAR));
+  jsvUnLock(prototypeName);prototypeName=0;
+  jsvUnLock(jsvAddNamedChild(obj, objFuncName, JSPARSE_CONSTRUCTOR_VAR));
+  jsvUnLock(objFuncName);
+  if (name) {
+    JsVar *objName = jsvAddNamedChild(parse->root, obj, name);
+    jsvUnLock(obj);
+    if (!objName) { // out of memory
+      return 0;
+    }
+    return objName;
+  } else
+    return obj;
+}
+
+/** Returns true if the constructor function given is the same as that
+ * of the object with the given name. */
+bool jspIsConstructor(JsVar *constructor, const char *constructorName) {
+  JsVar *objFunc = jsvObjectGetChild(execInfo.parse->root, constructorName, 0);
+  if (!objFunc) return false;
+  bool isConstructor = objFunc == constructor;
+  jsvUnLock(objFunc);
+  return isConstructor;
+}
+
+// -----------------------------------------------------------------------------
+
+void jspSoftInit(JsParse *parse) {
+  parse->root = jsvFindOrCreateRoot();
+  // Root now has a lock and a ref
+}
+
+/** Is v likely to have been created by this parser? */
+bool jspIsCreatedObject(JsParse *parse, JsVar *v) {
+  return
+      v==parse->root;
+}
+
+void jspSoftKill(JsParse *parse) {
+  jsvUnLock(parse->root);
+  // Root now has just a ref
+}
+
+void jspInit(JsParse *parse) {
+  jspSoftInit(parse);
+}
+
+void jspKill(JsParse *parse) {
+  jspSoftKill(parse);
+  // Unreffing this should completely kill everything attached to root
+  JsVar *r = jsvFindOrCreateRoot();
+  jsvUnRef(r);
+  jsvUnLock(r);
+}
+
+
+
+JsVar *jspEvaluateVar(JsParse *parse, JsVar *str, JsVar *scope) {
+  JsLex lex;
+  JsVar *v = 0;
+  JSP_SAVE_EXECUTE();
+  JsExecInfo oldExecInfo = execInfo;
+
+  assert(jsvIsString(str));
+  jslInit(&lex, str);
+
+  jspeiInit(parse, &lex);
+  bool scopeAdded = false;
+  if (scope)
+    scopeAdded = jspeiAddScope(jsvGetRef(scope));
+  while (!JSP_HAS_ERROR && execInfo.lex->tk != LEX_EOF) {
+    jsvUnLock(v);
+    v = jspeBlockOrStatement();
+  }
+  // clean up
+  if (scopeAdded) jspeiRemoveScope();
+  jspeiKill();
+  jslKill(&lex);
+
+  // restore state
+  JSP_RESTORE_EXECUTE();
+  oldExecInfo.execute = execInfo.execute; // JSP_RESTORE_EXECUTE has made this ok.
+  execInfo = oldExecInfo;
+
+  // It may have returned a reference, but we just want the value...
+  if (v) {
+    return jsvSkipNameAndUnLock(v);
+  }
+  // nothing returned
+  return 0;
+}
+
+JsVar *jspEvaluate(JsParse *parse, const char *str) {
+  JsVar *v = 0;
+
+  JsVar *evCode = jsvNewFromString(str);
+  if (!jsvIsMemoryFull())
+    v = jspEvaluateVar(parse, evCode, 0);
+  jsvUnLock(evCode);
+
+  return v;
+}
+
+bool jspExecuteFunction(JsParse *parse, JsVar *func, JsVar *parent, int argCount, JsVar **argPtr) {
+  JSP_SAVE_EXECUTE();
+  JsExecInfo oldExecInfo = execInfo;
+
+  jspeiInit(parse, 0);
+  JsVar *resultVar = jspeFunctionCall(func, 0, parent, false, argCount, argPtr);
+  bool result = jsvGetBool(resultVar);
+  jsvUnLock(resultVar);
+  // clean up
+  jspeiKill();
+  // restore state
+  JSP_RESTORE_EXECUTE();
+  oldExecInfo.execute = execInfo.execute; // JSP_RESTORE_EXECUTE has made this ok.
+  execInfo = oldExecInfo;
+
+
+  return result;
+}
+
+
+/// Evaluate a JavaScript module and return its exports
+JsVar *jspEvaluateModule(JsParse *parse, JsVar *moduleContents) {
+  assert(jsvIsString(moduleContents));
+  JsVar *scope = jsvNewWithFlags(JSV_OBJECT);
+  if (!scope) return 0; // out of mem
+  JsVar *scopeExports = jsvNewWithFlags(JSV_OBJECT);
+  if (!scopeExports) { jsvUnLock(scope); return 0; } // out of mem
+  jsvUnLock(jsvAddNamedChild(scope, scopeExports, "exports"));
+
+  jsvUnLock(jspEvaluateVar(parse, moduleContents, scope));
+
+  jsvUnLock(scope);
+  return scopeExports;
+}

+ 126 - 0
components/external/espruino/src/jsparse.h

@@ -0,0 +1,126 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Recursive descent parser for code execution
+ * ----------------------------------------------------------------------------
+ */
+#ifndef JSPARSE_H_
+#define JSPARSE_H_
+
+#include "jsvar.h"
+#include "jslex.h"
+
+
+typedef struct {
+  JsVar  *root;   ///< root of symbol table
+} JsParse;
+
+void jspInit(JsParse *parse);
+void jspKill(JsParse *parse);
+
+// jspSoft* - 'release' or 'claim' anything we are using, but ensure that it doesn't get freed
+void jspSoftInit(JsParse *parse); ///< used when recovering from or saving to flash
+void jspSoftKill(JsParse *parse); ///< used when recovering from or saving to flash
+bool jspIsCreatedObject(JsParse *parse, JsVar *v); ///< Is v likely to have been created by this parser?
+/** Returns true if the constructor function given is the same as that
+ * of the object with the given name. */
+bool jspIsConstructor(JsVar *constructor, const char *constructorName);
+
+/// Create a new built-in object that jswrapper can use to check for built-in functions
+JsVar *jspNewBuiltin(const char *name);
+/** Create a new object of the given instance and add it to root with name 'name'.
+ * If name!=0, added to root with name, and the name is returned
+ * If name==0, not added to root and Object itself returned */
+JsVar *jspNewObject(JsParse *parse, const char *name, const char *instanceOf);
+
+/// if interrupting execution, this is set
+bool jspIsInterrupted();
+/// if interrupting execution, this is set
+void jspSetInterrupted(bool interrupt);
+/// Has there been an error during parsing
+bool jspHasError();
+
+bool jspAddNativeFunction(JsParse *parse, const char *funcDesc, JsCallback callbackPtr);
+JsVar *jspEvaluateVar(JsParse *parse, JsVar *str, JsVar *scope);
+JsVar *jspEvaluate(JsParse *parse, const char *str);
+bool jspExecuteFunction(JsParse *parse, JsVar *func, JsVar *parent, int argCount, JsVar **argPtr);
+
+/// Evaluate a JavaScript module and return its exports
+JsVar *jspEvaluateModule(JsParse *parse, JsVar *moduleContents);
+
+/** When parsing, this enum defines whether
+ we are executing or not */
+typedef enum  {
+  EXEC_NO = 0,
+  EXEC_YES = 1,
+  EXEC_BREAK = 2,
+  EXEC_CONTINUE = 4,
+
+  EXEC_INTERRUPTED = 8, // true if execution has been interrupted
+  EXEC_ERROR = 16,
+  EXEC_ERROR_LINE_REPORTED = 32, // if an error has been reported, set this so we don't do it too much
+
+  EXEC_FOR_INIT = 64, // when in for initialiser parsing - hack to avoid getting confused about multiple use for IN
+  EXEC_IN_LOOP = 128, // when in a loop, set this - we can then block break/continue outside it
+  EXEC_IN_SWITCH = 256, // when in a switch, set this - we can then block break outside it/loops
+
+  EXEC_RUN_MASK = EXEC_YES|EXEC_BREAK|EXEC_CONTINUE|EXEC_INTERRUPTED,
+  EXEC_ERROR_MASK = EXEC_INTERRUPTED|EXEC_ERROR,
+  EXEC_SAVE_RESTORE_MASK = EXEC_YES|EXEC_IN_LOOP|EXEC_IN_SWITCH, // the things JSP_SAVE/RESTORE_EXECUTE should keep track of
+} JsExecFlags;
+
+/** This structure is used when parsing the JavaScript. It contains
+ * everything that should be needed. */
+typedef struct {
+  JsParse *parse;
+  JsLex *lex;
+
+  // TODO: could store scopes as JsVar array for speed
+  JsVarRef scopes[JSPARSE_MAX_SCOPES];
+  int scopeCount;
+  /// Value of 'this' reserved word
+  JsVar *thisVar;
+
+  JsExecFlags execute;
+} JsExecInfo;
+
+/// flags for jspParseFunction
+typedef enum {
+  JSP_NOSKIP_A = 1,
+  JSP_NOSKIP_B = 2,
+  JSP_NOSKIP_C = 4,
+  JSP_NOSKIP_D = 8,
+  JSP_NOSKIP_E = 16,
+  JSP_NOSKIP_F = 32,
+  JSP_NOSKIP_G = 64,
+  JSP_NOSKIP_H = 128,
+} JspSkipFlags;
+
+/// parse function with max 4 arguments (can set arg to 0 to avoid parse). Usually first arg will be 0, but if we DON'T want to skip names on an arg stuff, we can say
+bool jspParseFunction(JspSkipFlags skipName, JsVar **a, JsVar **b, JsVar **c, JsVar **d);
+/// parse function with max 8 arguments (can set arg to 0 to avoid parse). Usually first arg will be 0, but if we DON'T want to skip names on an arg stuff, we can say
+bool jspParseFunction8(JspSkipFlags skipName, JsVar **a, JsVar **b, JsVar **c, JsVar **d, JsVar **e, JsVar **f, JsVar **g, JsVar **h);
+
+bool jspParseVariableName();     ///< parse single variable name
+bool jspParseEmptyFunction();    ///< parse function with no arguments
+JsVar *jspParseSingleFunction(); ///< parse function with a single argument, return its value (no names!)
+JsVar *jspParseFunctionAsArray(); ///< parse a function with any number of argument, and return an array of de-named aruments
+
+/** Handle a function call (assumes we've parsed the function name and we're
+ * on the start bracket). 'thisArg' is the value of the 'this' variable when the
+ * function is executed (it's usually the parent object)
+ *
+ * If !isParsing and arg0!=0, argument 0 is set to what is supplied (same with arg1)
+ *
+ * functionName is used only for error reporting - and can be 0
+ */
+JsVar *jspeFunctionCall(JsVar *function, JsVar *functionName, JsVar *thisArg, bool isParsing, int argCount, JsVar **argPtr);
+
+#endif /* JSPARSE_H_ */

+ 150 - 0
components/external/espruino/src/jspin.c

@@ -0,0 +1,150 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Utilities and definitions for handling Pins
+ * ----------------------------------------------------------------------------
+ */
+
+#include "jspin.h"
+#include "jspininfo.h" // auto-generated
+
+bool jshIsPinValid(Pin pin) {
+  // Note, PIN_UNDEFINED is always > JSH_PIN_COUNT
+  return pin < JSH_PIN_COUNT && pinInfo[pin].port!=JSH_PORT_NONE;
+}
+
+
+Pin jshGetPinFromString(const char *s) {
+  // built in constants
+
+  if (s[0]=='B' && s[1]=='T' && s[2]=='N') {
+#ifdef BTN1_PININDEX
+    if (!s[3]) return BTN1_PININDEX;
+    if (s[3]=='1' && !s[4]) return BTN1_PININDEX;
+#endif
+#ifdef BTN2_PININDEX
+    if (s[3]=='2' && !s[4]) return BTN2_PININDEX;
+#endif
+#ifdef BTN3_PININDEX
+    if (s[3]=='3' && !s[4]) return BTN3_PININDEX;
+#endif
+#ifdef BTN4_PININDEX
+    if (s[3]=='4' && !s[4]) return BTN4_PININDEX;
+#endif
+  }
+  if (s[0]=='L' && s[1]=='E' && s[2]=='D') {
+#ifdef LED1_PININDEX
+    if (!s[3]) return LED1_PININDEX;
+    if (s[3]=='1' && !s[4]) return LED1_PININDEX;
+#endif
+#ifdef LED2_PININDEX
+    if (s[3]=='2' && !s[4]) return LED2_PININDEX;
+#endif
+#ifdef LED3_PININDEX
+    if (s[3]=='3' && !s[4]) return LED3_PININDEX;
+#endif
+#ifdef LED4_PININDEX
+    if (s[3]=='4' && !s[4]) return LED4_PININDEX;
+#endif
+#ifdef LED5_PININDEX
+    if (s[3]=='5' && !s[4]) return LED5_PININDEX;
+#endif
+#ifdef LED6_PININDEX
+    if (s[3]=='6' && !s[4]) return LED6_PININDEX;
+#endif
+#ifdef LED7_PININDEX
+    if (s[3]=='7' && !s[4]) return LED7_PININDEX;
+#endif
+#ifdef LED8_PININDEX
+    if (s[3]=='8' && !s[4]) return LED8_PININDEX;
+#endif
+  }
+
+  if ((s[0]>='A' && s[0]<='H') && s[1]) { // first 6 are analogs
+    int port = JSH_PORTA+s[0]-'A';
+    Pin pin = 127;
+    if (!s[2] && (s[1]>='0' && s[1]<='9')) { // D0-D9
+      pin = (Pin)(s[1]-'0');
+    } else if (!s[3] && (s[1]>='1' && s[1]<='3' && s[2]>='0' && s[2]<='9')) { // D1X-D3X
+      pin = (Pin)((s[1]-'0')*10 + (s[2]-'0'));
+    }
+    if (port == JSH_PORTA) {
+      if (pin<JSH_PORTA_COUNT) return (Pin)(JSH_PORTA_OFFSET + pin);
+    } else if (port == JSH_PORTB) {
+      if (pin<JSH_PORTB_COUNT) return (Pin)(JSH_PORTB_OFFSET + pin);
+    } else if (port == JSH_PORTC) {
+      if (pin<JSH_PORTC_COUNT) return (Pin)(JSH_PORTC_OFFSET + pin);
+    } else if (port == JSH_PORTD) {
+      if (pin<JSH_PORTD_COUNT) return (Pin)(JSH_PORTD_OFFSET + pin);
+#if JSH_PORTE_OFFSET!=-1
+    } else if (port == JSH_PORTE) {
+      if (pin<JSH_PORTE_COUNT) return (Pin)(JSH_PORTE_OFFSET + pin);
+#endif
+#if JSH_PORTF_OFFSET!=-1
+    } else if (port == JSH_PORTF) {
+      if (pin<JSH_PORTF_COUNT) return (Pin)(JSH_PORTF_OFFSET + pin);
+#endif
+#if JSH_PORTG_OFFSET!=-1
+    } else if (port == JSH_PORTG) {
+      if (pin<JSH_PORTG_COUNT) return (Pin)(JSH_PORTG_OFFSET + pin);
+#endif
+#if JSH_PORTH_OFFSET!=-1
+    } else if (port == JSH_PORTH) {
+      if (pin<JSH_PORTH_COUNT) return (Pin)(JSH_PORTH_OFFSET + pin);
+#endif
+    }
+  }
+
+  return PIN_UNDEFINED;
+}
+
+/** Write the pin name to a string. String must have at least 8 characters (to be safe) */
+void jshGetPinString(char *result, Pin pin) {
+  result[0] = 0; // just in case
+  if (
+#if JSH_PORTA_OFFSET!=0
+      pin>=JSH_PORTA_OFFSET &&
+#endif
+      pin<JSH_PORTA_OFFSET+JSH_PORTA_COUNT) {
+    result[0]='A';
+    itoa(pin-JSH_PORTA_OFFSET,&result[1],10);
+  } else if (pin>=JSH_PORTB_OFFSET && pin<JSH_PORTB_OFFSET+JSH_PORTB_COUNT) {
+    result[0]='B';
+    itoa(pin-JSH_PORTB_OFFSET,&result[1],10);
+  } else if (pin>=JSH_PORTC_OFFSET && pin<JSH_PORTC_OFFSET+JSH_PORTC_COUNT) {
+    result[0]='C';
+    itoa(pin-JSH_PORTC_OFFSET,&result[1],10);
+  } else if (pin>=JSH_PORTD_OFFSET && pin<JSH_PORTD_OFFSET+JSH_PORTD_COUNT) {
+    result[0]='D';
+    itoa(pin-JSH_PORTD_OFFSET,&result[1],10);
+#if JSH_PORTE_OFFSET!=-1
+  } else if (pin>=JSH_PORTE_OFFSET && pin<JSH_PORTE_OFFSET+JSH_PORTE_COUNT) {
+    result[0]='E';
+    itoa(pin-JSH_PORTE_OFFSET,&result[1],10);
+#endif
+#if JSH_PORTF_OFFSET!=-1
+  } else if (pin>=JSH_PORTF_OFFSET && pin<JSH_PORTF_OFFSET+JSH_PORTF_COUNT) {
+    result[0]='F';
+    itoa(pin-JSH_PORTF_OFFSET,&result[1],10);
+#endif
+#if JSH_PORTG_OFFSET!=-1
+  } else if (pin>=JSH_PORTG_OFFSET && pin<JSH_PORTG_OFFSET+JSH_PORTG_COUNT) {
+    result[0]='G';
+    itoa(pin-JSH_PORTG_OFFSET,&result[1],10);
+#endif
+#if JSH_PORTH_OFFSET!=-1
+  } else if (pin>=JSH_PORTH_OFFSET && pin<JSH_PORTH_OFFSET+JSH_PORTH_COUNT) {
+    result[0]='H';
+    itoa(pin-JSH_PORTH_OFFSET,&result[1],10);
+#endif
+  } else {
+    strncpy(result, "UNKNOWN", 8);
+  }
+}

+ 228 - 0
components/external/espruino/src/jspin.h

@@ -0,0 +1,228 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Utilities and definitions for handling Pins
+ * ----------------------------------------------------------------------------
+ */
+
+#ifndef JSPIN_H
+#define JSPIN_H
+
+#include "jsutils.h"
+#include "jsvar.h"
+
+typedef unsigned char Pin; ///< for specifying pins for hardware
+#define PIN_UNDEFINED ((Pin)0xFF)
+
+typedef enum {
+  JSH_PORT_NONE,
+  JSH_PORTA=1,
+  JSH_PORTB,
+  JSH_PORTC,
+  JSH_PORTD,
+  JSH_PORTE,
+  JSH_PORTF,
+  JSH_PORTG,
+  JSH_PORTH,
+} PACKED_FLAGS JsvPinInfoPort;
+
+typedef enum {
+  JSH_PIN0 = 0,
+  JSH_PIN1,
+  JSH_PIN2,
+  JSH_PIN3,
+  JSH_PIN4,
+  JSH_PIN5,
+  JSH_PIN6,
+  JSH_PIN7,
+  JSH_PIN8,
+  JSH_PIN9,
+  JSH_PIN10,
+  JSH_PIN11,
+  JSH_PIN12,
+  JSH_PIN13,
+  JSH_PIN14,
+  JSH_PIN15,
+#ifndef ARM
+  JSH_PIN16,
+  JSH_PIN17,
+  JSH_PIN18,
+  JSH_PIN19,
+  JSH_PIN20,
+  JSH_PIN21,
+  JSH_PIN22,
+  JSH_PIN23,
+  JSH_PIN24,
+  JSH_PIN25,
+  JSH_PIN26,
+  JSH_PIN27,
+  JSH_PIN28,
+  JSH_PIN29,
+  JSH_PIN30,
+  JSH_PIN31,
+#endif
+} PACKED_FLAGS JsvPinInfoPin;
+
+typedef enum {
+  JSH_ANALOG_NONE = 0,
+  JSH_ANALOG1     = 32,
+  JSH_ANALOG2     = 64,
+  JSH_ANALOG3     = 128,
+  JSH_ANALOG4     = 256,
+  JSH_ANALOG12    = JSH_ANALOG1|JSH_ANALOG2,
+  JSH_ANALOG123   = JSH_ANALOG1|JSH_ANALOG2|JSH_ANALOG3,
+  JSH_ANALOG34    = JSH_ANALOG3|JSH_ANALOG4,
+
+  JSH_ANALOG_CH0 = 0,
+  JSH_ANALOG_CH1,
+  JSH_ANALOG_CH2,
+  JSH_ANALOG_CH3,
+  JSH_ANALOG_CH4,
+  JSH_ANALOG_CH5,
+  JSH_ANALOG_CH6,
+  JSH_ANALOG_CH7,
+  JSH_ANALOG_CH8,
+  JSH_ANALOG_CH9,
+  JSH_ANALOG_CH10,
+  JSH_ANALOG_CH11,
+  JSH_ANALOG_CH12,
+  JSH_ANALOG_CH13,
+  JSH_ANALOG_CH14,
+  JSH_ANALOG_CH15,
+  JSH_ANALOG_CH16,
+
+  JSH_MASK_ANALOG_CH  = 31,
+  JSH_MASK_ANALOG_ADC = JSH_ANALOG1|JSH_ANALOG2|JSH_ANALOG3|JSH_ANALOG4,
+
+} PACKED_FLAGS JsvPinInfoAnalog;
+
+typedef enum {
+  // ---------------------------- JSH_MASK_AF
+  JSH_AF0 = 0,
+  JSH_AF1,
+  JSH_AF2,
+  JSH_AF3,
+  JSH_AF4,
+  JSH_AF5,
+  JSH_AF6,
+  JSH_AF7,
+  JSH_AF8,
+  JSH_AF9,
+  JSH_AF10,
+  JSH_AF11,
+  JSH_AF12,
+  JSH_AF13,
+  JSH_AF14,
+  JSH_AF15,
+
+  // ---------------------------- JSH_MASK_TYPE
+  JSH_TIMER1  = 0x0010,
+  JSH_TIMER2  = 0x0020,
+  JSH_TIMER3  = 0x0030,
+  JSH_TIMER4  = 0x0040,
+  JSH_TIMER5  = 0x0050,
+  JSH_TIMER6  = 0x0060,
+  JSH_TIMER7  = 0x0070,
+  JSH_TIMER8  = 0x0080,
+  JSH_TIMER9  = 0x0090,
+  JSH_TIMER10 = 0x00A0,
+  JSH_TIMER11 = 0x00B0,
+  JSH_TIMER12 = 0x00C0,
+  JSH_TIMER13  = 0x00D0,
+  JSH_TIMER14  = 0x00E0,
+  JSH_TIMER15  = 0x00F0,
+  JSH_TIMER16  = 0x0100,
+  JSH_TIMER17  = 0x0110,
+  JSH_TIMER18  = 0x0120,
+  JSH_TIMERMAX = JSH_TIMER18,
+  JSH_DAC      = 0x0180,
+  JSH_SPI1     = 0x0200,
+  JSH_SPI2     = 0x0210,
+  JSH_SPI3     = 0x0220,
+  JSH_SPIMAX   = JSH_SPI3,
+  JSH_I2C1     = 0x0280,
+  JSH_I2C2     = 0x0290,
+  JSH_I2C3     = 0x02A0,
+  JSH_I2CMAX   = JSH_I2C3,
+  JSH_USART1   = 0x0300,
+  JSH_USART2   = 0x0310,
+  JSH_USART3   = 0x0320,
+  JSH_USART4   = 0x0330,
+  JSH_USART5   = 0x0340,
+  JSH_USART6   = 0x0350,
+  JSH_USARTMAX = JSH_USART6,
+
+  // ---------------------------- JSH_MASK_INFO
+
+  JSH_TIMER_CH1 = 0x0000,
+  JSH_TIMER_CH2 = 0x1000,
+  JSH_TIMER_CH3 = 0x2000,
+  JSH_TIMER_CH4 = 0x3000,
+  JSH_MASK_TIMER_CH = 0x7000,
+  JSH_TIMER_NEGATED = 0x8000,
+
+  JSH_USART_RX = 0x0000,
+  JSH_USART_TX = 0x1000,
+
+  JSH_SPI_MISO = 0x0000,
+  JSH_SPI_MOSI = 0x1000,
+  JSH_SPI_SCK  = 0x2000,
+
+  JSH_I2C_SCL  = 0x0000,
+  JSH_I2C_SDA  = 0x1000,
+
+  JSH_DAC_CH1 = 0x0000,
+  JSH_DAC_CH2 = 0x1000,
+
+  // ---------------------------- Masks
+  JSH_MASK_AF = 0x000F,
+  JSH_MASK_TYPE = 0x0FF0,
+  JSH_MASK_INFO = 0xF000,
+} PACKED_FLAGS JshPinFunction;
+
+#define JSH_PINFUNCTION_IS_TIMER(F) ( \
+  (((F)&JSH_MASK_TYPE)>=JSH_TIMER1) && \
+  (((F)&JSH_MASK_TYPE)<=JSH_TIMER18))
+#define JSH_PINFUNCTION_IS_DAC(F) ( \
+  (((F)&JSH_MASK_TYPE)==JSH_DAC) || \
+0 )
+#define JSH_PINFUNCTION_IS_USART(F) ( \
+  (((F)&JSH_MASK_TYPE)>=JSH_USART1) && \
+  (((F)&JSH_MASK_TYPE)<=JSH_USART6))
+#define JSH_PINFUNCTION_IS_I2C(F) ( \
+  (((F)&JSH_MASK_TYPE)>=JSH_I2C1) && \
+  (((F)&JSH_MASK_TYPE)<=JSH_I2CMAX))
+#define JSH_PINFUNCTION_IS_SPI(F) ( \
+  (((F)&JSH_MASK_TYPE)>=JSH_SPI1) && \
+  (((F)&JSH_MASK_TYPE)<=JSH_SPIMAX))
+
+bool jshIsPinValid(Pin pin); ///< is the specific pin actually valid?
+
+/// Given a string, convert it to a pin ID (or -1 if it doesn't exist)
+Pin jshGetPinFromString(const char *s);
+/** Write the pin name to a string. String must have at least 8 characters (to be safe) */
+void jshGetPinString(char *result, Pin pin);
+
+/// Given a var, convert it to a pin ID (or -1 if it doesn't exist). safe for undefined!
+static inline Pin jshGetPinFromVar(JsVar *pinv) {
+  if (jsvIsString(pinv) && pinv->varData.str[5]==0/*should never be more than 4 chars!*/) {
+    return jshGetPinFromString(&pinv->varData.str[0]);
+  } else if (jsvIsInt(pinv) /* This also tests for the Pin datatype */) {
+    return (Pin)jsvGetInteger(pinv);
+  } else return PIN_UNDEFINED;
+}
+
+static inline Pin jshGetPinFromVarAndUnLock(JsVar *pinv) {
+  Pin pin = jshGetPinFromVar(pinv);
+  jsvUnLock(pinv);
+  return pin;
+}
+
+#endif //JSPIN_H

+ 439 - 0
components/external/espruino/src/jsutils.c

@@ -0,0 +1,439 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Misc utils and cheapskate stdlib implementation
+ * ----------------------------------------------------------------------------
+ */
+#include "jsutils.h"
+#include "jslex.h"
+#include "jshardware.h"
+#include "jsinteractive.h"
+
+// needed for isnan / isfinite
+#ifdef ARM
+#include "mconf.h"
+#include "protos.h"
+#else
+#include <math.h>
+#endif
+
+extern double jswrap_math_pow(double x, double y); // for pow
+
+bool isIDString(const char *s) {
+    if (!isAlpha(*s))
+        return false;
+    while (*s) {
+        if (!(isAlpha(*s) || isNumeric(*s)))
+            return false;
+        s++;
+    }
+    return true;
+}
+
+/** escape a character - if it is required. This may return a reference to a static array,
+so you can't store the value it returns in a variable and call it again. */
+const char *escapeCharacter(char ch) {
+  if (ch=='\b') return "\\b";
+  if (ch=='\f') return "\\f";
+  if (ch=='\n') return "\\n";
+  if (ch=='\a') return "\\a";
+  if (ch=='\r') return "\\r";
+  if (ch=='\t') return "\\t";
+  if (ch=='\\') return "\\\\";
+  if (ch=='"') return "\\\"";
+  static char buf[5];
+  if (ch<32) {
+    /** just encode as hex - it's more understandable
+     * and doesn't have the issue of "\16"+"1" != "\161" */
+    buf[0]='\\';
+    buf[1]='x';
+    int n = (ch>>4)&15;
+    buf[2] = (char)((n<10)?('0'+n):('A'+n-10));
+    n=ch&15;
+    buf[3] = (char)((n<10)?('0'+n):('A'+n-10));
+    buf[4] = 0;
+    return buf;
+  }
+  buf[1] = 0;
+  buf[0] = ch;
+  return buf;
+}
+
+/* convert a number in the given radix to an int. if radix=0, autodetect */
+JsVarInt stringToIntWithRadix(const char *s, int forceRadix, bool *hasError) {
+  bool isNegated = false;
+  JsVarInt v = 0;
+  JsVarInt radix = 10;
+  if (*s == '-') {
+    isNegated = true;
+    s++;
+  }
+  if (*s == '0') {
+    radix = 8;
+    s++;
+
+    // OctalIntegerLiteral: 0o01, 0O01
+    if (*s == 'o' || *s == 'O') {
+      radix = 8;
+      s++;
+
+    // HexIntegerLiteral: 0x01, 0X01
+    } else if (*s == 'x' || *s == 'X') {
+      radix = 16;
+      s++;
+
+    // BinaryIntegerLiteral: 0b01, 0B01
+    } else if (*s == 'b' || *s == 'B') {
+      radix = 2;
+      s++;
+    }
+  }
+  if (forceRadix>0 && forceRadix<=36)
+    radix = forceRadix;
+
+  while (*s) {
+    int digit = 0;
+    if (*s >= '0' && *s <= '9')
+      digit = (*s - '0');
+    else if (*s >= 'a' && *s <= 'f')
+      digit = (10 + *s - 'a');
+    else if (*s >= 'A' && *s <= 'F')
+      digit = (10 + *s - 'A');
+    else break;
+    if (digit>=radix)
+      break;
+    v = v*radix + digit;
+    s++;
+  }
+
+  if (hasError) *hasError = *s!=0; // we're ok if we reached the end of the string
+
+  if (isNegated) return -v;
+  return v;
+}
+
+/* convert hex, binary, octal or decimal string into an int */
+JsVarInt stringToInt(const char *s) {
+    return stringToIntWithRadix(s,0,0);
+}
+
+void jsError(const char *fmt, ...) {
+  jsiConsoleRemoveInputLine();
+  jsiConsolePrint("ERROR: ");
+  va_list argp;
+  va_start(argp, fmt);
+  vcbprintf((vcbprintf_callback)jsiConsolePrint,0, fmt, argp);
+  va_end(argp);
+  jsiConsolePrint("\n");
+}
+
+void jsErrorInternal(const char *fmt, ...) {
+  jsiConsoleRemoveInputLine();
+  jsiConsolePrint("INTERNAL ERROR: ");
+  va_list argp;
+  va_start(argp, fmt);
+  vcbprintf((vcbprintf_callback)jsiConsolePrint,0, fmt, argp);
+  va_end(argp);
+  jsiConsolePrint("\n");
+}
+
+void jsErrorAt(const char *message, struct JsLex *lex, int tokenPos) {
+  jsiConsoleRemoveInputLine();
+  jsiConsolePrint("ERROR: ");
+  jsiConsolePrint(message);
+  jsiConsolePrint(" at ");
+  jsiConsolePrintPosition(lex, tokenPos);
+  jsiConsolePrintTokenLineMarker(lex, tokenPos);
+}
+
+void jsWarn(const char *fmt, ...) {
+  jsiConsoleRemoveInputLine();
+  jsiConsolePrint("WARNING: ");
+  va_list argp;
+  va_start(argp, fmt);
+  vcbprintf((vcbprintf_callback)jsiConsolePrint,0, fmt, argp);
+  va_end(argp);
+  jsiConsolePrint("\n");
+}
+
+void jsWarnAt(const char *message, struct JsLex *lex, int tokenPos) {
+  jsiConsoleRemoveInputLine();
+  jsiConsolePrint("WARNING: ");
+  jsiConsolePrint(message);
+  jsiConsolePrint(" at ");
+  jsiConsolePrintPosition(lex, tokenPos);
+}
+
+void jsAssertFail(const char *file, int line, const char *expr) {
+  jsiConsoleRemoveInputLine();
+  if (expr) {
+    jsiConsolePrintf("ASSERT(%s) FAILED AT ", expr);
+  } else
+    jsiConsolePrint("ASSERT FAILED AT ");
+  jsiConsolePrintf("%s:%d\n",file,line);
+
+  jsvTrace(jsvGetRef(jsvFindOrCreateRoot()), 2);
+  exit(1);
+}
+
+#ifdef SDCC
+void exit(int errcode) {dst;
+    jsiConsolePrint("EXIT CALLED.\n");
+}
+#endif
+
+#ifdef FAKE_STDLIB
+int __errno;
+
+void exit(int errcode) {
+    NOT_USED(errcode);
+    jsiConsolePrint("EXIT CALLED.\n");
+    while (1);
+}
+
+char * strncat(char *dst, const char *src, size_t c) {
+        char *dstx = dst;
+        while (*(++dstx)) c--;
+        while (*src && c>1) {
+          *(dstx++) = *(src++);
+          c--;
+        }
+        if (c>0) *dstx = 0;
+        return dst;
+}
+char *strncpy(char *dst, const char *src, size_t c) {
+        char *dstx = dst;
+        while (*src && c>1) {
+          *(dstx++) = *(src++);
+          c--;
+        }
+        if (c>0) *dstx = 0;
+        return dst;
+}
+size_t strlen(const char *s) {
+        size_t l=0;
+        while (*(s++)) l++;
+        return l;
+}
+int strcmp(const char *a, const char *b) {
+        while (*a && *b) {
+                if (*a != *b)
+                        return *a - *b; // correct?
+                a++;b++;
+        }
+        return *a - *b;
+}
+void *memcpy(void *dst, const void *src, size_t size) {
+        size_t i;
+        for (i=0;i<size;i++)
+                ((char*)dst)[i] = ((char*)src)[i];
+        return dst;
+}
+
+void *memset(void *dst, int val, size_t size) {
+  unsigned char *d = (unsigned char*)dst;
+  unsigned int i;
+  for (i=0;i<size;i++)
+    d[i]=(unsigned char)val;
+  return dst;
+}
+
+unsigned int rand() {
+    static unsigned int m_w = 0xDEADBEEF;    /* must not be zero */
+    static unsigned int m_z = 0xCAFEBABE;    /* must not be zero */
+
+    m_z = 36969 * (m_z & 65535) + (m_z >> 16);
+    m_w = 18000 * (m_w & 65535) + (m_w >> 16);
+    return (m_z << 16) + m_w;  /* 32-bit result */
+}
+#endif
+
+JsVarFloat stringToFloat(const char *s) {
+  bool isNegated = false;
+  JsVarFloat v = 0;
+  JsVarFloat mul = 0.1;
+  if (*s == '-') {
+    isNegated = true;
+    s++;
+  }
+  // handle integer part
+  while (*s) {
+    if (*s >= '0' && *s <= '9')
+      v = (v*10) + (*s - '0');
+    else break;
+    s++;
+  }
+  // handle decimal point
+  if (*s == '.') {
+    s++; // skip .
+
+    while (*s) {
+      if (*s >= '0' && *s <= '9')
+        v += mul*(*s - '0');
+      else break;
+      mul /= 10;
+      s++;
+    }
+  }
+
+  // handle exponentials
+  if (*s == 'e' || *s == 'E') {
+    s++;  // skip E
+    bool isENegated = false;
+    if (*s == '-' || *s == '+') {
+      isENegated = *s=='-';
+      s++;
+    }
+    int e = 0;
+    while (*s) {
+      if (*s >= '0' && *s <= '9')
+        e = (e*10) + (*s - '0');
+      else break;
+      s++;
+    }
+    if (isENegated) e=-e;
+    v = v * jswrap_math_pow(10, e);
+  }
+  // check we have parsed everything
+  if (*s!=0) return NAN;
+
+  if (isNegated) return -v;
+  return v;
+}
+
+
+char itoch(int val) {
+  if (val<10) return (char)('0'+val);
+  return (char)('A'+val-10);
+}
+
+#ifndef HAS_STDLIB
+void itoa(JsVarInt vals,char *str,unsigned int base) {
+  JsVarIntUnsigned val;
+  if (vals<0) {
+    *(str++)='-';
+    val = (JsVarIntUnsigned)(-vals);
+  } else {
+    val = (JsVarIntUnsigned)vals;
+  }
+  JsVarIntUnsigned d = 1;
+  while (d*base <= val) d*=base;
+  while (d > 1) {
+    unsigned int v = (unsigned int)(val / d);
+    val -= v*d;
+    *(str++) = itoch((int)v);
+    d /= base;
+  }
+  *(str++)=itoch((int)val);
+  *(str++)=0;
+}
+#endif
+
+void ftoa(JsVarFloat val,char *str) {
+  if (isnan(val)) strncpy(str,"NaN",4);
+  else if (!isfinite(val)) {
+    if (val<0) strncpy(str,"-Infinity",10);
+    else strncpy(str,"Infinity",10);
+  } else {
+    const JsVarFloat base = 10;
+    if (val<0) {
+      *(str++)='-';
+      val = -val;
+    }
+    JsVarFloat d = 1;
+    while (d*base <= val) d*=base;
+    while (d >= 1) {
+      int v = (int)(val / d);
+      val -= v*d;
+      *(str++)=itoch(v);
+      d /= base;
+    }
+  #ifndef USE_NO_FLOATS
+    if (val>0) {
+      *(str++)='.';
+      while (val>0.000001) {
+        int v = (int)((val / d) + 0.0000005);
+        val -= v*d;
+        *(str++)=itoch(v);
+        d /= base;
+      }
+    }
+  #endif
+
+    *(str++)=0;
+  }
+}
+
+
+/// Wrap a value so it is always between 0 and size (eg. wrapAround(angle, 360))
+JsVarFloat wrapAround(JsVarFloat val, JsVarFloat size) {
+  val = val / size;
+  val = val - (int)val;
+  return val * size;
+}
+
+/** Espruino-special printf with a callback
+ * Supported are:
+ *   %d = int
+ *   %x = int as hex
+ *   %L = JsVarInt
+ *   %Lx = JsVarInt as hex
+ *   %f = JsVarFloat
+ *   %s = string (char *)
+ *   %c = char
+ *   %v = JsVar * (prints var as string)
+ *   %t = JsVar * (prints type of var)
+ *   %p = Pin
+ *
+ * Anything else will assert
+ */
+void vcbprintf(vcbprintf_callback user_callback, void *user_data, const char *fmt, va_list argp) {
+  char buf[32];
+  while (*fmt) {
+    if (*fmt == '%') {
+      fmt++;
+      switch (*fmt++) {
+      case 'd': itoa(va_arg(argp, int), buf, 10); user_callback(buf,user_data); break;
+      case 'x': itoa(va_arg(argp, int), buf, 16); user_callback(buf,user_data); break;
+      case 'L': {
+        unsigned int rad = 10;
+        if (*fmt=='x') { rad=16; fmt++; }
+        itoa(va_arg(argp, JsVarInt), buf, rad); user_callback(buf,user_data);
+      } break;
+      case 'f': ftoa(va_arg(argp, JsVarFloat), buf); user_callback(buf,user_data);  break;
+      case 's': user_callback(va_arg(argp, char *), user_data); break;
+      case 'c': buf[0]=(char)va_arg(argp, int/*char*/);buf[1]=0; user_callback(buf, user_data); break;
+      case 'v': {
+        JsVar *v = jsvAsString(va_arg(argp, JsVar*), false/*no unlock*/);
+        buf[1] = 0;
+        JsvStringIterator it;
+        jsvStringIteratorNew(&it, v, 0);
+        // OPT: this could be faster than it is (sending whole blocks at once)
+        while (jsvStringIteratorHasChar(&it)) {
+          buf[0] = jsvStringIteratorGetChar(&it);
+          user_callback(buf,user_data);
+          jsvStringIteratorNext(&it);
+        }
+        jsvStringIteratorFree(&it);
+        jsvUnLock(v);
+      } break;
+      case 't': user_callback(jsvGetTypeOf(va_arg(argp, JsVar*)), user_data); break;
+      case 'p': jshGetPinString(buf, (Pin)va_arg(argp, int/*Pin*/)); user_callback(buf, user_data); break;
+      default: assert(0); return; // eep
+      }
+    } else {
+      buf[0] = *(fmt++);
+      buf[1] = 0;
+      user_callback(&buf[0], user_data);
+    }
+  }
+}
+
+

+ 374 - 0
components/external/espruino/src/jsutils.h

@@ -0,0 +1,374 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Misc utils and cheapskate stdlib implementation
+ * ----------------------------------------------------------------------------
+ */
+#ifndef JSUTILS_H_
+#define JSUTILS_H_
+
+#include "platform_config.h"
+
+#ifndef FAKE_STDLIB
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+#include <stdarg.h> // for va_args
+
+#define JS_VERSION "1v46"
+/*
+  In code:
+  TODO - should be fixed
+  FIXME - will probably break if used
+  OPT - potential for speed optimisation
+*/
+
+// surely bool is defined??
+#ifdef RT_USING_JS
+typedef unsigned int size_t;
+#else
+#ifdef ARM
+typedef unsigned int size_t;
+#define alloca(x) __builtin_alloca(x)
+#endif
+#endif
+
+#if !defined(__USB_TYPE_H) && !defined(CPLUSPLUS) // it is defined in this file too!
+typedef enum {FALSE = 0, TRUE = !FALSE} bool;
+//typedef unsigned char bool;
+//#define TRUE (1)
+//#define FALSE (0)
+#endif
+
+#define true (1)
+#define false (0)
+#define NAN (((JsVarFloat)0)/(JsVarFloat)0)
+
+/* Number of Js Variables allowed and Js Reference format.
+
+   JsVarRef = char -> 15 bytes/JsVar   so JSVAR_CACHE_SIZE = (RAM - 3000) / 15
+   JsVarRef = short -> 20 bytes/JsVar   so JSVAR_CACHE_SIZE = (RAM - 3000) / 20
+   JsVarRef = int -> 26 bytes/JsVar   so JSVAR_CACHE_SIZE = (RAM - 3000) / 26
+
+   NOTE: JSVAR_CACHE_SIZE must be at least 2 less than the number we can fit in JsVarRef
+         See jshardware.c FLASH constants - all this must be able to fit in flash
+
+
+*/
+
+#ifdef RESIZABLE_JSVARS
+ //  probably linux - allow us to allocate more blocks of variables
+  typedef unsigned int JsVarRef;
+  #define JSVAR_SIZE 30
+  #define JSVAR_DATA_STRING_LEN  8 // Actually 9 seems like a good number as 'prototype'==9
+  #define JSVAR_DATA_STRING_MAX_LEN 24 // (JSVAR_DATA_STRING_LEN + sizeof(JsVarRef)*3 + sizeof(JsVarRefCounter)) - but see JSV_STRING_LEN_MAX - WE HAVE TO CLIP!
+#else
+   /** JsVerRef stores References for variables - We treat 0 as null
+   *  NOTE: we store JSVAR_DATA_STRING_* as actual values so we can do #if on them below
+   *
+   */
+  #if JSVAR_CACHE_SIZE <= 254
+    typedef unsigned char JsVarRef;
+    #define JSVAR_SIZE 15
+    #define JSVAR_DATA_STRING_LEN  8 // Actually 9 seems like a good number as 'prototype'==9
+    #define JSVAR_DATA_STRING_MAX_LEN 12 // (JSVAR_DATA_STRING_LEN + sizeof(JsVarRef)*3 + sizeof(JsVarRefCounter)) - but see JSV_STRING_LEN_MAX too
+  #else
+    typedef unsigned short JsVarRef;
+    #define JSVAR_SIZE 20
+    #define JSVAR_DATA_STRING_LEN  8 // Actually 9 seems like a good number as 'prototype'==9
+    #define JSVAR_DATA_STRING_MAX_LEN 16 // (JSVAR_DATA_STRING_LEN + sizeof(JsVarRef)*3 + sizeof(JsVarRefCounter)) - but see JSV_STRING_LEN_MAX too - WE HAVE TO CLIP!
+  #endif
+#endif
+
+typedef long long JsVarInt;
+typedef unsigned long long JsVarIntUnsigned;
+#ifdef USE_FLOATS
+typedef float JsVarFloat;
+#else
+typedef double JsVarFloat;
+#endif
+
+typedef short JslCharPos;
+#define JSSYSTIME_MAX 0x7FFFFFFFFFFFFFFFLL
+typedef long long JsSysTime;
+
+#define JSLEX_MAX_TOKEN_LENGTH  64
+#define JS_ERROR_BUF_SIZE 64 // size of buffer error messages are written into
+#define JS_ERROR_TOKEN_BUF_SIZE 16 // see jslTokenAsString
+
+#define JS_NUMBER_BUFFER_SIZE 24
+
+#define JSPARSE_MAX_SCOPES  8
+// Don't restrict number of iterations now
+//#define JSPARSE_MAX_LOOP_ITERATIONS 8192
+
+#define STRINGIFY_HELPER(x) #x
+#define STRINGIFY(x) STRINGIFY_HELPER(x)
+#define NOT_USED(x) ( (void)(x) )
+
+// javascript specific names
+#define JSPARSE_RETURN_VAR "return" // variable name used for returning function results
+#define JSPARSE_PROTOTYPE_VAR "prototype"
+#define JSPARSE_CONSTRUCTOR_VAR "constructor"
+#define JSPARSE_INHERITS_VAR "__proto__"
+// internal names that hopefully nobody will be able to access
+#define JS_HIDDEN_CHAR '>' // initial character of var name determines that we shouldn't see this stuff
+#define JS_HIDDEN_CHAR_STR ">"
+#define JSPARSE_FUNCTION_CODE_NAME JS_HIDDEN_CHAR_STR"code"
+#define JSPARSE_FUNCTION_SCOPE_NAME JS_HIDDEN_CHAR_STR"scope"
+#define JSPARSE_MODULE_CACHE_NAME JS_HIDDEN_CHAR_STR"modules"
+
+#if !defined(NO_ASSERT)
+ #ifdef __STRING
+   #define assert(X) if (!(X)) jsAssertFail(__FILE__,__LINE__,__STRING(X));
+ #else
+   #define assert(X) if (!(X)) jsAssertFail(__FILE__,__LINE__,"");
+ #endif
+#else
+ #define assert(X)
+#endif
+
+/// Used when we have enums we want to squash down
+#define PACKED_FLAGS  __attribute__ ((__packed__))
+
+/// Used before functions that we want to ensure are not inlined (eg. "void NO_INLINE foo() {}")
+#define NO_INLINE __attribute__ ((noinline))
+
+/// Maximum amount of locks we ever expect to have on a variable (this could limit recursion) must be 2^n-1
+#define JSV_LOCK_MAX  15
+
+/// preprocessor power of 2 - suitable up to 16 bits
+#define NEXT_POWER_2(X) \
+   (((X) | (X)>>1 | (X)>>2 | (X)>>3 | \
+    (X)>>4 | (X)>>5 | (X)>>6 | (X)>>7 | \
+    (X)>>8 | (X)>>9 | (X)>>10 | (X)>>11 | \
+    (X)>>12 | (X)>>13 | (X)>>14 | (X)>>15)+1)
+/// Proprocessor get bit number
+#define GET_BIT_NUMBER(X) \
+  (((X)==    1)? 0: \
+   ((X)==    2)? 1: \
+   ((X)==    4)? 2: \
+   ((X)==    8)? 3: \
+   ((X)==   16)? 4: \
+   ((X)==   32)? 5: \
+   ((X)==   64)? 6: \
+   ((X)==  128)? 7: \
+   ((X)==  256)? 8: \
+   ((X)==  512)? 9: \
+   ((X)== 1024)?10: \
+   ((X)== 2048)?11: \
+   ((X)== 4096)?12: \
+   ((X)== 8192)?13: \
+   ((X)==16384)?14: \
+   ((X)==32768)?15:10000/*error*/)
+
+
+
+/** These flags are at the top of each JsVar and provide information about what it is, as
+ * well as how many Locks it has. Everything is packed in as much as possible to allow us to
+ * get down to within 2 bytes. */
+typedef enum {
+    JSV_UNUSED      = 0, ///< Variable not used for anything
+    JSV_ROOT        = JSV_UNUSED+1, ///< The root of everything - there is only one of these
+    // UNDEFINED is now just stored using '0' as the variable Ref
+    JSV_NULL        = JSV_ROOT+1, ///< it seems null is its own data type
+    JSV_STRING      = JSV_NULL+1, ///< string
+    JSV_STRING_0    = JSV_STRING, // string of length 0
+    JSV_STRING_MAX  = JSV_STRING_0+JSVAR_DATA_STRING_LEN,
+    JSV_STRING_EXT  = JSV_STRING_MAX+1, ///< extra character data for string (if it didn't fit in first JsVar). These use unused pointer fields for extra characters
+    JSV_STRING_EXT_0 = JSV_STRING_EXT,
+    JSV_STRING_EXT_MAX = JSV_STRING_EXT_0+JSVAR_DATA_STRING_MAX_LEN,
+    JSV_ARRAY = JSV_STRING_EXT_MAX+1, ///< A JavaScript Array Buffer - Implemented just like a String at the moment
+    JSV_ARRAYBUFFER  = JSV_ARRAY+1,
+    JSV_OBJECT      = JSV_ARRAYBUFFER+1,
+    JSV_FUNCTION    = JSV_OBJECT+1,
+    JSV_NUMERICSTART = JSV_FUNCTION+1, ///< --------- Start of numeric variable types
+    JSV_INTEGER     = JSV_NUMERICSTART, ///< integer number (note JSV_NUMERICMASK)
+    JSV_FLOAT       = JSV_INTEGER+1, ///< floating point double (note JSV_NUMERICMASK)
+    JSV_BOOLEAN     = JSV_FLOAT+1, ///< boolean (note JSV_NUMERICMASK)
+    JSV_PIN         = JSV_BOOLEAN+1, ///< pin (note JSV_NUMERICMASK)
+    JSV_NUMERICEND  = JSV_PIN, ///< --------- End of numeric variable types
+    JSV_VAR_END     = JSV_NUMERICEND, ///< End of numeric variable types
+
+    JSV_VARTYPEMASK = NEXT_POWER_2(JSV_VAR_END)-1,
+
+    // names can be STRING,
+    JSV_NAME        = JSV_VARTYPEMASK+1, ///< a NAME of a variable - this isn't a variable itself (and can be an int/string/etc.)
+    JSV_NATIVE      = JSV_NAME<<1, ///< to specify this is a native function, root, function parameter, OR that it should not be freed
+    JSV_GARBAGE_COLLECT = JSV_NATIVE<<1, ///< When garbage collecting, this flag is true IF we should GC!
+    JSV_IS_RECURSING = JSV_GARBAGE_COLLECT<<1, ///< used to stop recursive loops in jsvTrace
+    JSV_LOCK_ONE    = JSV_IS_RECURSING<<1,
+    JSV_LOCK_MASK   = JSV_LOCK_MAX * JSV_LOCK_ONE,
+
+
+
+    JSV_ARRAYBUFFERNAME = JSV_NAME|JSV_ARRAYBUFFER, ///< used for indexing into an ArrayBuffer. varData is an INT in this case
+    JSV_FUNCTION_PARAMETER = JSV_NATIVE | JSV_NAME, ///< this is inside a function, so it should be quite obvious
+
+} PACKED_FLAGS JsVarFlags; // aiming to get this in 2 bytes!
+
+/// The amount of bits we must shift to get the number of locks - forced to be a constant
+static const int JSV_LOCK_SHIFT = GET_BIT_NUMBER(JSV_LOCK_ONE);
+
+typedef enum LEX_TYPES {
+    LEX_EOF = 0,
+    LEX_ID = 256,
+    LEX_INT,
+    LEX_FLOAT,
+    LEX_STR,
+    LEX_UNFINISHED_COMMENT,
+
+    LEX_EQUAL,
+    LEX_TYPEEQUAL,
+    LEX_NEQUAL,
+    LEX_NTYPEEQUAL,
+    LEX_LEQUAL,
+    LEX_LSHIFT,
+    LEX_LSHIFTEQUAL,
+    LEX_GEQUAL,
+    LEX_RSHIFT,
+    LEX_RSHIFTUNSIGNED,
+    LEX_RSHIFTEQUAL,
+    LEX_RSHIFTUNSIGNEDEQUAL,
+    LEX_PLUSEQUAL,
+    LEX_MINUSEQUAL,
+    LEX_PLUSPLUS,
+    LEX_MINUSMINUS,
+    LEX_MULEQUAL,
+    LEX_DIVEQUAL,
+    LEX_MODEQUAL,
+    LEX_ANDEQUAL,
+    LEX_ANDAND,
+    LEX_OREQUAL,
+    LEX_OROR,
+    LEX_XOREQUAL,
+    // reserved words
+#define LEX_R_LIST_START LEX_R_IF
+    LEX_R_IF,
+    LEX_R_ELSE,
+    LEX_R_DO,
+    LEX_R_WHILE,
+    LEX_R_FOR,
+    LEX_R_BREAK,
+    LEX_R_CONTINUE,
+    LEX_R_FUNCTION,
+    LEX_R_RETURN,
+    LEX_R_VAR,
+    LEX_R_THIS,
+    LEX_R_TRUE,
+    LEX_R_FALSE,
+    LEX_R_NULL,
+    LEX_R_UNDEFINED,
+    LEX_R_NEW,
+    LEX_R_IN,
+    LEX_R_INSTANCEOF,
+    LEX_R_SWITCH,
+    LEX_R_CASE,
+    LEX_R_DEFAULT,
+    LEX_R_TYPEOF,
+    LEX_R_VOID,
+
+    LEX_R_LIST_END /* always the last entry */
+} LEX_TYPES;
+
+// To handle variable size bit fields
+#define BITFIELD_DECL(BITFIELD, N) unsigned int BITFIELD[(N+31)/32]
+#define BITFIELD_GET(BITFIELD, N) ((BITFIELD[(N)>>5] >> ((N)&31))&1)
+#define BITFIELD_SET(BITFIELD, N, VALUE) (BITFIELD[(N)>>5] = (BITFIELD[(N)>>5]& (unsigned int)~(1 << ((N)&31))) | (unsigned int)((VALUE)?(1 << ((N)&31)):0)  )
+
+
+static inline bool isWhitespace(char ch) {
+    return (ch==' ') || (ch=='\t') || (ch=='\n') || (ch=='\r');
+}
+
+static inline bool isNumeric(char ch) {
+    return (ch>='0') && (ch<='9');
+}
+
+static inline bool isHexadecimal(char ch) {
+    return ((ch>='0') && (ch<='9')) ||
+           ((ch>='a') && (ch<='f')) ||
+           ((ch>='A') && (ch<='F'));
+}
+static inline bool isAlpha(char ch) {
+    return ((ch>='a') && (ch<='z')) || ((ch>='A') && (ch<='Z')) || ch=='_';
+}
+
+
+bool isIDString(const char *s);
+
+/** escape a character - if it is required. This may return a reference to a static array,
+so you can't store the value it returns in a variable and call it again. */
+const char *escapeCharacter(char ch);
+/* convert a number in the given radix to an int. if radix=0, autodetect */
+JsVarInt stringToIntWithRadix(const char *s, int radix, bool *hasError);
+/* convert hex, binary, octal or decimal string into an int */
+JsVarInt stringToInt(const char *s);
+
+// forward decl
+struct JsLex;
+// ------------
+
+void jsError(const char *fmt, ...);
+void jsErrorInternal(const char *fmt, ...);
+void jsErrorAt(const char *message, struct JsLex *lex, int tokenPos);
+void jsWarn(const char *fmt, ...);
+void jsWarnAt(const char *message, struct JsLex *lex, int tokenPos);
+void jsAssertFail(const char *file, int line, const char *expr);
+
+#ifdef FAKE_STDLIB
+void exit(int errcode);
+char *strncat(char *dst, const char *src, size_t c);
+char *strncpy(char *dst, const char *src, size_t c);
+size_t strlen(const char *s);
+int strcmp(const char *a, const char *b);
+void *memcpy(void *dst, const void *src, size_t size);
+void *memset(void *dst, int val, size_t size);
+#define RAND_MAX (0xFFFFFFFFU)
+unsigned int rand();
+#else
+// FIXME: use itoa/ftoa direct - sprintf is huge
+//#define itoa(val,str,base) sprintf(str,"%d",(int)val)
+//#define ftoa(val,str) sprintf(str,"%f",val)
+
+#endif
+
+JsVarFloat stringToFloat(const char *str);
+
+#ifndef HAS_STDLIB
+void itoa(JsVarInt val,char *str,unsigned int base);
+#endif
+char itoch(int val);
+void ftoa(JsVarFloat val,char *str);
+
+/// Wrap a value so it is always between 0 and size (eg. wrapAround(angle, 360))
+JsVarFloat wrapAround(JsVarFloat val, JsVarFloat size);
+
+
+typedef void (*vcbprintf_callback)(const char *str, void *user_data);
+/** Espruino-special printf with a callback
+ * Supported are:
+ *   %d = int
+ *   %x = int as hex
+ *   %L = JsVarInt
+ *   %Lx = JsVarInt as hex
+ *   %f = JsVarFloat
+ *   %s = string (char *)
+ *   %c = char
+ *   %v = JsVar *
+ *   %p = Pin
+ *
+ * Anything else will assert
+ */
+void vcbprintf(vcbprintf_callback user_callback, void *user_data, const char *fmt, va_list argp);
+
+
+#endif /* JSUTILS_H_ */

+ 2542 - 0
components/external/espruino/src/jsvar.c

@@ -0,0 +1,2542 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Variables
+ * ----------------------------------------------------------------------------
+ */
+#include "jsvar.h"
+#include "jslex.h"
+#include "jsparse.h"
+#include "jswrap_json.h"
+#include "jsinteractive.h"
+#include "jswrapper.h"
+
+
+/** Basically, JsVars are stored in one big array, so save the need for
+ * lots of memory allocation. On Linux, the arrays are in blocks, so that
+ * more blocks can be allocated. We can't use realloc on one big block as
+ * this may change the address of vars that are already locked!
+ *
+ */
+
+#ifdef RESIZABLE_JSVARS
+JsVar **jsVarBlocks = 0;
+unsigned int jsVarsSize = 0;
+#define JSVAR_BLOCK_SIZE 1024
+#define JSVAR_BLOCK_SHIFT 10
+#else
+JsVar jsVars[JSVAR_CACHE_SIZE];
+unsigned int jsVarsSize = JSVAR_CACHE_SIZE;
+#endif
+
+JsVarRef jsVarFirstEmpty; ///< reference of first unused variable (variables are in a linked list)
+
+/** Return a pointer - UNSAFE for null refs.
+ * This is effectively a Lock without locking! */
+static inline JsVar *jsvGetAddressOf(JsVarRef ref) {
+  assert(ref);
+#ifdef RESIZABLE_JSVARS
+  JsVarRef t = ref-1;
+  return &jsVarBlocks[t>>JSVAR_BLOCK_SHIFT][t&(JSVAR_BLOCK_SIZE-1)];
+#else
+  return &jsVars[ref-1];
+#endif
+}
+
+JsVar *_jsvGetAddressOf(JsVarRef ref) {
+  return jsvGetAddressOf(ref);
+}
+
+
+// For debugging/testing ONLY - maximum # of vars we are allowed to use
+void jsvSetMaxVarsUsed(unsigned int size) {
+#ifdef RESIZABLE_JSVARS
+  assert(size < JSVAR_BLOCK_SIZE); // remember - this is only for DEBUGGING - as such it doesn't use multiple blocks
+#else
+  assert(size < JSVAR_CACHE_SIZE);
+#endif
+  jsVarsSize = size;
+}
+
+
+// maps the empty variables in...
+void jsvSoftInit() {
+  jsVarFirstEmpty = 0;
+  JsVar *lastEmpty = 0;
+  JsVarRef i;
+  for (i=1;i<=jsVarsSize;i++) {
+    if ((jsvGetAddressOf(i)->flags&JSV_VARTYPEMASK) == JSV_UNUSED) {
+      jsvGetAddressOf(i)->nextSibling = 0;
+      if (lastEmpty)
+        lastEmpty->nextSibling = i;
+      else
+        jsVarFirstEmpty = i;
+      lastEmpty = jsvGetAddressOf(i);
+    }
+  }
+}
+
+void jsvSoftKill() {
+}
+
+/** This links all JsVars together, so we can have our nice
+ * linked list of free JsVars. It returns the ref of the first
+ * item - that we should set jsVarFirstEmpty to (if it is 0) */
+static JsVarRef jsvInitJsVars(JsVarRef start, unsigned int count) {
+  JsVarRef i;
+  for (i=start;i<start+count;i++) {
+    JsVar *v = jsvGetAddressOf(i);
+    v->flags = JSV_UNUSED;
+#ifdef LARGE_MEM
+    v->this = i;
+#endif
+    // v->locks = 0; // locks is 0 anyway because it is stored in flags
+    v->nextSibling = (JsVarRef)(i+1); // link to next
+  }
+  jsvGetAddressOf((JsVarRef)(start+count-1))->nextSibling = (JsVarRef)0; // set the final one to 0
+  return start;
+}
+
+void jsvInit() {
+#ifdef RESIZABLE_JSVARS
+  jsVarsSize = JSVAR_BLOCK_SIZE;
+  jsVarBlocks = malloc(sizeof(JsVar*)); // just 1
+  jsVarBlocks[0] = malloc(sizeof(JsVar) * JSVAR_BLOCK_SIZE);
+#endif
+
+  jsVarFirstEmpty = jsvInitJsVars(1/*first*/, jsVarsSize);
+  jsvSoftInit();
+}
+
+void jsvKill() {
+#ifdef RESIZABLE_JSVARS
+
+  unsigned int i;
+  for (i=0;i<jsVarsSize>>JSVAR_BLOCK_SHIFT;i++)
+    free(jsVarBlocks[i]);
+  free(jsVarBlocks);
+  jsVarBlocks = 0;
+  jsVarsSize = 0;
+#endif
+}
+
+/** Find or create the ROOT variable item - used mainly
+ * if recovering from a saved state. */
+JsVar *jsvFindOrCreateRoot() {
+  JsVarRef i;
+  for (i=1;i<=jsVarsSize;i++)
+    if (jsvIsRoot(jsvGetAddressOf(i)))
+      return jsvLock(i);
+
+  return jsvRef(jsvNewWithFlags(JSV_ROOT));
+}
+
+/// Get number of memory records (JsVars) used
+unsigned int jsvGetMemoryUsage() {
+  unsigned int usage = 0;
+  unsigned int i;
+  for (i=1;i<=jsVarsSize;i++) {
+    JsVar *v = jsvGetAddressOf((JsVarRef)i);
+    if ((v->flags&JSV_VARTYPEMASK) != JSV_UNUSED)
+      usage++;
+  }
+  return usage;
+}
+
+/// Get total amount of memory records
+unsigned int jsvGetMemoryTotal() {
+  return jsVarsSize;
+}
+
+/// Try and allocate more memory - only works if RESIZABLE_JSVARS is defined
+void jsvSetMemoryTotal(unsigned int jsNewVarCount) {
+#ifdef RESIZABLE_JSVARS
+  if (jsNewVarCount <= jsVarsSize) return; // never allow us to have less!
+  // When resizing, we just allocate a bunch more
+  unsigned int oldSize = jsVarsSize;
+  unsigned int oldBlockCount = jsVarsSize >> JSVAR_BLOCK_SHIFT;
+  unsigned int newBlockCount = (jsNewVarCount+JSVAR_BLOCK_SIZE-1) >> JSVAR_BLOCK_SHIFT;
+  jsVarsSize = newBlockCount << JSVAR_BLOCK_SHIFT;
+  // resize block table
+  jsVarBlocks = realloc(jsVarBlocks, sizeof(JsVar*)*newBlockCount);
+  // allocate more blocks
+  unsigned int i;
+  for (i=oldBlockCount;i<newBlockCount;i++)
+    jsVarBlocks[i] = malloc(sizeof(JsVar) * JSVAR_BLOCK_SIZE);
+  /** and now reset all the newly allocated vars. We know jsVarFirstEmpty
+   * is 0 (because jsiFreeMoreMemory returned 0) so we can just assign it.  */
+  assert(!jsVarFirstEmpty);
+  jsVarFirstEmpty = jsvInitJsVars(oldSize+1, jsVarsSize-oldSize);
+  // jsiConsolePrintf("Resized memory from %d blocks to %d\n", oldBlockCount, newBlockCount);
+#else
+  NOT_USED(jsNewVarCount);
+  assert(0);
+#endif
+}
+
+/// Get whether memory is full or not
+bool jsvIsMemoryFull() {
+  return !jsVarFirstEmpty;
+}
+
+// Show what is still allocated, for debugging memory problems
+void jsvShowAllocated() {
+  JsVarRef i;
+  for (i=1;i<=jsVarsSize;i++) {
+    if ((jsvGetAddressOf(i)->flags&JSV_VARTYPEMASK) != JSV_UNUSED) {
+      jsiConsolePrintf("USED VAR #%d:",i);
+      jsvTrace(i, 2);
+    }
+  }
+}
+
+bool jsvHasCharacterData(const JsVar *v) {
+  return jsvIsString(v) || jsvIsStringExt(v);
+}
+
+bool jsvHasStringExt(const JsVar *v) {
+  return jsvIsString(v) || jsvIsStringExt(v);
+}
+
+bool jsvHasChildren(const JsVar *v) {
+  return jsvIsFunction(v) || jsvIsObject(v) || jsvIsArray(v) || jsvIsRoot(v);
+}
+
+/// Is this variable a type that uses firstChild to point to a single Variable (ie. it doesn't have multiple children)
+bool jsvHasSingleChild(const JsVar *v) {
+  return jsvIsName(v) || jsvIsArrayBuffer(v) || jsvIsArrayBufferName(v);
+}
+
+
+
+JsVar *jsvNew() {
+  if (jsVarFirstEmpty!=0) {
+      JsVar *v = jsvLock(jsVarFirstEmpty);
+      jsVarFirstEmpty = v->nextSibling; // move our reference to the next in the free list
+      assert((v->flags&JSV_VARTYPEMASK) == JSV_UNUSED);
+      // reset it
+      v->refs = 0;
+      //v->locks = 1;
+      v->flags = JSV_LOCK_ONE;
+      v->varData.callback = 0;
+      v->firstChild = 0;
+      v->lastChild = 0;
+      v->prevSibling = 0;
+      v->nextSibling = 0;
+      // return pointer
+      return v;
+  }
+  /* we don't have memort - second last hope - run garbage collector */
+  if (jsvGarbageCollect())
+    return jsvNew(); // if it freed something, continue
+  /* we don't have memory - last hope - ask jsInteractive to try and free some it
+   may have kicking around */
+  if (jsiFreeMoreMemory())
+    return jsvNew();
+  /* We couldn't claim any more memory by Garbage collecting... */
+#ifdef RESIZABLE_JSVARS
+  jsvSetMemoryTotal(jsVarsSize*2);
+  return jsvNew();
+#else
+  // On a micro, we're screwed.
+  jsError("Out of Memory!");
+  jspSetInterrupted(true);
+  return 0;
+#endif
+}
+
+void jsvFreePtr(JsVar *var) {
+    /* To be here, we're not supposed to be part of anything else. If
+     * we were, we'd have been freed by jsvGarbageCollect */
+    assert(jsvIsStringExt(var) || (!var->nextSibling && !var->prevSibling));
+
+    // Names that Link to other things
+    if (jsvHasSingleChild(var)) {
+      if (var->firstChild) {
+        JsVar *child = jsvLock(var->firstChild);
+        jsvUnRef(child); var->firstChild = 0; // unlink the child
+        jsvUnLock(child); // unlock should trigger a free
+      }
+    }
+    /* No else, because a String Name may have a single child, but
+     * also StringExts  */
+
+    /* Now, free children - see jsvar.h comments for how! */
+    if (jsvHasStringExt(var)) {
+      // TODO: make string free this non-recursive
+      JsVarRef stringDataRef = var->lastChild;
+      var->lastChild = 0;
+      if (stringDataRef) {
+        JsVar *child = jsvLock(stringDataRef);
+        assert(jsvIsStringExt(child));
+        jsvFreePtr(child);
+        jsvUnLock(child);
+      }
+    } else if (jsvHasChildren(var)) {
+      JsVarRef childref = var->firstChild;
+      var->firstChild = 0;
+      var->lastChild = 0;
+      while (childref) {
+        JsVar *child = jsvLock(childref);
+        assert(jsvIsName(child));
+        childref = child->nextSibling;
+        child->prevSibling = 0;
+        child->nextSibling = 0;
+        jsvUnRef(child);
+        jsvUnLock(child);
+      }
+    } else {
+      assert(!var->firstChild);
+      assert(!var->lastChild);
+    }
+
+    // free!
+    var->flags = (var->flags & ~JSV_VARTYPEMASK) | JSV_UNUSED;
+    // add this to our free list
+    var->nextSibling = jsVarFirstEmpty;
+    jsVarFirstEmpty = jsvGetRef(var);
+}
+
+/// Get a reference from a var - SAFE for null vars
+JsVarRef jsvGetRef(JsVar *var) {
+    if (!var) return 0;
+#ifdef LARGE_MEM
+    return var->this;
+#else
+ #ifdef RESIZABLE_JSVARS
+    unsigned int i, c = jsVarsSize>>JSVAR_BLOCK_SHIFT;
+    for (i=0;i<c;i++) {
+      if (var>=jsVarBlocks[i] && var<&jsVarBlocks[i][JSVAR_BLOCK_SIZE]) {
+        JsVarRef r = (JsVarRef)(1 + (i<<JSVAR_BLOCK_SHIFT) + (var - jsVarBlocks[i]));
+        return r;
+      }
+    }
+    return 0;
+ #else
+    return (JsVarRef)(1 + (var - jsVars));
+ #endif
+#endif
+}
+
+/// Lock this reference and return a pointer - UNSAFE for null refs
+JsVar *jsvLock(JsVarRef ref) {
+  JsVar *var = jsvGetAddressOf(ref);
+  //var->locks++;
+  assert(jsvGetLocks(var) < JSV_LOCK_MAX);
+  var->flags += JSV_LOCK_ONE;
+#ifdef DEBUG
+  if (jsvGetLocks(var)==0) {
+    jsError("Too many locks to Variable!");
+    //jsPrint("Var #");jsPrintInt(ref);jsPrint("\n");
+  }
+#endif
+  return var;
+}
+
+/// Lock this pointer and return a pointer - UNSAFE for null pointer
+JsVar *jsvLockAgain(JsVar *var) {
+  assert(var);
+  assert(jsvGetLocks(var) < JSV_LOCK_MAX);
+  var->flags += JSV_LOCK_ONE;
+#ifdef DEBUG
+  if (var->locks==0) {
+    jsError("Too many locks to Variable!");
+    //jsPrint("Var #");jsPrintInt(ref);jsPrint("\n");
+  }
+#endif
+  return var;
+}
+
+/// Unlock this variable - this is SAFE for null variables
+void jsvUnLock(JsVar *var) {
+  if (!var) return;
+  assert(jsvGetLocks(var)>0);
+  var->flags -= JSV_LOCK_ONE;
+  /* if we know we're free, then we can just free
+   * this variable right now. Loops of variables
+   * are handled by the Garbage Collector.
+   * Note: we check var->refs first as it is fastest and most likely to be false */
+  if (var->refs == 0 && jsvHasRef(var) && jsvGetLocks(var) == 0 && (var->flags&JSV_VARTYPEMASK)!=JSV_UNUSED) {
+    jsvFreePtr(var);
+  }
+}
+
+/// Reference - set this variable as used by something
+JsVar *jsvRef(JsVar *v) {
+  assert(v && jsvHasRef(v));
+  v->refs++;
+  return v;
+}
+
+/// Unreference - set this variable as not used by anything
+void jsvUnRef(JsVar *var) {
+  assert(var && var->refs>0 && jsvHasRef(var));
+  var->refs--;
+  // locks are never 0 here, so why bother checking!
+  assert(jsvGetLocks(var)>0);
+}
+
+/// Helper fn, Reference - set this variable as used by something
+JsVarRef jsvRefRef(JsVarRef ref) {
+  JsVar *v;
+  assert(ref);
+  v = jsvLock(ref);
+  assert(!jsvIsStringExt(v));
+  jsvRef(v);
+  jsvUnLock(v);
+  return ref;
+}
+
+/// Helper fn, Unreference - set this variable as not used by anything
+JsVarRef jsvUnRefRef(JsVarRef ref) {
+  JsVar *v;
+  assert(ref);
+  v = jsvLock(ref);
+  assert(!jsvIsStringExt(v));
+  jsvUnRef(v);
+  jsvUnLock(v);
+  return 0;
+}
+
+JsVar *jsvNewFromString(const char *str) {
+  // Create a var
+  JsVar *first = jsvNewWithFlags(JSV_STRING);
+  if (!first) {
+    jsWarn("Unable to create string as not enough memory");
+    return 0;
+  }
+  // Now we copy the string, but keep creating new jsVars if we go
+  // over the end
+  JsVar *var = jsvLockAgain(first);
+  while (*str) {
+    // copy data in
+    size_t i, l = jsvGetMaxCharactersInVar(var);
+    for (i=0;i<l && *str;i++)
+      var->varData.str[i] = *(str++);
+    // might as well shove a zero terminator on it if we can
+    if (i<l) var->varData.str[i]=0;
+    // we've stopped if the string was empty
+    jsvSetCharactersInVar(var, i);
+
+    // if there is still some left, it's because we filled up our var...
+    // make a new one, link it in, and unlock the old one.
+    if (*str) {
+      JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
+      if (!next) {
+        jsWarn("Truncating string as not enough memory");
+        jsvUnLock(var);
+        return first;
+      }
+      // we don't ref, because  StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
+      var->lastChild = jsvGetRef(next);
+      jsvUnLock(var);
+      var = next;
+    }
+  }
+  jsvUnLock(var);
+  // return
+  return first;
+}
+
+JsVar *jsvNewStringOfLength(unsigned int byteLength) {
+  // Create a var
+    JsVar *first = jsvNewWithFlags(JSV_STRING);
+    if (!first) {
+      jsWarn("Unable to create string as not enough memory");
+      return 0;
+    }
+    // Now zero the string, but keep creating new jsVars if we go
+    // over the end
+    JsVar *var = jsvLockAgain(first);
+    while (byteLength>0) {
+      // copy data in
+      size_t i, l = jsvGetMaxCharactersInVar(var);
+      for (i=0;i<l && byteLength>0;i++,byteLength--)
+        var->varData.str[i] = 0;
+      // might as well shove a zero terminator on it if we can
+      if (i<l) var->varData.str[i]=0;
+      // we've stopped if the string was empty
+      jsvSetCharactersInVar(var, i);
+
+      // if there is still some left, it's because we filled up our var...
+      // make a new one, link it in, and unlock the old one.
+      if (byteLength>0) {
+        JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
+        if (!next) {
+          jsWarn("Truncating string as not enough memory");
+          jsvUnLock(var);
+          return first;
+        }
+        // we don't ref, because  StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
+        var->lastChild = jsvGetRef(next);
+        jsvUnLock(var);
+        var = next;
+      }
+    }
+    jsvUnLock(var);
+    // return
+    return first;
+}
+
+JsVar *jsvNewFromLexer(struct JsLex *lex, JslCharPos charFrom, JslCharPos charTo) {
+  // Create a var
+  JsVar *var = jsvNewFromEmptyString();
+  if (!var) { // out of memory
+    return 0;
+  }
+
+  jsvAppendStringVar(var, lex->sourceVar, charFrom, (JslCharPos)charTo-charFrom);
+  return var;
+}
+
+JsVar *jsvNewWithFlags(JsVarFlags flags) {
+  JsVar *var = jsvNew();
+  if (!var) return 0; // no memory
+  var->flags = (var->flags&(JsVarFlags)(~JSV_VARTYPEMASK)) | (flags&(JsVarFlags)(~JSV_LOCK_MASK));
+  return var;
+}
+JsVar *jsvNewFromInteger(JsVarInt value) {
+  JsVar *var = jsvNewWithFlags(JSV_INTEGER);
+  if (!var) return 0; // no memory
+  var->varData.integer = value;
+  return var;
+}
+JsVar *jsvNewFromBool(bool value) {
+  JsVar *var = jsvNewWithFlags(JSV_BOOLEAN);
+  if (!var) return 0; // no memory
+  var->varData.integer = value ? 1 : 0;
+  return var;
+}
+JsVar *jsvNewFromFloat(JsVarFloat value) {
+  JsVar *var = jsvNewWithFlags(JSV_FLOAT);
+  if (!var) return 0; // no memory
+  var->varData.floating = value;
+  return var;
+}
+JsVar *jsvMakeIntoVariableName(JsVar *var, JsVar *valueOrZero) {
+  if (!var) return 0;
+  assert(var->refs==0); // make sure it's unused
+  var->flags |= JSV_NAME;
+  if (valueOrZero)
+    var->firstChild = jsvGetRef(jsvRef(valueOrZero));
+  return var;
+}
+
+JsVar *jsvNewFromPin(int pin) {
+  JsVar *v = jsvNewFromInteger((JsVarInt)pin);
+  if (v) {
+    v->flags = (JsVarFlags)((v->flags & ~JSV_VARTYPEMASK) | JSV_PIN);
+  }
+  return v;
+}
+
+bool jsvIsBasicVarEqual(JsVar *a, JsVar *b) {
+  // quick checks
+  if (a==b) return true;
+  if (!a || !b) return false; // one of them is undefined
+  // OPT: would this be useful as compare instead?
+  assert(jsvIsBasic(a) && jsvIsBasic(b));
+  if (jsvIsNumeric(a) && jsvIsNumeric(b)) {
+    if (jsvIsInt(a)) {
+      if (jsvIsInt(b)) {
+        return a->varData.integer == b->varData.integer;
+      } else {
+        assert(jsvIsFloat(b));
+        return a->varData.integer == b->varData.floating;
+      }
+    } else {
+      assert(jsvIsFloat(a));
+      if (jsvIsInt(b)) {
+        return a->varData.floating == b->varData.integer;
+      } else {
+        assert(jsvIsFloat(b));
+        return a->varData.floating == b->varData.floating;
+      }
+    }
+  } else if (jsvIsString(a) && jsvIsString(b)) {
+    JsvStringIterator ita, itb;
+    jsvStringIteratorNew(&ita, a, 0);
+    jsvStringIteratorNew(&itb, b, 0);
+    while (true) {
+      char a = jsvStringIteratorGetChar(&ita);
+      char b = jsvStringIteratorGetChar(&itb);
+      if (a != b) {
+        jsvStringIteratorFree(&ita);
+        jsvStringIteratorFree(&itb);
+        return false;
+      }
+      if (!a) { // equal, but end of string
+        jsvStringIteratorFree(&ita);
+        jsvStringIteratorFree(&itb);
+        return true;
+      }
+      jsvStringIteratorNext(&ita);
+      jsvStringIteratorNext(&itb);
+    }
+    // we never get here
+    return false; // make compiler happy
+  } else {
+    //TODO: are there any other combinations we should check here?? String v int?
+    return false;
+  }
+}
+
+bool jsvIsEqual(JsVar *a, JsVar *b) {
+  if (jsvIsBasic(a) && jsvIsBasic(b))
+    return jsvIsBasicVarEqual(a,b);
+  return jsvGetRef(a)==jsvGetRef(b);
+}
+
+/// Get a const string representing this variable - if we can. Otherwise return 0
+const char *jsvGetConstString(const JsVar *v) {
+    if (jsvIsUndefined(v)) {
+      return "undefined";
+    } else if (jsvIsNull(v)) {
+      return "null";
+    } else if (jsvIsBoolean(v)) {
+      return jsvGetBool(v) ? "true" : "false";
+    } else if (jsvIsRoot(v)) {
+      return "[object Hardware]";
+    } else if (jsvIsObject(v)) {
+      return "[object Object]";
+    }
+    return 0;
+}
+
+/// Return the 'type' of the JS variable (eg. JS's typeof operator)
+const char *jsvGetTypeOf(const JsVar *v) {
+  if (jsvIsNull(v)) return "object";
+  if (jsvIsUndefined(v)) return "undefined";
+  if (jsvIsFunction(v)) return "function";
+  if (jsvIsObject(v) || jsvIsArray(v)) return "object";
+  if (jsvIsString(v)) return "string";
+  if (jsvIsBoolean(v)) return "boolean";
+  if (jsvIsNumeric(v)) return "number";
+  return "?";
+}
+
+/// Save this var as a string to the given buffer, and return how long it was (return val doesn't include terminating 0)
+size_t jsvGetString(const JsVar *v, char *str, size_t len) {
+   const char *s = jsvGetConstString(v);
+   if (s) {
+     strncpy(str, s, len);
+     return strlen(s);
+   } else if (jsvIsInt(v)) {
+     itoa(v->varData.integer, str, 10);
+     return strlen(str);
+   } else if (jsvIsFloat(v)) {
+     ftoa(v->varData.floating, str);
+     return strlen(str);
+   } else if (jsvHasCharacterData(v)) {
+       if (jsvIsStringExt(v))
+         jsErrorInternal("Calling jsvGetString on a JSV_STRING_EXT");
+      size_t l = len;
+      JsvStringIterator it;
+      jsvStringIteratorNewConst(&it, v, 0);
+      while (jsvStringIteratorHasChar(&it)) {
+        if (l--<=1) {
+          *str = 0;
+          jsWarn("jsvGetString overflowed\n");
+          jsvStringIteratorFree(&it);
+          return len;
+        }
+        *(str++) = jsvStringIteratorGetChar(&it);
+        jsvStringIteratorNext(&it);
+      }
+      jsvStringIteratorFree(&it);
+      *str = 0;
+      return len-l;
+    } else {
+      // Try and get as a JsVar string, and try again
+      JsVar *stringVar = jsvAsString((JsVar*)v, false); // we know we're casting to non-const here
+      if (stringVar) {
+        size_t l = jsvGetString(stringVar, str, len); // call again - but this time with converted var
+        jsvUnLock(stringVar);
+        return l;
+      } else {
+        strncpy(str, "", len);
+        jsErrorInternal("Variable type cannot be converted to string");
+        return 0;
+      }
+    }
+}
+
+/// Set the Data in this string. This must JUST overwrite - not extend or shrink
+void jsvSetString(JsVar *v, char *str, size_t len) {
+  assert(jsvHasCharacterData(v));
+  assert(len == jsvGetStringLength(v));
+
+  JsvStringIterator it;
+  jsvStringIteratorNew(&it, v, 0);
+  size_t i;
+  for (i=0;i<len;i++) {
+    jsvStringIteratorSetChar(&it, str[i]);
+    jsvStringIteratorNext(&it);
+  }
+  jsvStringIteratorFree(&it);
+}
+
+/** If var is a string, lock and return it, else
+ * create a new string. unlockVar means this will auto-unlock 'var'  */
+JsVar *jsvAsString(JsVar *v, bool unlockVar) {
+  JsVar *str = 0;
+  // If it is string-ish, but not quite a string, copy it
+  if (jsvHasCharacterData(v) && jsvIsName(v)) {
+    str = jsvNewFromEmptyString();
+    if (str) jsvAppendStringVarComplete(str,v);
+  } else if (jsvIsString(v)) { // If it is a string - just return a reference
+    str = jsvLockAgain(v);
+  } else {
+    const char *constChar = jsvGetConstString(v);
+    if (constChar) {
+      // if we could get this as a simple const char, do that..
+      str = jsvNewFromString(constChar);
+    } else if (jsvIsPin(v)) {
+      char buf[8];
+      jshGetPinString(buf, (Pin)v->varData.integer);
+      str = jsvNewFromString(buf);
+    } else if (jsvIsInt(v)) {
+      char buf[JS_NUMBER_BUFFER_SIZE];
+      itoa(v->varData.integer, buf, 10);
+      str = jsvNewFromString(buf);
+    } else if (jsvIsFloat(v)) {
+      char buf[JS_NUMBER_BUFFER_SIZE];
+      ftoa(v->varData.floating, buf);
+      str = jsvNewFromString(buf);
+    } else if (jsvIsArray(v) || jsvIsArrayBuffer(v)) {
+      JsVar *filler = jsvNewFromString(",");
+      str = jsvArrayJoin(v, filler);
+      jsvUnLock(filler);
+    } else if (jsvIsFunction(v)) {
+      str = jsvNewFromEmptyString();
+      if (str) jsfGetJSON(v, str);
+    } else {
+      jsErrorInternal("Variable type cannot be converted to string");
+      str = 0;
+    }
+  }
+
+  if (unlockVar) jsvUnLock(v);
+  return str;
+}
+
+size_t jsvGetStringLength(JsVar *v) {
+  size_t strLength = 0;
+  JsVar *var = v;
+  JsVarRef ref = 0;
+
+  if (!jsvHasCharacterData(v)) return 0;
+
+  while (var) {
+    JsVarRef refNext = var->lastChild;
+    strLength += jsvGetCharactersInVar(var);
+
+    // Go to next
+    if (ref) jsvUnLock(var); // note use of if (ref), not var
+    ref = refNext;
+    var = ref ? jsvLock(ref) : 0;
+  }
+  if (ref) jsvUnLock(var); // note use of if (ref), not var
+  return strLength;
+}
+
+//  IN A STRING  get the number of lines in the string (min=1)
+int jsvGetLinesInString(JsVar *v) {
+  int lines = 1;
+  JsvStringIterator it;
+  jsvStringIteratorNew(&it, v, 0);
+  while (jsvStringIteratorHasChar(&it)) {
+    if (jsvStringIteratorGetChar(&it)=='\n') lines++;
+    jsvStringIteratorNext(&it);
+  }
+  jsvStringIteratorFree(&it);
+  return lines;
+}
+
+// IN A STRING Get the number of characters on a line - lines start at 1
+int jsvGetCharsOnLine(JsVar *v, int line) {
+  int currentLine = 1;
+  int chars = 0;
+  JsvStringIterator it;
+  jsvStringIteratorNew(&it, v, 0);
+  while (jsvStringIteratorHasChar(&it)) {
+    if (jsvStringIteratorGetChar(&it)=='\n') {
+      currentLine++;
+      if (currentLine > line) break;
+    } else if (currentLine==line) chars++;
+    jsvStringIteratorNext(&it);
+  }
+  jsvStringIteratorFree(&it);
+  return chars;
+}
+
+//  IN A STRING, get the line and column of the given character. Both values must be non-null
+void jsvGetLineAndCol(JsVar *v, int charIdx, int* line, int *col) {
+  int x = 1;
+  int y = 1;
+  int n = 0;
+  assert(line && col);
+
+  JsvStringIterator it;
+  jsvStringIteratorNew(&it, v, 0);
+  while (jsvStringIteratorHasChar(&it)) {
+    char ch = jsvStringIteratorGetChar(&it);
+    if (n==charIdx) {
+      jsvStringIteratorFree(&it);
+      *line = y;
+      *col = x;
+      return;
+    }
+    x++;
+    if (ch=='\n') {
+      x=1; y++;
+    }
+    n++;
+    jsvStringIteratorNext(&it);
+  }
+  jsvStringIteratorFree(&it);
+  // uh-oh - not found
+  *line = y;
+  *col = x;
+}
+
+//  IN A STRING, get a character index from a line and column
+int jsvGetIndexFromLineAndCol(JsVar *v, int line, int col) {
+  int x = 1;
+  int y = 1;
+  int n = 0;
+  JsvStringIterator it;
+  jsvStringIteratorNew(&it, v, 0);
+  while (jsvStringIteratorHasChar(&it)) {
+    char ch = jsvStringIteratorGetChar(&it);
+    if ((y==line && x>=col) || y>line) {
+      jsvStringIteratorFree(&it);
+      return (y>line) ? (n-1) : n;
+    }
+    x++;
+    if (ch=='\n') {
+      x=1; y++;
+    }
+    n++;
+    jsvStringIteratorNext(&it);
+  }
+  jsvStringIteratorFree(&it);
+  return n;
+}
+
+void jsvAppendString(JsVar *var, const char *str) {
+  assert(jsvIsString(var));
+  JsVar *block = jsvLockAgain(var);
+  // Find the block at end of the string...
+  while (block->lastChild) {
+    JsVarRef next = block->lastChild;
+    jsvUnLock(block);
+    block = jsvLock(next);
+  }
+  // find how full the block is
+  size_t blockChars = jsvGetCharactersInVar(block);
+  // now start appending
+  while (*str) {
+    // copy data in
+    size_t i, l=jsvGetMaxCharactersInVar(block);
+    for (i=blockChars;i<l && *str;i++) {
+      block->varData.str[i] = *(str++);
+    }
+    jsvSetCharactersInVar(block, i);
+    // if there is still some left, it's because we filled up our var...
+    // make a new one, link it in, and unlock the old one.
+    if (*str) {
+      JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
+      if (!next) break;
+      // we don't ref, because  StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
+      block->lastChild = jsvGetRef(next);
+      jsvUnLock(block);
+      block = next;
+      blockChars=0; // it's new, so empty
+    }
+  }
+  jsvUnLock(block);
+}
+
+void jsvAppendStringBuf(JsVar *var, const char *str, int length) {
+  assert(jsvIsString(var));
+  JsVar *block = jsvLockAgain(var);
+  // Find the block at end of the string...
+  while (block->lastChild) {
+    JsVarRef next = block->lastChild;
+    jsvUnLock(block);
+    block = jsvLock(next);
+  }
+  // find how full the block is
+  size_t blockChars = jsvGetCharactersInVar(block);
+  // now start appending
+  while (length) {
+    // copy data in
+    size_t i, l=jsvGetMaxCharactersInVar(block);
+    for (i=blockChars;i<l && length;i++) {
+      block->varData.str[i] = *(str++);
+      length--;
+    }
+    jsvSetCharactersInVar(block, i);
+    // if there is still some left, it's because we filled up our var...
+    // make a new one, link it in, and unlock the old one.
+    if (length) {
+      JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
+      if (!next) break;
+      // we don't ref, because  StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
+      block->lastChild = jsvGetRef(next);
+      jsvUnLock(block);
+      block = next;
+      blockChars=0; // it's new, so empty
+    }
+  }
+  jsvUnLock(block);
+}
+
+static void _jsvAppendPrintf(const char *str, void *user_data) {
+  while (*str)
+    jsvStringIteratorAppend((JsvStringIterator *)user_data, *(str++));
+}
+
+void jsvAppendPrintf(JsVar *var, const char *fmt, ...) {
+  JsvStringIterator it;
+  jsvStringIteratorNew(&it, var, 0);
+  jsvStringIteratorGotoEnd(&it);
+
+  va_list argp;
+  va_start(argp, fmt);
+  vcbprintf((vcbprintf_callback)&_jsvAppendPrintf,&it, fmt, argp);
+  va_end(argp);
+
+  jsvStringIteratorFree(&it);
+}
+
+/** Append str to var. Both must be strings. stridx = start char or str, maxLength = max number of characters (can be JSVAPPENDSTRINGVAR_MAXLENGTH).
+ *  stridx can be negative to go from end of string */
+void jsvAppendStringVar(JsVar *var, const JsVar *str, int stridx, int maxLength) {
+  JsVar *block = jsvLockAgain(var);
+  assert(jsvIsString(var));
+  // Find the block at end of the string...
+  while (block->lastChild) {
+    JsVarRef next = block->lastChild;
+    jsvUnLock(block);
+    block = jsvLock(next);
+  }
+  // find how full the block is
+  size_t blockChars = jsvGetCharactersInVar(block);
+  // now start appending
+  JsvStringIterator it;
+  jsvStringIteratorNewConst(&it, str, stridx);
+  while (jsvStringIteratorHasChar(&it) && (maxLength-->0)) {
+    char ch = jsvStringIteratorGetChar(&it);
+    if (blockChars >= jsvGetMaxCharactersInVar(block)) {
+      jsvSetCharactersInVar(block, blockChars);
+      JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
+      if (!next) break; // out of memory
+      // we don't ref, because  StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
+      block->lastChild = jsvGetRef(next);
+      jsvUnLock(block);
+      block = next;
+      blockChars=0; // it's new, so empty
+    }
+    block->varData.str[blockChars++] = ch;
+    jsvStringIteratorNext(&it);
+  }
+  jsvStringIteratorFree(&it);
+  jsvSetCharactersInVar(block, blockChars);
+  jsvUnLock(block);
+}
+
+/** Append all of str to var. Both must be strings.  */
+void jsvAppendStringVarComplete(JsVar *var, const JsVar *str) {
+  jsvAppendStringVar(var, str, 0, JSVAPPENDSTRINGVAR_MAXLENGTH);
+}
+
+char jsvGetCharInString(JsVar *v, int idx) {
+  if (!jsvIsString(v)) return 0;
+  if (idx<0) idx += (int)jsvGetStringLength(v); // <0 goes from end of string
+  if (idx<0) return 0;
+
+  JsvStringIterator it;
+  jsvStringIteratorNew(&it, v, idx);
+  char ch = jsvStringIteratorGetChar(&it);
+  jsvStringIteratorFree(&it);
+  return ch;
+}
+
+/** Does this string contain only Numeric characters? */
+bool jsvIsStringNumeric(const JsVar *var) {
+  assert(jsvIsString(var));
+  JsvStringIterator it;
+  jsvStringIteratorNewConst(&it, var, 0); // we know it's non const
+  int chars = 0;
+  while (jsvStringIteratorHasChar(&it)) {
+    chars++;
+    char ch = jsvStringIteratorGetChar(&it);
+    if (!isNumeric(ch)) { // FIXME: should check for non-integer values (floating point?)
+      jsvStringIteratorFree(&it);
+      return false;
+    }
+    jsvStringIteratorNext(&it);
+  }
+  jsvStringIteratorFree(&it);
+  return chars>0;
+}
+
+/** Does this string contain only Numeric characters? This is for arrays
+ * and makes the assertion that int_to_string(string_to_int(var))==var */
+bool jsvIsStringNumericStrict(const JsVar *var) {
+  assert(jsvIsString(var));
+  JsvStringIterator it;
+  jsvStringIteratorNewConst(&it, var, 0);  // we know it's non const
+  bool hadNonZero = false;
+  bool hasLeadingZero = false;
+  int chars = 0;
+  while (jsvStringIteratorHasChar(&it)) {
+    chars++;
+    char ch = jsvStringIteratorGetChar(&it);
+    if (!isNumeric(ch)) {
+      // test for leading zero ensures int_to_string(string_to_int(var))==var
+      jsvStringIteratorFree(&it);
+      return false;
+    }
+    if (!hadNonZero && ch=='0') hasLeadingZero=true;
+    if (ch!='0') hadNonZero=true;
+    jsvStringIteratorNext(&it);
+  }
+  jsvStringIteratorFree(&it);
+  return chars>0 && (!hasLeadingZero || chars==1);
+}
+
+JsVarInt jsvGetInteger(const JsVar *v) {
+    if (!v) return 0; // undefined
+    /* strtol understands about hex and octal */
+    if (jsvIsInt(v) || jsvIsBoolean(v) || jsvIsPin(v) || jsvIsArrayBufferName(v)) return v->varData.integer;
+    if (jsvIsNull(v)) return 0;
+    if (jsvIsUndefined(v)) return 0;
+    if (jsvIsFloat(v)) return (JsVarInt)v->varData.floating;
+    if (jsvIsString(v) && jsvIsStringNumeric(v)) {
+      char buf[32];
+      jsvGetString(v, buf, sizeof(buf));
+      return stringToInt(buf);
+    }
+    return 0;
+}
+
+void jsvSetInteger(JsVar *v, JsVarInt value) {
+  assert(jsvIsInt(v));
+  v->varData.integer  = value;
+}
+
+bool jsvGetBool(const JsVar *v) {
+  return jsvGetInteger(v)!=0;
+}
+
+JsVarFloat jsvGetFloat(const JsVar *v) {
+    if (!v) return NAN; // undefined
+    if (jsvIsFloat(v)) return v->varData.floating;
+    if (jsvIsInt(v)) return (JsVarFloat)v->varData.integer;
+    if (jsvIsNull(v)) return 0;
+    if (jsvIsString(v) && jsvIsStringNumeric(v)) {
+      char buf[32];
+      jsvGetString(v, buf, sizeof(buf));
+      return stringToFloat(buf);
+    }
+    return NAN;
+}
+
+/// Convert the given variable to a number
+JsVar *jsvAsNumber(JsVar *var) {
+  if (jsvIsInt(var) || jsvIsFloat(var)) return jsvLockAgain(var);
+  if (jsvIsBoolean(var) || jsvIsPin(var)) return jsvNewFromInteger(var->varData.integer);
+  return jsvNewFromFloat(jsvGetFloat(var));
+}
+
+#ifdef SAVE_ON_FLASH
+JsVarInt jsvGetIntegerAndUnLock(JsVar *v) { return _jsvGetIntegerAndUnLock(v); }
+JsVarFloat jsvGetFloatAndUnLock(JsVar *v) { return _jsvGetFloatAndUnLock(v); }
+bool jsvGetBoolAndUnLock(JsVar *v) { return _jsvGetBoolAndUnLock(v); }
+#endif
+
+/** Get the item at the given location in the array buffer and return the result */
+size_t jsvGetArrayBufferLength(JsVar *arrayBuffer) {
+  assert(jsvIsArrayBuffer(arrayBuffer));
+  return arrayBuffer->varData.arraybuffer.length;
+}
+
+/** Get the item at the given location in the array buffer and return the result */
+JsVar *jsvArrayBufferGet(JsVar *arrayBuffer, JsVarInt idx) {
+  JsvArrayBufferIterator it;
+  jsvArrayBufferIteratorNew(&it, arrayBuffer, idx);
+  JsVar *v = jsvArrayBufferIteratorGetValue(&it);
+  jsvArrayBufferIteratorFree(&it);
+  return v;
+}
+
+/** Set the item at the given location in the array buffer */
+void jsvArrayBufferSet(JsVar *arrayBuffer, JsVarInt idx, JsVar *value) {
+  JsvArrayBufferIterator it;
+  jsvArrayBufferIteratorNew(&it, arrayBuffer, idx);
+  jsvArrayBufferIteratorSetValue(&it, value);
+  jsvArrayBufferIteratorFree(&it);
+}
+
+
+/** Given an integer name that points to an arraybuffer or an arraybufferview, evaluate it and return the result */
+JsVar *jsvArrayBufferGetFromName(JsVar *name) {
+  assert(jsvIsArrayBufferName(name));
+  JsVarInt idx = jsvGetInteger(name);
+  JsVar *arrayBuffer = jsvLock(name->firstChild);
+  JsVar *value = jsvArrayBufferGet(arrayBuffer, idx);
+  jsvUnLock(arrayBuffer);
+  return value;
+}
+
+/** If a is a name skip it and go to what it points to - and so on.
+ * ALWAYS locks - so must unlock what it returns. It MAY
+ * return 0. */
+JsVar *jsvSkipName(JsVar *a) {
+  JsVar *pa = a;
+  if (!a) return 0;
+  if (jsvIsArrayBufferName(pa)) return jsvArrayBufferGetFromName(pa);
+  while (jsvIsName(pa)) {
+    JsVarRef n = pa->firstChild;
+    if (pa!=a) jsvUnLock(pa);
+    if (!n) return 0;
+    pa = jsvLock(n);
+  }
+  if (pa==a) jsvLockAgain(pa);
+  return pa;
+}
+
+/** If a is a name skip it and go to what it points to.
+ * ALWAYS locks - so must unlock what it returns. It MAY
+ * return 0.  */
+JsVar *jsvSkipOneName(JsVar *a) {
+  JsVar *pa = a;
+  if (!a) return 0;
+  if (jsvIsArrayBufferName(pa)) return jsvArrayBufferGetFromName(pa);
+  if (jsvIsName(pa)) {
+    JsVarRef n = pa->firstChild;
+    if (pa!=a) jsvUnLock(pa);
+    if (!n) return 0;
+    pa = jsvLock(n);
+  }
+  if (pa==a) jsvLockAgain(pa);
+  return pa;
+}
+
+/** If a is a's child is a name skip it and go to what it points to.
+ * ALWAYS locks - so must unlock what it returns.  */
+JsVar *jsvSkipToLastName(JsVar *a) {
+  assert(jsvIsName(a));
+  a = jsvLockAgain(a);
+  while (true) {
+    if (!a->firstChild) return a;
+    JsVar *child = jsvLock(a->firstChild);
+    if (jsvIsName(child)) {
+      jsvUnLock(a);
+      a = child;
+    } else {
+      jsvUnLock(child);
+      return a;
+    }
+  }
+  return 0; // not called
+}
+
+
+// Also see jsvIsBasicVarEqual
+bool jsvIsStringEqual(JsVar *var, const char *str) {
+  if (!jsvHasCharacterData(var)) {
+    assert(jsvIsBasic(var));
+    return 0; // not a string so not equal!
+  }
+
+  JsvStringIterator it;
+  jsvStringIteratorNew(&it, var, 0);
+  while (jsvStringIteratorHasChar(&it) && *str) {
+    if (jsvStringIteratorGetChar(&it) != *str) {
+      jsvStringIteratorFree(&it);
+      return false;
+    }
+    str++;
+    jsvStringIteratorNext(&it);
+  }
+  bool eq = jsvStringIteratorGetChar(&it)==*str; // should both be 0 if equal
+  jsvStringIteratorFree(&it);
+  return eq;
+}
+
+
+/** Compare 2 strings, starting from the given character positions. equalAtEndOfString means that
+ * if one of the strings ends, we treat them as equal.
+ * For a basic strcmp, do: jsvCompareString(a,b,0,0,false)
+ *  */
+int jsvCompareString(JsVar *va, JsVar *vb, int starta, int startb, bool equalAtEndOfString) {
+  JsvStringIterator ita, itb;
+  jsvStringIteratorNew(&ita, va, starta);
+  jsvStringIteratorNew(&itb, vb, startb);
+   // step to first positions
+  while (true) {
+    int ca = jsvStringIteratorGetCharOrMinusOne(&ita);
+    int cb = jsvStringIteratorGetCharOrMinusOne(&itb);
+
+    if (ca != cb) {
+      jsvStringIteratorFree(&ita);
+      jsvStringIteratorFree(&itb);
+      if ((ca<0 || cb<0) && equalAtEndOfString) return 0;
+      return ca - cb;
+    }
+    if (ca < 0) { // both equal, but end of string
+      jsvStringIteratorFree(&ita);
+      jsvStringIteratorFree(&itb);
+      return 0;
+    }
+    jsvStringIteratorNext(&ita);
+    jsvStringIteratorNext(&itb);
+  }
+  // never get here, but the compiler warns...
+  return true;
+}
+
+/** Compare 2 integers, >0 if va>vb,  <0 if va<vb. If compared with a non-integer, that gets put later */
+int jsvCompareInteger(JsVar *va, JsVar *vb) {
+  if (jsvIsInt(va) && jsvIsInt(vb))
+    return (int)(jsvGetInteger(va) - jsvGetInteger(vb));
+  else if (jsvIsInt(va))
+    return -1;
+  else if (jsvIsInt(vb))
+    return 1;
+  else
+    return 0;
+}
+
+/** Copy only a name, not what it points to. ALTHOUGH the link to what it points to is maintained unless linkChildren=false
+    If keepAsName==false, this will be converted into a normal variable */
+JsVar *jsvCopyNameOnly(JsVar *src, bool linkChildren, bool keepAsName) {
+  assert(jsvIsName(src));
+  JsVarFlags flags = src->flags;
+  if (!keepAsName) flags &= (JsVarFlags)~JSV_NAME; // make sure this is NOT a name
+  JsVar *dst = jsvNewWithFlags(flags);
+  if (!dst) return 0; // out of memory
+
+  memcpy(&dst->varData, &src->varData, sizeof(JsVarData));
+
+  dst->lastChild = 0;
+  dst->firstChild = 0;
+  dst->prevSibling = 0;
+  dst->nextSibling = 0;
+  // Copy LINK of what it points to
+  if (linkChildren && src->firstChild) {
+    dst->firstChild = jsvRefRef(src->firstChild);
+  }
+  // Copy extra string data if there was any
+  if (jsvHasStringExt(src)) {
+      // copy extra bits of string if there were any
+      if (src->lastChild) {
+        JsVar *child = jsvLock(src->lastChild);
+        JsVar *childCopy = jsvCopy(child);
+        if (childCopy) { // could be out of memory
+          dst->lastChild = jsvGetRef(childCopy); // no ref for stringext
+          jsvUnLock(childCopy);
+        }
+        jsvUnLock(child);
+      }
+  } else {
+    assert(jsvIsBasic(src)); // in case we missed something!
+  }
+  return dst;
+}
+
+JsVar *jsvCopy(JsVar *src) {
+  JsVar *dst = jsvNewWithFlags(src->flags);
+  if (!dst) return 0; // out of memory
+  if (!jsvIsStringExt(src)) {
+    memcpy(&dst->varData, &src->varData, sizeof(JsVarData));
+    dst->lastChild = 0;
+    dst->firstChild = 0;
+    dst->prevSibling = 0;
+    dst->nextSibling = 0;
+  } else {
+    // stringexts use the extra pointers after varData to store characters
+    // see jsvGetMaxCharactersInVar
+    memcpy(&dst->varData, &src->varData, JSVAR_DATA_STRING_MAX_LEN);
+    dst->lastChild = 0;
+  }
+
+  // Copy what names point to
+  if (jsvIsName(src)) {
+    if (src->firstChild) {
+      JsVar *child = jsvLock(src->firstChild);
+      JsVar *childCopy = jsvRef(jsvCopy(child));
+      jsvUnLock(child);
+      if (childCopy) { // could have been out of memory
+        dst->firstChild = jsvGetRef(childCopy);
+        jsvUnLock(childCopy);
+      }
+    }
+  }
+
+  if (jsvHasStringExt(src)) {
+    // copy extra bits of string if there were any
+    if (src->lastChild) {
+      JsVar *child = jsvLock(src->lastChild);
+      JsVar *childCopy = jsvCopy(child);
+      if (childCopy) {// could be out of memory
+        dst->lastChild = jsvGetRef(childCopy); // no ref for stringext
+        jsvUnLock(childCopy);
+      }
+      jsvUnLock(child);
+    }
+  } else if (jsvHasChildren(src)) {
+    // Copy children..
+    JsVarRef vr;
+    vr = src->firstChild;
+    while (vr) {
+      JsVar *name = jsvLock(vr);
+      JsVar *child = jsvCopyNameOnly(name, true/*link children*/, true/*keep as name*/); // NO DEEP COPY!
+      if (child) { // could have been out of memory
+        jsvAddName(dst, child);
+        jsvUnLock(child);
+      }
+      vr = name->nextSibling;
+      jsvUnLock(name);
+    }
+  } else {
+    assert(jsvIsBasic(src)); // in case we missed something!
+  }
+
+  return dst;
+}
+
+void jsvAddName(JsVar *parent, JsVar *namedChild) {
+  namedChild = jsvRef(namedChild); // ref here VERY important as adding to structure!
+  assert(jsvIsName(namedChild));
+
+  if (parent->lastChild) { // we have children already
+    JsVar *insertAfter = jsvLock(parent->lastChild);
+    if (jsvIsArray(parent)) {
+      // we must insert in order - so step back until we get the right place
+      while (insertAfter && jsvCompareInteger(namedChild, insertAfter)<0) {
+        JsVarRef prev = insertAfter->prevSibling;
+        jsvUnLock(insertAfter);
+        insertAfter = prev ? jsvLock(prev) : 0;
+      }
+    }
+
+    if (insertAfter) {
+      if (insertAfter->nextSibling) {
+        // great, we're in the middle...
+        JsVar *insertBefore = jsvLock(insertAfter->nextSibling);
+        insertBefore->prevSibling = jsvGetRef(namedChild);
+        namedChild->nextSibling = jsvGetRef(insertBefore);
+        jsvUnLock(insertBefore);
+      } else {
+        // We're at the end - just set up the parent
+        parent->lastChild = jsvGetRef(namedChild);
+      }
+      insertAfter->nextSibling = jsvGetRef(namedChild);
+      namedChild->prevSibling = jsvGetRef(insertAfter);
+      jsvUnLock(insertAfter);
+    } else { // Insert right at the beginning of the array
+      // Link 2 children together
+      JsVar *firstChild = jsvLock(parent->firstChild);
+      firstChild->prevSibling = jsvGetRef(namedChild);
+      jsvUnLock(firstChild);
+
+      namedChild->nextSibling = parent->firstChild;
+      // finally set the new child as the first one
+      parent->firstChild = jsvGetRef(namedChild);
+    }
+  } else { // we have no children - just add it
+    parent->firstChild = parent->lastChild = jsvGetRef(namedChild);
+
+  }
+}
+
+JsVar *jsvAddNamedChild(JsVar *parent, JsVar *child, const char *name) {
+  JsVar *namedChild = jsvMakeIntoVariableName(jsvNewFromString(name), child);
+  if (!namedChild) return 0; // Out of memory
+  jsvAddName(parent, namedChild);
+  return namedChild;
+}
+
+JsVar *jsvSetNamedChild(JsVar *parent, JsVar *child, const char *name) {
+  JsVar *namedChild = jsvFindChildFromString(parent, name, true);
+  if (namedChild) // could be out of memory
+    return jsvSetValueOfName(namedChild, child);
+  return 0;
+}
+
+JsVar *jsvSetValueOfName(JsVar *name, JsVar *src) {
+  assert(name && jsvIsName(name));
+  assert(name!=src); // no infinite loops!
+  // all is fine, so replace the existing child...
+  /* Existing child may be null in the case of Z = 0 where
+   * we create 'Z' and pass it down to '=' to have the value
+   * filled in (or it may be undefined). */
+  if (name->firstChild) jsvUnRefRef(name->firstChild); // free existing
+  if (src) {
+      // we can link to a name if we want (so can remove the assert!)
+      name->firstChild = jsvGetRef(jsvRef(src));
+  } else
+      name->firstChild = 0;
+  return name;
+}
+
+JsVar *jsvFindChildFromString(JsVar *parent, const char *name, bool addIfNotFound) {
+  /* Pull out first 4 bytes, and ensure that everything
+   * is 0 padded so that we can do a nice speedy check. */
+  char fastCheck[4];
+  fastCheck[0] = name[0];
+  if (name[0]) {
+    fastCheck[1] = name[1];
+    if (name[1]) {
+      fastCheck[2] = name[2];
+      if (name[2]) {
+        fastCheck[3] = name[3];
+      } else {
+        fastCheck[3] = 0;
+      }
+    } else {
+      fastCheck[2] = 0;
+      fastCheck[3] = 0;
+    }
+  } else {
+    fastCheck[1] = 0;
+    fastCheck[2] = 0;
+    fastCheck[3] = 0;
+  }
+
+  assert(jsvHasChildren(parent));
+  JsVarRef childref = parent->firstChild;
+  while (childref) {
+    // Don't Lock here, just use GetAddressOf - to try and speed up the finding
+    // TODO: We can do this now, but when/if we move to cacheing vars, it'll break
+    JsVar *child = jsvGetAddressOf(childref);
+    if (*(int*)fastCheck==*(int*)child->varData.str && // speedy check of first 4 bytes
+        jsvIsStringEqual(child, name)) {
+       // found it! unlock parent but leave child locked
+       return jsvLockAgain(child);
+    }
+    childref = child->nextSibling;
+  }
+
+  JsVar *child = 0;
+  if (addIfNotFound) {
+    child = jsvMakeIntoVariableName(jsvNewFromString(name), 0);
+    if (child) // could be out of memory
+      jsvAddName(parent, child);
+  }
+  return child;
+}
+
+/** Non-recursive finding */
+JsVar *jsvFindChildFromVar(JsVar *parent, JsVar *childName, bool addIfNotFound) {
+  JsVar *child;
+  JsVarRef childref = parent->firstChild;
+
+  while (childref) {
+    child = jsvLock(childref);
+    if (jsvIsBasicVarEqual(child, childName)) {
+      // found it! unlock parent but leave child locked
+      return child;
+    }
+    childref = child->nextSibling;
+    jsvUnLock(child);
+  }
+
+  child = 0;
+  if (addIfNotFound && childName) {
+    if (childName->refs == 0) {
+      // Not reffed - great! let's just use it
+      if (!jsvIsName(childName))
+        childName = jsvMakeIntoVariableName(childName, 0);
+      child = jsvLockAgain(childName);
+    } else { // it was reffed, we must add a new one
+      child = jsvMakeIntoVariableName(jsvCopy(childName), 0);
+    }
+    jsvAddName(parent, child);
+  }
+  return child;
+}
+
+void jsvRemoveChild(JsVar *parent, JsVar *child) {
+    assert(jsvHasChildren(parent));
+    JsVarRef childref = jsvGetRef(child);
+    // unlink from parent
+    if (parent->firstChild == childref)
+        parent->firstChild = child->nextSibling;
+    if (parent->lastChild == childref)
+        parent->lastChild = child->prevSibling;
+    // unlink from child list
+    if (child->prevSibling) {
+        JsVar *v = jsvLock(child->prevSibling);
+        v->nextSibling = child->nextSibling;
+        jsvUnLock(v);
+    }
+    if (child->nextSibling) {
+        JsVar *v = jsvLock(child->nextSibling);
+        v->prevSibling = child->prevSibling;
+        jsvUnLock(v);
+    }
+    child->prevSibling = 0;
+    child->nextSibling = 0;
+
+    jsvUnRef(child);
+}
+
+void jsvRemoveAllChildren(JsVar *parent) {
+    assert(jsvHasChildren(parent));
+    while (parent->firstChild) {
+      JsVar *v = jsvLock(parent->firstChild);
+      jsvRemoveChild(parent, v);
+      jsvUnLock(v);
+    }
+}
+
+/// Get the named child of an object. If createChild!=0 then create the child
+JsVar *jsvObjectGetChild(JsVar *obj, const char *name, JsVarFlags createChild) {
+  if (!obj) return 0;
+  assert(jsvHasChildren(obj));
+  JsVar *childName = jsvFindChildFromString(obj, name, createChild);
+  if (!childName && createChild) {
+    JsVar *child = jsvNewWithFlags(createChild);
+    jsvSetValueOfName(childName, child);
+    jsvUnLock(childName);
+    return child;
+  }
+  if (childName)
+    return jsvSkipNameAndUnLock(childName);
+  return 0;
+}
+
+/// Set the named child of an object, and return the child (so you can choose to unlock it if you want)
+JsVar *jsvObjectSetChild(JsVar *obj, const char *name, JsVar *child) {
+  assert(jsvHasChildren(obj));
+  // child can actually be a name (for instance if it is a named function)
+  JsVar *childName = jsvFindChildFromString(obj, name, true);
+  if (!childName) return 0; // out of memory
+  jsvSetValueOfName(childName, child);
+  jsvUnLock(childName);
+  return child;
+}
+
+int jsvGetChildren(JsVar *v) {
+  //OPT: could length be stored as the value of the array?
+  int children = 0;
+  JsVarRef childref = v->firstChild;
+  while (childref) {
+    JsVar *child = jsvLock(childref);
+    children++;
+    childref = child->nextSibling;
+    jsvUnLock(child);
+  }
+  return children;
+}
+
+
+JsVarInt jsvGetArrayLength(JsVar *arr) {
+  JsVarRef childref = arr->lastChild;
+  // Just look at last non-string element!
+  while (childref) {
+    JsVar *child = jsvLock(childref);
+    if (jsvIsInt(child)) {
+      JsVarInt lastIdx = jsvGetInteger(child);
+      jsvUnLock(child);
+      return lastIdx+1;
+    }
+    // if not an int, keep going
+    childref = child->prevSibling;
+    jsvUnLock(child);
+  }
+  return 0;
+}
+
+JsVarInt jsvGetLength(JsVar *src) {
+  if (jsvIsArray(src)) {
+    return jsvGetArrayLength(src);
+  } else if (jsvIsArrayBuffer(src)) {
+    return (JsVarInt)jsvGetArrayBufferLength(src);
+  } else if (jsvIsString(src)) {
+    return (JsVarInt)jsvGetStringLength(src);
+  } else if (jsvIsObject(src) || jsvIsFunction(src)) {
+      return jsvGetChildren(src);
+  } else {
+    return 1;
+  }
+}
+
+/** Count the amount of JsVars used. Mostly useful for debugging */
+size_t jsvCountJsVarsUsed(JsVar *v) {
+  size_t count = 1;
+  if (jsvHasChildren(v)) {
+    JsVarRef childref = v->firstChild;
+    while (childref) {
+      JsVar *child = jsvLock(childref);
+      count += jsvCountJsVarsUsed(child);
+      childref = child->nextSibling;
+      jsvUnLock(child);
+    }
+  }
+  if (jsvHasCharacterData(v)) {
+    size_t count = 0;
+    JsVarRef childref = v->lastChild;
+    while (childref) {
+      JsVar *child = jsvLock(childref);
+      count++;
+      childref = child->lastChild;
+      jsvUnLock(child);
+    }
+  }
+  if (jsvIsName(v) && v->firstChild) {
+    JsVar *child = jsvLock(v->firstChild);
+    count += jsvCountJsVarsUsed(child);
+    jsvUnLock(child);
+  }
+  return count;
+}
+
+
+JsVar *jsvGetArrayItem(JsVar *arr, int index) {
+  JsVarRef childref = arr->firstChild;
+  while (childref) {
+    JsVarInt childIndex;
+    JsVar *child = jsvLock(childref);
+
+    assert(jsvIsInt(child));
+    childIndex = jsvGetInteger(child);
+    if (childIndex == index) {
+      JsVar *item = child->firstChild ? jsvLock(child->firstChild) : 0;
+      jsvUnLock(child);
+      return item;
+    }
+    childref = child->nextSibling;
+    jsvUnLock(child);
+  }
+  return 0; // undefined
+}
+
+/// Get the index of the value in the array (matchExact==use pointer, not equality check)
+JsVar *jsvGetArrayIndexOf(JsVar *arr, JsVar *value, bool matchExact) {
+  JsVarRef indexref;
+  assert(jsvIsArray(arr) || jsvIsObject(arr));
+  indexref = arr->firstChild;
+  while (indexref) {
+    JsVar *childIndex = jsvLock(indexref);
+    assert(jsvIsName(childIndex))
+    if (childIndex->firstChild) {
+      JsVar *childValue = jsvLock(childIndex->firstChild);
+      if ((matchExact && childValue==value) ||
+          (!matchExact && jsvIsBasicVarEqual(childValue, value))) {
+        jsvUnLock(childValue);
+        return childIndex;
+      }
+      jsvUnLock(childValue);
+    } else if (jsvIsUndefined(value))
+      return childIndex; // both are undefined, so we return the index
+    indexref = childIndex->nextSibling;
+    jsvUnLock(childIndex);
+  }
+  return 0; // undefined
+}
+
+/// Adds new elements to the end of an array, and returns the new length. initialValue is the item index when no items are currently in the array.
+JsVarInt jsvArrayPushWithInitialSize(JsVar *arr, JsVar *value, JsVarInt initialValue) {
+  assert(jsvIsArray(arr));
+  JsVarInt index = jsvGetArrayLength(arr);
+  if (index==0) index=initialValue;
+  JsVar *idx = jsvMakeIntoVariableName(jsvNewFromInteger(index), value);
+  if (!idx) {
+    jsWarn("Out of memory while appending to array");
+    return 0;
+  }
+  jsvAddName(arr, idx);
+  jsvUnLock(idx);
+  return index+1; // new size
+}
+
+/// Adds new elements to the end of an array, and returns the new length
+JsVarInt jsvArrayPush(JsVar *arr, JsVar *value) {
+  return jsvArrayPushWithInitialSize(arr, value, 0);
+}
+
+/// Adds a new element to the end of an array, unlocks it, and returns the new length
+JsVarInt jsvArrayPushAndUnLock(JsVar *arr, JsVar *value) {
+  JsVarInt l = jsvArrayPushWithInitialSize(arr, value, 0);
+  jsvUnLock(value);
+  return l;
+}
+
+/// Removes the last element of an array, and returns that element (or 0 if empty). includes the NAME
+JsVar *jsvArrayPop(JsVar *arr) {
+  assert(jsvIsArray(arr));
+  if (arr->lastChild) {
+    JsVar *child = jsvLock(arr->lastChild);
+    if (arr->firstChild == arr->lastChild)
+      arr->firstChild = 0; // if 1 item in array
+    arr->lastChild = child->prevSibling; // unlink from end of array
+    jsvUnRef(child); // as no longer in array
+    if (child->prevSibling) {
+      JsVar *v = jsvLock(child->prevSibling);
+      v->nextSibling = 0;
+      jsvUnLock(v);
+    }
+    child->prevSibling = 0;
+    return child; // and return it
+  } else {
+    // no children!
+    return 0;
+  }
+}
+
+/// Removes the first element of an array, and returns that element (or 0 if empty).
+JsVar *jsvArrayPopFirst(JsVar *arr) {
+  assert(jsvIsArray(arr));
+  if (arr->firstChild) {
+    JsVar *child = jsvLock(arr->firstChild);
+    if (arr->firstChild == arr->lastChild)
+      arr->lastChild = 0; // if 1 item in array
+    arr->firstChild = child->nextSibling; // unlink from end of array
+    jsvUnRef(child); // as no longer in array
+    if (child->nextSibling) {
+      JsVar *v = jsvLock(child->nextSibling);
+      v->prevSibling = 0;
+      jsvUnLock(v);
+    }
+    child->nextSibling = 0;
+    return child; // and return it
+  } else {
+    // no children!
+    return 0;
+  }
+}
+
+///  Get the last element of an array (does not remove, unlike jsvArrayPop), and returns that element (or 0 if empty) includes the NAME
+JsVar *jsvArrayGetLast(JsVar *arr) {
+  assert(jsvIsArray(arr));
+  if (arr->lastChild) {
+    return jsvLock(arr->lastChild);
+  } else { // no children!
+    return 0;
+  }
+}
+
+/// Join all elements of an array together into a string
+JsVar *jsvArrayJoin(JsVar *arr, JsVar *filler) {
+  JsVar *str = jsvNewFromEmptyString();
+  if (!str) return 0; // out of memory
+
+  JsVarInt index = 0;
+  JsvIterator it;
+  jsvIteratorNew(&it, arr);
+  while (jsvIteratorHasElement(&it)) {
+    JsVar *key = jsvIteratorGetKey(&it);
+    if (jsvIsInt(key)) {
+      JsVarInt thisIndex = jsvGetInteger(key);
+      // add the filler
+      if (filler) {
+        while (index<thisIndex) {
+          index++;
+          jsvAppendStringVarComplete(str, filler);
+        }
+      }
+      // add the value
+      JsVar *value = jsvIteratorGetValue(&it);
+      if (value) {
+        JsVar *valueStr = jsvAsString(value, true /* UNLOCK */);
+        if (valueStr) { // could be out of memory
+          jsvAppendStringVarComplete(str, valueStr);
+          jsvUnLock(valueStr);
+        }
+      }
+    }
+    jsvUnLock(key);
+    jsvIteratorNext(&it);
+  }
+  jsvIteratorFree(&it);
+  return str;
+}
+
+/// Insert a new element before beforeIndex, DOES NOT UPDATE INDICES
+void jsvArrayInsertBefore(JsVar *arr, JsVar *beforeIndex, JsVar *element) {
+  if (beforeIndex) {
+    JsVar *idxVar = jsvMakeIntoVariableName(jsvNewFromInteger(0), element);
+    if (!idxVar) return; // out of memory
+
+    JsVarRef idxRef = jsvGetRef(jsvRef(idxVar));
+    JsVarRef prev = beforeIndex->prevSibling;
+    if (prev) {
+      JsVar *prevVar = jsvRef(jsvLock(prev));
+      jsvSetInteger(idxVar, jsvGetInteger(prevVar)+1); // update index number
+      prevVar->nextSibling = idxRef;
+      jsvUnLock(prevVar);
+      idxVar->prevSibling = prev;
+    } else {
+      idxVar->prevSibling = 0;
+      arr->firstChild = idxRef;
+    }
+    beforeIndex->prevSibling = idxRef;
+    idxVar->nextSibling = jsvGetRef(jsvRef(beforeIndex));
+    jsvUnLock(idxVar);
+  } else
+    jsvArrayPush(arr, element);
+}
+
+/** Same as jsvMathsOpPtr, but if a or b are a name, skip them
+ * and go to what they point to. */
+JsVar *jsvMathsOpSkipNames(JsVar *a, JsVar *b, int op) {
+  JsVar *pa = jsvSkipName(a);
+  JsVar *pb = jsvSkipName(b);
+  JsVar *res = jsvMathsOp(pa,pb,op);
+  jsvUnLock(pa);
+  jsvUnLock(pb);
+  return res;
+}
+
+
+JsVar *jsvMathsOpError(int op, const char *datatype) {
+    char buf[JS_ERROR_BUF_SIZE];
+    size_t bufpos = 0;
+    strncpy(&buf[bufpos], "Operation ", JS_ERROR_BUF_SIZE-bufpos);
+    bufpos=strlen(buf);
+    jslTokenAsString(op, &buf[bufpos], JS_ERROR_TOKEN_BUF_SIZE-bufpos);
+    bufpos=strlen(buf);
+    strncat(&buf[bufpos], " not supported on the  ", JS_ERROR_BUF_SIZE-bufpos);
+    bufpos=strlen(buf);
+    strncat(&buf[bufpos], datatype, JS_ERROR_BUF_SIZE-bufpos);
+    bufpos=strlen(buf);
+    strncat(&buf[bufpos], " datatype", JS_ERROR_BUF_SIZE-bufpos);
+    jsError(buf);
+    return 0;
+}
+
+JsVar *jsvMathsOp(JsVar *a, JsVar *b, int op) {
+    // Type equality check
+    if (op == LEX_TYPEEQUAL || op == LEX_NTYPEEQUAL) {
+      // check type first, then call again to check data
+      bool eql = (a==0) == (b==0);
+      if (a && b) eql = ((a->flags & JSV_VARTYPEMASK) ==
+                         (b->flags & JSV_VARTYPEMASK));
+      if (eql) {
+        JsVar *contents = jsvMathsOp(a,b, LEX_EQUAL);
+        if (!jsvGetBool(contents)) eql = false;
+        jsvUnLock(contents);
+      }
+      if (op == LEX_TYPEEQUAL)
+        return jsvNewFromBool(eql);
+      else
+        return jsvNewFromBool(!eql);
+    }
+
+    bool needsInt = op=='&' || op=='|' || op=='^' || op=='%' || op==LEX_LSHIFT || op==LEX_RSHIFT || op==LEX_RSHIFTUNSIGNED;
+    bool needsNumeric = needsInt || op=='*' || op=='/' || op=='%' || op=='-';
+
+    // do maths...
+    if (jsvIsUndefined(a) && jsvIsUndefined(b)) {
+      if (op == LEX_EQUAL)
+        return jsvNewFromBool(true);
+      else if (op == LEX_NEQUAL)
+        return jsvNewFromBool(false);
+      else
+        return 0; // undefined
+    } else if (needsNumeric ||
+               ((jsvIsNumeric(a) || jsvIsUndefined(a) || jsvIsNull(a)) &&
+                (jsvIsNumeric(b) || jsvIsUndefined(b) || jsvIsNull(b)))) {
+      if (needsInt || !(jsvIsFloat(a) || jsvIsFloat(b))) {
+            // use ints
+            JsVarInt da = jsvGetInteger(a);
+            JsVarInt db = jsvGetInteger(b);
+            switch (op) {
+                case '+': return jsvNewFromInteger(da+db);
+                case '-': return jsvNewFromInteger(da-db);
+                case '*': return jsvNewFromInteger(da*db);
+                case '/': return jsvNewFromFloat((JsVarFloat)da/(JsVarFloat)db);
+                case '&': return jsvNewFromInteger(da&db);
+                case '|': return jsvNewFromInteger(da|db);
+                case '^': return jsvNewFromInteger(da^db);
+                case '%': return jsvNewFromInteger(da%db);
+                case LEX_LSHIFT: return jsvNewFromInteger(da << db);
+                case LEX_RSHIFT: return jsvNewFromInteger(da >> db);
+                case LEX_RSHIFTUNSIGNED: return jsvNewFromInteger((JsVarInt)(((JsVarIntUnsigned)da) >> db));
+                case LEX_EQUAL:     return jsvNewFromBool(da==db);
+                case LEX_NEQUAL:    return jsvNewFromBool(da!=db);
+                case '<':           return jsvNewFromBool(da<db);
+                case LEX_LEQUAL:    return jsvNewFromBool(da<=db);
+                case '>':           return jsvNewFromBool(da>db);
+                case LEX_GEQUAL:    return jsvNewFromBool(da>=db);
+                default: return jsvMathsOpError(op, "Integer");
+            }
+        } else {
+            // use doubles
+            JsVarFloat da = jsvGetFloat(a);
+            JsVarFloat db = jsvGetFloat(b);
+            switch (op) {
+                case '+': return jsvNewFromFloat(da+db);
+                case '-': return jsvNewFromFloat(da-db);
+                case '*': return jsvNewFromFloat(da*db);
+                case '/': return jsvNewFromFloat(da/db);
+                case LEX_EQUAL:     return jsvNewFromBool(da==db);
+                case LEX_NEQUAL:    return jsvNewFromBool(da!=db);
+                case '<':           return jsvNewFromBool(da<db);
+                case LEX_LEQUAL:    return jsvNewFromBool(da<=db);
+                case '>':           return jsvNewFromBool(da>db);
+                case LEX_GEQUAL:    return jsvNewFromBool(da>=db);
+                default: return jsvMathsOpError(op, "Double");
+            }
+        }
+    } else if ((jsvIsArray(a) || jsvIsObject(a) ||
+                jsvIsArray(b) || jsvIsObject(b)) &&
+                (op == LEX_EQUAL || op==LEX_NEQUAL)) {
+      bool isArray = jsvIsArray(a);
+      /* Just check pointers */
+      switch (op) {
+           case LEX_EQUAL:  return jsvNewFromBool(a==b);
+           case LEX_NEQUAL: return jsvNewFromBool(a!=b);
+           default: return jsvMathsOpError(op, isArray?"Array":"Object");
+      }
+    } else {
+       JsVar *da = jsvAsString(a, false);
+       JsVar *db = jsvAsString(b, false);
+       if (!da || !db) { // out of memory
+         jsvUnLock(da);
+         jsvUnLock(db);
+         return 0;
+       }
+       if (op=='+') {
+         JsVar *v = jsvCopy(da);
+         // TODO: can we be fancy and not copy da if we know it isn't reffed? what about locks?
+         if (v) // could be out of memory
+           jsvAppendStringVarComplete(v, db);
+         jsvUnLock(da);
+         jsvUnLock(db);
+         return v;
+       }
+
+       int cmp = jsvCompareString(da,db,0,0,false);
+       jsvUnLock(da);
+       jsvUnLock(db);
+       // use strings
+       switch (op) {
+           case LEX_EQUAL:     return jsvNewFromBool(cmp==0);
+           case LEX_NEQUAL:    return jsvNewFromBool(cmp!=0);
+           case '<':           return jsvNewFromBool(cmp<0);
+           case LEX_LEQUAL:    return jsvNewFromBool(cmp<=0);
+           case '>':           return jsvNewFromBool(cmp>0);
+           case LEX_GEQUAL:    return jsvNewFromBool(cmp>=0);
+           default: return jsvMathsOpError(op, "String");
+       }
+    }
+}
+
+JsVar *jsvNegateAndUnLock(JsVar *v) {
+  JsVar *zero = jsvNewFromInteger(0);
+  JsVar *res = jsvMathsOpSkipNames(zero, v, '-');
+  jsvUnLock(zero);
+  jsvUnLock(v);
+  return res;
+}
+
+void jsvTraceLockInfo(JsVar *v) {
+    jsiConsolePrintf("#%d[r%d,l%d] ",jsvGetRef(v),v->refs,jsvGetLocks(v)-1);
+}
+
+/** Get the lowest level at which searchRef appears */
+int _jsvTraceGetLowestLevel(JsVarRef ref, JsVarRef searchRef) {
+  if (ref == searchRef) return 0;
+  int found = -1;
+  JsVar *var = jsvLock(ref);
+
+  // Use IS_RECURSING  flag to stop recursion
+  if (var->flags & JSV_IS_RECURSING) {
+    jsvUnLock(var);
+    return -1;
+  }
+  var->flags |= JSV_IS_RECURSING;
+
+  if (jsvHasSingleChild(var) && var->firstChild) {
+    int f = _jsvTraceGetLowestLevel(var->firstChild, searchRef);
+    if (f>=0 && (found<0 || f<found)) found=f+1;
+  }
+  if (jsvHasChildren(var)) {
+    JsVarRef childRef = var->firstChild;
+    while (childRef) {
+      int f = _jsvTraceGetLowestLevel(childRef, searchRef);
+      if (f>=0 && (found<0 || f<found)) found=f+1;
+
+      JsVar *child = jsvLock(childRef);
+      childRef = child->nextSibling;
+      jsvUnLock(child);
+    }
+  }
+
+  var->flags &= ~JSV_IS_RECURSING;
+  jsvUnLock(var);
+
+  return found; // searchRef not found
+}
+
+void _jsvTrace(JsVarRef ref, int indent, JsVarRef baseRef, int level) {
+#ifdef SAVE_ON_FLASH
+  jsiConsolePrint("Trace unimplemented in this version.\n");
+#else
+    int i;
+    for (i=0;i<indent;i++) jsiConsolePrint(" ");
+
+    if (!ref) {
+        jsiConsolePrint("undefined\n");
+        return;
+    }
+    /*jsiConsolePrint("<");
+    jsiConsolePrintInt(level);
+    jsiConsolePrint(":");
+    jsiConsolePrintInt(_jsvTraceGetLowestLevel(baseRef, ref));
+    jsiConsolePrint("> ");*/
+
+
+    JsVar *var = jsvLock(ref);
+    jsvTraceLockInfo(var);
+
+
+    if (jsvIsName(var)) {
+      if (jsvIsFunctionParameter(var))
+        jsiConsolePrint("Param ");
+      JsVar *str = jsvAsString(var, false);
+      if (jsvIsInt(var)) {
+        jsiConsolePrintf("Name: int %v  ", str);
+      } else if (jsvIsFloat(var)) {
+        jsiConsolePrintf("Name: flt %v  ", str);
+      } else if (jsvIsString(var) || jsvIsFunctionParameter(var)) {
+        jsiConsolePrintf("Name: '%v'  ", str);
+      } else if (jsvIsArrayBufferName(var)) {
+        jsiConsolePrintf("ArrayBufferName[%d] ", jsvGetInteger(var));
+      } else {
+        assert(0);
+      }
+      jsvUnLock(str);
+      // go to what the name points to
+      ref = var->firstChild;
+      jsvUnLock(var);
+      if (ref) {
+        level++;
+        int lowestLevel = _jsvTraceGetLowestLevel(baseRef, ref);
+        /*jsiConsolePrint("<");
+        jsiConsolePrintInt(level);
+        jsiConsolePrint(":");
+        jsiConsolePrintInt(lowestLevel);
+        jsiConsolePrint("> ");*/
+
+        var = jsvLock(ref);
+        jsvTraceLockInfo(var);
+        if (lowestLevel < level) {
+          // If this data is available elsewhere in the tree (but nearer the root)
+          // then don't print it. This makes the dump significantly more readable!
+          // It also stops us getting in recursive loops ...
+          jsiConsolePrint("...\n");
+          jsvUnLock(var);
+          return;
+        }
+
+      } else {
+        jsiConsolePrint("undefined\n");
+        return;
+      }
+    }
+
+    if (jsvIsName(var)) {
+      jsiConsolePrint("\n");
+      _jsvTrace(jsvGetRef(var), indent+2, baseRef, level+1);
+      jsvUnLock(var);
+      return;
+    }
+    if (jsvIsObject(var)) jsiConsolePrint("Object {");
+    else if (jsvIsArray(var)) jsiConsolePrint("Array [");
+    else if (jsvIsPin(var)) jsiConsolePrint("Pin ");
+    else if (jsvIsInt(var)) jsiConsolePrint("Integer ");
+    else if (jsvIsBoolean(var)) jsiConsolePrint("Bool ");
+    else if (jsvIsFloat(var)) jsiConsolePrint("Double ");
+    else if (jsvIsString(var)) jsiConsolePrint("String ");
+    else if (jsvIsArrayBuffer(var)) {
+      jsiConsolePrintf("%s ", jswGetBasicObjectName(var)); // way to get nice name
+      _jsvTrace(var->firstChild, indent+1, baseRef, level+1);
+      jsvUnLock(var);
+      return;
+    } else if (jsvIsFunction(var)) jsiConsolePrint("Function {");
+    else {
+        jsiConsolePrintf("Flags %d\n", var->flags & (JsVarFlags)~(JSV_LOCK_MASK));
+    }
+
+    if (!jsvIsObject(var) && !jsvIsArray(var) && !jsvIsFunction(var)) {
+      JsVar *str = jsvAsString(var, false);
+      if (str) {
+        JsvStringIterator it;
+        jsvStringIteratorNew(&it, str, 0);
+        while (jsvStringIteratorHasChar(&it)) {
+          char ch = jsvStringIteratorGetChar(&it);
+          jsiConsolePrint(escapeCharacter(ch));
+          jsvStringIteratorNext(&it);
+        }
+        jsvStringIteratorFree(&it);
+        jsvUnLock(str);
+      }
+    }
+
+    if (jsvHasStringExt(var)) {
+      if (!jsvIsStringExt(var) && var->firstChild) { // stringext don't have children (the use them for chars)
+        jsiConsolePrint("( Multi-block string ");
+        JsVarRef child = var->firstChild;
+        while (child) {
+          JsVar *childVar = jsvLock(child);
+          jsvTraceLockInfo(childVar);
+          child = childVar->firstChild;
+          jsvUnLock(childVar);
+        }
+        jsiConsolePrint(")\n");
+      } else
+          jsiConsolePrint("\n");
+    } else {
+      JsVarRef child = var->firstChild;
+      jsiConsolePrint("\n");
+      // dump children
+      while (child) {
+        JsVar *childVar;
+        _jsvTrace(child, indent+2, baseRef, level+1);
+        childVar = jsvLock(child);
+        child = childVar->nextSibling;
+        jsvUnLock(childVar);
+      }
+    }
+
+    if (jsvIsObject(var) || jsvIsFunction(var) || jsvIsArray(var)) {
+      int i;
+      for (i=0;i<indent;i++) jsiConsolePrint(" ");
+      jsiConsolePrint(jsvIsArray(var) ? "]\n" : "}\n");
+    }
+
+    jsvUnLock(var);
+#endif
+}
+
+/** Write debug info for this Var out to the console */
+void jsvTrace(JsVarRef ref, int indent) {
+  _jsvTrace(ref,indent,ref,0);
+}
+
+
+/** Recursively mark the variable */
+static void jsvGarbageCollectMarkUsed(JsVar *var) {
+  var->flags &= (JsVarFlags)~JSV_GARBAGE_COLLECT;
+
+  if (jsvHasCharacterData(var)) {
+      if (var->lastChild) {
+        JsVar *childVar = jsvGetAddressOf(var->lastChild);
+        if (childVar->flags & JSV_GARBAGE_COLLECT)
+          jsvGarbageCollectMarkUsed(childVar);
+      }
+  }
+  // intentionally no else
+  if (jsvHasSingleChild(var)) {
+    if (var->firstChild) {
+      JsVar *childVar = jsvGetAddressOf(var->firstChild);
+      if (childVar->flags & JSV_GARBAGE_COLLECT)
+        jsvGarbageCollectMarkUsed(childVar);
+    }
+  } else if (jsvHasChildren(var)) {
+    JsVarRef child = var->firstChild;
+    while (child) {
+      JsVar *childVar;
+      childVar = jsvGetAddressOf(child);
+      if (childVar->flags & JSV_GARBAGE_COLLECT)
+        jsvGarbageCollectMarkUsed(childVar);
+      child = childVar->nextSibling;
+    }
+  }
+}
+
+/** Run a garbage collection sweep - return true if things have been freed */
+bool jsvGarbageCollect() {
+  JsVarRef i;
+  // clear garbage collect flags
+  for (i=1;i<=jsVarsSize;i++)  {
+    JsVar *var = jsvGetAddressOf(i);
+    if ((var->flags&JSV_VARTYPEMASK) != JSV_UNUSED) // if it is not unused
+      var->flags |= (JsVarFlags)JSV_GARBAGE_COLLECT;
+  }
+  // recursively add 'native' vars
+  for (i=1;i<=jsVarsSize;i++)  {
+      JsVar *var = jsvGetAddressOf(i);
+    if ((var->flags & JSV_GARBAGE_COLLECT) && // not already GC'd
+        jsvGetLocks(var)>0) // or it is locked
+      jsvGarbageCollectMarkUsed(var);
+  }
+  // now sweep for things that we can GC!
+  bool freedSomething = false;
+  for (i=1;i<=jsVarsSize;i++)  {
+    JsVar *var = jsvGetAddressOf(i);
+    if (var->flags & JSV_GARBAGE_COLLECT) {
+      freedSomething = true;
+      // free!
+      var->flags = JSV_UNUSED;
+      // add this to our free list
+      var->nextSibling = jsVarFirstEmpty;
+      jsVarFirstEmpty = jsvGetRef(var);
+    }
+  }
+  return freedSomething;
+}
+
+/** Remove whitespace to the right of a string - on MULTIPLE LINES */
+JsVar *jsvStringTrimRight(JsVar *srcString) {
+  JsvStringIterator src, dst;
+  JsVar *dstString = jsvNewFromEmptyString();
+  jsvStringIteratorNew(&src, srcString, 0);
+  jsvStringIteratorNew(&dst, dstString, 0);
+  int spaces = 0;
+  while (jsvStringIteratorHasChar(&src)) {
+    char ch = jsvStringIteratorGetChar(&src);
+    jsvStringIteratorNext(&src);
+
+    if (ch==' ') spaces++;
+    else if (ch=='\n') {
+      spaces = 0;
+      jsvStringIteratorAppend(&dst, ch);
+    } else {
+      for (;spaces>0;spaces--)
+        jsvStringIteratorAppend(&dst, ' ');
+      jsvStringIteratorAppend(&dst, ch);
+    }
+  }
+  jsvStringIteratorFree(&src);
+  jsvStringIteratorFree(&dst);
+  return dstString;
+}
+
+/// If v is the key of a function, return true if it is internal and shouldn't be visible to the user
+bool jsvIsInternalFunctionKey(JsVar *v) {
+  return (jsvIsString(v) && (
+              v->varData.str[0]==JS_HIDDEN_CHAR)
+                            ) ||
+         jsvIsFunctionParameter(v);
+}
+
+/// If v is the key of an object, return true if it is internal and shouldn't be visible to the user
+bool jsvIsInternalObjectKey(JsVar *v) {
+  return (jsvIsString(v) && (
+              v->varData.str[0]==JS_HIDDEN_CHAR ||
+              jsvIsStringEqual(v, JSPARSE_INHERITS_VAR) ||
+              jsvIsStringEqual(v, JSPARSE_CONSTRUCTOR_VAR)
+                            ));
+}
+
+// --------------------------------------------------------------------------------------------
+
+void jsvStringIteratorNew(JsvStringIterator *it, JsVar *str, int startIdx) {
+  assert(jsvHasCharacterData(str));
+  it->var = jsvLockAgain(str);
+  it->charsInVar = jsvGetCharactersInVar(str);
+  it->charIdx = (size_t)startIdx;
+  it->index = (size_t)startIdx;
+  while (it->charIdx>0 && it->charIdx >= it->charsInVar) {
+    it->charIdx -= it->charsInVar;
+    if (it->var) {
+      if (it->var->lastChild) {
+        JsVar *next = jsvLock(it->var->lastChild);
+        jsvUnLock(it->var);
+        it->var = next;
+        it->charsInVar = jsvGetCharactersInVar(it->var);
+      } else {
+        jsvUnLock(it->var);
+        it->var = 0;
+        it->charsInVar = 0;
+        return; // get out of loop
+      }
+    }
+  }
+}
+
+void jsvStringIteratorNext(JsvStringIterator *it) {
+  jsvStringIteratorNextInline(it);
+}
+
+void jsvStringIteratorGotoEnd(JsvStringIterator *it) {
+  assert(it->var);
+  while (it->var->lastChild) {
+     it->index += it->charsInVar;
+     JsVar *next = jsvLock(it->var->lastChild);
+     jsvUnLock(it->var);
+     it->var = next;
+     it->charsInVar = jsvGetCharactersInVar(it->var);
+   }
+  if (it->charsInVar) it->charIdx = it->charsInVar-1;
+  else it->charIdx = 0;
+}
+
+void jsvStringIteratorAppend(JsvStringIterator *it, char ch) {
+  if (!it->var) return;
+  if (it->charsInVar>0) {
+    assert(it->charIdx+1 == it->charsInVar /* check at end */);
+    it->charIdx++;
+    it->index++;
+  } else
+    assert(it->charIdx == 0);
+  if (it->charIdx >= jsvGetMaxCharactersInVar(it->var)) {
+    assert(!it->var->lastChild);
+    JsVar *next = jsvNewWithFlags(JSV_STRING_EXT);
+    if (!next) return; // out of memory
+    // we don't ref, because  StringExts are never reffed as they only have one owner (and ALWAYS have an owner)
+    it->var->lastChild = jsvGetRef(next);
+    jsvUnLock(it->var);
+    it->var = next;
+
+    it->charIdx = 0; // it's new, so empty
+  }
+
+  it->var->varData.str[it->charIdx] = ch;
+  it->charsInVar = it->charIdx+1;
+  jsvSetCharactersInVar(it->var, it->charsInVar);
+}
+
+
+// --------------------------------------------------------------------------------------------
+void   jsvArrayBufferIteratorNew(JsvArrayBufferIterator *it, JsVar *arrayBuffer, JsVarInt index) {
+  assert(jsvIsArrayBuffer(arrayBuffer));
+  it->index = index;
+  it->type = arrayBuffer->varData.arraybuffer.type;
+  it->byteLength = arrayBuffer->varData.arraybuffer.length * JSV_ARRAYBUFFER_GET_SIZE(it->type);
+  it->byteOffset = arrayBuffer->varData.arraybuffer.byteOffset;
+  JsVar *arrayBufferData = jsvLock(arrayBuffer->firstChild);
+  while (jsvIsArrayBuffer(arrayBufferData)) {
+    JsVar *s = jsvLock(arrayBufferData->firstChild);
+    jsvUnLock(arrayBufferData);
+    arrayBufferData = s;
+  }
+  assert(jsvIsString(arrayBufferData));
+
+  it->byteLength += it->byteOffset; // because we'll check if we have more bytes using this
+  it->byteOffset = it->byteOffset + index*JSV_ARRAYBUFFER_GET_SIZE(it->type);
+  if (it->byteOffset<0 || (it->byteLength>=0 && it->byteOffset>=(it->byteLength+1-JSV_ARRAYBUFFER_GET_SIZE(it->type)))) {
+    jsvUnLock(arrayBufferData);
+    it->type = ARRAYBUFFERVIEW_UNDEFINED;
+    return;
+  }
+  jsvStringIteratorNew(&it->it, arrayBufferData, (int)it->byteOffset);
+  jsvUnLock(arrayBufferData);
+  it->hasAccessedElement = false;
+}
+
+static void jsvArrayBufferIteratorGetValueData(JsvArrayBufferIterator *it, char *data) {
+  if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return;
+  assert(!it->hasAccessedElement); // we just haven't implemented this case yet
+  unsigned int i,dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
+  for (i=0;i<dataLen;i++) {
+    data[i] = jsvStringIteratorGetChar(&it->it);
+    if (dataLen!=1) jsvStringIteratorNext(&it->it);
+  }
+  if (dataLen!=1) it->hasAccessedElement = true;
+}
+
+static JsVarInt jsvArrayBufferIteratorDataToInt(JsvArrayBufferIterator *it, char *data) {
+  unsigned int dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
+  JsVarInt v = 0;
+  if (dataLen==1) v = *(char*)data;
+  else if (dataLen==2) v = *(short*)data;
+  else if (dataLen==4) v = *(int*)data;
+  else if (dataLen==8) v = *(long long*)data;
+  else assert(0);
+  if ((!JSV_ARRAYBUFFER_IS_SIGNED(it->type)) && v<0)
+    v += 1 << (8*dataLen);
+  return v;
+}
+
+static JsVarFloat jsvArrayBufferIteratorDataToFloat(JsvArrayBufferIterator *it, char *data) {
+  unsigned int dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
+  JsVarFloat v = 0;
+  if (dataLen==4) v = *(float*)data;
+  else if (dataLen==8) v = *(double*)data;
+  else assert(0);
+  return v;
+}
+
+JsVar *jsvArrayBufferIteratorGetValue(JsvArrayBufferIterator *it) {
+  if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return 0;
+  char data[8];
+  jsvArrayBufferIteratorGetValueData(it, data);
+  if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) {
+    return jsvNewFromFloat(jsvArrayBufferIteratorDataToFloat(it, data));
+  } else {
+    return jsvNewFromInteger(jsvArrayBufferIteratorDataToInt(it, data));
+  }
+}
+
+JsVarInt jsvArrayBufferIteratorGetIntegerValue(JsvArrayBufferIterator *it) {
+  if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return 0;
+  char data[8];
+  jsvArrayBufferIteratorGetValueData(it, data);
+  if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) {
+    return (JsVarInt)jsvArrayBufferIteratorDataToFloat(it, data);
+  } else {
+    return jsvArrayBufferIteratorDataToInt(it, data);
+  }
+}
+
+JsVarFloat jsvArrayBufferIteratorGetFloatValue(JsvArrayBufferIterator *it) {
+  if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return 0;
+  char data[8];
+  jsvArrayBufferIteratorGetValueData(it, data);
+  if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) {
+    return jsvArrayBufferIteratorDataToFloat(it, data);
+  } else {
+    return (JsVarFloat)jsvArrayBufferIteratorDataToInt(it, data);
+  }
+}
+
+void   jsvArrayBufferIteratorSetValue(JsvArrayBufferIterator *it, JsVar *value) {
+  if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return;
+  assert(!it->hasAccessedElement); // we just haven't implemented this case yet
+  char data[8];
+  unsigned int i,dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
+
+  if (JSV_ARRAYBUFFER_IS_FLOAT(it->type)) {
+    JsVarFloat v = jsvGetFloat(value);       ;
+    if (dataLen==4) { float f = (float)v; memcpy(data,&f,dataLen); }
+    else if (dataLen==8) { double f = (double)v; memcpy(data,&f,dataLen); }
+    else assert(0);
+  } else {
+    JsVarInt v = jsvGetInteger(value);
+    // we don't care about sign when writing - as it gets truncated
+    if (dataLen==1) { char c = (char)v; memcpy(data,&c,dataLen); }
+    else if (dataLen==2) { short c = (short)v; memcpy(data,&c,dataLen); }
+    else if (dataLen==4) { int c = (int)v; memcpy(data,&c,dataLen); }
+    else if (dataLen==8) { long long c = (long long)v; memcpy(data,&c,dataLen); }
+    else assert(0);
+  }
+
+  for (i=0;i<dataLen;i++) {
+    jsvStringIteratorSetChar(&it->it, data[i]);
+    if (dataLen!=1) jsvStringIteratorNext(&it->it);
+  }
+  if (dataLen!=1) it->hasAccessedElement = true;
+}
+
+void   jsvArrayBufferIteratorSetIntegerValue(JsvArrayBufferIterator *it, JsVarInt value) {
+  // FIXME: Do this without the allocation!
+  JsVar *val = jsvNewFromInteger(value);
+  jsvArrayBufferIteratorSetValue(it, val);
+  jsvUnLock(val);
+}
+
+JsVar* jsvArrayBufferIteratorGetIndex(JsvArrayBufferIterator *it) {
+  return jsvNewFromInteger(it->index);
+}
+
+bool   jsvArrayBufferIteratorHasElement(JsvArrayBufferIterator *it) {
+  if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return false;
+  if (it->hasAccessedElement) return true;
+  if (it->byteLength>=0)
+    return it->byteOffset <= (it->byteLength-JSV_ARRAYBUFFER_GET_SIZE(it->type));
+  else
+    return jsvStringIteratorHasChar(&it->it);
+}
+
+void   jsvArrayBufferIteratorNext(JsvArrayBufferIterator *it) {
+  it->index++;
+  it->byteOffset += JSV_ARRAYBUFFER_GET_SIZE(it->type);
+  if (!it->hasAccessedElement) {
+    unsigned int i,dataLen = JSV_ARRAYBUFFER_GET_SIZE(it->type);
+    for (i=0;i<dataLen;i++)
+      jsvStringIteratorNext(&it->it);
+  } else
+    it->hasAccessedElement = false;
+}
+
+void   jsvArrayBufferIteratorFree(JsvArrayBufferIterator *it) {
+  if (it->type == ARRAYBUFFERVIEW_UNDEFINED) return;
+  jsvStringIteratorFree(&it->it);
+}
+// --------------------------------------------------------------------------------------------
+/* General Purpose iterator, for Strings, Arrays, Objects, Typed Arrays */
+
+void jsvIteratorNew(JsvIterator *it, JsVar *obj) {
+  if (jsvIsArray(obj)) {
+    it->type = JSVI_ARRAY;
+    jsvArrayIteratorNew(&it->it.arr, obj);
+  } else if (jsvIsObject(obj) || jsvIsFunction(obj)) {
+    it->type = JSVI_OBJECT;
+    jsvObjectIteratorNew(&it->it.obj, obj);
+  } else if (jsvIsArrayBuffer(obj)) {
+    it->type = JSVI_ARRAYBUFFER;
+    jsvArrayBufferIteratorNew(&it->it.buf, obj, 0);
+  } else if (jsvHasCharacterData(obj)) {
+    it->type = JSVI_STRING;
+    jsvStringIteratorNew(&it->it.str, obj, 0);
+  } else assert(0);
+}
+
+JsVar *jsvIteratorGetKey(JsvIterator *it) {
+  switch (it->type) {
+  case JSVI_ARRAY : return jsvArrayIteratorGetIndex(&it->it.arr);
+  case JSVI_OBJECT : return jsvObjectIteratorGetKey(&it->it.obj);
+  case JSVI_STRING : return jsvNewFromInteger((JsVarInt)jsvStringIteratorGetIndex(&it->it.str));
+  case JSVI_ARRAYBUFFER : return jsvArrayBufferIteratorGetIndex(&it->it.buf);
+  default: assert(0); return 0;
+  }
+}
+
+JsVar *jsvIteratorGetValue(JsvIterator *it) {
+  switch (it->type) {
+  case JSVI_ARRAY : return jsvArrayIteratorGetElement(&it->it.arr);
+  case JSVI_OBJECT : return jsvObjectIteratorGetValue(&it->it.obj);
+  case JSVI_STRING : { char buf[2] = {jsvStringIteratorGetChar(&it->it.str),0}; return jsvNewFromString(buf); }
+  case JSVI_ARRAYBUFFER : return jsvArrayBufferIteratorGetValue(&it->it.buf);
+  default: assert(0); return 0;
+  }
+}
+
+JsVarInt jsvIteratorGetIntegerValue(JsvIterator *it) {
+  switch (it->type) {
+  case JSVI_ARRAY : return jsvGetIntegerAndUnLock(jsvArrayIteratorGetElement(&it->it.arr));
+  case JSVI_OBJECT : return jsvGetIntegerAndUnLock(jsvObjectIteratorGetValue(&it->it.obj));
+  case JSVI_STRING : return (JsVarInt)jsvStringIteratorGetChar(&it->it.str);
+  case JSVI_ARRAYBUFFER : return jsvArrayBufferIteratorGetIntegerValue(&it->it.buf);
+  default: assert(0); return 0;
+  }
+}
+
+bool jsvIteratorHasElement(JsvIterator *it) {
+  switch (it->type) {
+  case JSVI_ARRAY : return jsvArrayIteratorHasElement(&it->it.arr);
+  case JSVI_OBJECT : return jsvObjectIteratorHasElement(&it->it.obj);
+  case JSVI_STRING : return jsvStringIteratorHasChar(&it->it.str);
+  case JSVI_ARRAYBUFFER : return jsvArrayBufferIteratorHasElement(&it->it.buf);
+  default: assert(0); return 0;
+  }
+}
+
+void jsvIteratorNext(JsvIterator *it) {
+  switch (it->type) {
+  case JSVI_ARRAY : jsvArrayIteratorNext(&it->it.arr); break;
+  case JSVI_OBJECT : jsvObjectIteratorNext(&it->it.obj); break;
+  case JSVI_STRING : jsvStringIteratorNext(&it->it.str); break;
+  case JSVI_ARRAYBUFFER : jsvArrayBufferIteratorNext(&it->it.buf); break;
+  default: assert(0); break;
+  }
+}
+void jsvIteratorFree(JsvIterator *it) {
+  switch (it->type) {
+  case JSVI_ARRAY : jsvArrayIteratorFree(&it->it.arr); break;
+  case JSVI_OBJECT : jsvObjectIteratorFree(&it->it.obj); break;
+  case JSVI_STRING : jsvStringIteratorFree(&it->it.str); break;
+  case JSVI_ARRAYBUFFER : jsvArrayBufferIteratorFree(&it->it.buf); break;
+  default: assert(0); break;
+  }
+}
+
+

+ 612 - 0
components/external/espruino/src/jsvar.h

@@ -0,0 +1,612 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * Variables
+ * ----------------------------------------------------------------------------
+ */
+#ifndef JSVAR_H_
+#define JSVAR_H_
+
+#include "jsutils.h"
+
+
+typedef void (*JsCallback)(JsVarRef var) 
+#ifdef SDCC
+__reentrant
+#endif
+;
+
+/** To avoid confusion - JsVarRefCounter should be big enough
+ * to store as many refs as can possibly be created - so it's
+ * safe just to set it to the same size as JsVarRef. However
+ * it is NOT a reference itself.
+ */
+typedef JsVarRef JsVarRefCounter;
+
+typedef enum {
+  ARRAYBUFFERVIEW_UNDEFINED = 0,
+  ARRAYBUFFERVIEW_ARRAYBUFFER = 1 | 64, ///< Basic ArrayBuffer type
+
+  ARRAYBUFFERVIEW_MASK_SIZE = 15,
+  ARRAYBUFFERVIEW_SIGNED = 16,
+  ARRAYBUFFERVIEW_FLOAT = 32,
+
+  ARRAYBUFFERVIEW_UINT8   = 1,
+  ARRAYBUFFERVIEW_INT8    = 1 | ARRAYBUFFERVIEW_SIGNED,
+  ARRAYBUFFERVIEW_UINT16  = 2,
+  ARRAYBUFFERVIEW_INT16   = 2 | ARRAYBUFFERVIEW_SIGNED,
+  ARRAYBUFFERVIEW_UINT32  = 4,
+  ARRAYBUFFERVIEW_INT32   = 4 | ARRAYBUFFERVIEW_SIGNED,
+  ARRAYBUFFERVIEW_FLOAT32 = 4 | ARRAYBUFFERVIEW_FLOAT,
+  ARRAYBUFFERVIEW_FLOAT64 = 8 | ARRAYBUFFERVIEW_FLOAT,
+} PACKED_FLAGS JsVarDataArrayBufferViewType;
+#define JSV_ARRAYBUFFER_GET_SIZE(T) ((T)&ARRAYBUFFERVIEW_MASK_SIZE)
+#define JSV_ARRAYBUFFER_IS_SIGNED(T) (((T)&ARRAYBUFFERVIEW_SIGNED)!=0)
+#define JSV_ARRAYBUFFER_IS_FLOAT(T) (((T)&ARRAYBUFFERVIEW_FLOAT)!=0)
+
+#define JSV_ARRAYBUFFER_MAX_LENGTH 65535
+
+typedef struct {
+  unsigned short byteOffset;
+  unsigned short length;
+  JsVarDataArrayBufferViewType type;
+} PACKED_FLAGS JsVarDataArrayBufferView;
+
+typedef union {
+    char str[JSVAR_DATA_STRING_LEN]; ///< The contents of this variable if it is a string
+    /* NOTE: For str above, we INTENTIONALLY OVERFLOW str (and hence data) in the case of STRING_EXTS
+     * to overwrite 3 references in order to grab another 6 bytes worth of string data */
+    // TODO do some magic with union/structs in order to make sure we don't intentionally write off the end of arrays
+    JsVarInt integer; ///< The contents of this variable if it is an int
+    JsVarFloat floating; ///< The contents of this variable if it is a double
+    JsCallback callback; ///< Callback for native functions, or 0
+    JsVarDataArrayBufferView arraybuffer; ///< information for array buffer views.
+} PACKED_FLAGS JsVarData;
+
+typedef struct {
+#ifdef LARGE_MEM
+  JsVarRef this; ///< The reference of this variable itself (so we can get back)
+#endif
+  JsVarFlags flags; ///< the flags determine the type of the variable - int/double/string/etc.
+
+  JsVarData varData;
+  /* NOTE: WE INTENTIONALLY OVERFLOW data in the case of STRING_EXTS
+   * to overwrite the following 3 references in order to grab another
+   * 6 bytes worth of string data */
+
+  /* For Variable NAMES (e.g. Object/Array keys) these store actual next/previous pointers for a linked list
+   * For STRING_EXT - extra characters
+   * Not used for other stuff
+   */
+  JsVarRef nextSibling;
+  JsVarRef prevSibling;
+
+  JsVarRefCounter refs; ///< The number of references held to this - used for automatic garbage collection. NOT USED for STRINGEXT though (it is just extra characters)
+
+  /**
+   * For OBJECT/ARRAY/FUNCTION - this is the first child
+   * For NAMES and REF - this is a link to the variable it points to
+   * For STRING_EXT - extra character data (NOT a link)
+   * For ARRAYBUFFER - a link to a string containing the data for the array buffer   *
+   */
+  JsVarRef firstChild;
+
+  /**
+   * For OBJECT/ARRAY/FUNCTION - this is the last child
+   * For STRINGS/STRING_EXT/NAME+STRING - this is a link to more string data if it is needed
+   * For REF - this is the 'parent' that the firstChild is a member of
+   */
+  JsVarRef lastChild;
+} PACKED_FLAGS JsVar;
+
+/* We have a few different types:
+ *
+ *  OBJECT/ARRAY - uses firstChild/lastChild to link to NAMEs.
+ *  BUILT-IN OBJECT - as above, but we use varData to store the name as well. This means built in object names must be LESS THAN 8 CHARACTERS
+ *  FUNCTION - uses firstChild/lastChild to link to NAMEs, and callback is used
+ *  NAME - use nextSibling/prevSibling linking to other NAMEs, and firstChild to link to a Variable of some kind
+ *  STRING - use firstChild to link to other STRINGs if String value is too long
+ *  INT/DOUBLE - firstChild never used
+ */
+
+static inline unsigned char jsvGetLocks(JsVar *v) { return (unsigned char)((v->flags>>JSV_LOCK_SHIFT) & JSV_LOCK_MAX); }
+
+// For debugging/testing ONLY - maximum # of vars we are allowed to use
+void jsvSetMaxVarsUsed(unsigned int size);
+
+// Init/kill vars as a whole
+void jsvInit();
+void jsvKill();
+void jsvSoftInit(); ///< called when loading from flash
+void jsvSoftKill(); ///< called when saving to flash
+JsVar *jsvFindOrCreateRoot(); ///< Find or create the ROOT variable item - used mainly if recovering from a saved state.
+unsigned int jsvGetMemoryUsage(); ///< Get number of memory records (JsVars) used
+unsigned int jsvGetMemoryTotal(); ///< Get total amount of memory records
+bool jsvIsMemoryFull(); ///< Get whether memory is full or not
+void jsvShowAllocated(); ///< Show what is still allocated, for debugging memory problems
+/// Try and allocate more memory - only works if RESIZABLE_JSVARS is defined
+void jsvSetMemoryTotal(unsigned int jsNewVarCount);
+
+
+// Note that jsvNew* don't REF a variable for you, but the do LOCK it
+JsVar *jsvNew(); ///< Create a new variable
+JsVar *jsvNewWithFlags(JsVarFlags flags);
+JsVar *jsvNewFromString(const char *str); ///< Create a new string
+JsVar *jsvNewStringOfLength(unsigned int byteLength); ///< Create a new string of the given length - full of 0s
+static inline JsVar *jsvNewFromEmptyString() { return jsvNewWithFlags(JSV_STRING); } ;///< Create a new empty string
+JsVar *jsvNewFromLexer(struct JsLex *lex, JslCharPos charFrom, JslCharPos charTo); // Create a new STRING from part of the lexer
+JsVar *jsvNewFromInteger(JsVarInt value);
+JsVar *jsvNewFromBool(bool value);
+JsVar *jsvNewFromFloat(JsVarFloat value);
+// Turns var into a Variable name that links to the given value... No locking so no need to unlock var
+JsVar *jsvMakeIntoVariableName(JsVar *var, JsVar *valueOrZero);
+JsVar *jsvNewFromPin(int pin);
+
+/// DO NOT CALL THIS DIRECTLY - this frees an unreffed/locked var
+void jsvFreePtr(JsVar *var);
+
+/// Get a reference from a var - SAFE for null vars
+JsVarRef jsvGetRef(JsVar *var);
+
+/// SCARY - only to be used for vital stuff like load/save
+JsVar *_jsvGetAddressOf(JsVarRef ref);
+
+/// Lock this reference and return a pointer - UNSAFE for null refs
+JsVar *jsvLock(JsVarRef ref);
+
+/// Lock this pointer and return a pointer - UNSAFE for null pointer
+JsVar *jsvLockAgain(JsVar *var);
+
+/// Unlock this variable - this is SAFE for null variables
+void jsvUnLock(JsVar *var);
+
+
+/// Reference - set this variable as used by something
+JsVar *jsvRef(JsVar *v);
+
+/// Unreference - set this variable as not used by anything
+void jsvUnRef(JsVar *var);
+
+/// Helper fn, Reference - set this variable as used by something
+JsVarRef jsvRefRef(JsVarRef ref);
+
+/// Helper fn, Unreference - set this variable as not used by anything
+JsVarRef jsvUnRefRef(JsVarRef ref);
+
+static inline bool jsvIsRoot(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_ROOT; }
+static inline bool jsvIsPin(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_PIN; }
+static inline bool jsvIsInt(const JsVar *v) { return v && ((v->flags&JSV_VARTYPEMASK)==JSV_INTEGER || (v->flags&JSV_VARTYPEMASK)==JSV_PIN); }
+static inline bool jsvIsFloat(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_FLOAT; }
+static inline bool jsvIsBoolean(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_BOOLEAN; }
+static inline bool jsvIsString(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)>=JSV_STRING_0 && (v->flags&JSV_VARTYPEMASK)<=JSV_STRING_MAX; }
+static inline bool jsvIsStringExt(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)>=JSV_STRING_EXT_0 && (v->flags&JSV_VARTYPEMASK)<=JSV_STRING_EXT_MAX; } ///< The extra bits dumped onto the end of a string to store more data
+static inline bool jsvIsNumeric(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)>=JSV_NUMERICSTART && (v->flags&JSV_VARTYPEMASK)<=JSV_NUMERICEND; }
+static inline bool jsvIsFunction(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_FUNCTION; }
+static inline bool jsvIsFunctionParameter(const JsVar *v) { return v && (v->flags&JSV_FUNCTION_PARAMETER) == JSV_FUNCTION_PARAMETER; }
+static inline bool jsvIsObject(const JsVar *v) { return v && (((v->flags&JSV_VARTYPEMASK)==JSV_OBJECT) || ((v->flags&JSV_VARTYPEMASK)==JSV_ROOT)); }
+static inline bool jsvIsArray(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_ARRAY; }
+static inline bool jsvIsArrayBuffer(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_ARRAYBUFFER; }
+static inline bool jsvIsArrayBufferName(const JsVar *v) { return v && (v->flags&(JSV_VARTYPEMASK|JSV_NAME))==JSV_ARRAYBUFFERNAME; }
+static inline bool jsvIsNative(const JsVar *v) { return v && (v->flags&JSV_NATIVE)!=0; }
+static inline bool jsvIsUndefined(const JsVar *v) { return v==0; }
+static inline bool jsvIsNull(const JsVar *v) { return v && (v->flags&JSV_VARTYPEMASK)==JSV_NULL; }
+static inline bool jsvIsBasic(const JsVar *v) { return jsvIsNumeric(v) || jsvIsString(v);} ///< Is this *not* an array/object/etc
+static inline bool jsvIsName(const JsVar *v) { return v && (v->flags & JSV_NAME)!=0; } ///< NAMEs are what's used to name a variable (it is not the data itself)
+
+static inline bool jsvIsIterable(const JsVar *v) {
+  return jsvIsArray(v) || jsvIsObject(v) || jsvIsFunction(v) ||
+         jsvIsString(v) || jsvIsArrayBuffer(v);
+}
+
+/** Does this string contain only Numeric characters? */
+bool jsvIsStringNumeric(const JsVar *var);
+/** Does this string contain only Numeric characters? This is for arrays
+ * and makes the assertion that int_to_string(string_to_int(var))==var */
+bool jsvIsStringNumericStrict(const JsVar *var);
+
+// TODO: maybe isName shouldn't include ArrayBufferName?
+bool jsvHasCharacterData(const JsVar *v); ///< does the v->data union contain character data?
+bool jsvHasStringExt(const JsVar *v);
+/// Does this variable use firstChild/lastChild to point to multiple childrem
+bool jsvHasChildren(const JsVar *v);
+/// Is this variable a type that uses firstChild to point to a single Variable (ie. it doesn't have multiple children)
+bool jsvHasSingleChild(const JsVar *v);
+/// Does this variable have a 'ref' argument? Stringexts use it for extra character data
+static inline bool jsvHasRef(const JsVar *v) { return !jsvIsStringExt(v); }
+
+/// This is the number of characters a JsVar can contain, NOT string length
+static inline size_t jsvGetMaxCharactersInVar(const JsVar *v) {
+  // see jsvCopy - we need to know about this in there too
+  if (jsvIsStringExt(v)) return JSVAR_DATA_STRING_MAX_LEN;
+  assert(jsvHasCharacterData(v));
+  return JSVAR_DATA_STRING_LEN;
+}
+
+/// This is the number of characters a JsVar can contain, NOT string length
+static inline size_t jsvGetCharactersInVar(const JsVar *v) {
+  assert(jsvIsString(v) || jsvIsStringExt(v));
+  int f = v->flags&JSV_VARTYPEMASK;
+  return (size_t)(f - ((f < JSV_STRING_EXT_0) ? JSV_STRING_0 : JSV_STRING_EXT_0));
+}
+
+/// This is the number of characters a JsVar can contain, NOT string length
+static inline void jsvSetCharactersInVar(JsVar *v, size_t chars) {
+  assert(jsvIsString(v) || jsvIsStringExt(v));
+  if (jsvIsString(v)) { assert(chars <= JSVAR_DATA_STRING_LEN); }
+  if (jsvIsStringExt(v)) { assert(chars <= JSVAR_DATA_STRING_MAX_LEN); }
+
+  int f = v->flags&JSV_VARTYPEMASK;
+  v->flags = (JsVarFlags)((v->flags&(~JSV_VARTYPEMASK)) | (((f < JSV_STRING_EXT_0) ? JSV_STRING_0 : JSV_STRING_EXT_0) + (int)chars));
+}
+
+
+/** Check if two Basic Variables are equal (this IGNORES the value that is pointed to,
+ * so 'a=5'=='a=7' but 'a=5'!='b=5')
+ */
+bool jsvIsBasicVarEqual(JsVar *a, JsVar *b);
+
+/** Check if two things are equal. Basic vars are done by value,
+ * for anything else the reference/pointer must be equal */
+bool jsvIsEqual(JsVar *a, JsVar *b);
+
+
+const char *jsvGetConstString(const JsVar *v); ///< Get a const string representing this variable - if we can. Otherwise return 0
+const char *jsvGetTypeOf(const JsVar *v); ///< Return the 'type' of the JS variable (eg. JS's typeof operator)
+size_t jsvGetString(const JsVar *v, char *str, size_t len); ///< Save this var as a string to the given buffer, and return how long it was (return val doesn't include terminating 0)
+void jsvSetString(JsVar *v, char *str, size_t len); ///< Set the Data in this string. This must JUST overwrite - not extend or shrink
+JsVar *jsvAsString(JsVar *var, bool unlockVar); ///< If var is a string, lock and return it, else create a new string
+size_t jsvGetStringLength(JsVar *v); ///< Get the length of this string, IF it is a string
+int jsvGetLinesInString(JsVar *v); ///<  IN A STRING get the number of lines in the string (min=1)
+int jsvGetCharsOnLine(JsVar *v, int line); ///<  IN A STRING Get the number of characters on a line - lines start at 1
+void jsvGetLineAndCol(JsVar *v, int charIdx, int* line, int *col); ///< IN A STRING, get the line and column of the given character. Both values must be non-null
+int jsvGetIndexFromLineAndCol(JsVar *v, int line, int col); ///<  IN A STRING, get a character index from a line and column
+bool jsvIsStringEqual(JsVar *var, const char *str);
+int jsvCompareString(JsVar *va, JsVar *vb, int starta, int startb, bool equalAtEndOfString); ///< Compare 2 strings, starting from the given character positions
+int jsvCompareInteger(JsVar *va, JsVar *vb); ///< Compare 2 integers, >0 if va>vb,  <0 if va<vb. If compared with a non-integer, that gets put later
+void jsvAppendString(JsVar *var, const char *str); ///< Append the given string to this one
+void jsvAppendStringBuf(JsVar *var, const char *str, int length); ///< Append the given string to this one - but does not use null-terminated strings
+void jsvAppendPrintf(JsVar *var, const char *fmt, ...); ///< Append the formatted string to a variable (see vcbprintf)
+static inline void jsvAppendCharacter(JsVar *var, char ch) { jsvAppendStringBuf(var, &ch, 1); }; ///< Append the given character to this string
+#define JSVAPPENDSTRINGVAR_MAXLENGTH (0x7FFFFFFF)
+void jsvAppendStringVar(JsVar *var, const JsVar *str, int stridx, int maxLength); ///< Append str to var. Both must be strings. stridx = start char or str, maxLength = max number of characters (can be JSVAPPENDSTRINGVAR_MAXLENGTH). stridx can be negative to go from end of string
+void jsvAppendStringVarComplete(JsVar *var, const JsVar *str); ///< Append all of str to var. Both must be strings.
+char jsvGetCharInString(JsVar *v, int idx);
+
+JsVarInt jsvGetInteger(const JsVar *v);
+void jsvSetInteger(JsVar *v, JsVarInt value); ///< Set an integer value (use carefully!)
+JsVarFloat jsvGetFloat(const JsVar *v); ///< Get the floating point representation of this var
+bool jsvGetBool(const JsVar *v);
+JsVar *jsvAsNumber(JsVar *var); ///< Convert the given variable to a number
+static inline JsVarInt _jsvGetIntegerAndUnLock(JsVar *v) { JsVarInt i = jsvGetInteger(v); jsvUnLock(v); return i; }
+static inline JsVarFloat _jsvGetFloatAndUnLock(JsVar *v) { JsVarFloat f = jsvGetFloat(v); jsvUnLock(v); return f; }
+static inline bool _jsvGetBoolAndUnLock(JsVar *v) { bool b = jsvGetBool(v); jsvUnLock(v); return b; }
+#ifdef SAVE_ON_FLASH
+JsVarInt jsvGetIntegerAndUnLock(JsVar *v);
+JsVarFloat jsvGetFloatAndUnLock(JsVar *v);
+bool jsvGetBoolAndUnLock(JsVar *v);
+#else
+#define jsvGetIntegerAndUnLock _jsvGetIntegerAndUnLock
+#define jsvGetFloatAndUnLock _jsvGetFloatAndUnLock
+#define jsvGetBoolAndUnLock _jsvGetBoolAndUnLock
+#endif
+
+
+/** Get the item at the given location in the array buffer and return the result */
+size_t jsvGetArrayBufferLength(JsVar *arrayBuffer);
+/** Get the item at the given location in the array buffer and return the result */
+JsVar *jsvArrayBufferGet(JsVar *arrayBuffer, JsVarInt index);
+/** Set the item at the given location in the array buffer */
+void jsvArrayBufferSet(JsVar *arrayBuffer, JsVarInt index, JsVar *value);
+/** Given an integer name that points to an arraybuffer or an arraybufferview, evaluate it and return the result */
+JsVar *jsvArrayBufferGetFromName(JsVar *name);
+
+/** If a is a name skip it and go to what it points to - and so on.
+ * ALWAYS locks - so must unlock what it returns. It MAY
+ * return 0.  */
+JsVar *jsvSkipName(JsVar *a);
+
+/** If a is a name skip it and go to what it points to.
+ * ALWAYS locks - so must unlock what it returns. It MAY
+ * return 0.  */
+JsVar *jsvSkipOneName(JsVar *a);
+
+/** If a is a's child is a name skip it and go to what it points to.
+ * ALWAYS locks - so must unlock what it returns.  */
+JsVar *jsvSkipToLastName(JsVar *a);
+
+/** Same as jsvSkipName, but ensures that 'a' is unlocked if it was
+ * a name, so it can be used INLINE_FUNC */
+static inline JsVar *jsvSkipNameAndUnLock(JsVar *a) {
+  JsVar *b = jsvSkipName(a);
+  jsvUnLock(a);
+  return b;
+}
+
+/** Same as jsvSkipOneName, but ensures that 'a' is unlocked if it was
+ * a name, so it can be used INLINE_FUNC */
+static inline JsVar *jsvSkipOneNameAndUnLock(JsVar *a) {
+  JsVar *b = jsvSkipOneName(a);
+  jsvUnLock(a);
+  return b;
+}
+
+/// MATHS!
+JsVar *jsvMathsOpSkipNames(JsVar *a, JsVar *b, int op);
+JsVar *jsvMathsOp(JsVar *a, JsVar *b, int op);
+/// Negates an integer/double value
+JsVar *jsvNegateAndUnLock(JsVar *v);
+
+/// Copy this variable and return the locked copy
+JsVar *jsvCopy(JsVar *src);
+/** Copy only a name, not what it points to. ALTHOUGH the link to what it points to is maintained unless linkChildren=false.
+    If keepAsName==false, this will be converted into a normal variable */
+JsVar *jsvCopyNameOnly(JsVar *src, bool linkChildren, bool keepAsName);
+/// Tree related stuff
+void jsvAddName(JsVar *parent, JsVar *nameChild); // Add a child, which is itself a name
+JsVar *jsvAddNamedChild(JsVar *parent, JsVar *child, const char *name); // Add a child, and create a name for it. Returns a LOCKED var. DOES NOT CHECK FOR DUPLICATES
+JsVar *jsvSetNamedChild(JsVar *parent, JsVar *child, const char *name); // Add a child, and create a name for it. Returns a LOCKED name var. CHECKS FOR DUPLICATES
+JsVar *jsvSetValueOfName(JsVar *name, JsVar *src); // Set the value of a child created with jsvAddName,jsvAddNamedChild. Returns the UNLOCKED name argument
+JsVar *jsvFindChildFromString(JsVar *parent, const char *name, bool createIfNotFound); // Non-recursive finding of child with name. Returns a LOCKED var
+JsVar *jsvFindChildFromVar(JsVar *parent, JsVar *childName, bool addIfNotFound); // Non-recursive finding of child with name. Returns a LOCKED var
+static inline JsVar *jsvFindChildFromStringRef(JsVarRef parentref, const char *name, bool addIfNotFound) { // Non-recursive finding of child with name. Returns a LOCKED var
+  JsVar *p = jsvLock(parentref);
+  JsVar *v = jsvFindChildFromString(p, name, addIfNotFound);
+  jsvUnLock(p);
+  return v;
+}
+static inline JsVar *jsvFindChildFromVarRef(JsVarRef parentref, JsVar *childName, bool addIfNotFound) { // Non-recursive finding of child with name. Returns a LOCKED var
+  JsVar *p = jsvLock(parentref);
+  JsVar *v = jsvFindChildFromVar(p, childName, addIfNotFound);
+  jsvUnLock(p);
+  return v;
+}
+/// Remove a child - note that the child MUST ACTUALLY BE A CHILD!
+void jsvRemoveChild(JsVar *parent, JsVar *child);
+void jsvRemoveAllChildren(JsVar *parent);
+static inline void jsvRemoveNamedChild(JsVar *parent, const char *name) {
+  JsVar *child = jsvFindChildFromString(parent, name, false);
+  if (child) {
+    jsvRemoveChild(parent, child);
+    jsvUnLock(child);
+  }
+}
+
+/// Get the named child of an object. If createChild!=0 then create the child
+JsVar *jsvObjectGetChild(JsVar *obj, const char *name, JsVarFlags createChild);
+/// Set the named child of an object, and return the child (so you can choose to unlock it if you want)
+JsVar *jsvObjectSetChild(JsVar *obj, const char *name, JsVar *child);
+
+int jsvGetChildren(JsVar *v); ///< number of children of a variable. also see jsvGetArrayLength and jsvGetLength
+JsVarInt jsvGetArrayLength(JsVar *arr); ///< Not the same as GetChildren, as it can be a sparse array
+JsVarInt jsvGetLength(JsVar *src); ///< General purpose length function. Does the 'right' thing
+size_t jsvCountJsVarsUsed(JsVar *v); ///< Count the amount of JsVars used. Mostly useful for debugging
+JsVar *jsvGetArrayItem(JsVar *arr, int index); ///< Get an item at the specified index in the array (and lock it)
+JsVar *jsvGetArrayIndexOf(JsVar *arr, JsVar *value, bool matchExact); ///< Get the index of the value in the array (matchExact==use pointer, not equality check)
+JsVarInt jsvArrayPushWithInitialSize(JsVar *arr, JsVar *value, JsVarInt initialValue); ///< Adds new elements to the end of an array, and returns the new length. initialValue is the item index when no items are currently in the array.
+JsVarInt jsvArrayPush(JsVar *arr, JsVar *value); ///< Adds a new element to the end of an array, and returns the new length
+JsVarInt jsvArrayPushAndUnLock(JsVar *arr, JsVar *value); ///< Adds a new element to the end of an array, unlocks it, and returns the new length
+JsVar *jsvArrayPop(JsVar *arr); ///< Removes the last element of an array, and returns that element (or 0 if empty). includes the NAME
+JsVar *jsvArrayPopFirst(JsVar *arr); ///< Removes the first element of an array, and returns that element (or 0 if empty) includes the NAME
+JsVar *jsvArrayGetLast(JsVar *arr); ///< Get the last element of an array (does not remove, unlike jsvArrayPop), and returns that element (or 0 if empty) includes the NAME
+JsVar *jsvArrayJoin(JsVar *arr, JsVar *filler); ///< Join all elements of an array together into a string
+void jsvArrayInsertBefore(JsVar *arr, JsVar *beforeIndex, JsVar *element); ///< Insert a new element before beforeIndex, DOES NOT UPDATE INDICES
+static inline bool jsvArrayIsEmpty(JsVar *arr) { assert(jsvIsArray(arr)); return !arr->firstChild; } ///< Return true is array is empty
+
+/** Write debug info for this Var out to the console */
+void jsvTrace(JsVarRef ref, int indent);
+
+/** Run a garbage collection sweep - return true if things have been freed */
+bool jsvGarbageCollect();
+
+/** Remove whitespace to the right of a string - on MULTIPLE LINES */
+JsVar *jsvStringTrimRight(JsVar *srcString);
+
+/** If v is the key of a function, return true if it is internal and shouldn't be visible to the user */
+bool jsvIsInternalFunctionKey(JsVar *v);
+
+/// If v is the key of an object, return true if it is internal and shouldn't be visible to the user
+bool jsvIsInternalObjectKey(JsVar *v);
+// --------------------------------------------------------------------------------------------
+typedef struct JsvStringIterator {
+  size_t charIdx; ///< index of character in var
+  size_t charsInVar; ///< total characters in var
+  size_t index; ///< index in string
+  JsVar *var; ///< current StringExt we're looking at
+} JsvStringIterator;
+
+// slight hack to enure we can use string iterator with const JsVars
+#define jsvStringIteratorNewConst(it,str,startIdx) jsvStringIteratorNew(it,(JsVar*)str,startIdx)
+
+void jsvStringIteratorNew(JsvStringIterator *it, JsVar *str, int startIdx);
+
+/// Gets the current character (or 0)
+static inline char jsvStringIteratorGetChar(JsvStringIterator *it) {
+  if (!it->var) return 0;
+  return  it->var->varData.str[it->charIdx];
+}
+
+/// Gets the current (>=0) character (or -1)
+static inline int jsvStringIteratorGetCharOrMinusOne(JsvStringIterator *it) {
+  if (!it->var) return -1;
+  return (int)(unsigned char)it->var->varData.str[it->charIdx];
+}
+
+/// Do we have a character, or are we at the end?
+static inline bool jsvStringIteratorHasChar(JsvStringIterator *it) {
+  return it->charIdx < it->charsInVar;
+}
+
+/// Sets a character (will not extend the string - just overwrites)
+static inline void jsvStringIteratorSetChar(JsvStringIterator *it, char c) {
+  if (jsvStringIteratorHasChar(it))
+    it->var->varData.str[it->charIdx] = c;
+}
+
+/// Gets the current index in the string
+static inline size_t jsvStringIteratorGetIndex(JsvStringIterator *it) {
+  return  it->index;
+}
+
+/// Move to next character
+void jsvStringIteratorNext(JsvStringIterator *it);
+
+/// Move to next character (this one is inlined where speed is needed)
+static inline void jsvStringIteratorNextInline(JsvStringIterator *it) {
+  it->charIdx++;
+  it->index++;
+  if (it->charIdx >= it->charsInVar) {
+    it->charIdx -= it->charsInVar;
+    if (it->var && it->var->lastChild) {
+      JsVar *next = jsvLock(it->var->lastChild);
+      jsvUnLock(it->var);
+      it->var = next;
+      it->charsInVar = jsvGetCharactersInVar(it->var);
+    } else {
+      jsvUnLock(it->var);
+      it->var = 0;
+      it->charsInVar = 0;
+    }
+  }
+}
+
+
+/// Go to the end of the string iterator - for use with jsvStringIteratorAppend
+void jsvStringIteratorGotoEnd(JsvStringIterator *it);
+
+/// Append a character TO THE END of a string iterator
+void jsvStringIteratorAppend(JsvStringIterator *it, char ch);
+
+static inline void jsvStringIteratorFree(JsvStringIterator *it) {
+  jsvUnLock(it->var);
+}
+
+// --------------------------------------------------------------------------------------------
+typedef struct JsArrayIterator {
+  JsVar *var;
+} JsArrayIterator;
+
+static inline void jsvArrayIteratorNew(JsArrayIterator *it, JsVar *arr) {
+  assert(jsvIsArray(arr));
+  it->var = arr->firstChild ? jsvLock(arr->firstChild) : 0;
+}
+
+/// Gets the current array element (or 0)
+static inline JsVar *jsvArrayIteratorGetElement(JsArrayIterator *it) {
+  if (!it->var) return 0; // end of array
+  return it->var->firstChild ? jsvLock(it->var->firstChild) : 0; // might even be undefined
+}
+
+/// Gets the current array element index (or 0)
+static inline JsVar *jsvArrayIteratorGetIndex(JsArrayIterator *it) {
+  if (!it->var) return 0;
+  return jsvLockAgain(it->var);
+}
+
+/// Do we have a character, or are we at the end?
+static inline bool jsvArrayIteratorHasElement(JsArrayIterator *it) {
+  return it->var != 0;
+}
+
+/// Move to next character
+static inline void jsvArrayIteratorNext(JsArrayIterator *it) {
+  if (it->var) {
+    JsVarRef next = it->var->nextSibling;
+    jsvUnLock(it->var);
+    it->var = next ? jsvLock(next) : 0;
+  }
+}
+
+static inline void jsvArrayIteratorFree(JsArrayIterator *it) {
+  jsvUnLock(it->var);
+}
+// --------------------------------------------------------------------------------------------
+typedef struct JsObjectIterator {
+  JsVar *var;
+} JsObjectIterator;
+
+static inline void jsvObjectIteratorNew(JsObjectIterator *it, JsVar *obj) {
+  assert(jsvIsObject(obj) || jsvIsFunction(obj));
+  it->var = obj->firstChild ? jsvLock(obj->firstChild) : 0;
+}
+
+/// Gets the current object element key (or 0)
+static inline JsVar *jsvObjectIteratorGetKey(JsObjectIterator *it) {
+  if (!it->var) return 0; // end of object
+  return jsvLockAgain(it->var);
+}
+
+/// Gets the current object element value (or 0)
+static inline JsVar *jsvObjectIteratorGetValue(JsObjectIterator *it) {
+  if (!it->var) return 0; // end of object
+  return it->var->firstChild ? jsvLock(it->var->firstChild) : 0; // might even be undefined
+}
+
+/// Do we have a key, or are we at the end?
+static inline bool jsvObjectIteratorHasElement(JsObjectIterator *it) {
+  return it->var != 0;
+}
+
+/// Move to next character
+static inline void jsvObjectIteratorNext(JsObjectIterator *it) {
+  if (it->var) {
+    JsVarRef next = it->var->nextSibling;
+    jsvUnLock(it->var);
+    it->var = next ? jsvLock(next) : 0;
+  }
+}
+
+static inline void jsvObjectIteratorFree(JsObjectIterator *it) {
+  jsvUnLock(it->var);
+}
+// --------------------------------------------------------------------------------------------
+typedef struct JsvArrayBufferIterator {
+  JsvStringIterator it;
+  JsVarDataArrayBufferViewType type;
+  JsVarInt byteLength;
+  JsVarInt byteOffset;
+  JsVarInt index;
+  bool hasAccessedElement;
+} JsvArrayBufferIterator;
+
+void   jsvArrayBufferIteratorNew(JsvArrayBufferIterator *it, JsVar *arrayBuffer, JsVarInt index);
+JsVar *jsvArrayBufferIteratorGetValue(JsvArrayBufferIterator *it);
+JsVarInt jsvArrayBufferIteratorGetIntegerValue(JsvArrayBufferIterator *it);
+JsVarFloat jsvArrayBufferIteratorGetFloatValue(JsvArrayBufferIterator *it);
+void   jsvArrayBufferIteratorSetValue(JsvArrayBufferIterator *it, JsVar *value);
+void   jsvArrayBufferIteratorSetIntegerValue(JsvArrayBufferIterator *it, JsVarInt value);
+JsVar* jsvArrayBufferIteratorGetIndex(JsvArrayBufferIterator *it);
+bool   jsvArrayBufferIteratorHasElement(JsvArrayBufferIterator *it);
+void   jsvArrayBufferIteratorNext(JsvArrayBufferIterator *it);
+void   jsvArrayBufferIteratorFree(JsvArrayBufferIterator *it);
+// --------------------------------------------------------------------------------------------
+union JsvIteratorUnion {
+  JsvStringIterator str;
+  JsObjectIterator obj;
+  JsArrayIterator arr;
+  JsvArrayBufferIterator buf;
+};
+
+/** General Purpose iterator, for Strings, Arrays, Objects, Typed Arrays */
+typedef struct JsvIterator {
+  enum {JSVI_STRING, JSVI_ARRAY, JSVI_OBJECT, JSVI_ARRAYBUFFER } type;
+  union JsvIteratorUnion it;
+} JsvIterator;
+
+void jsvIteratorNew(JsvIterator *it, JsVar *obj);
+JsVar *jsvIteratorGetKey(JsvIterator *it);
+JsVar *jsvIteratorGetValue(JsvIterator *it);
+JsVarInt jsvIteratorGetIntegerValue(JsvIterator *it);
+bool jsvIteratorHasElement(JsvIterator *it);
+void jsvIteratorNext(JsvIterator *it);
+void jsvIteratorFree(JsvIterator *it);
+
+
+
+#endif /* JSVAR_H_ */

+ 328 - 0
components/external/espruino/src/jswrap_array.c

@@ -0,0 +1,328 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * This file is designed to be parsed during the build process
+ *
+ * JavaScript methods for Arrays
+ * ----------------------------------------------------------------------------
+ */
+#include "jswrap_array.h"
+#include "jsparse.h"
+
+#define min(a,b) (((a)<(b))?(a):(b))
+#define max(a,b) (((a)>(b))?(a):(b))
+
+
+/*JSON{ "type":"class",
+        "class" : "Array",
+        "check" : "jsvIsArray(var)",
+        "description" : ["This is the built-in JavaScript class for arrays.",
+                         "Arrays can be defined with ```[]```, ```new Array()```, or ```new Array(length)```" ]
+}*/
+
+/*JSON{ "type":"constructor", "class": "Array",  "name": "Array",
+         "description" : "Create an Array. Either give it one integer argument (>=0) which is the length of the array, or any number of arguments ",
+         "generate" : "jswrap_array_constructor",
+         "params" : [ [ "args", "JsVarArray", "The length of the array OR any number of items to add to the array" ] ],
+         "return" : [ "JsVar", "An Array" ]
+
+}*/
+JsVar *jswrap_array_constructor(JsVar *args) {
+  assert(args);
+  if (jsvGetArrayLength(args)==1) {
+    JsVar *firstArg = jsvSkipNameAndUnLock(jsvArrayGetLast(args)); // also the first!
+    if (jsvIsInt(firstArg) && jsvGetInteger(firstArg)>=0) {
+      JsVarInt count = jsvGetInteger(firstArg);
+      // we cheat - no need to fill the array - just the last element
+      if (count>0) {
+        JsVar *arr = jsvNewWithFlags(JSV_ARRAY);
+        if (!arr) return 0; // out of memory
+        JsVar *idx = jsvMakeIntoVariableName(jsvNewFromInteger(count-1), 0);
+        if (idx) { // could be out of memory
+          jsvAddName(arr, idx);
+          jsvUnLock(idx);
+        }
+        jsvUnLock(firstArg);
+        return arr;
+      }
+    }
+    jsvUnLock(firstArg);
+  }
+  // Otherwise, we just return the array!
+  return jsvLockAgain(args);
+}
+
+/*JSON{ "type":"method", "class": "Array", "name" : "contains",
+         "description" : "Return true if this array contains the given value",
+         "generate" : "jswrap_array_contains",
+         "params" : [ [ "value", "JsVar", "The value to check for"] ],
+         "return" : ["bool", "Whether value is in the array or not"]
+}*/
+bool jswrap_array_contains(JsVar *parent, JsVar *value) {
+  // ArrayIndexOf will return 0 if not found
+  JsVar *arrElement = jsvGetArrayIndexOf(parent, value, false/*not exact*/);
+  bool contains = arrElement!=0;
+  jsvUnLock(arrElement);
+  return contains;
+}
+
+/*JSON{ "type":"method", "class": "Array", "name" : "indexOf",
+         "description" : "Return the index of the value in the array, or -1",
+         "generate" : "jswrap_array_indexOf",
+         "params" : [ [ "value", "JsVar", "The value to check for"] ],
+         "return" : ["JsVar", "the index of the value in the array, or -1"]
+}*/
+JsVar *jswrap_array_indexOf(JsVar *parent, JsVar *value) {
+  JsVar *idxName = jsvGetArrayIndexOf(parent, value, false/*not exact*/);
+  // but this is the name - we must turn it into a var
+  if (idxName == 0) return jsvNewFromInteger(-1); // not found!
+  JsVar *idx = jsvCopyNameOnly(idxName, false/* no children */, false/* Make sure this is not a name*/);
+  jsvUnLock(idxName);
+  return idx;
+}
+
+/*JSON{ "type":"method", "class": "Array", "name" : "join",
+         "description" : "Join all elements of this array together into one string, using 'separator' between them. eg. ```[1,2,3].join(' ')=='1 2 3'```",
+         "generate" : "jswrap_array_join",
+         "params" : [ [ "separator", "JsVar", "The separator"] ],
+         "return" : ["JsVar", "A String representing the Joined array"]
+}*/
+JsVar *jswrap_array_join(JsVar *parent, JsVar *filler) {
+  if (jsvIsUndefined(filler))
+    filler = jsvNewFromString(","); // the default it seems
+  else
+    filler = jsvAsString(filler, false);
+  if (!filler) return 0; // out of memory
+  JsVar *str = jsvArrayJoin(parent, filler);
+  jsvUnLock(filler);
+  return str;
+}
+
+/*JSON{ "type":"method", "class": "Array", "name" : "push",
+         "description" : "Push a new value onto the end of this array'",
+         "generate_full" : "jsvArrayPush(parent, value)",
+         "params" : [ [ "value", "JsVar", "The value to add"] ],
+         "return" : ["int", "The new size of the array"]
+}*/
+
+/*JSON{ "type":"method", "class": "Array", "name" : "pop",
+         "description" : "Pop a new value off of the end of this array",
+         "generate_full" : "jsvArrayPop(parent)",
+         "return" : ["JsVar", "The value that is popped off"]
+}*/
+
+JsVar *_jswrap_array_map_or_forEach(JsVar *parent, JsVar *funcVar, JsVar *thisVar, bool isMap) {
+  if (!jsvIsFunction(funcVar)) {
+    jsError("Array.map's first argument should be a function");
+    return 0;
+  }
+  if (!jsvIsUndefined(thisVar) && !jsvIsObject(thisVar)) {
+    jsError("Arraymap's second argument should be undefined, or an object");
+    return 0;
+  }
+  JsVar *array = 0;
+  if (isMap)
+    array = jsvNewWithFlags(JSV_ARRAY);
+  if (array || !isMap) {
+   JsVarRef childRef = parent->firstChild;
+   while (childRef) {
+     JsVar *child = jsvLock(childRef);
+     if (jsvIsInt(child)) {
+       JsVar *args[3], *mapped;
+       args[0] = jsvLock(child->firstChild);
+       // child is a variable name, create a new variable for the index
+       args[1] = jsvNewFromInteger(jsvGetInteger(child));
+       args[2] = parent;
+       mapped = jspeFunctionCall(funcVar, 0, thisVar, false, 3, args);
+       jsvUnLock(args[0]);
+       jsvUnLock(args[1]);
+       if (mapped) {
+         if (isMap) {
+           JsVar *name = jsvCopyNameOnly(child, false/*linkChildren*/, true/*keepAsName*/);
+           if (name) { // out of memory?
+             name->firstChild = jsvGetRef(jsvRef(mapped));
+             jsvAddName(array, name);
+             jsvUnLock(name);
+           }
+         }
+         jsvUnLock(mapped);
+       }
+     }
+     childRef = child->nextSibling;
+     jsvUnLock(child);
+   }
+  }
+  return array;
+}
+
+/*JSON{ "type":"method", "class": "Array", "name" : "map",
+         "description" : "Return an array which is made from the following: ```A.map(function) = [function(A[0]), function(A[1]), ...]```",
+         "generate" : "jswrap_array_map",
+         "params" : [ [ "function", "JsVar", "Function used to map one item to another"] ,
+                      [ "thisArg", "JsVar", "if specified, the function is called with 'this' set to thisArg (optional)"] ],
+         "return" : ["JsVar", "The value that is popped off"]
+}*/
+JsVar *jswrap_array_map(JsVar *parent, JsVar *funcVar, JsVar *thisVar) {
+  return _jswrap_array_map_or_forEach(parent, funcVar, thisVar, true);
+}
+
+
+/*JSON{ "type":"method", "class": "Array", "name" : "splice",
+         "description" : "Both remove and add items to an array",
+         "generate" : "jswrap_array_splice",
+         "params" : [ [ "index", "int", "Index at which to start changing the array. If negative, will begin that many elements from the end"],
+                      [ "howMany", "JsVar", "An integer indicating the number of old array elements to remove. If howMany is 0, no elements are removed."],
+                      [ "element1", "JsVar", "A new item to add (optional)" ],
+                      [ "element2", "JsVar", "A new item to add (optional)" ],
+                      [ "element3", "JsVar", "A new item to add (optional)" ],
+                      [ "element4", "JsVar", "A new item to add (optional)" ],
+                      [ "element5", "JsVar", "A new item to add (optional)" ],
+                      [ "element6", "JsVar", "A new item to add (optional)" ] ],
+         "return" : ["JsVar", "An array containing the removed elements. If only one element is removed, an array of one element is returned."]
+}*/
+JsVar *jswrap_array_splice(JsVar *parent, JsVarInt index, JsVar *howManyVar, JsVar *element1, JsVar *element2, JsVar *element3, JsVar *element4, JsVar *element5, JsVar *element6) {
+  JsVarInt len = jsvGetArrayLength(parent);
+  if (index<0) index+=len;
+  if (index<0) index=0;
+  if (index>len) index=len;
+  JsVarInt howMany = len; // how many to delete!
+  if (jsvIsInt(howManyVar)) howMany = jsvGetInteger(howManyVar);
+  if (howMany > len-index) howMany = len-index;
+  JsVarInt newItems = 0;
+  if (element1) newItems++;
+  if (element2) newItems++;
+  if (element3) newItems++;
+  if (element4) newItems++;
+  if (element5) newItems++;
+  if (element6) newItems++;
+  JsVarInt shift = newItems-howMany;
+
+  bool needToAdd = false;
+  JsVar *result = jsvNewWithFlags(JSV_ARRAY);
+
+  JsArrayIterator it;
+  jsvArrayIteratorNew(&it, parent);
+  while (jsvArrayIteratorHasElement(&it) && !needToAdd) {
+    bool goToNext = true;
+    JsVar *idxVar = jsvArrayIteratorGetIndex(&it);
+    if (idxVar && jsvIsInt(idxVar)) {
+      JsVarInt idx = jsvGetInteger(idxVar);
+      if (idx<index) {
+        // do nothing...
+      } else if (idx<index+howMany) { // must delete
+        if (result) { // append to result array
+          JsVar *el = jsvArrayIteratorGetElement(&it);
+          jsvArrayPushAndUnLock(result, el);
+        }
+        // delete
+        goToNext = false;
+        JsVar *toRemove = jsvArrayIteratorGetIndex(&it);
+        jsvArrayIteratorNext(&it);
+        jsvRemoveChild(parent, toRemove);
+        jsvUnLock(toRemove);
+      } else { // we're greater than the amount we need to remove now
+        needToAdd = true;
+        goToNext = false;
+      }
+    }
+    jsvUnLock(idxVar);
+    if (goToNext) jsvArrayIteratorNext(&it);
+  }
+  // now we add everything
+  JsVar *beforeIndex = jsvArrayIteratorGetIndex(&it);
+  if (element1) jsvArrayInsertBefore(parent, beforeIndex, element1);
+  if (element2) jsvArrayInsertBefore(parent, beforeIndex, element2);
+  if (element3) jsvArrayInsertBefore(parent, beforeIndex, element3);
+  if (element4) jsvArrayInsertBefore(parent, beforeIndex, element4);
+  if (element5) jsvArrayInsertBefore(parent, beforeIndex, element5);
+  if (element6) jsvArrayInsertBefore(parent, beforeIndex, element6);
+  jsvUnLock(beforeIndex);
+  // And finally renumber
+  while (jsvArrayIteratorHasElement(&it)) {
+      JsVar *idxVar = jsvArrayIteratorGetIndex(&it);
+      if (idxVar && jsvIsInt(idxVar)) {
+        jsvSetInteger(idxVar, jsvGetInteger(idxVar)+shift);
+      }
+      jsvUnLock(idxVar);
+      jsvArrayIteratorNext(&it);
+    }
+  // free
+  jsvArrayIteratorFree(&it);
+
+  return result;
+}
+
+
+/*JSON{ "type":"method", "class": "Array", "name" : "slice",
+         "description" : "Return a copy of a portion of the calling array",
+         "generate" : "jswrap_array_slice",
+         "params" : [ [ "start", "JsVar", "Start index"],
+                      [ "end", "JsVar", "End index (optional)"] ],
+         "return" : ["JsVar", "A new array"]
+}*/
+JsVar *jswrap_array_slice(JsVar *parent, JsVar *startVar, JsVar *endVar) {
+  JsVarInt len = jsvGetArrayLength(parent);
+  JsVarInt start = 0;
+  JsVarInt end = len;
+
+  if (!jsvIsUndefined(startVar))
+    start = jsvGetInteger(startVar);
+
+  if (!jsvIsUndefined(endVar))
+    end = jsvGetInteger(endVar);
+
+  JsVarInt k = 0;
+  JsVarInt final = len;
+  JsVar *array = jsvNewWithFlags(JSV_ARRAY);
+
+  if (!array) return 0;
+
+  if (start<0) k = max((len + start), 0);
+  else k = min(start, len);
+
+  if (end<0) final = max((len + end), 0);
+  else final = min(end, len);
+
+  bool isDone = false;
+
+  JsArrayIterator it;
+  jsvArrayIteratorNew(&it, parent);
+
+  while (jsvArrayIteratorHasElement(&it) && !isDone) {
+    JsVarInt idx = jsvGetInteger(jsvArrayIteratorGetIndex(&it));
+
+    if (idx < k) {
+      jsvArrayIteratorNext(&it);
+    } else {
+      if (k < final) {
+        jsvArrayPushAndUnLock(array, jsvArrayIteratorGetElement(&it));
+        jsvArrayIteratorNext(&it);
+        k++;
+      } else {
+        isDone = true;
+      }
+    }
+  }
+
+  jsvArrayIteratorFree(&it);
+
+  return array;
+}
+
+
+/*JSON{ "type":"method", "class": "Array", "name" : "forEach",
+         "description" : "Executes a provided function once per array element.",
+         "generate" : "jswrap_array_forEach",
+         "params" : [ [ "function", "JsVar", "Function to be executed"] ,
+                      [ "thisArg", "JsVar", "if specified, the function is called with 'this' set to thisArg (optional)"] ]
+}*/
+void jswrap_array_forEach(JsVar *parent, JsVar *funcVar, JsVar *thisVar) {
+  _jswrap_array_map_or_forEach(parent, funcVar, thisVar, false);
+}

+ 23 - 0
components/external/espruino/src/jswrap_array.h

@@ -0,0 +1,23 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * JavaScript methods for Arrays
+ * ----------------------------------------------------------------------------
+ */
+#include "jsvar.h"
+
+JsVar *jswrap_array_constructor(JsVar *args);
+bool jswrap_array_contains(JsVar *parent, JsVar *value);
+JsVar *jswrap_array_indexOf(JsVar *parent, JsVar *value);
+JsVar *jswrap_array_join(JsVar *parent, JsVar *filler);
+JsVar *jswrap_array_map(JsVar *parent, JsVar *funcVar, JsVar *thisVar);
+JsVar *jswrap_array_slice(JsVar *parent, JsVar *startVar, JsVar *endVar);
+JsVar *jswrap_array_splice(JsVar *parent, JsVarInt index, JsVar *howManyVar, JsVar *element1, JsVar *element2, JsVar *element3, JsVar *element4, JsVar *element5, JsVar *element6);
+void jswrap_array_forEach(JsVar *parent, JsVar *funcVar, JsVar *thisVar);

+ 315 - 0
components/external/espruino/src/jswrap_arraybuffer.c

@@ -0,0 +1,315 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * This file is designed to be parsed during the build process
+ *
+ * JavaScript methods for ArrayBuffers
+ * ----------------------------------------------------------------------------
+ */
+#include "jswrap_arraybuffer.h"
+#include "jsparse.h"
+#include "jsinteractive.h"
+
+/*JSON{ "type":"class",
+        "class" : "ArrayBuffer",
+        "check" : "jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_ARRAYBUFFER",
+        "not_real_object" : "Don't treat this as a real object - it's handled differently internally",
+        "description" : ["This is the built-in JavaScript class for array buffers." ]
+}*/
+
+/*JSON{ "type":"class",
+        "class" : "ArrayBufferView",
+        "description" : ["This is the built-in JavaScript class that is the prototype for Uint8Array / Float32Array / etc" ]
+}*/
+
+/*JSON{ "type":"class",
+        "class" : "Uint8Array", "prototype" : "ArrayBufferView",
+        "check" : "jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_UINT8",
+        "not_real_object" : "Don't treat this as a real object - it's handled differently internally",
+        "description" : ["This is the built-in JavaScript class for a typed array.",
+                         "Instantiate this in order to efficiently store arrays of data (Espruino's normal arrays store data in a map, which is inefficient for non-sparse arrays). " ]
+}*/
+/*JSON{ "type":"class",
+        "class" : "Int8Array", "prototype" : "ArrayBufferView",
+        "check" : "jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_INT8",
+        "not_real_object" : "Don't treat this as a real object - it's handled differently internally",
+        "description" : ["This is the built-in JavaScript class for a typed array.",
+                         "Instantiate this in order to efficiently store arrays of data (Espruino's normal arrays store data in a map, which is inefficient for non-sparse arrays). " ]
+}*/
+/*JSON{ "type":"class",
+        "class" : "Uint16Array", "prototype" : "ArrayBufferView",
+        "check" : "jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_UINT16",
+        "not_real_object" : "Don't treat this as a real object - it's handled differently internally",
+        "description" : ["This is the built-in JavaScript class for a typed array.",
+                         "Instantiate this in order to efficiently store arrays of data (Espruino's normal arrays store data in a map, which is inefficient for non-sparse arrays). " ]
+}*/
+/*JSON{ "type":"class",
+        "class" : "Int16Array", "prototype" : "ArrayBufferView",
+        "check" : "jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_INT16",
+        "not_real_object" : "Don't treat this as a real object - it's handled differently internally",
+        "description" : ["This is the built-in JavaScript class for a typed array.",
+                         "Instantiate this in order to efficiently store arrays of data (Espruino's normal arrays store data in a map, which is inefficient for non-sparse arrays). " ]
+}*/
+/*JSON{ "type":"class",
+        "class" : "Uint32Array", "prototype" : "ArrayBufferView",
+        "check" : "jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_UINT32",
+        "not_real_object" : "Don't treat this as a real object - it's handled differently internally",
+        "description" : ["This is the built-in JavaScript class for a typed array.",
+                         "Instantiate this in order to efficiently store arrays of data (Espruino's normal arrays store data in a map, which is inefficient for non-sparse arrays). " ]
+}*/
+/*JSON{ "type":"class",
+        "class" : "Int32Array", "prototype" : "ArrayBufferView",
+        "check" : "jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_INT32",
+        "not_real_object" : "Don't treat this as a real object - it's handled differently internally",
+        "description" : ["This is the built-in JavaScript class for a typed array.",
+                         "Instantiate this in order to efficiently store arrays of data (Espruino's normal arrays store data in a map, which is inefficient for non-sparse arrays). " ]
+}*/
+/*JSON{ "type":"class",
+        "class" : "Float32Array", "prototype" : "ArrayBufferView",
+        "check" : "jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_FLOAT32",
+        "not_real_object" : "Don't treat this as a real object - it's handled differently internally",
+        "description" : ["This is the built-in JavaScript class for a typed array.",
+                         "Instantiate this in order to efficiently store arrays of data (Espruino's normal arrays store data in a map, which is inefficient for non-sparse arrays). " ]
+}*/
+/*JSON{ "type":"class",
+        "class" : "Float64Array", "prototype" : "ArrayBufferView",
+        "check" : "jsvIsArrayBuffer(var) && var->varData.arraybuffer.type==ARRAYBUFFERVIEW_FLOAT64",
+        "not_real_object" : "Don't treat this as a real object - it's handled differently internally",
+        "description" : ["This is the built-in JavaScript class for a typed array.",
+                         "Instantiate this in order to efficiently store arrays of data (Espruino's normal arrays store data in a map, which is inefficient for non-sparse arrays). " ]
+}*/
+
+
+/*JSON{ "type":"constructor", "class": "ArrayBuffer",  "name": "ArrayBuffer",
+         "description" : "Create an Array Buffer object",
+         "generate" : "jswrap_arraybuffer_constructor",
+         "params" : [ [ "byteLength", "int", "The length in Bytes" ] ],
+         "return" : [ "JsVar", "An ArrayBuffer object" ]
+
+}*/
+JsVar *jswrap_arraybuffer_constructor(JsVarInt byteLength) {
+  if (byteLength <= 0 || byteLength>65535) {
+    jsError("Invalid length for ArrayBuffer\n");
+    return 0;
+  }
+  if (byteLength > JSV_ARRAYBUFFER_MAX_LENGTH) {
+    jsError("ArrayBuffer too long\n");
+    return 0;
+  }
+  JsVar *arrData = jsvNewStringOfLength((unsigned int)byteLength);
+  if (!arrData) return 0;
+  JsVar *arr = jsvNewWithFlags(JSV_ARRAYBUFFER);
+  if (!arr) {
+    jsvUnLock(arrData);
+    return 0;
+  }
+  arr->firstChild = jsvGetRef(jsvRef(arrData));
+  jsvUnLock(arrData);
+  arr->varData.arraybuffer.type = ARRAYBUFFERVIEW_ARRAYBUFFER;
+  arr->varData.arraybuffer.byteOffset = 0;
+  arr->varData.arraybuffer.length = (unsigned short)byteLength;
+  return arr;
+}
+
+
+/*
+ * Potential invocations:
+ * Uint8Array Uint8Array(unsigned long length);
+ * Uint8Array Uint8Array(TypedArray array);
+ * Uint8Array Uint8Array(sequence<type> array);
+ * Uint8Array Uint8Array(ArrayBuffer buffer, optional unsigned long byteOffset, optional unsigned long length);
+ */
+
+/*JSON{ "type":"constructor", "class": "Uint8Array",  "name": "Uint8Array",
+         "description" : "Create a typed array based on the given input. Either an existing Array Buffer, an Integer as a Length, or a simple array",
+         "generate_full" : "jswrap_typedarray_constructor(ARRAYBUFFERVIEW_UINT8, arr, jsvGetInteger(byteOffset), jsvGetInteger(length))",
+         "params" : [ [ "arr", "JsVar", "The array or typed array to base this off, or an integer which is the array length" ],
+                      [ "byteOffset", "int", "The byte offset in the ArrayBuffer  (ONLY IF the first argument was an ArrayBuffer)" ],
+                      [ "length", "int", "The length (ONLY IF the first argument was an ArrayBuffer)" ] ],
+         "return" : [ "JsVar", "A typed array" ]
+}*/
+/*JSON{ "type":"constructor", "class": "Int8Array",  "name": "Int8Array",
+         "description" : "Create a typed array based on the given input. Either an existing Array Buffer, an Integer as a Length, or a simple array",
+         "generate_full" : "jswrap_typedarray_constructor(ARRAYBUFFERVIEW_INT8, arr, jsvGetInteger(byteOffset), jsvGetInteger(length))",
+         "params" : [ [ "arr", "JsVar", "The array or typed array to base this off, or an integer which is the array length" ],
+                      [ "byteOffset", "int", "The byte offset in the ArrayBuffer  (ONLY IF the first argument was an ArrayBuffer)" ],
+                      [ "length", "int", "The length (ONLY IF the first argument was an ArrayBuffer)" ] ],
+         "return" : [ "JsVar", "A typed array" ]
+}*/
+/*JSON{ "type":"constructor", "class": "Uint16Array",  "name": "Uint16Array",
+         "description" : "Create a typed array based on the given input. Either an existing Array Buffer, an Integer as a Length, or a simple array",
+         "generate_full" : "jswrap_typedarray_constructor(ARRAYBUFFERVIEW_UINT16, arr, jsvGetInteger(byteOffset), jsvGetInteger(length))",
+         "params" : [ [ "arr", "JsVar", "The array or typed array to base this off, or an integer which is the array length" ],
+                      [ "byteOffset", "int", "The byte offset in the ArrayBuffer  (ONLY IF the first argument was an ArrayBuffer)" ],
+                      [ "length", "int", "The length (ONLY IF the first argument was an ArrayBuffer)" ] ],
+         "return" : [ "JsVar", "A typed array" ]
+}*/
+/*JSON{ "type":"constructor", "class": "Int16Array",  "name": "Int16Array",
+         "description" : "Create a typed array based on the given input. Either an existing Array Buffer, an Integer as a Length, or a simple array",
+         "generate_full" : "jswrap_typedarray_constructor(ARRAYBUFFERVIEW_INT16, arr, jsvGetInteger(byteOffset), jsvGetInteger(length))",
+         "params" : [ [ "arr", "JsVar", "The array or typed array to base this off, or an integer which is the array length" ],
+                      [ "byteOffset", "int", "The byte offset in the ArrayBuffer  (ONLY IF the first argument was an ArrayBuffer)" ],
+                      [ "length", "int", "The length (ONLY IF the first argument was an ArrayBuffer)" ] ],
+         "return" : [ "JsVar", "A typed array" ]
+}*/
+/*JSON{ "type":"constructor", "class": "Uint32Array",  "name": "Uint32Array",
+         "description" : "Create a typed array based on the given input. Either an existing Array Buffer, an Integer as a Length, or a simple array",
+         "generate_full" : "jswrap_typedarray_constructor(ARRAYBUFFERVIEW_UINT32, arr, jsvGetInteger(byteOffset), jsvGetInteger(length))",
+         "params" : [ [ "arr", "JsVar", "The array or typed array to base this off, or an integer which is the array length" ],
+                      [ "byteOffset", "int", "The byte offset in the ArrayBuffer  (ONLY IF the first argument was an ArrayBuffer)" ],
+                      [ "length", "int", "The length (ONLY IF the first argument was an ArrayBuffer)" ] ],
+         "return" : [ "JsVar", "A typed array" ]
+}*/
+/*JSON{ "type":"constructor", "class": "Int32Array",  "name": "Int32Array",
+         "description" : "Create a typed array based on the given input. Either an existing Array Buffer, an Integer as a Length, or a simple array",
+         "generate_full" : "jswrap_typedarray_constructor(ARRAYBUFFERVIEW_INT32, arr, jsvGetInteger(byteOffset), jsvGetInteger(length))",
+         "params" : [ [ "arr", "JsVar", "The array or typed array to base this off, or an integer which is the array length" ],
+                      [ "byteOffset", "int", "The byte offset in the ArrayBuffer  (ONLY IF the first argument was an ArrayBuffer)" ],
+                      [ "length", "int", "The length (ONLY IF the first argument was an ArrayBuffer)" ] ],
+         "return" : [ "JsVar", "A typed array" ]
+}*/
+/*JSON{ "type":"constructor", "class": "Float32Array",  "name": "Float32Array",
+         "description" : "Create a typed array based on the given input. Either an existing Array Buffer, an Integer as a Length, or a simple array",
+         "generate_full" : "jswrap_typedarray_constructor(ARRAYBUFFERVIEW_FLOAT32, arr, jsvGetInteger(byteOffset), jsvGetInteger(length))",
+         "params" : [ [ "arr", "JsVar", "The array or typed array to base this off, or an integer which is the array length" ],
+                      [ "byteOffset", "int", "The byte offset in the ArrayBuffer  (ONLY IF the first argument was an ArrayBuffer)" ],
+                      [ "length", "int", "The length (ONLY IF the first argument was an ArrayBuffer)" ] ],
+         "return" : [ "JsVar", "A typed array" ]
+}*/
+/*JSON{ "type":"constructor", "class": "Float64Array",  "name": "Float64Array",
+         "description" : "Create a typed array based on the given input. Either an existing Array Buffer, an Integer as a Length, or a simple array",
+         "generate_full" : "jswrap_typedarray_constructor(ARRAYBUFFERVIEW_FLOAT64, arr, jsvGetInteger(byteOffset), jsvGetInteger(length))",
+         "params" : [ [ "arr", "JsVar", "The array or typed array to base this off, or an integer which is the array length" ],
+                      [ "byteOffset", "int", "The byte offset in the ArrayBuffer  (ONLY IF the first argument was an ArrayBuffer)" ],
+                      [ "length", "int", "The length (ONLY IF the first argument was an ArrayBuffer)" ] ],
+         "return" : [ "JsVar", "A typed array" ]
+}*/
+
+
+
+JsVar *jswrap_typedarray_constructor(JsVarDataArrayBufferViewType type, JsVar *arr, JsVarInt byteOffset, JsVarInt length) {
+  JsVar *arrayBuffer = 0;
+  if (jsvIsArrayBuffer(arr)) {
+    arrayBuffer = jsvLockAgain(arr);
+  } else if (jsvIsInt(arr)) {
+    length = jsvGetInteger(arr);
+    byteOffset = 0;
+    arrayBuffer = jswrap_arraybuffer_constructor(JSV_ARRAYBUFFER_GET_SIZE(type)*length);
+  } else if (jsvIsArray(arr)) {
+    length = jsvGetArrayLength(arr);
+    byteOffset = 0;
+    arrayBuffer = jswrap_arraybuffer_constructor(JSV_ARRAYBUFFER_GET_SIZE(type)*length);
+    // later on we'll populate this
+  }
+  if (!arrayBuffer) {
+    jsError("Unsupported first argument\n");
+    return 0;
+  }
+  if (length<=0) length = (JsVarInt)jsvGetArrayBufferLength(arrayBuffer) / JSV_ARRAYBUFFER_GET_SIZE(type);
+  JsVar *typedArr = jsvNewWithFlags(JSV_ARRAYBUFFER);
+  if (typedArr) {
+    typedArr->varData.arraybuffer.type = type;
+    typedArr->varData.arraybuffer.byteOffset = (unsigned short)byteOffset;
+    typedArr->varData.arraybuffer.length = (unsigned short)length;
+    typedArr->firstChild = jsvGetRef(jsvRef(arrayBuffer));
+
+    if (jsvIsArray(arr)) {
+      // if we were given an array, populate this ArrayBuffer
+      JsArrayIterator it;
+      jsvArrayIteratorNew(&it, arr);
+      while (jsvArrayIteratorHasElement(&it)) {
+        JsVar *idx = jsvArrayIteratorGetIndex(&it);
+        if (jsvIsInt(idx)) {
+          JsVar *val = jsvArrayIteratorGetElement(&it);
+          jsvArrayBufferSet(typedArr, jsvGetInteger(idx), val);
+          jsvUnLock(val);
+        }
+        jsvUnLock(idx);
+        jsvArrayIteratorNext(&it);
+      }
+      jsvArrayIteratorFree(&it);
+    }
+  }
+  jsvUnLock(arrayBuffer);
+  return typedArr;
+}
+
+
+/*JSON{ "type":"property", "class": "ArrayBufferView",  "name": "buffer",
+         "description" : "The buffer this view references",
+         "generate_full" : "jsvLock(parent->firstChild)",
+         "return" : [ "JsVar", "An ArrayBuffer object" ]
+}*/
+/*JSON{ "type":"property", "class": "ArrayBufferView",  "name": "byteLength",
+         "description" : "The length, in bytes, of the view",
+         "generate_full" : "parent->varData.arraybuffer.length * JSV_ARRAYBUFFER_GET_SIZE(parent->varData.arraybuffer.type)",
+         "return" : [ "int", "The Length" ]
+}*/
+/*JSON{ "type":"property", "class": "ArrayBufferView",  "name": "byteOffset",
+         "description" : "The offset, in bytes, to the first byte of the view within the ArrayBuffer",
+         "generate_full" : "parent->varData.arraybuffer.byteOffset",
+         "return" : [ "int", "The byte Offset" ]
+}*/
+
+/*JSON{ "type":"method", "class": "ArrayBufferView",  "name": "interpolate",
+         "description" : "Interpolate between two adjacent values in the Typed Array",
+         "generate" : "jswrap_arraybufferview_interpolate",
+         "params" : [ [ "index", "float", "Floating point index to access" ] ],
+         "return" : [ "float", "The result of interpolating between (int)index and (int)(index+1)" ]
+}*/
+JsVarFloat jswrap_arraybufferview_interpolate(JsVar *parent, JsVarFloat findex) {
+  int idx = (int)findex;
+  JsVarFloat a = findex-idx;
+  JsvArrayBufferIterator it;
+  jsvArrayBufferIteratorNew(&it, parent, idx);
+  JsVarFloat fa = jsvArrayBufferIteratorGetFloatValue(&it);
+  jsvArrayBufferIteratorNext(&it);
+  JsVarFloat fb = jsvArrayBufferIteratorGetFloatValue(&it);
+  jsvArrayBufferIteratorFree(&it);
+  return fa*(1-a) + fb*a;
+} 
+
+/*JSON{ "type":"method", "class": "ArrayBufferView",  "name": "interpolate2d",
+         "description" : "Interpolate between two adjacent values in the Typed Array",
+         "generate" : "jswrap_arraybufferview_interpolate2d",
+         "params" : [ [ "width", "int", "Integer 'width' of 2d array" ],
+                      [ "x", "float", "Floating point X index to access" ],
+                      [ "y", "float", "Floating point Y index to access" ] ],
+         "return" : [ "float", "The result of interpolating in 2d between the 4 surrounding cells" ]
+}*/
+JsVarFloat jswrap_arraybufferview_interpolate2d(JsVar *parent, JsVarInt width, JsVarFloat x, JsVarFloat y) {
+  int yidx = (int)y;
+  JsVarFloat ay = y-yidx;
+
+  JsVarFloat findex = x + (JsVarFloat)(yidx*width);
+  int idx = (int)findex;
+  JsVarFloat ax = findex-idx;
+
+  JsvArrayBufferIterator it;
+  jsvArrayBufferIteratorNew(&it, parent, idx);
+
+  JsVarFloat xa,xb;
+  int i;
+
+  xa = jsvArrayBufferIteratorGetFloatValue(&it);
+  jsvArrayBufferIteratorNext(&it);
+  xb = jsvArrayBufferIteratorGetFloatValue(&it);
+  JsVarFloat ya = xa*(1-ax) + xb*ax;
+
+  for (i=1;i<width;i++) jsvArrayBufferIteratorNext(&it);
+
+  xa = jsvArrayBufferIteratorGetFloatValue(&it);
+  jsvArrayBufferIteratorNext(&it);
+  xb = jsvArrayBufferIteratorGetFloatValue(&it);
+  jsvArrayBufferIteratorFree(&it);
+  JsVarFloat yb = xa*(1-ax) + xb*ax;
+
+  return ya*(1-ay) + yb*ay;
+}

+ 19 - 0
components/external/espruino/src/jswrap_arraybuffer.h

@@ -0,0 +1,19 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * JavaScript methods and functions in the global namespace
+ * ----------------------------------------------------------------------------
+ */
+#include "jsvar.h"
+
+JsVar *jswrap_arraybuffer_constructor(JsVarInt byteLength);
+JsVar *jswrap_typedarray_constructor(JsVarDataArrayBufferViewType type, JsVar *arr, JsVarInt byteOffset, JsVarInt length);
+JsVarFloat jswrap_arraybufferview_interpolate(JsVar *parent, JsVarFloat index);
+JsVarFloat jswrap_arraybufferview_interpolate2d(JsVar *parent, JsVarInt width, JsVarFloat x, JsVarFloat y);

+ 102 - 0
components/external/espruino/src/jswrap_functions.c

@@ -0,0 +1,102 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * This file is designed to be parsed during the build process
+ *
+ * JavaScript methods and functions in the global namespace
+ * ----------------------------------------------------------------------------
+ */
+#include "jswrap_functions.h"
+#include "jslex.h"
+#include "jsparse.h"
+#include "jsinteractive.h"
+
+/*JSON{ "type":"variable", "name" : "arguments",
+         "description" : "A variable containing the arguments given to the function",
+         "generate" : "jswrap_arguments",
+         "return" : ["JsVar", "An array containing all the arguments given to the function"]
+}*/
+extern JsExecInfo execInfo;
+JsVar *jswrap_arguments() {
+  JsVar *scope = 0;
+  if (execInfo.scopeCount>0)
+    scope = jsvLock(execInfo.scopes[execInfo.scopeCount-1]);
+  if (!jsvIsFunction(scope)) {
+    jsvUnLock(scope);
+    jsError("Can only use 'arguments' variable inside a function");
+    return 0;
+  }
+
+  JsVar *args = jsvNewWithFlags(JSV_ARRAY);
+  if (!args) return 0; // out of memory
+
+  JsObjectIterator it;
+  jsvObjectIteratorNew(&it, scope);
+  while (jsvObjectIteratorHasElement(&it)) {
+    JsVar *idx = jsvObjectIteratorGetKey(&it);
+    if (jsvIsFunctionParameter(idx)) {
+      JsVar *val = jsvSkipOneName(idx);
+      jsvArrayPushAndUnLock(args, val);
+    }
+    jsvUnLock(idx);
+    jsvObjectIteratorNext(&it);
+  }
+  jsvObjectIteratorFree(&it);
+  jsvUnLock(scope);
+
+  return args;
+}
+
+
+/*JSON{ "type":"function", "name" : "eval",
+         "description" : "Evaluate a string containing JavaScript code",
+         "generate" : "jswrap_eval",
+         "params" : [ [ "code", "JsVar", ""] ],
+         "return" : ["JsVar", "The result of evaluating the string"]
+}*/
+JsVar *jswrap_eval(JsVar *v) {
+  if (!v) return 0;
+  JsVar *s = jsvAsString(v, false); // get as a string
+  JsVar *result = jspEvaluateVar(jsiGetParser(), s, 0);
+  jsvUnLock(s);
+  return result;
+}
+
+/*JSON{ "type":"function", "name" : "parseInt",
+         "description" : "Convert a string representing a number into an integer",
+         "generate" : "jswrap_parseInt",
+         "params" :  [ [ "string", "JsVar", ""],
+                       [ "radix", "JsVar", "The Radix of the string (optional)"] ],
+         "return" : ["JsVar", "The integer value of the string (or NaN)"]
+}*/
+JsVar *jswrap_parseInt(JsVar *v, JsVar *radixVar) {
+  int radix = 0/*don't force radix*/;
+  if (jsvIsNumeric(radixVar))
+    radix = (int)jsvGetInteger(radixVar);
+
+  char buffer[JS_NUMBER_BUFFER_SIZE];
+  jsvGetString(v, buffer, JS_NUMBER_BUFFER_SIZE);
+  bool hasError;
+  JsVarInt i = stringToIntWithRadix(buffer, radix, &hasError);
+  if (hasError) return jsvNewFromFloat(NAN);
+  return jsvNewFromInteger(i);
+}
+
+/*JSON{ "type":"function", "name" : "parseFloat",
+         "description" : "Convert a string representing a number into an float",
+         "generate" : "jswrap_parseFloat",
+         "params" :  [ [ "string", "JsVar", ""] ],
+         "return" : ["float", "The value of the string"]
+}*/
+JsVarFloat jswrap_parseFloat(JsVar *v) {
+  char buffer[JS_NUMBER_BUFFER_SIZE];
+  jsvGetString(v, buffer, JS_NUMBER_BUFFER_SIZE);
+  return stringToFloat(buffer);
+}

+ 19 - 0
components/external/espruino/src/jswrap_functions.h

@@ -0,0 +1,19 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * JavaScript methods and functions in the global namespace
+ * ----------------------------------------------------------------------------
+ */
+#include "jsvar.h"
+
+JsVar *jswrap_arguments();
+JsVar *jswrap_eval(JsVar *v);
+JsVar *jswrap_parseInt(JsVar *v, JsVar *radixVar);
+JsVarFloat jswrap_parseFloat(JsVar *v);

+ 203 - 0
components/external/espruino/src/jswrap_interactive.c

@@ -0,0 +1,203 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * This file is designed to be parsed during the build process
+ *
+ * JavaScript methods and functions for the interactive shell
+ * ----------------------------------------------------------------------------
+ */
+#include "jswrap_interactive.h"
+#include "jsinteractive.h"
+
+/*JSON{ "type":"function", "name" : "setBusyIndicator",
+         "description" : "When Espruino is busy, set the pin specified here high. Set this to undefined to disable the feature.",
+         "generate" : "jswrap_interface_setBusyIndicator",
+         "params" : [ [ "pin", "JsVar", ""] ]
+}*/
+void jswrap_interface_setBusyIndicator(JsVar *pinVar) {
+  Pin oldPin = pinBusyIndicator;
+  pinBusyIndicator = jshGetPinFromVar(pinVar);
+  // we should be busy right now anyway, so set stuff up right
+  if (pinBusyIndicator!=oldPin) {
+    if (oldPin!=PIN_UNDEFINED) jshPinOutput(oldPin, 0);
+    if (pinBusyIndicator!=PIN_UNDEFINED) jshPinOutput(pinBusyIndicator, 1);
+  }
+}
+
+/*JSON{ "type":"function", "name" : "setSleepIndicator",
+         "description" : "When Espruino is asleep, set the pin specified here high. Set this to undefined to disable the feature.",
+         "generate" : "jswrap_interface_setSleepIndicator",
+         "params" : [ [ "pin", "JsVar", ""] ]
+}*/
+void jswrap_interface_setSleepIndicator(JsVar *pinVar) {
+  pinSleepIndicator = jshGetPinFromVar(pinVar);
+}
+
+/*JSON{ "type":"function", "name" : "setDeepSleep",
+         "description" : [ "Set whether we can enter deep sleep mode, which reduces power consumption to around 1mA. This only works on the Espruino Board.",
+                           "Deep Sleep is currently beta. Espruino will only enter Deep Sleep when there are no timers and it is not connected to USB. USB will not wake Espruino from Deep Sleep, nor will Serial comms (only setWatch will wake it). The System Timer will also pause." ],
+         "generate" : "jswrap_interface_setDeepSleep",
+         "params" : [ [ "sleep", "bool", ""] ]
+}*/
+void jswrap_interface_setDeepSleep(bool sleep) {
+  allowDeepSleep = sleep;
+}
+
+
+/*JSON{ "type":"function", "name" : "trace", "ifndef" : "SAVE_ON_FLASH",
+         "description" : "Output debugging information",
+         "generate" : "jswrap_interface_trace",
+         "params" : [ [ "root", "JsVarName", "The symbol to output (optional). If nothing is specified, everything will be output"] ]
+}*/
+void jswrap_interface_trace(JsVar *root) {
+  if (jsvIsUndefined(root)) {
+    jsvTrace(jsvGetRef(jsiGetParser()->root), 0);
+  } else {
+    jsvTrace(jsvGetRef(root), 0);
+  }
+}
+
+/*XXX{ "type":"function", "name" : "dotty",
+         "description" : "Output dotty-format graph of debugging information",
+         "generate_full" : "jsvDottyOutput()"
+}*/
+/*JSON{ "type":"function", "name" : "dump",
+         "description" : ["Output current interpreter state in a text form such that it can be copied to a new device",
+                          "Note: 'Internal' functions are currently not handled correctly. You will need to recreate these in the onInit function."],
+         "generate_full" : "jsiDumpState()"
+}*/
+/*JSON{ "type":"function", "name" : "load",
+         "description" : ["Load program memory out of flash",
+                          "This command only executes when the Interpreter returns to the Idle state - for instance ```a=1;load();a=2;``` will still leave 'a' as undefined (or what it was set to in the saved program)."],
+         "generate_full" : "jsiSetTodo(TODO_FLASH_LOAD)"
+}*/
+/*JSON{ "type":"function", "name" : "save",
+         "description" : ["Save program memory into flash. It will then be loaded automatically every time Espruino powers on or is hard-reset.",
+                          "This command only executes when the Interpreter returns to the Idle state - for instance ```a=1;save();a=2;``` will save 'a' as 2.",
+                          "In order to stop the program saved with this command being loaded automatically, hold down Button 1 while also pressing reset. On some boards, Button 1 enters bootloader mode, so you will need to press Reset with Button 1 raised, and then hold Button 1 down a fraction of a second later."],
+         "generate_full" : "jsiSetTodo(TODO_FLASH_SAVE)"
+}*/
+/*JSON{ "type":"function", "name" : "reset",
+         "description" : ["Reset the interpreter - clear program memory, and do not load a saved program from flash. This does NOT reset the underlying hardware (which allows you to reset the device without it disconnecting from USB).",
+           "This command only executes when the Interpreter returns to the Idle state - for instance ```a=1;reset();a=2;``` will still leave 'a' as undefined.",
+           "The safest way to do a full reset is to hit the reset button."],
+         "generate_full" : "jsiSetTodo(TODO_RESET)"
+}*/
+/*JSON{ "type":"function", "name" : "print",
+         "description" : "Print the supplied string",
+         "generate" : "jswrap_interface_print",
+         "params" : [ [ "text", "JsVarArray", ""] ]
+}*/
+/*JSON{ "type":"staticmethod", "class":"console", "name" : "log",
+         "description" : "Print the supplied string(s)",
+         "generate" : "jswrap_interface_print",
+         "params" : [ [ "text", "JsVarArray", "One or more arguments to print"] ]
+}*/
+void jswrap_interface_print(JsVar *v) {
+  assert(jsvIsArray(v));
+  JsArrayIterator it;
+  jsvArrayIteratorNew(&it, v);
+  while (jsvArrayIteratorHasElement(&it)) {
+    JsVar *v = jsvAsString(jsvArrayIteratorGetElement(&it), true);
+    jsiConsoleRemoveInputLine();
+    jsiConsolePrintStringVar(v);
+    jsvUnLock(v);
+    jsvArrayIteratorNext(&it);
+    if (jsvArrayIteratorHasElement(&it))
+      jsiConsolePrint(" ");
+  }
+  jsvArrayIteratorFree(&it);
+  jsiConsolePrint("\n");
+}
+
+/*JSON{ "type":"function", "name" : "edit",
+        "description" : ["Fill the console with the contents of the given function, so you can edit it.",
+                         "NOTE: This is a convenience function - it will not edit 'inner functions'. For that, you must edit the 'outer function' and re-execute it."],
+        "generate" : "jswrap_interface_edit",
+        "params" : [ [ "funcName", "JsVarName", "The name of the function to edit (either a string or just the unquoted name)"] ]
+}*/
+void jswrap_interface_edit(JsVar *funcName) {
+  if (jsvIsString(funcName)) {
+    JsVar *func = 0;
+    if (jsvIsName(funcName))
+      func = jsvSkipName(funcName);
+    else
+      func = jsvSkipNameAndUnLock(jsvFindChildFromVar(jsiGetParser()->root, funcName, 0));
+    if (jsvIsFunction(func)) {
+      JsVar *scopeVar = jsvFindChildFromString(func, JSPARSE_FUNCTION_SCOPE_NAME, false);
+      JsVarRef scope = jsvGetRef(scopeVar);
+      jsvUnLock(scopeVar);
+      JsVar *newLine = jsvNewFromEmptyString();
+      if (newLine) { // could be out of memory
+        jsvAppendStringVarComplete(newLine, funcName);
+        if (scope) {
+          // If we have a scope, it's an internal function so we will need to write different code
+          jsvAppendString(newLine, ".replaceWith(");
+        } else {
+          jsvAppendString(newLine, " = ");
+        }
+        JsVar *funcData = jsvAsString(func, false);
+        if (funcData)
+          jsvAppendStringVarComplete(newLine, funcData);
+        jsvUnLock(funcData);
+        if (scope) {
+          jsvAppendString(newLine, ");");
+        } else {
+          jsvAppendString(newLine, ";");
+        }
+        jsiReplaceInputLine(newLine);
+        jsvUnLock(newLine);
+      }
+    } else {
+      jsError("Edit should be called with the name of a function");
+    }
+    jsvUnLock(func);
+  } else {
+    jsError("Edit should be called with edit(funcName) or edit('funcName')");
+  }
+}
+
+
+/*JSON{ "type":"function", "name" : "echo",
+         "description" : "Should TinyJS echo what you type back to you? true = yes (Default), false = no. When echo is off, the result of executing a command is not returned. Instead, you must use 'print' to send output.",
+         "generate" : "jswrap_interface_echo",
+         "params" : [ [ "echoOn", "bool", ""] ]
+}*/
+void jswrap_interface_echo(bool echoOn) {
+  echo = echoOn;
+}
+
+/*JSON{ "type":"function", "name" : "getTime",
+         "description" : "Return the current system time in Seconds (as a floating point number)",
+         "generate_full" : "(JsVarFloat)jshGetSystemTime() / (JsVarFloat)jshGetTimeFromMilliseconds(1000)",
+         "return" : ["float", ""]
+}*/
+
+
+/*JSON{ "type":"function", "name" : "getSerial",
+         "description" : "Get the serial number of this board",
+         "generate" : "jswrap_interface_getSerial",
+         "return" : ["JsVar", "The board's serial number"]
+}*/
+JsVar *jswrap_interface_getSerial() {
+  char buf[8];
+  unsigned char serial[32];
+  int i, serialSize = jshGetSerialNumber(serial, sizeof(serial));
+
+  JsVar *str = jsvNewFromEmptyString();
+  if (!str) return 0;
+
+  for (i=0;i<serialSize;i++) {
+    if ((i&3)==0 && i) jsvAppendString(str, "-");
+    itoa(serial[i] | 0x100, buf, 16);
+    jsvAppendString(str, &buf[1]);
+  }
+  return str;
+}

+ 24 - 0
components/external/espruino/src/jswrap_interactive.h

@@ -0,0 +1,24 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * JavaScript methods and functions for the interactive shell
+ * ----------------------------------------------------------------------------
+ */
+#include "jsvar.h"
+#include "jsinteractive.h"
+
+void jswrap_interface_setBusyIndicator(JsVar *pinVar);
+void jswrap_interface_setSleepIndicator(JsVar *pinVar);
+void jswrap_interface_setDeepSleep(bool sleep);
+void jswrap_interface_trace(JsVar *root);
+void jswrap_interface_print(JsVar *v);
+void jswrap_interface_edit(JsVar *funcName);
+void jswrap_interface_echo(bool echoOn);
+JsVar *jswrap_interface_getSerial();

+ 437 - 0
components/external/espruino/src/jswrap_io.c

@@ -0,0 +1,437 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * This file is designed to be parsed during the build process
+ *
+ * JavaScript Hardware IO Functions
+ * ----------------------------------------------------------------------------
+ */
+#include "jswrap_io.h"
+#include "jsvar.h"
+
+
+/*JSON{ "type":"function", "name" : "peek8",
+         "description" : [ "Read 8 bits of memory at the given location - DANGEROUS!" ],
+         "generate_full" : "(JsVarInt)*(unsigned char*)jsvGetInteger(addr)",
+         "params" : [ [ "addr", "int", "The address in memory to read"] ],
+         "return" : ["int", "The value of memory at the given location"]
+}*/
+/*JSON{ "type":"function", "name" : "poke8",
+         "description" : [ "Write 8 bits of memory at the given location - VERY DANGEROUS!" ],
+         "generate_full" : "(*(unsigned char*)jsvGetInteger(addr)) = (unsigned char)jsvGetInteger(value)",
+         "params" : [ [ "addr", "int", "The address in memory to write"],
+                      [ "value", "int", "The value to write"] ]
+}*/
+/*JSON{ "type":"function", "name" : "peek16",
+         "description" : [ "Read 16 bits of memory at the given location - DANGEROUS!" ],
+         "generate_full" : "(JsVarInt)*(unsigned short*)jsvGetInteger(addr)",
+         "params" : [ [ "addr", "int", "The address in memory to read"] ],
+         "return" : ["int", "The value of memory at the given location"]
+}*/
+/*JSON{ "type":"function", "name" : "poke16",
+         "description" : [ "Write 16 bits of memory at the given location - VERY DANGEROUS!" ],
+         "generate_full" : "(*(unsigned short*)jsvGetInteger(addr)) = (unsigned short)jsvGetInteger(value)",
+         "params" : [ [ "addr", "int", "The address in memory to write"],
+                      [ "value", "int", "The value to write"] ]
+}*/
+/*JSON{ "type":"function", "name" : "peek32",
+         "description" : [ "Read 32 bits of memory at the given location - DANGEROUS!" ],
+         "generate_full" : "(JsVarInt)*(unsigned int*)jsvGetInteger(addr)",
+         "params" : [ [ "addr", "int", "The address in memory to read"] ],
+         "return" : ["int", "The value of memory at the given location"]
+}*/
+/*JSON{ "type":"function", "name" : "poke32",
+         "description" : [ "Write 32 bits of memory at the given location - VERY DANGEROUS!" ],
+         "generate_full" : "(*(unsigned int*)jsvGetInteger(addr)) = (unsigned int)jsvGetInteger(value)",
+         "params" : [ [ "addr", "int", "The address in memory to write"],
+                      [ "value", "int", "The value to write"] ]
+}*/
+
+/*JSON{ "type":"function", "name" : "analogRead",
+         "description" : ["Get the analog value of the given pin",
+                          "This is different to Arduino which only returns an integer between 0 and 1023",
+                          "However only pins connected to an ADC will work (see the datasheet)"],
+         "generate" : "jshPinAnalog",
+         "params" : [ [ "pin", "pin", "The pin to use"] ],
+         "return" : ["float", "The analog Value of the Pin between 0 and 1"]
+}*/
+/*JSON{ "type":"function", "name" : "analogWrite",
+         "description" : "Set the analog Value of a pin. It will be output using PWM",
+         "generate" : "jswrap_io_analogWrite",
+         "params" : [ [ "pin", "pin", "The pin to use"],
+                      [ "value", "float", "A value between 0 and 1"],
+                      [ "options", "JsVar", ["An object containing options.",
+                                            "Currently only freq (pulse frequency in Hz) is available: ```analogWrite(LED1,0.5,{ freq : 10 });``` ",
+                                            "Note that specifying a frequency will force PWM output, even if the pin has a DAC"] ]  ]
+}*/
+void jswrap_io_analogWrite(Pin pin, JsVarFloat value, JsVar *options) {
+  JsVarFloat freq = 0;
+  if (jsvIsObject(options)) {
+    freq = jsvGetFloatAndUnLock(jsvObjectGetChild(options, "freq", 0));
+  }
+
+  jshPinAnalogOutput(pin, value, freq);
+}
+
+/*JSON{ "type":"function", "name" : "digitalPulse",
+         "description" : ["Pulse the pin with the value for the given time in milliseconds",
+                          "eg. ```pulse(A0,1,5);``` pulses A0 high for 5ms",
+                          "digitalPulse is for SHORT pulses that need to be very accurate. If you're doing anything over a few milliseconds, use setTimeout instead" ],
+         "generate" : "jswrap_io_digitalPulse",
+         "params" : [ [ "pin", "pin", "The pin to use"],
+                      [ "value", "bool", "Whether to pulse high (true) or low (false)"],
+                      [ "time", "float", "A time in milliseconds"] ]
+}*/
+void jswrap_io_digitalPulse(Pin pin, bool value, JsVarFloat time) {
+  if (time<=0) {
+    jsWarn("Pulse Time given for digitalPulse is less that or equal to 0");
+  } else {
+    //jsPrintInt((JsVarInt)(time*1000));
+    jshPinPulse(pin, value, time);
+  }
+}
+
+/*JSON{ "type":"function", "name" : "digitalWrite",
+         "description" : ["Set the digital value of the given pin",
+                          "If pin is an array of pins, eg. ```[A2,A1,A0]``` the value will be treated as an integer where the first array element is the MSB" ],
+         "generate" : "jswrap_io_digitalWrite",
+         "params" : [ [ "pin", "JsVar", "The pin to use"],
+                      [ "value", "int", "Whether to pulse high (true) or low (false)"] ]
+}*/
+void jswrap_io_digitalWrite(JsVar *pinVar, JsVarInt value) {
+  if (jsvIsArray(pinVar)) {
+    JsVarRef pinName = pinVar->lastChild; // NOTE: start at end and work back!
+    while (pinName) {
+      JsVar *pinNamePtr = jsvLock(pinName);
+      JsVar *pinPtr = jsvSkipName(pinNamePtr);
+      jshPinOutput(jshGetPinFromVar(pinPtr), value&1);
+      jsvUnLock(pinPtr);
+      pinName = pinNamePtr->prevSibling;
+      jsvUnLock(pinNamePtr);
+      value = value>>1; // next bit down
+    }
+  } else {
+    Pin pin = jshGetPinFromVar(pinVar);
+    jshPinOutput(pin, value!=0);
+  }
+}
+
+
+/*JSON{ "type":"function", "name" : "digitalRead",
+         "description" : ["Get the digital value of the given pin",
+                          "If pin is an array of pins, eg. ```[A2,A1,A0]``` the value will be treated as an integer where the first array element is the MSB" ],
+         "generate" : "jswrap_io_digitalRead",
+         "params" : [ [ "pin", "JsVar", "The pin to use"] ],
+         "return" : ["int", "The digital Value of the Pin"]
+}*/
+JsVarInt jswrap_io_digitalRead(JsVar *pinVar) {
+  if (jsvIsArray(pinVar)) {
+    int pins = 0;
+    JsVarInt value = 0;
+    JsVarRef pinName = pinVar->firstChild;
+    while (pinName) {
+      JsVar *pinNamePtr = jsvLock(pinName);
+      JsVar *pinPtr = jsvSkipName(pinNamePtr);
+      value = (value<<1) | jshPinInput(jshGetPinFromVar(pinPtr));
+      jsvUnLock(pinPtr);
+      pinName = pinNamePtr->nextSibling;
+      jsvUnLock(pinNamePtr);
+      pins++;
+    }
+    if (pins==0) return 0; // return undefined if array empty
+    return value;
+  } else {
+    Pin pin = jshGetPinFromVar(pinVar);
+    return jshPinInput(pin);
+  }
+}
+
+/*JSON{ "type":"function", "name" : "pinMode",
+         "description" : ["Set the mode of the given pin - note that digitalRead/digitalWrite/etc set this automatically unless pinMode has been called first. If you want digitalRead/etc to set the pin mode automatically after you have called pinMode, simply call it again with no mode argument: ```pinMode(pin)```" ],
+         "generate" : "jswrap_io_pinMode",
+         "params" : [ [ "pin", "pin", "The pin to use"], [ "mode", "JsVar", "The mode - a string that is either 'input', 'output', 'input_pullup', or 'input_pulldown'. Do not include this argument if you want to revert to automatic pin mode setting."] ]
+}*/
+void jswrap_io_pinMode(Pin pin, JsVar *mode) {
+  if (!jshIsPinValid(pin)) {
+    jsError("Invalid pin");
+    return;
+  }
+  JshPinState m = JSHPINSTATE_UNDEFINED;
+  if (jsvIsString(mode)) {
+    if (jsvIsStringEqual(mode, "input")) m = JSHPINSTATE_GPIO_IN;
+    if (jsvIsStringEqual(mode, "input_pullup")) m = JSHPINSTATE_GPIO_IN_PULLUP;
+    if (jsvIsStringEqual(mode, "input_pulldown")) m = JSHPINSTATE_GPIO_IN_PULLDOWN;
+    if (jsvIsStringEqual(mode, "output")) m = JSHPINSTATE_GPIO_OUT;
+  }
+  if (m != JSHPINSTATE_UNDEFINED) {
+    jshSetPinStateIsManual(pin, true);
+    jshPinSetState(pin, m);
+  } else {
+    jshSetPinStateIsManual(pin, false);
+    if (!jsvIsUndefined(mode)) {
+      jsError("Unknown pin mode");
+    }
+  }
+}
+
+
+/*XXXX{ "type":"function", "name" : "bitBang",
+         "description" : ["bitBang out a message in a one-wire style BROKEN CURRENTLY" ],
+         "generate" : "jshBitBang",
+         "params" : [ [ "pin", "pin", "The pin to use"],
+                      [ "t0h", "float", "The time (in milliseconds) to spend high for a 0"],
+                      [ "t0l", "float", "The time (in milliseconds) to spend low for a 0"],
+                      [ "t1h", "float", "The time (in milliseconds) to spend high for a 1"],
+                      [ "t1l", "float", "The time (in milliseconds) to spend low for a 1"],
+                      [ "data", "JsVar", "A string representing the data"] ]
+}*/
+
+
+/*JSON{ "type":"function", "name" : "setInterval",
+         "description" : ["Call the function specified REPEATEDLY after the timeout in milliseconds.",
+                          "The function that is being called may also take an argument, which is an object containing a field called 'time' (the time in seconds at which the timer happened)",
+                          "for example: ```setInterval(function (e) { print(e.time); }, 1000);```",
+                          "This can also be removed using clearInterval" ],
+         "generate" : "jswrap_interface_setInterval",
+         "params" : [ [ "function", "JsVarName", "A Function or String to be executed"],
+                      [ "timeout", "float", "The time between calls to the function" ] ],
+         "return" : ["JsVar", "An ID that can be passed to clearInterval"]
+}*/
+/*JSON{ "type":"function", "name" : "setTimeout",
+         "description" : ["Call the function specified ONCE after the timeout in milliseconds.",
+                          "The function that is being called may also take an argument, which is an object containing a field called 'time' (the time in seconds at which the timer happened)",
+                          "for example: ```setTimeout(function (e) { print(e.time); }, 1000);```",
+                          "This can also be removed using clearTimeout" ],
+         "generate" : "jswrap_interface_setTimeout",
+         "params" : [ [ "function", "JsVarName", "A Function or String to be executed"],
+                      [ "timeout", "float", "The time until the function will be executed" ] ],
+         "return" : ["JsVar", "An ID that can be passed to clearTimeout"]
+}*/
+JsVar *_jswrap_interface_setTimeoutOrInterval(JsVar *func, JsVarFloat interval, bool isTimeout) {
+  JsVar *skippedFunc = jsvSkipName(func);
+  JsVar *itemIndex = 0;
+  if (!jsvIsFunction(skippedFunc) && !jsvIsString(skippedFunc)) {
+    jsError("Function or String not supplied!");
+  } else {
+    // Create a new timer
+    JsVar *timerPtr = jsvNewWithFlags(JSV_OBJECT);
+    if (interval<TIMER_MIN_INTERVAL) interval=TIMER_MIN_INTERVAL;
+    JsVar *v;
+    v = jsvNewFromInteger(jshGetSystemTime() + jshGetTimeFromMilliseconds(interval));
+    jsvUnLock(jsvAddNamedChild(timerPtr, v, "time"));
+    jsvUnLock(v);
+    v = jsvNewFromFloat(interval);
+    jsvUnLock(jsvAddNamedChild(timerPtr, v, "interval"));
+    jsvUnLock(v);
+    v = jsvNewFromBool(!isTimeout);
+    jsvUnLock(jsvAddNamedChild(timerPtr, v, "recur"));
+    jsvUnLock(v);
+    jsvUnLock(jsvAddNamedChild(timerPtr, func, "callback"));
+    //jsPrint("TIMER BEFORE ADD\n"); jsvTrace(timerArray,5);
+    JsVar *timerArrayPtr = jsvLock(timerArray);
+    itemIndex = jsvNewFromInteger(jsvArrayPushWithInitialSize(timerArrayPtr, timerPtr, 1) - 1);
+    //jsPrint("TIMER AFTER ADD\n"); jsvTrace(timerArray,5);
+    jsvUnLock(timerArrayPtr);
+    jsvUnLock(timerPtr);
+  }
+  jsvUnLock(skippedFunc);
+  //jsvTrace(jsiGetParser()->root, 0);
+  return itemIndex;
+}
+JsVar *jswrap_interface_setInterval(JsVar *func, JsVarFloat timeout) {
+  return _jswrap_interface_setTimeoutOrInterval(func, timeout, false);
+}
+JsVar *jswrap_interface_setTimeout(JsVar *func, JsVarFloat timeout) {
+  return _jswrap_interface_setTimeoutOrInterval(func, timeout, true);
+}
+
+/*JSON{ "type":"function", "name" : "setWatch",
+         "description" : ["Call the function specified when the pin changes",
+                          "The function may also take an argument, which is an object containing a field called 'time', which is the time in seconds at which the pin changed state, and 'state', which is the current state of the pin",
+                          " This can also be removed using clearWatch" ],
+         "generate" : "jswrap_interface_setWatch",
+         "params" : [ [ "function", "JsVarName", "A Function or String to be executed"],
+                      [ "pin", "pin", "The pin to watch" ],
+                      [ "options", "JsVar", ["If this is a boolean or integer, it determines whether to call this once (false = default) or every time a change occurs (true)",
+                                             "If this is an object, it can contain the following information: ```{ repeat: true/false(default), edge:'rising'/'falling'/'both'(default)}```" ] ]  ],
+         "return" : ["JsVar", "An ID that can be passed to clearWatch"]
+}*/
+JsVar *jswrap_interface_setWatch(JsVar *funcVar, Pin pin, JsVar *repeatOrObject) {
+  bool repeat = false;
+  int edge = 0;
+  if (jsvIsObject(repeatOrObject)) {
+    JsVar *v;
+    repeat = jsvGetBoolAndUnLock(jsvObjectGetChild(repeatOrObject, "repeat", 0));
+    v = jsvObjectGetChild(repeatOrObject, "edge", 0);
+    if (jsvIsString(v)) {
+      if (jsvIsStringEqual(v, "rising")) edge=1;
+      else if (jsvIsStringEqual(v, "falling")) edge=-1;
+      else if (jsvIsStringEqual(v, "both")) edge=0;
+      else jsWarn("'edge' in setWatch should be a string - either 'rising', 'falling' or 'both'");
+    } else if (!jsvIsUndefined(v))
+      jsWarn("'edge' in setWatch should be a string - either 'rising', 'falling' or 'both'");
+    jsvUnLock(v);
+  } else
+    repeat = jsvGetBool(repeatOrObject);
+
+  JsVarInt itemIndex = -1;
+  JsVar *skippedFunc = jsvSkipName(funcVar);
+  if (!jsvIsFunction(skippedFunc) && !jsvIsString(skippedFunc)) {
+    jsError("Function or String not supplied!");
+  } else {
+    // Create a new watch
+    JsVar *watchPtr = jsvNewWithFlags(JSV_OBJECT);
+    JsVar *v;
+    v = jsvNewFromPin(pin);
+    jsvUnLock(jsvAddNamedChild(watchPtr, v, "pin"));
+    jsvUnLock(v);
+    v = jsvNewFromBool(repeat);
+    jsvUnLock(jsvAddNamedChild(watchPtr, v, "recur"));
+    jsvUnLock(v);
+    v = jsvNewFromInteger(edge);
+    jsvUnLock(jsvAddNamedChild(watchPtr, v, "edge"));
+    jsvUnLock(v);
+    jsvUnLock(jsvAddNamedChild(watchPtr, funcVar, "callback"));
+    JsVar *watchArrayPtr = jsvLock(watchArray);
+    itemIndex = jsvArrayPushWithInitialSize(watchArrayPtr, watchPtr, 1) - 1;
+    jsvUnLock(watchArrayPtr);
+    jsvUnLock(watchPtr);
+    jshPinWatch(pin, true);
+  }
+  jsvUnLock(skippedFunc);
+  return (itemIndex>=0) ? jsvNewFromInteger(itemIndex) : 0/*undefined*/;
+}
+
+/*JSON{ "type":"function", "name" : "clearInterval",
+         "description" : ["Clear the Interval that was created with setInterval, for example:",
+                          "```var id = setInterval(function () { print('foo'); }, 1000);```",
+                          "```clearInterval(id);```",
+                          "If no argument is supplied, all timers and intervals are stopped" ],
+         "generate" : "jswrap_interface_clearInterval",
+         "params" : [ [ "id", "JsVar", "The id returned by a previous call to setInterval"] ]
+}*/
+/*JSON{ "type":"function", "name" : "clearTimeout",
+         "description" : ["Clear the Timeout that was created with setTimeout, for example:",
+                          "```var id = setTimeout(function () { print('foo'); }, 1000);```",
+                          "```clearTimeout(id);```",
+                          "If no argument is supplied, all timers and intervals are stopped" ],
+         "generate" : "jswrap_interface_clearTimeout",
+         "params" : [ [ "id", "JsVar", "The id returned by a previous call to setTimeout"] ]
+}*/
+void _jswrap_interface_clearTimeoutOrInterval(JsVar *idVar, bool isTimeout) {
+  if (jsvIsUndefined(idVar)) {
+    JsVar *timerArrayPtr = jsvLock(timerArray);
+    jsvRemoveAllChildren(timerArrayPtr);
+    jsvUnLock(timerArrayPtr);
+  } else {
+    JsVar *child = jsvIsBasic(idVar) ? jsvFindChildFromVarRef(timerArray, idVar, false) : 0;
+    if (child) {
+      JsVar *timerArrayPtr = jsvLock(timerArray);
+      jsvRemoveChild(timerArrayPtr, child);
+      jsvUnLock(child);
+      jsvUnLock(timerArrayPtr);
+    } else {
+      jsError(isTimeout ? "Unknown Timeout" : "Unknown Interval");
+    }
+  }
+}
+void jswrap_interface_clearInterval(JsVar *idVar) {
+  _jswrap_interface_clearTimeoutOrInterval(idVar, false);
+}
+void jswrap_interface_clearTimeout(JsVar *idVar) {
+  _jswrap_interface_clearTimeoutOrInterval(idVar, true);
+}
+
+/*JSON{ "type":"function", "name" : "changeInterval",
+         "description" : ["Change the Interval on a callback created with setInterval, for example:",
+                          "```var id = setInterval(function () { print('foo'); }, 1000); // every second```",
+                          "```changeInterval(id, 1500); // now runs every 1.5 seconds```",
+                          "This takes effect the text time the callback is called (so it is not immediate)."],
+         "generate" : "jswrap_interface_changeInterval",
+         "params" : [ [ "id", "JsVar", "The id returned by a previous call to setInterval"],
+                      [ "time","float","The new time period in ms" ] ]
+}*/
+void jswrap_interface_changeInterval(JsVar *idVar, JsVarFloat interval) {
+  if (interval<TIMER_MIN_INTERVAL) interval=TIMER_MIN_INTERVAL;
+  JsVar *timerName = jsvIsBasic(idVar) ? jsvFindChildFromVarRef(timerArray, idVar, false) : 0;
+
+  if (timerName) {
+    JsVar *timer = jsvSkipNameAndUnLock(timerName);
+    JsVar *v;
+    v = jsvNewFromFloat(interval);
+    jsvUnLock(jsvSetNamedChild(timer, v, "interval"));
+    jsvUnLock(v);
+    v = jsvNewFromInteger(jshGetSystemTime() + jshGetTimeFromMilliseconds(interval));
+    jsvUnLock(jsvSetNamedChild(timer, v, "time"));
+    jsvUnLock(v);
+    jsvUnLock(timer);
+    // timerName already unlocked
+  } else {
+    jsError("Unknown Interval");
+  }
+}
+
+/*JSON{ "type":"function", "name" : "clearWatch",
+         "description" : [ "Clear the Watch that was created with setWatch. If no parameter is supplied, all watches will be removed." ],
+         "generate" : "jswrap_interface_clearWatch",
+         "params" : [ [ "id", "JsVar", "The id returned by a previous call to setWatch"] ]
+}*/
+void jswrap_interface_clearWatch(JsVar *idVar) {
+  if (jsvIsUndefined(idVar)) {
+    JsVar *watchArrayPtr = jsvLock(watchArray);
+    // unwatch all pins
+    JsVarRef watch = watchArrayPtr->firstChild;
+    while (watch) {
+      JsVar *watchNamePtr = jsvLock(watch); // effectively the array index
+      JsVar *pinVar = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
+      jshPinWatch(jshGetPinFromVar(pinVar), false); // 'unwatch' pin because we know that we're removing ALL watches
+      jsvUnLock(pinVar);
+      watch = watchNamePtr->nextSibling;
+      jsvUnLock(watchNamePtr);
+    }
+    // remove all items
+    jsvRemoveAllChildren(watchArrayPtr);
+    jsvUnLock(watchArrayPtr);
+  } else {
+    JsVar *watchNamePtr = jsvFindChildFromVarRef(watchArray, idVar, false);
+    if (watchNamePtr) { // child is a 'name'
+      JsVar *pinVar = jsvSkipNameAndUnLock(jsvFindChildFromStringRef(watchNamePtr->firstChild, "pin", false));
+      Pin pin = jshGetPinFromVar(pinVar);
+      jsvUnLock(pinVar);
+
+      JsVar *watchArrayPtr = jsvLock(watchArray);
+      jsvRemoveChild(watchArrayPtr, watchNamePtr);
+      jsvUnLock(watchNamePtr);
+
+      // Now check if this pin is still being watched
+      bool stillWatched = false;
+      JsArrayIterator it;
+      jsvArrayIteratorNew(&it, watchArrayPtr);
+      while (jsvArrayIteratorHasElement(&it)) {
+        JsVar *watchPtr = jsvArrayIteratorGetElement(&it);
+        JsVar *pinVar = jsvObjectGetChild(watchPtr, "pin", 0);
+        if (jshGetPinFromVar(pinVar) == pin)
+          stillWatched = true;
+        jsvUnLock(pinVar);
+        jsvUnLock(watchPtr);
+        jsvArrayIteratorNext(&it);
+      }
+      jsvArrayIteratorFree(&it);
+      jsvUnLock(watchArrayPtr);
+
+      if (!stillWatched)
+        jshPinWatch(pin, false); // 'unwatch' pin
+    } else {
+      jsError("Unknown Watch");
+    }
+  }
+}
+
+

+ 29 - 0
components/external/espruino/src/jswrap_io.h

@@ -0,0 +1,29 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * JavaScript Hardware IO Functions
+ * ----------------------------------------------------------------------------
+ */
+#include "jsvar.h"
+#include "jsinteractive.h"
+
+void jswrap_io_analogWrite(Pin pin, JsVarFloat value, JsVar *options);
+void jswrap_io_digitalPulse(Pin pin, bool value, JsVarFloat time);
+void jswrap_io_digitalWrite(JsVar *pinVar, JsVarInt value);
+JsVarInt jswrap_io_digitalRead(JsVar *pinVar);
+void jswrap_io_pinMode(Pin pin, JsVar *mode);
+
+JsVar *jswrap_interface_setInterval(JsVar *func, JsVarFloat timeout);
+JsVar *jswrap_interface_setTimeout(JsVar *func, JsVarFloat timeout);
+JsVar *jswrap_interface_setWatch(JsVar *funcVar, Pin pin, JsVar *repeatOrObject);
+void jswrap_interface_clearInterval(JsVar *idVar);
+void jswrap_interface_clearTimeout(JsVar *idVar);
+void jswrap_interface_changeInterval(JsVar *idVar, JsVarFloat interval);
+void jswrap_interface_clearWatch(JsVar *idVar);

+ 167 - 0
components/external/espruino/src/jswrap_json.c

@@ -0,0 +1,167 @@
+/*
+ * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
+ *
+ * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * ----------------------------------------------------------------------------
+ * This file is designed to be parsed during the build process
+ *
+ * JavaScript JSON-handling Functions
+ * ----------------------------------------------------------------------------
+ */
+#include "jswrap_json.h"
+#include "jsparse.h"
+#include "jsinteractive.h"
+#include "jswrapper.h"
+
+/*JSON{ "type":"staticmethod",
+         "class" : "JSON", "name" : "stringify",
+         "description" : "Convert the given object into a JSON string which can subsequently be parsed with JSON.parse or eval",
+         "generate" : "jswrap_json_stringify",
+         "params" : [ [ "data", "JsVar", "The data to be converted to a JSON string"] ],
+         "return" : ["JsVar", "A JSON string"]
+}*/
+JsVar *jswrap_json_stringify(JsVar *v) {
+  JsVar *result = jsvNewFromEmptyString();
+  if (result) // could be out of memory
+    jsfGetJSON(v, result);
+  return result;
+}
+
+/*JSON{ "type":"staticmethod",
+         "class" : "JSON", "name" : "parse",
+         "description" : "Parse the given JSON string into a JavaScript object",
+         "generate" : "jswrap_json_parse",
+         "params" : [ [ "string", "JsVar", "A JSON string"] ],
+         "return" : ["JsVar", "The JavaScript object created by parsing the data string"]
+}*/
+JsVar *jswrap_json_parse(JsVar *v) {
+  JsVar *res = 0;
+  JsVar *bracketed = jsvNewFromString("(");
+  if (bracketed) { // could be out of memory
+    v = jsvAsString(v, true); // try and get this as a string
+    jsvAppendStringVarComplete(bracketed, v);
+    jsvAppendString(bracketed, ")");
+    res = jspEvaluateVar(jsiGetParser(), bracketed, 0);
+    jsvUnLock(bracketed);
+  }
+  return res;
+}
+
+/* This is like jsfGetJSONWithCallback, but handles ONLY functions (and does not print the initial 'function' text) */
+void jsfGetJSONForFunctionWithCallback(JsVar *var, JsfGetJSONCallbackString callbackString, JsfGetJSONCallbackVar callbackVar, void *callbackData) {
+  assert(jsvIsFunction(var));
+  JsVarRef coderef = 0; // TODO: this should really be in jsvAsString
+  JsVarRef childref = var->firstChild;
+  bool firstParm = true;
+  callbackString(callbackData, "(");
+  while (childref) {
+    JsVar *child = jsvLock(childref);
+    childref = child->nextSibling;
+    if (jsvIsFunctionParameter(child)) {
+      if (firstParm)
+        firstParm=false;
+      else
+        callbackString(callbackData, ",");
+      callbackVar(callbackData, child); // FIXME: escape the string
+    } else if (jsvIsString(child) && jsvIsStringEqual(child, JSPARSE_FUNCTION_CODE_NAME)) {
+      coderef = child->firstChild;
+    }
+    jsvUnLock(child);
+  }
+  callbackString(callbackData, ") ");
+  if (coderef) {
+     JsVar *codeVar = jsvLock(coderef);
+     callbackVar(callbackData, codeVar);
+     jsvUnLock(codeVar);
+  } else callbackString(callbackData, "{}");
+}
+
+void jsfGetJSONWithCallback(JsVar *var, JsfGetJSONCallbackString callbackString, JsfGetJSONCallbackVar callbackVar, void *callbackData) {
+  if (jsvIsUndefined(var)) {
+    callbackString(callbackData, "undefined");
+  } else if (jsvIsArray(var)) {
+    int length = (int)jsvGetArrayLength(var);
+    int i;
+    callbackString(callbackData, "[");
+    for (i=0;i<length;i++) {
+      JsVar *item = jsvGetArrayItem(var, i);
+      jsfGetJSONWithCallback(item, callbackString, callbackVar, callbackData);
+      jsvUnLock(item);
+      if (i<length-1) callbackString(callbackData, ",");
+    }
+    callbackString(callbackData, "]");
+  } else if (jsvIsArrayBuffer(var)) {
+    callbackString(callbackData, "new ");
+    callbackString(callbackData, jswGetBasicObjectName(var));
+    callbackString(callbackData, "([");
+    callbackVar(callbackData, jsvAsString(var, false));
+    callbackString(callbackData, "])");
+  } else if (jsvIsObject(var)) {
+    bool first = true;
+    JsVarRef childref = var->firstChild;
+    callbackString(callbackData, "{");
+    while (childref) {
+      JsVar *child = jsvLock(childref);
+      bool hidden = jsvIsInternalObjectKey(child);
+      if (!hidden) {
+        if (first)
+          first = false;
+        else
+          callbackString(callbackData, ",");
+
+        callbackString(callbackData, "\"");
+        callbackVar(callbackData, child); // FIXME: escape the string
+        callbackString(callbackData, "\":");
+      }
+      JsVar *childVar = child->firstChild ? jsvLock(child->firstChild) : 0;
+      childref = child->nextSibling;
+      jsvUnLock(child);
+
+      if (!hidden) {
+        jsfGetJSONWithCallback(childVar, callbackString, callbackVar, callbackData);
+      }
+      jsvUnLock(childVar);
+    }
+    callbackString(callbackData, "}");
+  } else if (jsvIsFunction(var)) {
+    callbackString(callbackData, "function ");
+    jsfGetJSONForFunctionWithCallback(var, callbackString, callbackVar, callbackData);
+  } else if (jsvIsString(var) && !jsvIsName(var)) {
+    // escape the string
+    callbackString(callbackData, "\"");
+    JsvStringIterator it;
+    jsvStringIteratorNew(&it, var, 0);
+    while (jsvStringIteratorHasChar(&it)) {
+      char ch = jsvStringIteratorGetChar(&it);
+      callbackString(callbackData, escapeCharacter(ch));
+      jsvStringIteratorNext(&it);
+    }
+    jsvStringIteratorFree(&it);
+    callbackString(callbackData, "\"");
+  } else {
+    JsVar *str = jsvAsString(var, false);
+    if (str) {
+      callbackVar(callbackData, str);
+      jsvUnLock(str);
+    }
+  }
+}
+
+void jsfGetJSON(JsVar *var, JsVar *result) {
+  assert(jsvIsString(result));
+  jsfGetJSONWithCallback(var, (JsfGetJSONCallbackString)jsvAppendString, (JsfGetJSONCallbackVar)jsvAppendStringVarComplete, result);
+}
+
+void _jsfPrintJSON_str(void *data, const char *str) { NOT_USED(data); jsiConsolePrint(str); }
+void _jsfPrintJSON_var(void *data, JsVar *var) { NOT_USED(data); jsiConsolePrintStringVar(var); }
+void jsfPrintJSON(JsVar *var) {
+  jsfGetJSONWithCallback(var, _jsfPrintJSON_str, _jsfPrintJSON_var, 0);
+}
+void jsfPrintJSONForFunction(JsVar *var) {
+  jsfGetJSONForFunctionWithCallback(var, _jsfPrintJSON_str, _jsfPrintJSON_var, 0);
+}

部分文件因为文件数量过多而无法显示