4
0
mirror of https://github.com/RT-Thread/rt-thread.git synced 2025-02-03 20:04:34 +08:00

440 lines
11 KiB
C
Raw Normal View History

2014-02-25 01:47:49 +08:00
/*
* 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);
}
}
}