regex uses C strings now
This commit is contained in:
445
source/quickjs.c
445
source/quickjs.c
@@ -1435,8 +1435,10 @@ static inline uint32_t js_array_cap (JSArray *arr) {
|
|||||||
|
|
||||||
/* JSRegExp: regular expression object data (must come before JSRecord/JSRecord) */
|
/* JSRegExp: regular expression object data (must come before JSRecord/JSRecord) */
|
||||||
typedef struct JSRegExp {
|
typedef struct JSRegExp {
|
||||||
JSText *pattern;
|
char *pattern; /* UTF-8, null-terminated, js_malloc_rt'd */
|
||||||
JSText *bytecode; /* also contains the flags */
|
uint32_t pattern_len;
|
||||||
|
uint8_t *bytecode; /* raw lre bytecode, js_malloc_rt'd */
|
||||||
|
uint32_t bytecode_len;
|
||||||
} JSRegExp;
|
} JSRegExp;
|
||||||
|
|
||||||
#define obj_is_stone(rec) objhdr_s ((rec)->mist_hdr)
|
#define obj_is_stone(rec) objhdr_s ((rec)->mist_hdr)
|
||||||
@@ -19757,9 +19759,13 @@ static int js_is_regexp (JSContext *ctx, JSValue obj);
|
|||||||
/* RegExp */
|
/* RegExp */
|
||||||
|
|
||||||
static void js_regexp_finalizer (JSRuntime *rt, JSValue val) {
|
static void js_regexp_finalizer (JSRuntime *rt, JSValue val) {
|
||||||
/* With copying GC, memory is reclaimed automatically */
|
JSRegExp *re = JS_GetOpaque (val, JS_CLASS_REGEXP);
|
||||||
|
if (re) {
|
||||||
|
js_free_rt (re->pattern);
|
||||||
|
js_free_rt (re->bytecode);
|
||||||
|
js_free_rt (re);
|
||||||
|
}
|
||||||
(void)rt;
|
(void)rt;
|
||||||
(void)val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create a string containing the RegExp bytecode */
|
/* create a string containing the RegExp bytecode */
|
||||||
@@ -19844,8 +19850,11 @@ static JSValue js_regexp_constructor_internal (JSContext *ctx, JSValue pattern,
|
|||||||
JSValue obj;
|
JSValue obj;
|
||||||
JSRecord *p;
|
JSRecord *p;
|
||||||
JSRegExp *re;
|
JSRegExp *re;
|
||||||
|
const char *pat_cstr;
|
||||||
|
size_t pat_len;
|
||||||
|
int bc_len, i;
|
||||||
|
|
||||||
/* sanity check - need heap strings for pattern and bytecode */
|
/* sanity check - need strings for pattern and bytecode */
|
||||||
if (!JS_IsText (bc) || !JS_IsText (pattern)) {
|
if (!JS_IsText (bc) || !JS_IsText (pattern)) {
|
||||||
JS_ThrowTypeError (ctx, "string expected");
|
JS_ThrowTypeError (ctx, "string expected");
|
||||||
fail:
|
fail:
|
||||||
@@ -19859,9 +19868,39 @@ static JSValue js_regexp_constructor_internal (JSContext *ctx, JSValue pattern,
|
|||||||
re = js_malloc (ctx, sizeof(JSRegExp));
|
re = js_malloc (ctx, sizeof(JSRegExp));
|
||||||
if (!re) goto fail;
|
if (!re) goto fail;
|
||||||
REC_SET_OPAQUE(p, re);
|
REC_SET_OPAQUE(p, re);
|
||||||
/* Store pattern and bytecode - need to handle both immediate and heap strings */
|
re->pattern = NULL;
|
||||||
re->pattern = MIST_IsImmediateASCII (pattern) ? NULL : (JSText *)JS_VALUE_GET_PTR (pattern);
|
re->bytecode = NULL;
|
||||||
re->bytecode = MIST_IsImmediateASCII (bc) ? NULL : (JSText *)JS_VALUE_GET_PTR (bc);
|
|
||||||
|
/* Extract pattern as UTF-8 C string */
|
||||||
|
pat_cstr = JS_ToCStringLen (ctx, &pat_len, pattern);
|
||||||
|
if (!pat_cstr) goto fail;
|
||||||
|
re->pattern = js_malloc_rt (pat_len + 1);
|
||||||
|
if (!re->pattern) {
|
||||||
|
JS_FreeCString (ctx, pat_cstr);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
memcpy (re->pattern, pat_cstr, pat_len + 1);
|
||||||
|
re->pattern_len = (uint32_t)pat_len;
|
||||||
|
JS_FreeCString (ctx, pat_cstr);
|
||||||
|
|
||||||
|
/* Extract bytecode as raw bytes via string_get (not JS_ToCStringLen
|
||||||
|
which UTF-8 encodes and would mangle bytes >= 128) */
|
||||||
|
if (MIST_IsImmediateASCII (bc)) {
|
||||||
|
bc_len = MIST_GetImmediateASCIILen (bc);
|
||||||
|
re->bytecode = js_malloc_rt (bc_len);
|
||||||
|
if (!re->bytecode) goto fail;
|
||||||
|
for (i = 0; i < bc_len; i++)
|
||||||
|
re->bytecode[i] = (uint8_t)MIST_GetImmediateASCIIChar (bc, i);
|
||||||
|
} else {
|
||||||
|
JSText *bc_str = (JSText *)JS_VALUE_GET_PTR (bc);
|
||||||
|
bc_len = (int)JSText_len (bc_str);
|
||||||
|
re->bytecode = js_malloc_rt (bc_len);
|
||||||
|
if (!re->bytecode) goto fail;
|
||||||
|
for (i = 0; i < bc_len; i++)
|
||||||
|
re->bytecode[i] = (uint8_t)string_get (bc_str, i);
|
||||||
|
}
|
||||||
|
re->bytecode_len = (uint32_t)bc_len;
|
||||||
|
|
||||||
{
|
{
|
||||||
JSValue key = JS_KEY_STR (ctx, "lastIndex");
|
JSValue key = JS_KEY_STR (ctx, "lastIndex");
|
||||||
JS_SetPropertyInternal (ctx, obj, key, JS_NewInt32 (ctx, 0));
|
JS_SetPropertyInternal (ctx, obj, key, JS_NewInt32 (ctx, 0));
|
||||||
@@ -19906,9 +19945,11 @@ static JSValue js_regexp_constructor (JSContext *ctx, JSValue this_val, int argc
|
|||||||
}
|
}
|
||||||
re = js_get_regexp (ctx, pat, FALSE);
|
re = js_get_regexp (ctx, pat, FALSE);
|
||||||
if (re) {
|
if (re) {
|
||||||
pattern = JS_MKPTR (re->pattern);
|
pattern = JS_NewString (ctx, re->pattern);
|
||||||
|
if (JS_IsException (pattern)) goto fail;
|
||||||
if (JS_IsNull (flags1)) {
|
if (JS_IsNull (flags1)) {
|
||||||
bc = JS_MKPTR (re->bytecode);
|
bc = js_new_string8_len (ctx, (const char *)re->bytecode, re->bytecode_len);
|
||||||
|
if (JS_IsException (bc)) goto fail;
|
||||||
goto no_compilation;
|
goto no_compilation;
|
||||||
} else {
|
} else {
|
||||||
flags = JS_ToString (ctx, flags1);
|
flags = JS_ToString (ctx, flags1);
|
||||||
@@ -19949,6 +19990,9 @@ static JSValue js_regexp_compile (JSContext *ctx, JSValue this_val, int argc, JS
|
|||||||
JSRegExp *re1, *re;
|
JSRegExp *re1, *re;
|
||||||
JSValue pattern1, flags1;
|
JSValue pattern1, flags1;
|
||||||
JSValue bc, pattern;
|
JSValue bc, pattern;
|
||||||
|
const char *pat_cstr;
|
||||||
|
size_t pat_len;
|
||||||
|
int bc_len, i;
|
||||||
|
|
||||||
re = js_get_regexp (ctx, this_val, TRUE);
|
re = js_get_regexp (ctx, this_val, TRUE);
|
||||||
if (!re) return JS_EXCEPTION;
|
if (!re) return JS_EXCEPTION;
|
||||||
@@ -19958,8 +20002,10 @@ static JSValue js_regexp_compile (JSContext *ctx, JSValue this_val, int argc, JS
|
|||||||
if (re1) {
|
if (re1) {
|
||||||
if (!JS_IsNull (flags1))
|
if (!JS_IsNull (flags1))
|
||||||
return JS_ThrowTypeError (ctx, "flags must be undefined");
|
return JS_ThrowTypeError (ctx, "flags must be undefined");
|
||||||
pattern = JS_MKPTR (re1->pattern);
|
pattern = JS_NewString (ctx, re1->pattern);
|
||||||
bc = JS_MKPTR (re1->bytecode);
|
if (JS_IsException (pattern)) goto fail;
|
||||||
|
bc = js_new_string8_len (ctx, (const char *)re1->bytecode, re1->bytecode_len);
|
||||||
|
if (JS_IsException (bc)) goto fail;
|
||||||
} else {
|
} else {
|
||||||
bc = JS_NULL;
|
bc = JS_NULL;
|
||||||
if (JS_IsNull (pattern1))
|
if (JS_IsNull (pattern1))
|
||||||
@@ -19970,9 +20016,41 @@ static JSValue js_regexp_compile (JSContext *ctx, JSValue this_val, int argc, JS
|
|||||||
bc = js_compile_regexp (ctx, pattern, flags1);
|
bc = js_compile_regexp (ctx, pattern, flags1);
|
||||||
if (JS_IsException (bc)) goto fail;
|
if (JS_IsException (bc)) goto fail;
|
||||||
}
|
}
|
||||||
/* No need to free old values - copying GC handles it */
|
/* Free old C buffers */
|
||||||
re->pattern = JS_VALUE_GET_STRING (pattern);
|
js_free_rt (re->pattern);
|
||||||
re->bytecode = JS_VALUE_GET_STRING (bc);
|
re->pattern = NULL;
|
||||||
|
js_free_rt (re->bytecode);
|
||||||
|
re->bytecode = NULL;
|
||||||
|
|
||||||
|
/* Extract pattern as UTF-8 C string */
|
||||||
|
pat_cstr = JS_ToCStringLen (ctx, &pat_len, pattern);
|
||||||
|
if (!pat_cstr) goto fail;
|
||||||
|
re->pattern = js_malloc_rt (pat_len + 1);
|
||||||
|
if (!re->pattern) {
|
||||||
|
JS_FreeCString (ctx, pat_cstr);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
memcpy (re->pattern, pat_cstr, pat_len + 1);
|
||||||
|
re->pattern_len = (uint32_t)pat_len;
|
||||||
|
JS_FreeCString (ctx, pat_cstr);
|
||||||
|
|
||||||
|
/* Extract bytecode as raw bytes */
|
||||||
|
if (MIST_IsImmediateASCII (bc)) {
|
||||||
|
bc_len = MIST_GetImmediateASCIILen (bc);
|
||||||
|
re->bytecode = js_malloc_rt (bc_len);
|
||||||
|
if (!re->bytecode) goto fail;
|
||||||
|
for (i = 0; i < bc_len; i++)
|
||||||
|
re->bytecode[i] = (uint8_t)MIST_GetImmediateASCIIChar (bc, i);
|
||||||
|
} else {
|
||||||
|
JSText *bc_str = (JSText *)JS_VALUE_GET_PTR (bc);
|
||||||
|
bc_len = (int)JSText_len (bc_str);
|
||||||
|
re->bytecode = js_malloc_rt (bc_len);
|
||||||
|
if (!re->bytecode) goto fail;
|
||||||
|
for (i = 0; i < bc_len; i++)
|
||||||
|
re->bytecode[i] = (uint8_t)string_get (bc_str, i);
|
||||||
|
}
|
||||||
|
re->bytecode_len = (uint32_t)bc_len;
|
||||||
|
|
||||||
{
|
{
|
||||||
JSValue key = JS_KEY_STR (ctx, "lastIndex");
|
JSValue key = JS_KEY_STR (ctx, "lastIndex");
|
||||||
int ret = JS_SetProperty (ctx, this_val, key, JS_NewInt32 (ctx, 0));
|
int ret = JS_SetProperty (ctx, this_val, key, JS_NewInt32 (ctx, 0));
|
||||||
@@ -20050,7 +20128,8 @@ static uint16_t *js_string_to_utf16 (JSContext *ctx, JSText *str, int *out_len)
|
|||||||
static JSValue js_regexp_exec (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
static JSValue js_regexp_exec (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||||
JSRegExp *re = js_get_regexp (ctx, this_val, TRUE);
|
JSRegExp *re = js_get_regexp (ctx, this_val, TRUE);
|
||||||
JSText *str;
|
JSText *str;
|
||||||
JSValue ret, str_val, res, val, groups, captures_arr, match0;
|
JSGCRef str_ref;
|
||||||
|
JSValue ret, res, val, groups, captures_arr, match0;
|
||||||
uint8_t *re_bytecode;
|
uint8_t *re_bytecode;
|
||||||
uint8_t **capture, *str_buf;
|
uint8_t **capture, *str_buf;
|
||||||
uint16_t *utf16_buf = NULL;
|
uint16_t *utf16_buf = NULL;
|
||||||
@@ -20061,8 +20140,30 @@ static JSValue js_regexp_exec (JSContext *ctx, JSValue this_val, int argc, JSVal
|
|||||||
|
|
||||||
if (!re) return JS_EXCEPTION;
|
if (!re) return JS_EXCEPTION;
|
||||||
|
|
||||||
str_val = JS_ToString (ctx, argv[0]);
|
JS_PushGCRef (ctx, &str_ref);
|
||||||
if (JS_IsException (str_val)) return JS_EXCEPTION;
|
str_ref.val = JS_ToString (ctx, argv[0]);
|
||||||
|
if (JS_IsException (str_ref.val)) {
|
||||||
|
JS_PopGCRef (ctx, &str_ref);
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
}
|
||||||
|
/* Ensure str_val is a heap string for JS_VALUE_GET_STRING */
|
||||||
|
if (MIST_IsImmediateASCII (str_ref.val)) {
|
||||||
|
int imm_len = MIST_GetImmediateASCIILen (str_ref.val);
|
||||||
|
/* Allocate at least 1 word even for empty string to ensure heap allocation */
|
||||||
|
JSText *hs = js_alloc_string (ctx, imm_len > 0 ? imm_len : 1);
|
||||||
|
if (!hs) {
|
||||||
|
JS_PopGCRef (ctx, &str_ref);
|
||||||
|
return JS_EXCEPTION;
|
||||||
|
}
|
||||||
|
for (int ci = 0; ci < imm_len; ci++)
|
||||||
|
string_put (hs, ci, MIST_GetImmediateASCIIChar (str_ref.val, ci));
|
||||||
|
/* Set capacity to actual length and stone it directly (bypass pretext_end
|
||||||
|
which returns JS_KEY_empty for len=0) */
|
||||||
|
hs->hdr = objhdr_set_cap56 (hs->hdr, imm_len);
|
||||||
|
hs->length = 0;
|
||||||
|
hs->hdr = objhdr_set_s (hs->hdr, true);
|
||||||
|
str_ref.val = JS_MKPTR (hs);
|
||||||
|
}
|
||||||
|
|
||||||
ret = JS_EXCEPTION;
|
ret = JS_EXCEPTION;
|
||||||
res = JS_NULL;
|
res = JS_NULL;
|
||||||
@@ -20075,11 +20176,10 @@ static JSValue js_regexp_exec (JSContext *ctx, JSValue this_val, int argc, JSVal
|
|||||||
if (JS_IsException (val) || JS_ToLength (ctx, &last_index, val))
|
if (JS_IsException (val) || JS_ToLength (ctx, &last_index, val))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
re_bytecode = (uint8_t *)re->bytecode->packed;
|
re_bytecode = re->bytecode;
|
||||||
re_flags = lre_get_flags (re_bytecode);
|
re_flags = lre_get_flags (re_bytecode);
|
||||||
if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) last_index = 0;
|
if ((re_flags & (LRE_FLAG_GLOBAL | LRE_FLAG_STICKY)) == 0) last_index = 0;
|
||||||
|
|
||||||
str = JS_VALUE_GET_STRING (str_val);
|
|
||||||
capture_count = lre_get_capture_count (re_bytecode);
|
capture_count = lre_get_capture_count (re_bytecode);
|
||||||
|
|
||||||
if (capture_count > 0) {
|
if (capture_count > 0) {
|
||||||
@@ -20087,12 +20187,17 @@ static JSValue js_regexp_exec (JSContext *ctx, JSValue this_val, int argc, JSVal
|
|||||||
if (!capture) goto fail;
|
if (!capture) goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Refresh str after potential GC from js_malloc */
|
||||||
|
str = JS_VALUE_GET_STRING (str_ref.val);
|
||||||
|
|
||||||
/* Convert UTF-32 string to UTF-16 for regex engine */
|
/* Convert UTF-32 string to UTF-16 for regex engine */
|
||||||
utf16_buf = js_string_to_utf16 (ctx, str, &utf16_len);
|
utf16_buf = js_string_to_utf16 (ctx, str, &utf16_len);
|
||||||
if (!utf16_buf) goto fail;
|
if (!utf16_buf) goto fail;
|
||||||
shift = 1; /* UTF-16 mode */
|
shift = 1; /* UTF-16 mode */
|
||||||
str_buf = (uint8_t *)utf16_buf;
|
str_buf = (uint8_t *)utf16_buf;
|
||||||
|
|
||||||
|
/* Refresh str again after potential GC from js_string_to_utf16 */
|
||||||
|
str = JS_VALUE_GET_STRING (str_ref.val);
|
||||||
if (last_index > (int)JSText_len (str)) {
|
if (last_index > (int)JSText_len (str)) {
|
||||||
rc = 2;
|
rc = 2;
|
||||||
} else {
|
} else {
|
||||||
@@ -20160,6 +20265,7 @@ static JSValue js_regexp_exec (JSContext *ctx, JSValue this_val, int argc, JSVal
|
|||||||
|
|
||||||
s = JS_NULL;
|
s = JS_NULL;
|
||||||
if (start != -1) {
|
if (start != -1) {
|
||||||
|
str = JS_VALUE_GET_STRING (str_ref.val);
|
||||||
s = js_sub_string (ctx, str, start, end);
|
s = js_sub_string (ctx, str, start, end);
|
||||||
if (JS_IsException (s)) goto fail;
|
if (JS_IsException (s)) goto fail;
|
||||||
}
|
}
|
||||||
@@ -20208,11 +20314,13 @@ static JSValue js_regexp_exec (JSContext *ctx, JSValue this_val, int argc, JSVal
|
|||||||
res = JS_NULL;
|
res = JS_NULL;
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
JS_PopGCRef (ctx, &str_ref);
|
||||||
js_free (ctx, capture);
|
js_free (ctx, capture);
|
||||||
js_free (ctx, utf16_buf);
|
js_free (ctx, utf16_buf);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
JS_PopGCRef (ctx, &str_ref);
|
||||||
js_free (ctx, capture);
|
js_free (ctx, capture);
|
||||||
js_free (ctx, utf16_buf);
|
js_free (ctx, utf16_buf);
|
||||||
return JS_EXCEPTION;
|
return JS_EXCEPTION;
|
||||||
@@ -27736,6 +27844,7 @@ typedef struct ASTParseState {
|
|||||||
cJSON *errors; /* array of error objects */
|
cJSON *errors; /* array of error objects */
|
||||||
int has_error;
|
int has_error;
|
||||||
int in_disruption;
|
int in_disruption;
|
||||||
|
char *decoded_str; /* allocated buffer for decoded string escapes */
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
const char *str;
|
const char *str;
|
||||||
@@ -27808,18 +27917,9 @@ static BOOL ast_is_arrow_function (ASTParseState *s) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void ast_free_token (ASTParseState *s) {
|
static void ast_free_token (ASTParseState *s) {
|
||||||
switch (s->token_val) {
|
if (s->decoded_str) {
|
||||||
case TOK_STRING:
|
sys_free (s->decoded_str);
|
||||||
case TOK_TEMPLATE:
|
s->decoded_str = NULL;
|
||||||
break;
|
|
||||||
case TOK_IDENT:
|
|
||||||
break;
|
|
||||||
case TOK_REGEXP:
|
|
||||||
break;
|
|
||||||
case TOK_NUMBER:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27882,6 +27982,45 @@ static void ast_error (ASTParseState *s, const uint8_t *ptr, const char *fmt, ..
|
|||||||
s->has_error = 1;
|
s->has_error = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Decode escape sequences in a string literal into dst. Returns decoded length. */
|
||||||
|
static int ast_decode_string (const uint8_t *src, int len, char *dst) {
|
||||||
|
const uint8_t *end = src + len;
|
||||||
|
char *out = dst;
|
||||||
|
while (src < end) {
|
||||||
|
if (*src == '\\' && src + 1 < end) {
|
||||||
|
src++;
|
||||||
|
switch (*src) {
|
||||||
|
case 'n': *out++ = '\n'; src++; break;
|
||||||
|
case 't': *out++ = '\t'; src++; break;
|
||||||
|
case 'r': *out++ = '\r'; src++; break;
|
||||||
|
case '\\': *out++ = '\\'; src++; break;
|
||||||
|
case '\'': *out++ = '\''; src++; break;
|
||||||
|
case '\"': *out++ = '\"'; src++; break;
|
||||||
|
case '0': *out++ = '\0'; src++; break;
|
||||||
|
case 'b': *out++ = '\b'; src++; break;
|
||||||
|
case 'f': *out++ = '\f'; src++; break;
|
||||||
|
case 'v': *out++ = '\v'; src++; break;
|
||||||
|
case 'u': {
|
||||||
|
src++;
|
||||||
|
unsigned int cp = 0;
|
||||||
|
for (int i = 0; i < 4 && src < end; i++, src++) {
|
||||||
|
cp <<= 4;
|
||||||
|
if (*src >= '0' && *src <= '9') cp |= *src - '0';
|
||||||
|
else if (*src >= 'a' && *src <= 'f') cp |= *src - 'a' + 10;
|
||||||
|
else if (*src >= 'A' && *src <= 'F') cp |= *src - 'A' + 10;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
out += unicode_to_utf8 ((uint8_t *)out, cp);
|
||||||
|
} break;
|
||||||
|
default: *out++ = *src++; break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*out++ = *src++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out - dst;
|
||||||
|
}
|
||||||
|
|
||||||
static int ast_next_token (ASTParseState *s) {
|
static int ast_next_token (ASTParseState *s) {
|
||||||
const uint8_t *p;
|
const uint8_t *p;
|
||||||
int c;
|
int c;
|
||||||
@@ -27933,8 +28072,24 @@ redo:
|
|||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
s->token_val = TOK_TEMPLATE;
|
s->token_val = TOK_TEMPLATE;
|
||||||
s->token_u.str.str = (const char *)(start + 1);
|
{
|
||||||
s->token_u.str.len = p - start - 2;
|
const uint8_t *raw = start + 1;
|
||||||
|
int raw_len = p - start - 2;
|
||||||
|
BOOL has_escape = FALSE;
|
||||||
|
for (int i = 0; i < raw_len; i++) {
|
||||||
|
if (raw[i] == '\\') { has_escape = TRUE; break; }
|
||||||
|
}
|
||||||
|
if (has_escape) {
|
||||||
|
char *buf = sys_malloc (raw_len * 4 + 1);
|
||||||
|
int decoded_len = ast_decode_string (raw, raw_len, buf);
|
||||||
|
s->decoded_str = buf;
|
||||||
|
s->token_u.str.str = buf;
|
||||||
|
s->token_u.str.len = decoded_len;
|
||||||
|
} else {
|
||||||
|
s->token_u.str.str = (const char *)raw;
|
||||||
|
s->token_u.str.len = raw_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
case '\'':
|
case '\'':
|
||||||
case '\"': {
|
case '\"': {
|
||||||
@@ -27951,10 +28106,27 @@ redo:
|
|||||||
goto redo;
|
goto redo;
|
||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
/* Store the string content without quotes */
|
/* Store the string content without quotes, decoding escape sequences */
|
||||||
s->token_val = TOK_STRING;
|
s->token_val = TOK_STRING;
|
||||||
s->token_u.str.str = (const char *)(start + 1);
|
{
|
||||||
s->token_u.str.len = p - start - 2;
|
const uint8_t *raw = start + 1;
|
||||||
|
int raw_len = p - start - 2;
|
||||||
|
/* Check if any escape sequences need decoding */
|
||||||
|
BOOL has_escape = FALSE;
|
||||||
|
for (int i = 0; i < raw_len; i++) {
|
||||||
|
if (raw[i] == '\\') { has_escape = TRUE; break; }
|
||||||
|
}
|
||||||
|
if (has_escape) {
|
||||||
|
char *buf = sys_malloc (raw_len * 4 + 1);
|
||||||
|
int decoded_len = ast_decode_string (raw, raw_len, buf);
|
||||||
|
s->decoded_str = buf;
|
||||||
|
s->token_u.str.str = buf;
|
||||||
|
s->token_u.str.len = decoded_len;
|
||||||
|
} else {
|
||||||
|
s->token_u.str.str = (const char *)raw;
|
||||||
|
s->token_u.str.len = raw_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
case '\r':
|
case '\r':
|
||||||
if (p[1] == '\n') p++;
|
if (p[1] == '\n') p++;
|
||||||
@@ -28584,8 +28756,29 @@ static cJSON *ast_parse_primary (ASTParseState *s) {
|
|||||||
while (p < tmpl_end) {
|
while (p < tmpl_end) {
|
||||||
if (*p == '\\' && p + 1 < tmpl_end) {
|
if (*p == '\\' && p + 1 < tmpl_end) {
|
||||||
p++; /* skip backslash */
|
p++; /* skip backslash */
|
||||||
if (len + 1 >= cap) { cap *= 2; fmt = sys_realloc (fmt, cap); }
|
if (len + 8 >= cap) { cap *= 2; fmt = sys_realloc (fmt, cap); }
|
||||||
fmt[len++] = *p++;
|
switch (*p) {
|
||||||
|
case 'n': fmt[len++] = '\n'; p++; break;
|
||||||
|
case 't': fmt[len++] = '\t'; p++; break;
|
||||||
|
case 'r': fmt[len++] = '\r'; p++; break;
|
||||||
|
case '\\': fmt[len++] = '\\'; p++; break;
|
||||||
|
case '`': fmt[len++] = '`'; p++; break;
|
||||||
|
case '$': fmt[len++] = '$'; p++; break;
|
||||||
|
case '0': fmt[len++] = '\0'; p++; break;
|
||||||
|
case 'u': {
|
||||||
|
p++;
|
||||||
|
unsigned int cp = 0;
|
||||||
|
for (int i = 0; i < 4 && p < tmpl_end; i++, p++) {
|
||||||
|
cp <<= 4;
|
||||||
|
if (*p >= '0' && *p <= '9') cp |= *p - '0';
|
||||||
|
else if (*p >= 'a' && *p <= 'f') cp |= *p - 'a' + 10;
|
||||||
|
else if (*p >= 'A' && *p <= 'F') cp |= *p - 'A' + 10;
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
len += unicode_to_utf8 ((uint8_t *)fmt + len, cp);
|
||||||
|
} break;
|
||||||
|
default: fmt[len++] = *p++; break;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (*p == '$' && p + 1 < tmpl_end && p[1] == '{') {
|
if (*p == '$' && p + 1 < tmpl_end && p[1] == '{') {
|
||||||
@@ -29784,6 +29977,7 @@ static cJSON *ast_parse_program (ASTParseState *s) {
|
|||||||
|
|
||||||
typedef struct ASTSemVar {
|
typedef struct ASTSemVar {
|
||||||
const char *name;
|
const char *name;
|
||||||
|
const char *scope_name; /* disambiguated name for block-scope vars (NULL = use name) */
|
||||||
int is_const;
|
int is_const;
|
||||||
const char *make; /* "def", "var", "function", "input" */
|
const char *make; /* "def", "var", "function", "input" */
|
||||||
int function_nr; /* which function this var belongs to */
|
int function_nr; /* which function this var belongs to */
|
||||||
@@ -29798,6 +29992,7 @@ typedef struct ASTSemScope {
|
|||||||
int in_loop;
|
int in_loop;
|
||||||
int function_nr; /* function_nr of enclosing function */
|
int function_nr; /* function_nr of enclosing function */
|
||||||
int is_function_scope; /* 1 if this is a function's top-level scope */
|
int is_function_scope; /* 1 if this is a function's top-level scope */
|
||||||
|
int block_depth; /* 0 = function scope, 1+ = block scope */
|
||||||
} ASTSemScope;
|
} ASTSemScope;
|
||||||
|
|
||||||
typedef struct ASTSemState {
|
typedef struct ASTSemState {
|
||||||
@@ -29806,6 +30001,7 @@ typedef struct ASTSemState {
|
|||||||
cJSON *scopes_array;
|
cJSON *scopes_array;
|
||||||
const char *intrinsics[256];
|
const char *intrinsics[256];
|
||||||
int intrinsic_count;
|
int intrinsic_count;
|
||||||
|
int block_var_counter; /* monotonically increasing counter for unique block var names */
|
||||||
} ASTSemState;
|
} ASTSemState;
|
||||||
|
|
||||||
static void ast_sem_error (ASTSemState *st, cJSON *node, const char *fmt, ...) {
|
static void ast_sem_error (ASTSemState *st, cJSON *node, const char *fmt, ...) {
|
||||||
@@ -29834,6 +30030,7 @@ static void ast_sem_add_var (ASTSemScope *scope, const char *name, int is_const,
|
|||||||
if (scope->var_count < AST_SEM_MAX_VARS) {
|
if (scope->var_count < AST_SEM_MAX_VARS) {
|
||||||
ASTSemVar *v = &scope->vars[scope->var_count];
|
ASTSemVar *v = &scope->vars[scope->var_count];
|
||||||
v->name = name;
|
v->name = name;
|
||||||
|
v->scope_name = NULL;
|
||||||
v->is_const = is_const;
|
v->is_const = is_const;
|
||||||
v->make = make;
|
v->make = make;
|
||||||
v->function_nr = function_nr;
|
v->function_nr = function_nr;
|
||||||
@@ -29843,6 +30040,26 @@ static void ast_sem_add_var (ASTSemScope *scope, const char *name, int is_const,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Propagate block-scope vars to the function scope (parent) with disambiguated names */
|
||||||
|
static void ast_sem_propagate_block_vars (ASTSemState *st, ASTSemScope *parent,
|
||||||
|
ASTSemScope *block) {
|
||||||
|
for (int i = 0; i < block->var_count; i++) {
|
||||||
|
ASTSemVar *v = &block->vars[i];
|
||||||
|
const char *sn = v->scope_name ? v->scope_name : v->name;
|
||||||
|
if (parent->var_count < AST_SEM_MAX_VARS) {
|
||||||
|
ASTSemVar *pv = &parent->vars[parent->var_count];
|
||||||
|
pv->name = sn;
|
||||||
|
pv->scope_name = NULL;
|
||||||
|
pv->is_const = v->is_const;
|
||||||
|
pv->make = v->make;
|
||||||
|
pv->function_nr = v->function_nr;
|
||||||
|
pv->nr_uses = v->nr_uses;
|
||||||
|
pv->closure = v->closure;
|
||||||
|
parent->var_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ASTSemVar *var;
|
ASTSemVar *var;
|
||||||
int level;
|
int level;
|
||||||
@@ -29934,6 +30151,8 @@ static void ast_sem_check_assign_target (ASTSemState *st, ASTSemScope *scope, cJ
|
|||||||
if (r.var) {
|
if (r.var) {
|
||||||
cJSON_AddNumberToObject (left, "level", r.level);
|
cJSON_AddNumberToObject (left, "level", r.level);
|
||||||
cJSON_AddNumberToObject (left, "function_nr", r.def_function_nr);
|
cJSON_AddNumberToObject (left, "function_nr", r.def_function_nr);
|
||||||
|
if (r.var->scope_name)
|
||||||
|
cJSON_AddStringToObject (left, "scope_name", r.var->scope_name);
|
||||||
} else {
|
} else {
|
||||||
cJSON_AddNumberToObject (left, "level", -1);
|
cJSON_AddNumberToObject (left, "level", -1);
|
||||||
}
|
}
|
||||||
@@ -30132,6 +30351,8 @@ static void ast_sem_check_expr (ASTSemState *st, ASTSemScope *scope, cJSON *expr
|
|||||||
cJSON_AddNumberToObject (expr, "function_nr", r.def_function_nr);
|
cJSON_AddNumberToObject (expr, "function_nr", r.def_function_nr);
|
||||||
r.var->nr_uses++;
|
r.var->nr_uses++;
|
||||||
if (r.level > 0) r.var->closure = 1;
|
if (r.level > 0) r.var->closure = 1;
|
||||||
|
if (r.var->scope_name)
|
||||||
|
cJSON_AddStringToObject (expr, "scope_name", r.var->scope_name);
|
||||||
} else {
|
} else {
|
||||||
cJSON_AddNumberToObject (expr, "level", -1);
|
cJSON_AddNumberToObject (expr, "level", -1);
|
||||||
ast_sem_add_intrinsic (st, name);
|
ast_sem_add_intrinsic (st, name);
|
||||||
@@ -30167,6 +30388,14 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
|
|||||||
ast_sem_error (st, left, "cannot redeclare constant '%s'", name);
|
ast_sem_error (st, left, "cannot redeclare constant '%s'", name);
|
||||||
}
|
}
|
||||||
ast_sem_add_var (scope, name, 0, "var", scope->function_nr);
|
ast_sem_add_var (scope, name, 0, "var", scope->function_nr);
|
||||||
|
if (scope->block_depth > 0) {
|
||||||
|
char buf[128];
|
||||||
|
snprintf (buf, sizeof (buf), "_%s_%d", name, st->block_var_counter++);
|
||||||
|
char *sn = sys_malloc (strlen (buf) + 1);
|
||||||
|
strcpy (sn, buf);
|
||||||
|
scope->vars[scope->var_count - 1].scope_name = sn;
|
||||||
|
cJSON_AddStringToObject (left, "scope_name", sn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ast_sem_check_expr (st, scope, cJSON_GetObjectItem (stmt, "right"));
|
ast_sem_check_expr (st, scope, cJSON_GetObjectItem (stmt, "right"));
|
||||||
return;
|
return;
|
||||||
@@ -30184,6 +30413,14 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
|
|||||||
ast_sem_error (st, left, "cannot redeclare '%s' as constant", name);
|
ast_sem_error (st, left, "cannot redeclare '%s' as constant", name);
|
||||||
}
|
}
|
||||||
ast_sem_add_var (scope, name, 1, "def", scope->function_nr);
|
ast_sem_add_var (scope, name, 1, "def", scope->function_nr);
|
||||||
|
if (scope->block_depth > 0) {
|
||||||
|
char buf[128];
|
||||||
|
snprintf (buf, sizeof (buf), "_%s_%d", name, st->block_var_counter++);
|
||||||
|
char *sn = sys_malloc (strlen (buf) + 1);
|
||||||
|
strcpy (sn, buf);
|
||||||
|
scope->vars[scope->var_count - 1].scope_name = sn;
|
||||||
|
cJSON_AddStringToObject (left, "scope_name", sn);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ast_sem_check_expr (st, scope, cJSON_GetObjectItem (stmt, "right"));
|
ast_sem_check_expr (st, scope, cJSON_GetObjectItem (stmt, "right"));
|
||||||
return;
|
return;
|
||||||
@@ -30197,14 +30434,35 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
|
|||||||
if (strcmp (kind, "if") == 0) {
|
if (strcmp (kind, "if") == 0) {
|
||||||
ast_sem_check_expr (st, scope, cJSON_GetObjectItem (stmt, "expression"));
|
ast_sem_check_expr (st, scope, cJSON_GetObjectItem (stmt, "expression"));
|
||||||
cJSON *s2;
|
cJSON *s2;
|
||||||
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "then")) {
|
{
|
||||||
ast_sem_check_stmt (st, scope, s2);
|
ASTSemScope then_scope = {0};
|
||||||
|
then_scope.parent = scope;
|
||||||
|
then_scope.function_nr = scope->function_nr;
|
||||||
|
then_scope.block_depth = scope->block_depth + 1;
|
||||||
|
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "then")) {
|
||||||
|
ast_sem_check_stmt (st, &then_scope, s2);
|
||||||
|
}
|
||||||
|
ast_sem_propagate_block_vars (st, scope, &then_scope);
|
||||||
}
|
}
|
||||||
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "list")) {
|
{
|
||||||
ast_sem_check_stmt (st, scope, s2);
|
ASTSemScope list_scope = {0};
|
||||||
|
list_scope.parent = scope;
|
||||||
|
list_scope.function_nr = scope->function_nr;
|
||||||
|
list_scope.block_depth = scope->block_depth + 1;
|
||||||
|
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "list")) {
|
||||||
|
ast_sem_check_stmt (st, &list_scope, s2);
|
||||||
|
}
|
||||||
|
ast_sem_propagate_block_vars (st, scope, &list_scope);
|
||||||
}
|
}
|
||||||
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "else")) {
|
{
|
||||||
ast_sem_check_stmt (st, scope, s2);
|
ASTSemScope else_scope = {0};
|
||||||
|
else_scope.parent = scope;
|
||||||
|
else_scope.function_nr = scope->function_nr;
|
||||||
|
else_scope.block_depth = scope->block_depth + 1;
|
||||||
|
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "else")) {
|
||||||
|
ast_sem_check_stmt (st, &else_scope, s2);
|
||||||
|
}
|
||||||
|
ast_sem_propagate_block_vars (st, scope, &else_scope);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -30215,10 +30473,12 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
|
|||||||
loop_scope.parent = scope;
|
loop_scope.parent = scope;
|
||||||
loop_scope.in_loop = 1;
|
loop_scope.in_loop = 1;
|
||||||
loop_scope.function_nr = scope->function_nr;
|
loop_scope.function_nr = scope->function_nr;
|
||||||
|
loop_scope.block_depth = scope->block_depth + 1;
|
||||||
cJSON *s2;
|
cJSON *s2;
|
||||||
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "statements")) {
|
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "statements")) {
|
||||||
ast_sem_check_stmt (st, &loop_scope, s2);
|
ast_sem_check_stmt (st, &loop_scope, s2);
|
||||||
}
|
}
|
||||||
|
ast_sem_propagate_block_vars (st, scope, &loop_scope);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30227,10 +30487,12 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
|
|||||||
loop_scope.parent = scope;
|
loop_scope.parent = scope;
|
||||||
loop_scope.in_loop = 1;
|
loop_scope.in_loop = 1;
|
||||||
loop_scope.function_nr = scope->function_nr;
|
loop_scope.function_nr = scope->function_nr;
|
||||||
|
loop_scope.block_depth = scope->block_depth + 1;
|
||||||
cJSON *s2;
|
cJSON *s2;
|
||||||
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "statements")) {
|
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "statements")) {
|
||||||
ast_sem_check_stmt (st, &loop_scope, s2);
|
ast_sem_check_stmt (st, &loop_scope, s2);
|
||||||
}
|
}
|
||||||
|
ast_sem_propagate_block_vars (st, scope, &loop_scope);
|
||||||
ast_sem_check_expr (st, scope, cJSON_GetObjectItem (stmt, "expression"));
|
ast_sem_check_expr (st, scope, cJSON_GetObjectItem (stmt, "expression"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -30240,6 +30502,7 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
|
|||||||
loop_scope.parent = scope;
|
loop_scope.parent = scope;
|
||||||
loop_scope.in_loop = 1;
|
loop_scope.in_loop = 1;
|
||||||
loop_scope.function_nr = scope->function_nr;
|
loop_scope.function_nr = scope->function_nr;
|
||||||
|
loop_scope.block_depth = scope->block_depth + 1;
|
||||||
/* init may be a var/def statement or expression */
|
/* init may be a var/def statement or expression */
|
||||||
cJSON *init = cJSON_GetObjectItem (stmt, "init");
|
cJSON *init = cJSON_GetObjectItem (stmt, "init");
|
||||||
if (init) {
|
if (init) {
|
||||||
@@ -30256,6 +30519,7 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
|
|||||||
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "statements")) {
|
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "statements")) {
|
||||||
ast_sem_check_stmt (st, &loop_scope, s2);
|
ast_sem_check_stmt (st, &loop_scope, s2);
|
||||||
}
|
}
|
||||||
|
ast_sem_propagate_block_vars (st, scope, &loop_scope);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30286,10 +30550,12 @@ static void ast_sem_check_stmt (ASTSemState *st, ASTSemScope *scope, cJSON *stmt
|
|||||||
ASTSemScope block_scope = {0};
|
ASTSemScope block_scope = {0};
|
||||||
block_scope.parent = scope;
|
block_scope.parent = scope;
|
||||||
block_scope.function_nr = scope->function_nr;
|
block_scope.function_nr = scope->function_nr;
|
||||||
|
block_scope.block_depth = scope->block_depth + 1;
|
||||||
cJSON *s2;
|
cJSON *s2;
|
||||||
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "statements")) {
|
cJSON_ArrayForEach (s2, cJSON_GetObjectItem (stmt, "statements")) {
|
||||||
ast_sem_check_stmt (st, &block_scope, s2);
|
ast_sem_check_stmt (st, &block_scope, s2);
|
||||||
}
|
}
|
||||||
|
ast_sem_propagate_block_vars (st, scope, &block_scope);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33361,11 +33627,13 @@ static int mach_gen_compound_assign (MachGenState *s, cJSON *node, const char *o
|
|||||||
|
|
||||||
if (strcmp (left_kind, "name") == 0) {
|
if (strcmp (left_kind, "name") == 0) {
|
||||||
const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (left, "name"));
|
const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (left, "name"));
|
||||||
|
const char *sn = cJSON_GetStringValue (cJSON_GetObjectItem (left, "scope_name"));
|
||||||
|
const char *ln = sn ? sn : name;
|
||||||
cJSON *level_node = cJSON_GetObjectItem (left, "level");
|
cJSON *level_node = cJSON_GetObjectItem (left, "level");
|
||||||
int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1;
|
int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1;
|
||||||
int left_slot = mach_gen_alloc_slot (s);
|
int left_slot = mach_gen_alloc_slot (s);
|
||||||
if (level == 0 || level == -1) {
|
if (level == 0 || level == -1) {
|
||||||
int local = mach_gen_find_var (s, name);
|
int local = mach_gen_find_var (s, ln);
|
||||||
if (local >= 0) {
|
if (local >= 0) {
|
||||||
mach_gen_emit_2 (s, "move", left_slot, local);
|
mach_gen_emit_2 (s, "move", left_slot, local);
|
||||||
level = 0; /* treat as local for the store below */
|
level = 0; /* treat as local for the store below */
|
||||||
@@ -33374,7 +33642,7 @@ static int mach_gen_compound_assign (MachGenState *s, cJSON *node, const char *o
|
|||||||
if (level > 0) {
|
if (level > 0) {
|
||||||
MachGenState *target = s;
|
MachGenState *target = s;
|
||||||
for (int i = 0; i < level; i++) target = target->parent;
|
for (int i = 0; i < level; i++) target = target->parent;
|
||||||
int slot = mach_gen_find_var (target, name);
|
int slot = mach_gen_find_var (target, ln);
|
||||||
mach_gen_emit_3 (s, "get", left_slot, slot, level);
|
mach_gen_emit_3 (s, "get", left_slot, slot, level);
|
||||||
} else if (level == -1) {
|
} else if (level == -1) {
|
||||||
cJSON *instr = cJSON_CreateArray ();
|
cJSON *instr = cJSON_CreateArray ();
|
||||||
@@ -33391,12 +33659,12 @@ static int mach_gen_compound_assign (MachGenState *s, cJSON *node, const char *o
|
|||||||
int dest = mach_gen_alloc_slot (s);
|
int dest = mach_gen_alloc_slot (s);
|
||||||
mach_gen_emit_3 (s, op, dest, left_slot, right_slot);
|
mach_gen_emit_3 (s, op, dest, left_slot, right_slot);
|
||||||
if (level == 0) {
|
if (level == 0) {
|
||||||
int local = mach_gen_find_var (s, name);
|
int local = mach_gen_find_var (s, ln);
|
||||||
if (local >= 0) mach_gen_emit_2 (s, "move", local, dest);
|
if (local >= 0) mach_gen_emit_2 (s, "move", local, dest);
|
||||||
} else if (level > 0) {
|
} else if (level > 0) {
|
||||||
MachGenState *target = s;
|
MachGenState *target = s;
|
||||||
for (int i = 0; i < level; i++) target = target->parent;
|
for (int i = 0; i < level; i++) target = target->parent;
|
||||||
int slot = mach_gen_find_var (target, name);
|
int slot = mach_gen_find_var (target, ln);
|
||||||
mach_gen_emit_3 (s, "put", dest, slot, level);
|
mach_gen_emit_3 (s, "put", dest, slot, level);
|
||||||
} else {
|
} else {
|
||||||
cJSON *instr = cJSON_CreateArray ();
|
cJSON *instr = cJSON_CreateArray ();
|
||||||
@@ -33450,15 +33718,27 @@ static int mach_gen_assign (MachGenState *s, cJSON *node) {
|
|||||||
if (strcmp (kind, ">>=") == 0) return mach_gen_compound_assign (s, node, "shr");
|
if (strcmp (kind, ">>=") == 0) return mach_gen_compound_assign (s, node, "shr");
|
||||||
if (strcmp (kind, ">>>=") == 0) return mach_gen_compound_assign (s, node, "ushr");
|
if (strcmp (kind, ">>>=") == 0) return mach_gen_compound_assign (s, node, "ushr");
|
||||||
|
|
||||||
|
/* Push: arr[] = val */
|
||||||
|
cJSON *push_flag = cJSON_GetObjectItem (node, "push");
|
||||||
|
if (push_flag && cJSON_IsTrue (push_flag)) {
|
||||||
|
cJSON *arr_expr = cJSON_GetObjectItem (left, "left");
|
||||||
|
int arr_slot = mach_gen_expr (s, arr_expr, -1);
|
||||||
|
int val_slot = mach_gen_expr (s, right, -1);
|
||||||
|
mach_gen_emit_2 (s, "push", arr_slot, val_slot);
|
||||||
|
return val_slot;
|
||||||
|
}
|
||||||
|
|
||||||
int val_slot = mach_gen_expr (s, right, -1);
|
int val_slot = mach_gen_expr (s, right, -1);
|
||||||
const char *left_kind = cJSON_GetStringValue (cJSON_GetObjectItem (left, "kind"));
|
const char *left_kind = cJSON_GetStringValue (cJSON_GetObjectItem (left, "kind"));
|
||||||
|
|
||||||
if (strcmp (left_kind, "name") == 0) {
|
if (strcmp (left_kind, "name") == 0) {
|
||||||
const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (left, "name"));
|
const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (left, "name"));
|
||||||
|
const char *sn = cJSON_GetStringValue (cJSON_GetObjectItem (left, "scope_name"));
|
||||||
|
const char *ln = sn ? sn : name;
|
||||||
cJSON *level_node = cJSON_GetObjectItem (left, "level");
|
cJSON *level_node = cJSON_GetObjectItem (left, "level");
|
||||||
int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1;
|
int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1;
|
||||||
if (level == 0 || level == -1) {
|
if (level == 0 || level == -1) {
|
||||||
int slot = mach_gen_find_var (s, name);
|
int slot = mach_gen_find_var (s, ln);
|
||||||
if (slot >= 0) mach_gen_emit_2 (s, "move", slot, val_slot);
|
if (slot >= 0) mach_gen_emit_2 (s, "move", slot, val_slot);
|
||||||
else if (level == -1) {
|
else if (level == -1) {
|
||||||
/* No annotation and not local — set global */
|
/* No annotation and not local — set global */
|
||||||
@@ -33471,7 +33751,7 @@ static int mach_gen_assign (MachGenState *s, cJSON *node) {
|
|||||||
} else if (level > 0) {
|
} else if (level > 0) {
|
||||||
MachGenState *target = s;
|
MachGenState *target = s;
|
||||||
for (int i = 0; i < level; i++) target = target->parent;
|
for (int i = 0; i < level; i++) target = target->parent;
|
||||||
int slot = mach_gen_find_var (target, name);
|
int slot = mach_gen_find_var (target, ln);
|
||||||
mach_gen_emit_3 (s, "put", val_slot, slot, level);
|
mach_gen_emit_3 (s, "put", val_slot, slot, level);
|
||||||
} else {
|
} else {
|
||||||
mach_gen_error (s, node, "cannot assign to unbound variable '%s'", name);
|
mach_gen_error (s, node, "cannot assign to unbound variable '%s'", name);
|
||||||
@@ -33512,6 +33792,18 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) {
|
|||||||
mach_gen_emit_const_str (s, slot, val ? val : "");
|
mach_gen_emit_const_str (s, slot, val ? val : "");
|
||||||
return slot;
|
return slot;
|
||||||
}
|
}
|
||||||
|
if (strcmp (kind, "regexp") == 0) {
|
||||||
|
int slot = target >= 0 ? target : mach_gen_alloc_slot (s);
|
||||||
|
const char *pattern = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "pattern"));
|
||||||
|
const char *flags = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "flags"));
|
||||||
|
cJSON *instr = cJSON_CreateArray ();
|
||||||
|
cJSON_AddItemToArray (instr, cJSON_CreateString ("regexp"));
|
||||||
|
cJSON_AddItemToArray (instr, cJSON_CreateNumber (slot));
|
||||||
|
cJSON_AddItemToArray (instr, cJSON_CreateString (pattern ? pattern : ""));
|
||||||
|
cJSON_AddItemToArray (instr, cJSON_CreateString (flags ? flags : ""));
|
||||||
|
mach_gen_add_instr (s, instr);
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
if (strcmp (kind, "true") == 0) {
|
if (strcmp (kind, "true") == 0) {
|
||||||
int slot = target >= 0 ? target : mach_gen_alloc_slot (s);
|
int slot = target >= 0 ? target : mach_gen_alloc_slot (s);
|
||||||
mach_gen_emit_const_bool (s, slot, 1);
|
mach_gen_emit_const_bool (s, slot, 1);
|
||||||
@@ -33534,16 +33826,18 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) {
|
|||||||
/* Variable reference — uses parser-provided level annotation */
|
/* Variable reference — uses parser-provided level annotation */
|
||||||
if (strcmp (kind, "name") == 0) {
|
if (strcmp (kind, "name") == 0) {
|
||||||
const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "name"));
|
const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "name"));
|
||||||
|
const char *scope_name = cJSON_GetStringValue (cJSON_GetObjectItem (expr, "scope_name"));
|
||||||
|
const char *lookup_name = scope_name ? scope_name : name;
|
||||||
cJSON *level_node = cJSON_GetObjectItem (expr, "level");
|
cJSON *level_node = cJSON_GetObjectItem (expr, "level");
|
||||||
int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1;
|
int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1;
|
||||||
if (level == 0 || level == -1) {
|
if (level == 0 || level == -1) {
|
||||||
/* level 0 = known local; level -1 = no annotation, try local first */
|
/* level 0 = known local; level -1 = no annotation, try local first */
|
||||||
int slot = mach_gen_find_var (s, name);
|
int slot = mach_gen_find_var (s, lookup_name);
|
||||||
if (slot >= 0) return slot;
|
if (slot >= 0) return slot;
|
||||||
} else if (level > 0) {
|
} else if (level > 0) {
|
||||||
MachGenState *target = s;
|
MachGenState *target = s;
|
||||||
for (int i = 0; i < level; i++) target = target->parent;
|
for (int i = 0; i < level; i++) target = target->parent;
|
||||||
int parent_slot = mach_gen_find_var (target, name);
|
int parent_slot = mach_gen_find_var (target, lookup_name);
|
||||||
int dest = mach_gen_alloc_slot (s);
|
int dest = mach_gen_alloc_slot (s);
|
||||||
mach_gen_emit_3 (s, "get", dest, parent_slot, level);
|
mach_gen_emit_3 (s, "get", dest, parent_slot, level);
|
||||||
return dest;
|
return dest;
|
||||||
@@ -33649,17 +33943,19 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) {
|
|||||||
|
|
||||||
if (strcmp (operand_kind, "name") == 0) {
|
if (strcmp (operand_kind, "name") == 0) {
|
||||||
const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (operand, "name"));
|
const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (operand, "name"));
|
||||||
|
const char *inc_sn = cJSON_GetStringValue (cJSON_GetObjectItem (operand, "scope_name"));
|
||||||
|
const char *inc_ln = inc_sn ? inc_sn : name;
|
||||||
cJSON *level_node = cJSON_GetObjectItem (operand, "level");
|
cJSON *level_node = cJSON_GetObjectItem (operand, "level");
|
||||||
int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1;
|
int level = level_node ? (int)cJSON_GetNumberValue (level_node) : -1;
|
||||||
int old_slot = mach_gen_alloc_slot (s);
|
int old_slot = mach_gen_alloc_slot (s);
|
||||||
/* Load current value */
|
/* Load current value */
|
||||||
if (level == 0) {
|
if (level == 0) {
|
||||||
int local = mach_gen_find_var (s, name);
|
int local = mach_gen_find_var (s, inc_ln);
|
||||||
if (local >= 0) mach_gen_emit_2 (s, "move", old_slot, local);
|
if (local >= 0) mach_gen_emit_2 (s, "move", old_slot, local);
|
||||||
} else if (level > 0) {
|
} else if (level > 0) {
|
||||||
MachGenState *target = s;
|
MachGenState *target = s;
|
||||||
for (int i = 0; i < level; i++) target = target->parent;
|
for (int i = 0; i < level; i++) target = target->parent;
|
||||||
int slot = mach_gen_find_var (target, name);
|
int slot = mach_gen_find_var (target, inc_ln);
|
||||||
mach_gen_emit_3 (s, "get", old_slot, slot, level);
|
mach_gen_emit_3 (s, "get", old_slot, slot, level);
|
||||||
} else {
|
} else {
|
||||||
cJSON *instr = cJSON_CreateArray ();
|
cJSON *instr = cJSON_CreateArray ();
|
||||||
@@ -33676,12 +33972,12 @@ static int mach_gen_expr (MachGenState *s, cJSON *expr, int target) {
|
|||||||
mach_gen_emit_3 (s, arith_op, new_slot, old_slot, one_slot);
|
mach_gen_emit_3 (s, arith_op, new_slot, old_slot, one_slot);
|
||||||
/* Store new value */
|
/* Store new value */
|
||||||
if (level == 0) {
|
if (level == 0) {
|
||||||
int local = mach_gen_find_var (s, name);
|
int local = mach_gen_find_var (s, inc_ln);
|
||||||
if (local >= 0) mach_gen_emit_2 (s, "move", local, new_slot);
|
if (local >= 0) mach_gen_emit_2 (s, "move", local, new_slot);
|
||||||
} else if (level > 0) {
|
} else if (level > 0) {
|
||||||
MachGenState *target = s;
|
MachGenState *target = s;
|
||||||
for (int i = 0; i < level; i++) target = target->parent;
|
for (int i = 0; i < level; i++) target = target->parent;
|
||||||
int slot = mach_gen_find_var (target, name);
|
int slot = mach_gen_find_var (target, inc_ln);
|
||||||
mach_gen_emit_3 (s, "put", new_slot, slot, level);
|
mach_gen_emit_3 (s, "put", new_slot, slot, level);
|
||||||
}
|
}
|
||||||
return postfix ? old_slot : new_slot;
|
return postfix ? old_slot : new_slot;
|
||||||
@@ -33848,7 +34144,18 @@ static void mach_gen_statement (MachGenState *s, cJSON *stmt) {
|
|||||||
cJSON *left = cJSON_GetObjectItem (stmt, "left");
|
cJSON *left = cJSON_GetObjectItem (stmt, "left");
|
||||||
cJSON *right = cJSON_GetObjectItem (stmt, "right");
|
cJSON *right = cJSON_GetObjectItem (stmt, "right");
|
||||||
const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (left, "name"));
|
const char *name = cJSON_GetStringValue (cJSON_GetObjectItem (left, "name"));
|
||||||
int local_slot = mach_gen_find_var (s, name);
|
const char *scope_name = cJSON_GetStringValue (cJSON_GetObjectItem (left, "scope_name"));
|
||||||
|
const char *lookup_name = scope_name ? scope_name : name;
|
||||||
|
int local_slot = mach_gen_find_var (s, lookup_name);
|
||||||
|
/* Pop: var val = arr[] */
|
||||||
|
cJSON *pop_flag = cJSON_GetObjectItem (stmt, "pop");
|
||||||
|
if (pop_flag && cJSON_IsTrue (pop_flag) && right) {
|
||||||
|
cJSON *arr_expr = cJSON_GetObjectItem (right, "left");
|
||||||
|
int arr_slot = mach_gen_expr (s, arr_expr, -1);
|
||||||
|
if (local_slot >= 0)
|
||||||
|
mach_gen_emit_2 (s, "pop", local_slot, arr_slot);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (right) {
|
if (right) {
|
||||||
int val_slot = mach_gen_expr (s, right, local_slot);
|
int val_slot = mach_gen_expr (s, right, local_slot);
|
||||||
if (local_slot >= 0 && val_slot != local_slot)
|
if (local_slot >= 0 && val_slot != local_slot)
|
||||||
@@ -34325,6 +34632,7 @@ static cJSON *mach_gen_program (MachGenState *s, cJSON *ast) {
|
|||||||
mach_gen_statement (s, stmt);
|
mach_gen_statement (s, stmt);
|
||||||
last_expr_slot = -1;
|
last_expr_slot = -1;
|
||||||
} else if (strcmp (kind, "var") == 0 || strcmp (kind, "def") == 0 ||
|
} else if (strcmp (kind, "var") == 0 || strcmp (kind, "def") == 0 ||
|
||||||
|
strcmp (kind, "var_list") == 0 || strcmp (kind, "def_list") == 0 ||
|
||||||
strcmp (kind, "function") == 0 || strcmp (kind, "block") == 0 ||
|
strcmp (kind, "function") == 0 || strcmp (kind, "block") == 0 ||
|
||||||
strcmp (kind, "if") == 0 || strcmp (kind, "while") == 0 ||
|
strcmp (kind, "if") == 0 || strcmp (kind, "while") == 0 ||
|
||||||
strcmp (kind, "do") == 0 || strcmp (kind, "for") == 0 ||
|
strcmp (kind, "do") == 0 || strcmp (kind, "for") == 0 ||
|
||||||
@@ -35673,6 +35981,27 @@ static JSValue mcode_exec(JSContext *ctx, JSMCode *code, JSValue this_obj,
|
|||||||
frame->slots[dest] = stoned;
|
frame->slots[dest] = stoned;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- Regexp literal ---- */
|
||||||
|
else if (strcmp(op, "regexp") == 0) {
|
||||||
|
int dest = (int)a1->valuedouble;
|
||||||
|
const char *pattern = a2 ? a2->valuestring : "";
|
||||||
|
cJSON *a3 = cJSON_GetArrayItem(instr, 3);
|
||||||
|
const char *flags_str = a3 ? a3->valuestring : "";
|
||||||
|
if (!pattern) pattern = "";
|
||||||
|
if (!flags_str) flags_str = "";
|
||||||
|
JSValue pat_val = JS_NewString(ctx, pattern);
|
||||||
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
|
JSValue flags_val = *flags_str ? JS_NewString(ctx, flags_str) : JS_NULL;
|
||||||
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
|
JSValue bc = js_compile_regexp(ctx, pat_val, flags_val);
|
||||||
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
|
if (JS_IsException(bc)) { goto disrupt; }
|
||||||
|
JSValue re_obj = js_regexp_constructor_internal(ctx, pat_val, bc);
|
||||||
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
|
if (JS_IsException(re_obj)) { goto disrupt; }
|
||||||
|
frame->slots[dest] = re_obj;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---- Push (append to array) ---- */
|
/* ---- Push (append to array) ---- */
|
||||||
else if (strcmp(op, "push") == 0) {
|
else if (strcmp(op, "push") == 0) {
|
||||||
int arr_slot = (int)a1->valuedouble;
|
int arr_slot = (int)a1->valuedouble;
|
||||||
|
|||||||
Reference in New Issue
Block a user