From 121bb5fcdfb3bc5f3586a0937e1b24885236b53d Mon Sep 17 00:00:00 2001 From: geniusgogo Date: Tue, 25 Feb 2014 01:47:49 +0800 Subject: add JavaScript Interpreter --- components/external/espruino/src/jsutils.c | 439 +++++++++++++++++++++++++++++ 1 file changed, 439 insertions(+) create mode 100644 components/external/espruino/src/jsutils.c (limited to 'components/external/espruino/src/jsutils.c') diff --git a/components/external/espruino/src/jsutils.c b/components/external/espruino/src/jsutils.c new file mode 100644 index 0000000000..669956989e --- /dev/null +++ b/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 + * + * 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 +#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> 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); + } + } +} + + -- cgit v1.3.1