jsutils.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. /*
  2. * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
  3. *
  4. * Copyright (C) 2013 Gordon Williams <gw@pur3.co.uk>
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. *
  10. * ----------------------------------------------------------------------------
  11. * Misc utils and cheapskate stdlib implementation
  12. * ----------------------------------------------------------------------------
  13. */
  14. #include "jsutils.h"
  15. #include "jslex.h"
  16. #include "jshardware.h"
  17. #include "jsinteractive.h"
  18. // needed for isnan / isfinite
  19. #ifdef ARM
  20. #include "mconf.h"
  21. #include "protos.h"
  22. #else
  23. #include <math.h>
  24. #endif
  25. extern double jswrap_math_pow(double x, double y); // for pow
  26. bool isIDString(const char *s) {
  27. if (!isAlpha(*s))
  28. return false;
  29. while (*s) {
  30. if (!(isAlpha(*s) || isNumeric(*s)))
  31. return false;
  32. s++;
  33. }
  34. return true;
  35. }
  36. /** escape a character - if it is required. This may return a reference to a static array,
  37. so you can't store the value it returns in a variable and call it again. */
  38. const char *escapeCharacter(char ch) {
  39. if (ch=='\b') return "\\b";
  40. if (ch=='\f') return "\\f";
  41. if (ch=='\n') return "\\n";
  42. if (ch=='\a') return "\\a";
  43. if (ch=='\r') return "\\r";
  44. if (ch=='\t') return "\\t";
  45. if (ch=='\\') return "\\\\";
  46. if (ch=='"') return "\\\"";
  47. static char buf[5];
  48. if (ch<32) {
  49. /** just encode as hex - it's more understandable
  50. * and doesn't have the issue of "\16"+"1" != "\161" */
  51. buf[0]='\\';
  52. buf[1]='x';
  53. int n = (ch>>4)&15;
  54. buf[2] = (char)((n<10)?('0'+n):('A'+n-10));
  55. n=ch&15;
  56. buf[3] = (char)((n<10)?('0'+n):('A'+n-10));
  57. buf[4] = 0;
  58. return buf;
  59. }
  60. buf[1] = 0;
  61. buf[0] = ch;
  62. return buf;
  63. }
  64. /* convert a number in the given radix to an int. if radix=0, autodetect */
  65. JsVarInt stringToIntWithRadix(const char *s, int forceRadix, bool *hasError) {
  66. bool isNegated = false;
  67. JsVarInt v = 0;
  68. JsVarInt radix = 10;
  69. if (*s == '-') {
  70. isNegated = true;
  71. s++;
  72. }
  73. if (*s == '0') {
  74. radix = 8;
  75. s++;
  76. // OctalIntegerLiteral: 0o01, 0O01
  77. if (*s == 'o' || *s == 'O') {
  78. radix = 8;
  79. s++;
  80. // HexIntegerLiteral: 0x01, 0X01
  81. } else if (*s == 'x' || *s == 'X') {
  82. radix = 16;
  83. s++;
  84. // BinaryIntegerLiteral: 0b01, 0B01
  85. } else if (*s == 'b' || *s == 'B') {
  86. radix = 2;
  87. s++;
  88. }
  89. }
  90. if (forceRadix>0 && forceRadix<=36)
  91. radix = forceRadix;
  92. while (*s) {
  93. int digit = 0;
  94. if (*s >= '0' && *s <= '9')
  95. digit = (*s - '0');
  96. else if (*s >= 'a' && *s <= 'f')
  97. digit = (10 + *s - 'a');
  98. else if (*s >= 'A' && *s <= 'F')
  99. digit = (10 + *s - 'A');
  100. else break;
  101. if (digit>=radix)
  102. break;
  103. v = v*radix + digit;
  104. s++;
  105. }
  106. if (hasError) *hasError = *s!=0; // we're ok if we reached the end of the string
  107. if (isNegated) return -v;
  108. return v;
  109. }
  110. /* convert hex, binary, octal or decimal string into an int */
  111. JsVarInt stringToInt(const char *s) {
  112. return stringToIntWithRadix(s,0,0);
  113. }
  114. void jsError(const char *fmt, ...) {
  115. jsiConsoleRemoveInputLine();
  116. jsiConsolePrint("ERROR: ");
  117. va_list argp;
  118. va_start(argp, fmt);
  119. vcbprintf((vcbprintf_callback)jsiConsolePrint,0, fmt, argp);
  120. va_end(argp);
  121. jsiConsolePrint("\n");
  122. }
  123. void jsErrorInternal(const char *fmt, ...) {
  124. jsiConsoleRemoveInputLine();
  125. jsiConsolePrint("INTERNAL ERROR: ");
  126. va_list argp;
  127. va_start(argp, fmt);
  128. vcbprintf((vcbprintf_callback)jsiConsolePrint,0, fmt, argp);
  129. va_end(argp);
  130. jsiConsolePrint("\n");
  131. }
  132. void jsErrorAt(const char *message, struct JsLex *lex, int tokenPos) {
  133. jsiConsoleRemoveInputLine();
  134. jsiConsolePrint("ERROR: ");
  135. jsiConsolePrint(message);
  136. jsiConsolePrint(" at ");
  137. jsiConsolePrintPosition(lex, tokenPos);
  138. jsiConsolePrintTokenLineMarker(lex, tokenPos);
  139. }
  140. void jsWarn(const char *fmt, ...) {
  141. jsiConsoleRemoveInputLine();
  142. jsiConsolePrint("WARNING: ");
  143. va_list argp;
  144. va_start(argp, fmt);
  145. vcbprintf((vcbprintf_callback)jsiConsolePrint,0, fmt, argp);
  146. va_end(argp);
  147. jsiConsolePrint("\n");
  148. }
  149. void jsWarnAt(const char *message, struct JsLex *lex, int tokenPos) {
  150. jsiConsoleRemoveInputLine();
  151. jsiConsolePrint("WARNING: ");
  152. jsiConsolePrint(message);
  153. jsiConsolePrint(" at ");
  154. jsiConsolePrintPosition(lex, tokenPos);
  155. }
  156. void jsAssertFail(const char *file, int line, const char *expr) {
  157. jsiConsoleRemoveInputLine();
  158. if (expr) {
  159. jsiConsolePrintf("ASSERT(%s) FAILED AT ", expr);
  160. } else
  161. jsiConsolePrint("ASSERT FAILED AT ");
  162. jsiConsolePrintf("%s:%d\n",file,line);
  163. jsvTrace(jsvGetRef(jsvFindOrCreateRoot()), 2);
  164. exit(1);
  165. }
  166. #ifdef SDCC
  167. void exit(int errcode) {dst;
  168. jsiConsolePrint("EXIT CALLED.\n");
  169. }
  170. #endif
  171. #ifdef FAKE_STDLIB
  172. int __errno;
  173. void exit(int errcode) {
  174. NOT_USED(errcode);
  175. jsiConsolePrint("EXIT CALLED.\n");
  176. while (1);
  177. }
  178. char * strncat(char *dst, const char *src, size_t c) {
  179. char *dstx = dst;
  180. while (*(++dstx)) c--;
  181. while (*src && c>1) {
  182. *(dstx++) = *(src++);
  183. c--;
  184. }
  185. if (c>0) *dstx = 0;
  186. return dst;
  187. }
  188. char *strncpy(char *dst, const char *src, size_t c) {
  189. char *dstx = dst;
  190. while (*src && c>1) {
  191. *(dstx++) = *(src++);
  192. c--;
  193. }
  194. if (c>0) *dstx = 0;
  195. return dst;
  196. }
  197. size_t strlen(const char *s) {
  198. size_t l=0;
  199. while (*(s++)) l++;
  200. return l;
  201. }
  202. int strcmp(const char *a, const char *b) {
  203. while (*a && *b) {
  204. if (*a != *b)
  205. return *a - *b; // correct?
  206. a++;b++;
  207. }
  208. return *a - *b;
  209. }
  210. void *memcpy(void *dst, const void *src, size_t size) {
  211. size_t i;
  212. for (i=0;i<size;i++)
  213. ((char*)dst)[i] = ((char*)src)[i];
  214. return dst;
  215. }
  216. void *memset(void *dst, int val, size_t size) {
  217. unsigned char *d = (unsigned char*)dst;
  218. unsigned int i;
  219. for (i=0;i<size;i++)
  220. d[i]=(unsigned char)val;
  221. return dst;
  222. }
  223. unsigned int rand() {
  224. static unsigned int m_w = 0xDEADBEEF; /* must not be zero */
  225. static unsigned int m_z = 0xCAFEBABE; /* must not be zero */
  226. m_z = 36969 * (m_z & 65535) + (m_z >> 16);
  227. m_w = 18000 * (m_w & 65535) + (m_w >> 16);
  228. return (m_z << 16) + m_w; /* 32-bit result */
  229. }
  230. #endif
  231. JsVarFloat stringToFloat(const char *s) {
  232. bool isNegated = false;
  233. JsVarFloat v = 0;
  234. JsVarFloat mul = 0.1;
  235. if (*s == '-') {
  236. isNegated = true;
  237. s++;
  238. }
  239. // handle integer part
  240. while (*s) {
  241. if (*s >= '0' && *s <= '9')
  242. v = (v*10) + (*s - '0');
  243. else break;
  244. s++;
  245. }
  246. // handle decimal point
  247. if (*s == '.') {
  248. s++; // skip .
  249. while (*s) {
  250. if (*s >= '0' && *s <= '9')
  251. v += mul*(*s - '0');
  252. else break;
  253. mul /= 10;
  254. s++;
  255. }
  256. }
  257. // handle exponentials
  258. if (*s == 'e' || *s == 'E') {
  259. s++; // skip E
  260. bool isENegated = false;
  261. if (*s == '-' || *s == '+') {
  262. isENegated = *s=='-';
  263. s++;
  264. }
  265. int e = 0;
  266. while (*s) {
  267. if (*s >= '0' && *s <= '9')
  268. e = (e*10) + (*s - '0');
  269. else break;
  270. s++;
  271. }
  272. if (isENegated) e=-e;
  273. v = v * jswrap_math_pow(10, e);
  274. }
  275. // check we have parsed everything
  276. if (*s!=0) return NAN;
  277. if (isNegated) return -v;
  278. return v;
  279. }
  280. char itoch(int val) {
  281. if (val<10) return (char)('0'+val);
  282. return (char)('A'+val-10);
  283. }
  284. #ifndef HAS_STDLIB
  285. void itoa(JsVarInt vals,char *str,unsigned int base) {
  286. JsVarIntUnsigned val;
  287. if (vals<0) {
  288. *(str++)='-';
  289. val = (JsVarIntUnsigned)(-vals);
  290. } else {
  291. val = (JsVarIntUnsigned)vals;
  292. }
  293. JsVarIntUnsigned d = 1;
  294. while (d*base <= val) d*=base;
  295. while (d > 1) {
  296. unsigned int v = (unsigned int)(val / d);
  297. val -= v*d;
  298. *(str++) = itoch((int)v);
  299. d /= base;
  300. }
  301. *(str++)=itoch((int)val);
  302. *(str++)=0;
  303. }
  304. #endif
  305. void ftoa(JsVarFloat val,char *str) {
  306. if (isnan(val)) strncpy(str,"NaN",4);
  307. else if (!isfinite(val)) {
  308. if (val<0) strncpy(str,"-Infinity",10);
  309. else strncpy(str,"Infinity",10);
  310. } else {
  311. const JsVarFloat base = 10;
  312. if (val<0) {
  313. *(str++)='-';
  314. val = -val;
  315. }
  316. JsVarFloat d = 1;
  317. while (d*base <= val) d*=base;
  318. while (d >= 1) {
  319. int v = (int)(val / d);
  320. val -= v*d;
  321. *(str++)=itoch(v);
  322. d /= base;
  323. }
  324. #ifndef USE_NO_FLOATS
  325. if (val>0) {
  326. *(str++)='.';
  327. while (val>0.000001) {
  328. int v = (int)((val / d) + 0.0000005);
  329. val -= v*d;
  330. *(str++)=itoch(v);
  331. d /= base;
  332. }
  333. }
  334. #endif
  335. *(str++)=0;
  336. }
  337. }
  338. /// Wrap a value so it is always between 0 and size (eg. wrapAround(angle, 360))
  339. JsVarFloat wrapAround(JsVarFloat val, JsVarFloat size) {
  340. val = val / size;
  341. val = val - (int)val;
  342. return val * size;
  343. }
  344. /** Espruino-special printf with a callback
  345. * Supported are:
  346. * %d = int
  347. * %x = int as hex
  348. * %L = JsVarInt
  349. * %Lx = JsVarInt as hex
  350. * %f = JsVarFloat
  351. * %s = string (char *)
  352. * %c = char
  353. * %v = JsVar * (prints var as string)
  354. * %t = JsVar * (prints type of var)
  355. * %p = Pin
  356. *
  357. * Anything else will assert
  358. */
  359. void vcbprintf(vcbprintf_callback user_callback, void *user_data, const char *fmt, va_list argp) {
  360. char buf[32];
  361. while (*fmt) {
  362. if (*fmt == '%') {
  363. fmt++;
  364. switch (*fmt++) {
  365. case 'd': itoa(va_arg(argp, int), buf, 10); user_callback(buf,user_data); break;
  366. case 'x': itoa(va_arg(argp, int), buf, 16); user_callback(buf,user_data); break;
  367. case 'L': {
  368. unsigned int rad = 10;
  369. if (*fmt=='x') { rad=16; fmt++; }
  370. itoa(va_arg(argp, JsVarInt), buf, rad); user_callback(buf,user_data);
  371. } break;
  372. case 'f': ftoa(va_arg(argp, JsVarFloat), buf); user_callback(buf,user_data); break;
  373. case 's': user_callback(va_arg(argp, char *), user_data); break;
  374. case 'c': buf[0]=(char)va_arg(argp, int/*char*/);buf[1]=0; user_callback(buf, user_data); break;
  375. case 'v': {
  376. JsVar *v = jsvAsString(va_arg(argp, JsVar*), false/*no unlock*/);
  377. buf[1] = 0;
  378. JsvStringIterator it;
  379. jsvStringIteratorNew(&it, v, 0);
  380. // OPT: this could be faster than it is (sending whole blocks at once)
  381. while (jsvStringIteratorHasChar(&it)) {
  382. buf[0] = jsvStringIteratorGetChar(&it);
  383. user_callback(buf,user_data);
  384. jsvStringIteratorNext(&it);
  385. }
  386. jsvStringIteratorFree(&it);
  387. jsvUnLock(v);
  388. } break;
  389. case 't': user_callback(jsvGetTypeOf(va_arg(argp, JsVar*)), user_data); break;
  390. case 'p': jshGetPinString(buf, (Pin)va_arg(argp, int/*Pin*/)); user_callback(buf, user_data); break;
  391. default: assert(0); return; // eep
  392. }
  393. } else {
  394. buf[0] = *(fmt++);
  395. buf[1] = 0;
  396. user_callback(&buf[0], user_data);
  397. }
  398. }
  399. }