summaryrefslogtreecommitdiff
path: root/components/external/espruino/src/jsutils.h
blob: 4097830ed0932fcb590bec7bef89a82bf70c38aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
/*
 * This file is part of Espruino, a JavaScript interpreter for Microcontrollers
 *
 * Copyright (C) 2013 Gordon Williams <[email protected]>
 *
 * 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_ */