context free tokenizing, parsing, compiling

This commit is contained in:
2026-02-06 15:14:18 -06:00
parent 0a45394689
commit 3c38e828e5
4 changed files with 318 additions and 298 deletions

View File

@@ -399,33 +399,15 @@ int cell_init(int argc, char **argv)
script = (char *)script_or_file; script = (char *)script_or_file;
} }
JSRuntime *rt = JS_NewRuntime(); char *json = JS_AST(script, strlen(script), filename);
if (!rt) {
printf("Failed to create JS runtime\n");
free(allocated_script);
return 1;
}
JSContext *ctx = JS_NewContext(rt);
if (!ctx) {
printf("Failed to create JS context\n");
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
char *json = JS_AST(ctx, script, strlen(script), filename);
if (json) { if (json) {
int has_errors = print_json_errors(json); int has_errors = print_json_errors(json);
printf("%s\n", json); printf("%s\n", json);
free(json); free(json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script); free(allocated_script);
return has_errors ? 1 : 0; return has_errors ? 1 : 0;
} else { } else {
printf("Failed to parse AST\n"); printf("Failed to parse AST\n");
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script); free(allocated_script);
return 1; return 1;
} }
@@ -460,33 +442,15 @@ int cell_init(int argc, char **argv)
script = (char *)script_or_file; script = (char *)script_or_file;
} }
JSRuntime *rt = JS_NewRuntime(); char *json = JS_Tokenize(script, strlen(script), filename);
if (!rt) {
printf("Failed to create JS runtime\n");
free(allocated_script);
return 1;
}
JSContext *ctx = JS_NewContext(rt);
if (!ctx) {
printf("Failed to create JS context\n");
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
char *json = JS_Tokenize(ctx, script, strlen(script), filename);
if (json) { if (json) {
int has_errors = print_json_errors(json); int has_errors = print_json_errors(json);
printf("%s\n", json); printf("%s\n", json);
free(json); free(json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script); free(allocated_script);
return has_errors ? 1 : 0; return has_errors ? 1 : 0;
} else { } else {
printf("Failed to tokenize\n"); printf("Failed to tokenize\n");
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script); free(allocated_script);
return 1; return 1;
} }
@@ -521,51 +485,31 @@ int cell_init(int argc, char **argv)
script = (char *)script_or_file; script = (char *)script_or_file;
} }
JSRuntime *rt = JS_NewRuntime(); char *ast_json = JS_AST(script, strlen(script), filename);
if (!rt) {
printf("Failed to create JS runtime\n");
free(allocated_script);
return 1;
}
JSContext *ctx = JS_NewContext(rt);
if (!ctx) {
printf("Failed to create JS context\n");
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
char *ast_json = JS_AST(ctx, script, strlen(script), filename);
if (!ast_json) { if (!ast_json) {
printf("Failed to parse AST\n"); printf("Failed to parse AST\n");
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script); free(allocated_script);
return 1; return 1;
} }
if (print_json_errors(ast_json)) { if (print_json_errors(ast_json)) {
free(ast_json); free(ast_json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script); free(allocated_script);
return 1; return 1;
} }
char *mcode_json = JS_Mcode(ctx, ast_json); char *mcode_json = JS_Mcode(ast_json);
free(ast_json); free(ast_json);
if (!mcode_json) { if (!mcode_json) {
printf("Failed to generate MCODE\n"); printf("Failed to generate MCODE\n");
JS_FreeContext(ctx); JS_FreeRuntime(rt); free(allocated_script); free(allocated_script);
return 1; return 1;
} }
printf("%s\n", mcode_json); printf("%s\n", mcode_json);
free(mcode_json); free(mcode_json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script); free(allocated_script);
return 0; return 0;
} }
@@ -592,40 +536,35 @@ int cell_init(int argc, char **argv)
script = (char *)script_or_file; script = (char *)script_or_file;
} }
JSRuntime *rt = JS_NewRuntime(); char *ast_json = JS_AST(script, strlen(script), filename);
if (!rt) { printf("Failed to create JS runtime\n"); free(allocated_script); return 1; }
/* Use a parser context (small heap) for AST + MCODE generation */
JSContext *parse_ctx = JS_NewContext(rt);
if (!parse_ctx) { printf("Failed to create JS context\n"); JS_FreeRuntime(rt); free(allocated_script); return 1; }
char *ast_json = JS_AST(parse_ctx, script, strlen(script), filename);
if (!ast_json) { if (!ast_json) {
printf("Failed to parse AST\n"); printf("Failed to parse AST\n");
JS_FreeContext(parse_ctx); JS_FreeRuntime(rt); free(allocated_script); free(allocated_script);
return 1; return 1;
} }
if (print_json_errors(ast_json)) { if (print_json_errors(ast_json)) {
free(ast_json); JS_FreeContext(parse_ctx); JS_FreeRuntime(rt); free(allocated_script); free(ast_json); free(allocated_script);
return 1; return 1;
} }
char *mcode_json = JS_Mcode(parse_ctx, ast_json); char *mcode_json = JS_Mcode(ast_json);
free(ast_json); free(ast_json);
JS_FreeContext(parse_ctx);
if (!mcode_json) { if (!mcode_json) {
printf("Failed to generate MCODE\n"); printf("Failed to generate MCODE\n");
JS_FreeRuntime(rt); free(allocated_script); free(allocated_script);
return 1; return 1;
} }
if (print_json_errors(mcode_json)) { if (print_json_errors(mcode_json)) {
free(mcode_json); JS_FreeRuntime(rt); free(allocated_script); free(mcode_json); free(allocated_script);
return 1; return 1;
} }
/* Use a larger heap context for execution */ /* Use a larger heap context for execution */
JSRuntime *rt = JS_NewRuntime();
if (!rt) { printf("Failed to create JS runtime\n"); free(mcode_json); free(allocated_script); return 1; }
JSContext *ctx = JS_NewContextWithHeapSize(rt, 64 * 1024); JSContext *ctx = JS_NewContextWithHeapSize(rt, 64 * 1024);
if (!ctx) { printf("Failed to create execution context\n"); free(mcode_json); JS_FreeRuntime(rt); free(allocated_script); return 1; } if (!ctx) { printf("Failed to create execution context\n"); free(mcode_json); JS_FreeRuntime(rt); free(allocated_script); return 1; }
@@ -676,37 +615,32 @@ int cell_init(int argc, char **argv)
script = (char *)script_or_file; script = (char *)script_or_file;
} }
JSRuntime *rt = JS_NewRuntime(); char *ast_json = JS_AST(script, strlen(script), filename);
if (!rt) {
printf("Failed to create JS runtime\n");
free(allocated_script);
return 1;
}
JSContext *ctx = JS_NewContext(rt);
if (!ctx) {
printf("Failed to create JS context\n");
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
char *ast_json = JS_AST(ctx, script, strlen(script), filename);
if (!ast_json) { if (!ast_json) {
printf("Failed to parse AST\n"); printf("Failed to parse AST\n");
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script); free(allocated_script);
return 1; return 1;
} }
if (print_json_errors(ast_json)) { if (print_json_errors(ast_json)) {
free(ast_json); free(ast_json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script); free(allocated_script);
return 1; return 1;
} }
JSRuntime *rt = JS_NewRuntime();
if (!rt) {
printf("Failed to create JS runtime\n");
free(ast_json); free(allocated_script);
return 1;
}
JSContext *ctx = JS_NewContext(rt);
if (!ctx) {
printf("Failed to create JS context\n");
free(ast_json); JS_FreeRuntime(rt); free(allocated_script);
return 1;
}
JS_DumpMach(ctx, ast_json, JS_NULL); JS_DumpMach(ctx, ast_json, JS_NULL);
free(ast_json); free(ast_json);
@@ -745,37 +679,32 @@ int cell_init(int argc, char **argv)
script = (char *)script_or_file; script = (char *)script_or_file;
} }
JSRuntime *rt = JS_NewRuntime(); char *ast_json = JS_AST(script, strlen(script), filename);
if (!rt) {
printf("Failed to create JS runtime\n");
free(allocated_script);
return 1;
}
JSContext *ctx = JS_NewContext(rt);
if (!ctx) {
printf("Failed to create JS context\n");
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
char *ast_json = JS_AST(ctx, script, strlen(script), filename);
if (!ast_json) { if (!ast_json) {
printf("Failed to parse AST\n"); printf("Failed to parse AST\n");
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script); free(allocated_script);
return 1; return 1;
} }
if (print_json_errors(ast_json)) { if (print_json_errors(ast_json)) {
free(ast_json); free(ast_json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script); free(allocated_script);
return 1; return 1;
} }
JSRuntime *rt = JS_NewRuntime();
if (!rt) {
printf("Failed to create JS runtime\n");
free(ast_json); free(allocated_script);
return 1;
}
JSContext *ctx = JS_NewContext(rt);
if (!ctx) {
printf("Failed to create JS context\n");
free(ast_json); JS_FreeRuntime(rt); free(allocated_script);
return 1;
}
JSValue result = JS_RunMach(ctx, ast_json, JS_NULL); JSValue result = JS_RunMach(ctx, ast_json, JS_NULL);
free(ast_json); free(ast_json);

View File

@@ -27857,7 +27857,6 @@ JSValue js_math_cycles_use (JSContext *ctx) {
============================================================ */ ============================================================ */
typedef struct ASTParseState { typedef struct ASTParseState {
JSContext *ctx;
const char *filename; const char *filename;
const uint8_t *buf_start; const uint8_t *buf_start;
const uint8_t *buf_ptr; const uint8_t *buf_ptr;
@@ -27870,23 +27869,42 @@ typedef struct ASTParseState {
int has_error; int has_error;
union { union {
struct { struct {
JSValue str; const char *str;
size_t len;
} str; } str;
struct { struct {
JSValue val; double val;
} num; } num;
struct { struct {
JSValue str; const char *str;
size_t len;
BOOL has_escape; BOOL has_escape;
BOOL is_reserved; BOOL is_reserved;
} ident; } ident;
struct { struct {
JSValue body; const char *body;
JSValue flags; size_t body_len;
const char *flags;
size_t flags_len;
} regexp; } regexp;
} token_u; } token_u;
} ASTParseState; } ASTParseState;
/* Add a length-delimited string to a cJSON object (source pointers aren't null-terminated) */
static void cjson_add_strn (cJSON *obj, const char *key, const char *str, size_t len) {
char *tmp = sys_malloc (len + 1);
memcpy (tmp, str, len);
tmp[len] = '\0';
cJSON_AddStringToObject (obj, key, tmp);
sys_free (tmp);
}
/* Compare a length-delimited token string against a null-terminated literal */
static inline BOOL tok_eq (const char *str, size_t len, const char *lit) {
size_t ll = strlen (lit);
return len == ll && memcmp (str, lit, ll) == 0;
}
static cJSON *ast_parse_expr (ASTParseState *s); static cJSON *ast_parse_expr (ASTParseState *s);
static cJSON *ast_parse_assign_expr (ASTParseState *s); static cJSON *ast_parse_assign_expr (ASTParseState *s);
static cJSON *ast_parse_statement (ASTParseState *s); static cJSON *ast_parse_statement (ASTParseState *s);
@@ -28046,7 +28064,8 @@ redo:
} }
p++; p++;
s->token_val = TOK_TEMPLATE; s->token_val = TOK_TEMPLATE;
s->token_u.str.str = JS_NewStringLen (s->ctx, (const char *)(start + 1), p - start - 2); s->token_u.str.str = (const char *)(start + 1);
s->token_u.str.len = p - start - 2;
} break; } break;
case '\'': case '\'':
case '\"': { case '\"': {
@@ -28065,7 +28084,8 @@ redo:
p++; p++;
/* Store the string content without quotes */ /* Store the string content without quotes */
s->token_val = TOK_STRING; s->token_val = TOK_STRING;
s->token_u.str.str = JS_NewStringLen (s->ctx, (const char *)(start + 1), p - start - 2); s->token_u.str.str = (const char *)(start + 1);
s->token_u.str.len = p - start - 2;
} break; } break;
case '\r': case '\r':
if (p[1] == '\n') p++; if (p[1] == '\n') p++;
@@ -28137,7 +28157,8 @@ redo:
} }
} }
size_t len = p - start; size_t len = p - start;
s->token_u.ident.str = JS_NewStringLen (s->ctx, (const char *)start, len); s->token_u.ident.str = (const char *)start;
s->token_u.ident.len = len;
s->token_u.ident.has_escape = ident_has_escape; s->token_u.ident.has_escape = ident_has_escape;
s->token_u.ident.is_reserved = FALSE; s->token_u.ident.is_reserved = FALSE;
s->token_val = TOK_IDENT; s->token_val = TOK_IDENT;
@@ -28222,7 +28243,7 @@ redo:
numstr[p - start] = '\0'; numstr[p - start] = '\0';
double val = strtod (numstr, NULL); double val = strtod (numstr, NULL);
sys_free (numstr); sys_free (numstr);
s->token_u.num.val = JS_NewFloat64 (s->ctx, val); s->token_u.num.val = val;
} break; } break;
case '*': case '*':
if (p[1] == '=') { p += 2; s->token_val = TOK_MUL_ASSIGN; } if (p[1] == '=') { p += 2; s->token_val = TOK_MUL_ASSIGN; }
@@ -28357,11 +28378,13 @@ static int tokenize_next (ASTParseState *s) {
if (p >= s->buf_end) { if (p >= s->buf_end) {
ast_error (s, start, "unterminated template literal"); ast_error (s, start, "unterminated template literal");
s->token_val = TOK_ERROR; s->token_val = TOK_ERROR;
s->token_u.str.str = JS_NewStringLen (s->ctx, (const char *)(start + 1), p - start - 1); s->token_u.str.str = (const char *)(start + 1);
s->token_u.str.len = p - start - 1;
} else { } else {
p++; p++;
s->token_val = TOK_TEMPLATE; s->token_val = TOK_TEMPLATE;
s->token_u.str.str = JS_NewStringLen (s->ctx, (const char *)(start + 1), p - start - 2); s->token_u.str.str = (const char *)(start + 1);
s->token_u.str.len = p - start - 2;
} }
} break; } break;
case '\'': case '\'':
@@ -28376,11 +28399,13 @@ static int tokenize_next (ASTParseState *s) {
if (p >= s->buf_end) { if (p >= s->buf_end) {
ast_error (s, start, "unterminated string literal"); ast_error (s, start, "unterminated string literal");
s->token_val = TOK_ERROR; s->token_val = TOK_ERROR;
s->token_u.str.str = JS_NewStringLen (s->ctx, (const char *)(start + 1), p - start - 1); s->token_u.str.str = (const char *)(start + 1);
s->token_u.str.len = p - start - 1;
} else { } else {
p++; p++;
s->token_val = TOK_STRING; s->token_val = TOK_STRING;
s->token_u.str.str = JS_NewStringLen (s->ctx, (const char *)(start + 1), p - start - 2); s->token_u.str.str = (const char *)(start + 1);
s->token_u.str.len = p - start - 2;
} }
} break; } break;
case '\r': case '\r':
@@ -28460,7 +28485,8 @@ static int tokenize_next (ASTParseState *s) {
} }
} }
size_t len = p - start; size_t len = p - start;
s->token_u.ident.str = JS_NewStringLen (s->ctx, (const char *)start, len); s->token_u.ident.str = (const char *)start;
s->token_u.ident.len = len;
s->token_u.ident.has_escape = ident_has_escape; s->token_u.ident.has_escape = ident_has_escape;
s->token_u.ident.is_reserved = FALSE; s->token_u.ident.is_reserved = FALSE;
s->token_val = TOK_IDENT; s->token_val = TOK_IDENT;
@@ -28544,7 +28570,7 @@ static int tokenize_next (ASTParseState *s) {
numstr[p - start] = '\0'; numstr[p - start] = '\0';
double val = strtod (numstr, NULL); double val = strtod (numstr, NULL);
sys_free (numstr); sys_free (numstr);
s->token_u.num.val = JS_NewFloat64 (s->ctx, val); s->token_u.num.val = val;
} break; } break;
case '*': case '*':
if (p[1] == '=') { p += 2; s->token_val = TOK_MUL_ASSIGN; } if (p[1] == '=') { p += 2; s->token_val = TOK_MUL_ASSIGN; }
@@ -28639,10 +28665,7 @@ static cJSON *ast_parse_primary (ASTParseState *s) {
switch (s->token_val) { switch (s->token_val) {
case TOK_NUMBER: { case TOK_NUMBER: {
node = ast_node (s, "number", start); node = ast_node (s, "number", start);
double d = JS_VALUE_GET_FLOAT64 (s->token_u.num.val); double d = s->token_u.num.val;
if (JS_VALUE_GET_TAG (s->token_u.num.val) == JS_TAG_INT) {
d = JS_VALUE_GET_INT (s->token_u.num.val);
}
/* Store original text representation */ /* Store original text representation */
size_t len = s->buf_ptr - start; size_t len = s->buf_ptr - start;
char *text = sys_malloc (len + 1); char *text = sys_malloc (len + 1);
@@ -28657,9 +28680,7 @@ static cJSON *ast_parse_primary (ASTParseState *s) {
case TOK_STRING: { case TOK_STRING: {
node = ast_node (s, "text", start); node = ast_node (s, "text", start);
const char *str = JS_ToCString (s->ctx, s->token_u.str.str); cjson_add_strn (node, "value", s->token_u.str.str, s->token_u.str.len);
cJSON_AddStringToObject (node, "value", str ? str : "");
JS_FreeCString (s->ctx, str);
ast_node_end (s, node, s->buf_ptr); ast_node_end (s, node, s->buf_ptr);
ast_next_token (s); ast_next_token (s);
} break; } break;
@@ -28681,9 +28702,7 @@ static cJSON *ast_parse_primary (ASTParseState *s) {
if (!has_expr) { if (!has_expr) {
/* Simple template — unchanged behavior */ /* Simple template — unchanged behavior */
node = ast_node (s, "text", start); node = ast_node (s, "text", start);
const char *str = JS_ToCString (s->ctx, s->token_u.str.str); cjson_add_strn (node, "value", s->token_u.str.str, s->token_u.str.len);
cJSON_AddStringToObject (node, "value", str ? str : "");
JS_FreeCString (s->ctx, str);
ast_node_end (s, node, s->buf_ptr); ast_node_end (s, node, s->buf_ptr);
ast_next_token (s); ast_next_token (s);
} else { } else {
@@ -28747,9 +28766,7 @@ static cJSON *ast_parse_primary (ASTParseState *s) {
node = ast_parse_arrow_function (s); node = ast_parse_arrow_function (s);
} else { } else {
node = ast_node (s, "name", start); node = ast_node (s, "name", start);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str); cjson_add_strn (node, "name", s->token_u.ident.str, s->token_u.ident.len);
cJSON_AddStringToObject (node, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
ast_node_end (s, node, s->buf_ptr); ast_node_end (s, node, s->buf_ptr);
ast_next_token (s); ast_next_token (s);
} }
@@ -28845,9 +28862,7 @@ static cJSON *ast_parse_primary (ASTParseState *s) {
while (s->token_val != ')' && s->token_val != TOK_EOF) { while (s->token_val != ')' && s->token_val != TOK_EOF) {
if (s->token_val == TOK_IDENT) { if (s->token_val == TOK_IDENT) {
cJSON *param = ast_node (s, "name", s->token_ptr); cJSON *param = ast_node (s, "name", s->token_ptr);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str); cjson_add_strn (param, "name", s->token_u.ident.str, s->token_u.ident.len);
cJSON_AddStringToObject (param, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
ast_node_end (s, param, s->buf_ptr); ast_node_end (s, param, s->buf_ptr);
ast_next_token (s); ast_next_token (s);
if (s->token_val == '=' || s->token_val == '|') { if (s->token_val == '=' || s->token_val == '|') {
@@ -28994,9 +29009,7 @@ static cJSON *ast_parse_postfix (ASTParseState *s) {
cJSON *new_node = ast_node (s, ".", start); cJSON *new_node = ast_node (s, ".", start);
cJSON_AddItemToObject (new_node, "left", node); cJSON_AddItemToObject (new_node, "left", node);
if (s->token_val == TOK_IDENT) { if (s->token_val == TOK_IDENT) {
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str); cjson_add_strn (new_node, "right", s->token_u.ident.str, s->token_u.ident.len);
cJSON_AddStringToObject (new_node, "right", str ? str : "");
JS_FreeCString (s->ctx, str);
ast_next_token (s); ast_next_token (s);
} else { } else {
ast_error (s, s->token_ptr, "expected property name after '.'"); ast_error (s, s->token_ptr, "expected property name after '.'");
@@ -29079,9 +29092,7 @@ static cJSON *ast_parse_postfix (ASTParseState *s) {
/* Optional property access: o?.a */ /* Optional property access: o?.a */
cJSON *new_node = ast_node (s, "?.", start); cJSON *new_node = ast_node (s, "?.", start);
cJSON_AddItemToObject (new_node, "left", node); cJSON_AddItemToObject (new_node, "left", node);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str); cjson_add_strn (new_node, "right", s->token_u.ident.str, s->token_u.ident.len);
cJSON_AddStringToObject (new_node, "right", str ? str : "");
JS_FreeCString (s->ctx, str);
ast_next_token (s); ast_next_token (s);
ast_node_end (s, new_node, s->buf_ptr); ast_node_end (s, new_node, s->buf_ptr);
node = new_node; node = new_node;
@@ -29363,9 +29374,7 @@ static cJSON *ast_parse_function_inner (ASTParseState *s, BOOL is_expr) {
/* Optional function name */ /* Optional function name */
if (s->token_val == TOK_IDENT) { if (s->token_val == TOK_IDENT) {
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str); cjson_add_strn (node, "name", s->token_u.ident.str, s->token_u.ident.len);
cJSON_AddStringToObject (node, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
ast_next_token (s); ast_next_token (s);
} }
@@ -29377,18 +29386,22 @@ static cJSON *ast_parse_function_inner (ASTParseState *s, BOOL is_expr) {
if (s->token_val == TOK_IDENT) { if (s->token_val == TOK_IDENT) {
const uint8_t *param_ptr = s->token_ptr; const uint8_t *param_ptr = s->token_ptr;
cJSON *param = ast_node (s, "name", param_ptr); cJSON *param = ast_node (s, "name", param_ptr);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str); cjson_add_strn (param, "name", s->token_u.ident.str, s->token_u.ident.len);
cJSON_AddStringToObject (param, "name", str ? str : "");
/* Check for duplicate parameter name */ /* Check for duplicate parameter name */
cJSON *prev; {
cJSON_ArrayForEach (prev, params) { char *tmp_name = sys_malloc (s->token_u.ident.len + 1);
const char *prev_name = cJSON_GetStringValue (cJSON_GetObjectItem (prev, "name")); memcpy (tmp_name, s->token_u.ident.str, s->token_u.ident.len);
if (prev_name && str && strcmp (prev_name, str) == 0) { tmp_name[s->token_u.ident.len] = '\0';
ast_error (s, param_ptr, "duplicate parameter name '%s'", str); cJSON *prev;
break; cJSON_ArrayForEach (prev, params) {
const char *prev_name = cJSON_GetStringValue (cJSON_GetObjectItem (prev, "name"));
if (prev_name && strcmp (prev_name, tmp_name) == 0) {
ast_error (s, param_ptr, "duplicate parameter name '%s'", tmp_name);
break;
}
} }
sys_free (tmp_name);
} }
JS_FreeCString (s->ctx, str);
ast_node_end (s, param, s->buf_ptr); ast_node_end (s, param, s->buf_ptr);
ast_next_token (s); ast_next_token (s);
if (s->token_val == '=' || s->token_val == '|') { if (s->token_val == '=' || s->token_val == '|') {
@@ -29448,9 +29461,7 @@ static cJSON *ast_parse_arrow_function (ASTParseState *s) {
if (s->token_val == TOK_IDENT) { if (s->token_val == TOK_IDENT) {
/* Single parameter without parens: x => ... */ /* Single parameter without parens: x => ... */
cJSON *param = ast_node (s, "name", s->token_ptr); cJSON *param = ast_node (s, "name", s->token_ptr);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str); cjson_add_strn (param, "name", s->token_u.ident.str, s->token_u.ident.len);
cJSON_AddStringToObject (param, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
ast_node_end (s, param, s->buf_ptr); ast_node_end (s, param, s->buf_ptr);
cJSON_AddItemToArray (params, param); cJSON_AddItemToArray (params, param);
ast_next_token (s); ast_next_token (s);
@@ -29461,18 +29472,22 @@ static cJSON *ast_parse_arrow_function (ASTParseState *s) {
if (s->token_val == TOK_IDENT) { if (s->token_val == TOK_IDENT) {
const uint8_t *param_ptr = s->token_ptr; const uint8_t *param_ptr = s->token_ptr;
cJSON *param = ast_node (s, "name", param_ptr); cJSON *param = ast_node (s, "name", param_ptr);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str); cjson_add_strn (param, "name", s->token_u.ident.str, s->token_u.ident.len);
cJSON_AddStringToObject (param, "name", str ? str : "");
/* Check for duplicate parameter name */ /* Check for duplicate parameter name */
cJSON *prev; {
cJSON_ArrayForEach (prev, params) { char *tmp_name = sys_malloc (s->token_u.ident.len + 1);
const char *prev_name = cJSON_GetStringValue (cJSON_GetObjectItem (prev, "name")); memcpy (tmp_name, s->token_u.ident.str, s->token_u.ident.len);
if (prev_name && str && strcmp (prev_name, str) == 0) { tmp_name[s->token_u.ident.len] = '\0';
ast_error (s, param_ptr, "duplicate parameter name '%s'", str); cJSON *prev;
break; cJSON_ArrayForEach (prev, params) {
const char *prev_name = cJSON_GetStringValue (cJSON_GetObjectItem (prev, "name"));
if (prev_name && strcmp (prev_name, tmp_name) == 0) {
ast_error (s, param_ptr, "duplicate parameter name '%s'", tmp_name);
break;
}
} }
sys_free (tmp_name);
} }
JS_FreeCString (s->ctx, str);
ast_node_end (s, param, s->buf_ptr); ast_node_end (s, param, s->buf_ptr);
ast_next_token (s); ast_next_token (s);
@@ -29587,8 +29602,11 @@ static cJSON *ast_parse_statement (ASTParseState *s) {
const uint8_t *var_ptr = s->token_ptr; const uint8_t *var_ptr = s->token_ptr;
node = ast_node (s, kind_name, start); node = ast_node (s, kind_name, start);
cJSON *left = ast_node (s, "name", s->token_ptr); cJSON *left = ast_node (s, "name", s->token_ptr);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str); cjson_add_strn (left, "name", s->token_u.ident.str, s->token_u.ident.len);
cJSON_AddStringToObject (left, "name", str ? str : ""); /* Save name for potential error message */
char *var_name = sys_malloc (s->token_u.ident.len + 1);
memcpy (var_name, s->token_u.ident.str, s->token_u.ident.len);
var_name[s->token_u.ident.len] = '\0';
ast_node_end (s, left, s->buf_ptr); ast_node_end (s, left, s->buf_ptr);
cJSON_AddItemToObject (node, "left", left); cJSON_AddItemToObject (node, "left", left);
ast_next_token (s); ast_next_token (s);
@@ -29602,9 +29620,9 @@ static cJSON *ast_parse_statement (ASTParseState *s) {
cJSON_AddBoolToObject (node, "pop", 1); cJSON_AddBoolToObject (node, "pop", 1);
} else if (is_def) { } else if (is_def) {
/* def (constant) requires initializer */ /* def (constant) requires initializer */
ast_error (s, var_ptr, "missing initializer for constant '%s'", str); ast_error (s, var_ptr, "missing initializer for constant '%s'", var_name);
} }
JS_FreeCString (s->ctx, str); sys_free (var_name);
ast_node_end (s, node, s->buf_ptr); ast_node_end (s, node, s->buf_ptr);
cJSON_AddItemToArray (decls, node); cJSON_AddItemToArray (decls, node);
decl_count++; decl_count++;
@@ -29772,9 +29790,7 @@ static cJSON *ast_parse_statement (ASTParseState *s) {
node = ast_node (s, "break", start); node = ast_node (s, "break", start);
ast_next_token (s); ast_next_token (s);
if (s->token_val == TOK_IDENT && !s->got_lf) { if (s->token_val == TOK_IDENT && !s->got_lf) {
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str); cjson_add_strn (node, "name", s->token_u.ident.str, s->token_u.ident.len);
cJSON_AddStringToObject (node, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
ast_next_token (s); ast_next_token (s);
} }
ast_expect_semi (s); ast_expect_semi (s);
@@ -29785,9 +29801,7 @@ static cJSON *ast_parse_statement (ASTParseState *s) {
node = ast_node (s, "continue", start); node = ast_node (s, "continue", start);
ast_next_token (s); ast_next_token (s);
if (s->token_val == TOK_IDENT && !s->got_lf) { if (s->token_val == TOK_IDENT && !s->got_lf) {
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str); cjson_add_strn (node, "name", s->token_u.ident.str, s->token_u.ident.len);
cJSON_AddStringToObject (node, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
ast_next_token (s); ast_next_token (s);
} }
ast_expect_semi (s); ast_expect_semi (s);
@@ -29816,9 +29830,7 @@ static cJSON *ast_parse_statement (ASTParseState *s) {
if (s->token_val == '(') { if (s->token_val == '(') {
ast_next_token (s); ast_next_token (s);
if (s->token_val == TOK_IDENT) { if (s->token_val == TOK_IDENT) {
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str); cjson_add_strn (catch_node, "name", s->token_u.ident.str, s->token_u.ident.len);
cJSON_AddStringToObject (catch_node, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
ast_next_token (s); ast_next_token (s);
} }
if (s->token_val == ')') ast_next_token (s); if (s->token_val == ')') ast_next_token (s);
@@ -29864,9 +29876,7 @@ static cJSON *ast_parse_statement (ASTParseState *s) {
if (p < s->buf_end && *p == ':') { if (p < s->buf_end && *p == ':') {
/* Labeled statement */ /* Labeled statement */
node = ast_node (s, "label", start); node = ast_node (s, "label", start);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str); cjson_add_strn (node, "name", s->token_u.ident.str, s->token_u.ident.len);
cJSON_AddStringToObject (node, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
ast_next_token (s); /* skip identifier */ ast_next_token (s); /* skip identifier */
ast_next_token (s); /* skip colon */ ast_next_token (s); /* skip colon */
cJSON *stmt = ast_parse_statement (s); cJSON *stmt = ast_parse_statement (s);
@@ -30540,11 +30550,10 @@ static void ast_semantic_check (cJSON *ast, cJSON **errors_out,
*intrinsics_out = intr_arr; *intrinsics_out = intr_arr;
} }
char *JS_AST (JSContext *ctx, const char *source, size_t len, const char *filename) { char *JS_AST (const char *source, size_t len, const char *filename) {
ASTParseState s; ASTParseState s;
memset (&s, 0, sizeof (s)); memset (&s, 0, sizeof (s));
s.ctx = ctx;
s.filename = filename; s.filename = filename;
s.buf_start = (const uint8_t *)source; s.buf_start = (const uint8_t *)source;
s.buf_ptr = (const uint8_t *)source; s.buf_ptr = (const uint8_t *)source;
@@ -30628,22 +30637,15 @@ static cJSON *build_token_object (ASTParseState *s) {
cJSON_AddStringToObject (tok, "value", text); cJSON_AddStringToObject (tok, "value", text);
sys_free (text); sys_free (text);
/* Store parsed number */ /* Store parsed number */
double d = JS_VALUE_GET_FLOAT64 (s->token_u.num.val); double d = s->token_u.num.val;
if (JS_VALUE_GET_TAG (s->token_u.num.val) == JS_TAG_INT) {
d = JS_VALUE_GET_INT (s->token_u.num.val);
}
cJSON_AddNumberToObject (tok, "number", d); cJSON_AddNumberToObject (tok, "number", d);
} break; } break;
case TOK_STRING: case TOK_STRING:
case TOK_TEMPLATE: { case TOK_TEMPLATE: {
const char *str = JS_ToCString (s->ctx, s->token_u.str.str); cjson_add_strn (tok, "value", s->token_u.str.str, s->token_u.str.len);
cJSON_AddStringToObject (tok, "value", str ? str : "");
JS_FreeCString (s->ctx, str);
} break; } break;
case TOK_IDENT: { case TOK_IDENT: {
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str); cjson_add_strn (tok, "value", s->token_u.ident.str, s->token_u.ident.len);
cJSON_AddStringToObject (tok, "value", str ? str : "");
JS_FreeCString (s->ctx, str);
} break; } break;
case TOK_ERROR: { case TOK_ERROR: {
/* Store the raw source text as value */ /* Store the raw source text as value */
@@ -30673,11 +30675,10 @@ static cJSON *build_token_object (ASTParseState *s) {
return tok; return tok;
} }
char *JS_Tokenize (JSContext *ctx, const char *source, size_t len, const char *filename) { char *JS_Tokenize (const char *source, size_t len, const char *filename) {
ASTParseState s; ASTParseState s;
memset (&s, 0, sizeof (s)); memset (&s, 0, sizeof (s));
s.ctx = ctx;
s.filename = filename; s.filename = filename;
s.buf_start = (const uint8_t *)source; s.buf_start = (const uint8_t *)source;
s.buf_ptr = (const uint8_t *)source; s.buf_ptr = (const uint8_t *)source;
@@ -30731,21 +30732,39 @@ typedef struct MachVarInfo {
} MachVarInfo; } MachVarInfo;
/* ---- Compile-time constant pool entry ---- */ /* ---- Compile-time constant pool entry ---- */
/* Stores raw data during compilation; converted to JSValues when building JSCodeRegister */ /* Stores raw data during compilation; converted to JSValues when loading into context */
typedef enum { MACH_CP_IMM, MACH_CP_STR } MachCPType; typedef enum { MACH_CP_INT, MACH_CP_FLOAT, MACH_CP_STR } MachCPType;
typedef struct { typedef struct {
MachCPType type; MachCPType type;
union { union {
JSValue val; /* immediate value (int, float) - no GC alloc */ int32_t ival; /* integer constant */
double fval; /* float constant */
char *str; /* owned C string */ char *str; /* owned C string */
}; };
} MachCPoolEntry; } MachCPoolEntry;
/* ---- Compiled output (context-free) ---- */
typedef struct MachCode {
uint16_t arity;
uint16_t nr_close_slots;
uint16_t nr_slots;
uint16_t entry_point;
uint32_t cpool_count;
MachCPoolEntry *cpool;
uint32_t instr_count;
MachInstr32 *instructions;
uint32_t func_count;
struct MachCode **functions;
char *name; /* owned C string, or NULL */
} MachCode;
/* ---- Compiler state ---- */ /* ---- Compiler state ---- */
typedef struct MachCompState { typedef struct MachCompState {
JSContext *ctx;
/* Instruction buffer (growable) */ /* Instruction buffer (growable) */
MachInstr32 *code; MachInstr32 *code;
int code_count; int code_count;
@@ -30757,7 +30776,7 @@ typedef struct MachCompState {
int cpool_capacity; int cpool_capacity;
/* Nested functions */ /* Nested functions */
JSCodeRegister **functions; MachCode **functions;
int func_count; int func_count;
int func_capacity; int func_capacity;
@@ -30820,26 +30839,33 @@ static void mach_free_reg_to(MachCompState *cs, int saved) {
cs->freereg = saved; cs->freereg = saved;
} }
/* Add a constant to the pool, return its index */ /* Add an integer constant to the pool, return its index */
static int mach_cpool_add(MachCompState *cs, JSValue val) { static int mach_cpool_add_int(MachCompState *cs, int32_t val) {
/* Check for duplicates (immediate values only) */
for (int i = 0; i < cs->cpool_count; i++) { for (int i = 0; i < cs->cpool_count; i++) {
MachCPoolEntry *e = &cs->cpool[i]; MachCPoolEntry *e = &cs->cpool[i];
if (e->type != MACH_CP_IMM) continue; if (e->type == MACH_CP_INT && e->ival == val) return i;
if (JS_IsInt(val) && JS_IsInt(e->val)) {
if (JS_VALUE_GET_INT(val) == JS_VALUE_GET_INT(e->val))
return i;
} else if (e->val == val) {
return i;
}
} }
if (cs->cpool_count >= cs->cpool_capacity) { if (cs->cpool_count >= cs->cpool_capacity) {
int new_cap = cs->cpool_capacity ? cs->cpool_capacity * 2 : 16; int new_cap = cs->cpool_capacity ? cs->cpool_capacity * 2 : 16;
cs->cpool = sys_realloc(cs->cpool, new_cap * sizeof(MachCPoolEntry)); cs->cpool = sys_realloc(cs->cpool, new_cap * sizeof(MachCPoolEntry));
cs->cpool_capacity = new_cap; cs->cpool_capacity = new_cap;
} }
cs->cpool[cs->cpool_count] = (MachCPoolEntry){ .type = MACH_CP_IMM, .val = val }; cs->cpool[cs->cpool_count] = (MachCPoolEntry){ .type = MACH_CP_INT, .ival = val };
return cs->cpool_count++;
}
/* Add a float constant to the pool, return its index */
static int mach_cpool_add_float(MachCompState *cs, double val) {
for (int i = 0; i < cs->cpool_count; i++) {
MachCPoolEntry *e = &cs->cpool[i];
if (e->type == MACH_CP_FLOAT && e->fval == val) return i;
}
if (cs->cpool_count >= cs->cpool_capacity) {
int new_cap = cs->cpool_capacity ? cs->cpool_capacity * 2 : 16;
cs->cpool = sys_realloc(cs->cpool, new_cap * sizeof(MachCPoolEntry));
cs->cpool_capacity = new_cap;
}
cs->cpool[cs->cpool_count] = (MachCPoolEntry){ .type = MACH_CP_FLOAT, .fval = val };
return cs->cpool_count++; return cs->cpool_count++;
} }
@@ -30869,14 +30895,18 @@ static JSValue *mach_materialize_cpool(JSContext *ctx, MachCPoolEntry *entries,
if (count == 0) { sys_free(entries); return NULL; } if (count == 0) { sys_free(entries); return NULL; }
JSValue *cpool = js_malloc_rt(count * sizeof(JSValue)); JSValue *cpool = js_malloc_rt(count * sizeof(JSValue));
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
if (entries[i].type == MACH_CP_STR) { switch (entries[i].type) {
case MACH_CP_INT:
cpool[i] = JS_NewInt32(ctx, entries[i].ival);
break;
case MACH_CP_FLOAT:
cpool[i] = JS_NewFloat64(ctx, entries[i].fval);
break;
case MACH_CP_STR:
cpool[i] = js_key_new(ctx, entries[i].str); cpool[i] = js_key_new(ctx, entries[i].str);
sys_free(entries[i].str); break;
} else {
cpool[i] = entries[i].val;
} }
} }
sys_free(entries);
return cpool; return cpool;
} }
@@ -30904,10 +30934,10 @@ static int mach_find_var(MachCompState *cs, const char *name) {
} }
/* Add a nested function, return its index */ /* Add a nested function, return its index */
static int mach_add_function(MachCompState *cs, JSCodeRegister *fn) { static int mach_add_function(MachCompState *cs, MachCode *fn) {
if (cs->func_count >= cs->func_capacity) { if (cs->func_count >= cs->func_capacity) {
int new_cap = cs->func_capacity ? cs->func_capacity * 2 : 4; int new_cap = cs->func_capacity ? cs->func_capacity * 2 : 4;
cs->functions = sys_realloc(cs->functions, new_cap * sizeof(JSCodeRegister*)); cs->functions = sys_realloc(cs->functions, new_cap * sizeof(MachCode*));
cs->func_capacity = new_cap; cs->func_capacity = new_cap;
} }
cs->functions[cs->func_count] = fn; cs->functions[cs->func_count] = fn;
@@ -30976,12 +31006,11 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
mach_emit(cs, MACH_AsBx(MACH_LOADI, dest, (int16_t)ival)); mach_emit(cs, MACH_AsBx(MACH_LOADI, dest, (int16_t)ival));
} else { } else {
/* Large number: use constant pool */ /* Large number: use constant pool */
JSValue val; int ki;
if (dval == (double)(int32_t)dval) if (dval == (double)(int32_t)dval)
val = JS_NewInt32(cs->ctx, (int32_t)dval); ki = mach_cpool_add_int(cs, (int32_t)dval);
else else
val = JS_NewFloat64(cs->ctx, dval); ki = mach_cpool_add_float(cs, dval);
int ki = mach_cpool_add(cs, val);
mach_emit(cs, MACH_ABx(MACH_LOADK, dest, ki)); mach_emit(cs, MACH_ABx(MACH_LOADK, dest, ki));
} }
} else { } else {
@@ -31417,7 +31446,6 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
if (dest < 0) dest = mach_reserve_reg(cs); if (dest < 0) dest = mach_reserve_reg(cs);
/* Compile nested function */ /* Compile nested function */
MachCompState child = {0}; MachCompState child = {0};
child.ctx = cs->ctx;
child.parent = cs; child.parent = cs;
child.scopes = cs->scopes; child.scopes = cs->scopes;
child.freereg = 1; /* slot 0 = this */ child.freereg = 1; /* slot 0 = this */
@@ -31460,10 +31488,11 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
/* Implicit return null */ /* Implicit return null */
mach_emit(&child, MACH_ABC(MACH_RETNIL, 0, 0, 0)); mach_emit(&child, MACH_ABC(MACH_RETNIL, 0, 0, 0));
/* Build JSCodeRegister for the child function */ /* Build MachCode for the child function */
cJSON *fn_scope = mach_find_scope_record(cs->scopes, child.function_nr); cJSON *fn_scope = mach_find_scope_record(cs->scopes, child.function_nr);
cJSON *fn_ncs = fn_scope ? cJSON_GetObjectItem(fn_scope, "nr_close_slots") : NULL; cJSON *fn_ncs = fn_scope ? cJSON_GetObjectItem(fn_scope, "nr_close_slots") : NULL;
JSCodeRegister *fn_code = js_mallocz_rt(sizeof(JSCodeRegister)); MachCode *fn_code = sys_malloc(sizeof(MachCode));
memset(fn_code, 0, sizeof(MachCode));
fn_code->arity = nparams; fn_code->arity = nparams;
fn_code->nr_slots = child.maxreg; fn_code->nr_slots = child.maxreg;
fn_code->nr_close_slots = fn_ncs ? (int)cJSON_GetNumberValue(fn_ncs) : 0; fn_code->nr_close_slots = fn_ncs ? (int)cJSON_GetNumberValue(fn_ncs) : 0;
@@ -31471,15 +31500,17 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
fn_code->instr_count = child.code_count; fn_code->instr_count = child.code_count;
fn_code->instructions = child.code; fn_code->instructions = child.code;
fn_code->cpool_count = child.cpool_count; fn_code->cpool_count = child.cpool_count;
fn_code->cpool = mach_materialize_cpool(cs->ctx, child.cpool, child.cpool_count); fn_code->cpool = child.cpool;
fn_code->func_count = child.func_count; fn_code->func_count = child.func_count;
fn_code->functions = child.functions; fn_code->functions = child.functions;
cJSON *fname = cJSON_GetObjectItem(node, "name"); cJSON *fname = cJSON_GetObjectItem(node, "name");
if (fname && cJSON_IsString(fname)) { if (fname && cJSON_IsString(fname)) {
fn_code->name = js_key_new(cs->ctx, cJSON_GetStringValue(fname)); const char *ns = cJSON_GetStringValue(fname);
fn_code->name = sys_malloc(strlen(ns) + 1);
strcpy(fn_code->name, ns);
} else { } else {
fn_code->name = JS_NULL; fn_code->name = NULL;
} }
/* Free child var table (not code/cpool, those are owned by fn_code now) */ /* Free child var table (not code/cpool, those are owned by fn_code now) */
@@ -31752,7 +31783,7 @@ static void mach_link_code(JSContext *ctx, JSCodeRegister *code, JSValue env) {
/* ---- Top-level compiler ---- */ /* ---- Top-level compiler ---- */
static JSCodeRegister *mach_compile_program(MachCompState *cs, cJSON *ast, JSValue env) { static MachCode *mach_compile_program(MachCompState *cs, cJSON *ast) {
cJSON *stmts = cJSON_GetObjectItem(ast, "statements"); cJSON *stmts = cJSON_GetObjectItem(ast, "statements");
if (!stmts || !cJSON_IsArray(stmts)) return NULL; if (!stmts || !cJSON_IsArray(stmts)) return NULL;
@@ -31776,8 +31807,9 @@ static JSCodeRegister *mach_compile_program(MachCompState *cs, cJSON *ast, JSVal
cJSON *prog_scope = mach_find_scope_record(cs->scopes, 0); cJSON *prog_scope = mach_find_scope_record(cs->scopes, 0);
cJSON *ncs_node = prog_scope ? cJSON_GetObjectItem(prog_scope, "nr_close_slots") : NULL; cJSON *ncs_node = prog_scope ? cJSON_GetObjectItem(prog_scope, "nr_close_slots") : NULL;
/* Build JSCodeRegister */ /* Build MachCode */
JSCodeRegister *code = js_mallocz_rt(sizeof(JSCodeRegister)); MachCode *code = sys_malloc(sizeof(MachCode));
memset(code, 0, sizeof(MachCode));
code->arity = 0; code->arity = 0;
code->nr_slots = cs->maxreg; code->nr_slots = cs->maxreg;
code->nr_close_slots = ncs_node ? (int)cJSON_GetNumberValue(ncs_node) : 0; code->nr_close_slots = ncs_node ? (int)cJSON_GetNumberValue(ncs_node) : 0;
@@ -31785,30 +31817,26 @@ static JSCodeRegister *mach_compile_program(MachCompState *cs, cJSON *ast, JSVal
code->instr_count = cs->code_count; code->instr_count = cs->code_count;
code->instructions = cs->code; code->instructions = cs->code;
code->cpool_count = cs->cpool_count; code->cpool_count = cs->cpool_count;
code->cpool = mach_materialize_cpool(cs->ctx, cs->cpool, cs->cpool_count); code->cpool = cs->cpool;
code->func_count = cs->func_count; code->func_count = cs->func_count;
code->functions = cs->functions; code->functions = cs->functions;
code->name = JS_NULL; code->name = NULL;
return code; return code;
} }
/* Public API: compile AST JSON to JSCodeRegister */ /* Public API: compile AST JSON to MachCode (context-free) */
JSCodeRegister *JS_CompileMach(JSContext *ctx, const char *ast_json, JSValue env) { MachCode *JS_CompileMach(const char *ast_json) {
cJSON *ast = cJSON_Parse(ast_json); cJSON *ast = cJSON_Parse(ast_json);
if (!ast) return NULL; if (!ast) return NULL;
MachCompState cs = {0}; MachCompState cs = {0};
cs.ctx = ctx;
cs.freereg = 1; /* slot 0 = this */ cs.freereg = 1; /* slot 0 = this */
cs.maxreg = 1; cs.maxreg = 1;
JSCodeRegister *code = mach_compile_program(&cs, ast, env); MachCode *code = mach_compile_program(&cs, ast);
/* Link: resolve GETNAME to GETINTRINSIC/GETENV */ /* Free var table (code/cpool/functions are owned by MachCode now) */
if (code) mach_link_code(ctx, code, env);
/* Free var table (code/cpool/functions are owned by JSCodeRegister now) */
for (int i = 0; i < cs.var_count; i++) for (int i = 0; i < cs.var_count; i++)
sys_free(cs.vars[i].name); sys_free(cs.vars[i].name);
sys_free(cs.vars); sys_free(cs.vars);
@@ -31817,6 +31845,55 @@ JSCodeRegister *JS_CompileMach(JSContext *ctx, const char *ast_json, JSValue env
return code; return code;
} }
/* Free a MachCode tree (compiled but not yet loaded) */
void JS_FreeMachCode(MachCode *mc) {
if (!mc) return;
sys_free(mc->instructions);
for (uint32_t i = 0; i < mc->cpool_count; i++) {
if (mc->cpool[i].type == MACH_CP_STR)
sys_free(mc->cpool[i].str);
}
sys_free(mc->cpool);
for (uint32_t i = 0; i < mc->func_count; i++)
JS_FreeMachCode(mc->functions[i]);
sys_free(mc->functions);
sys_free(mc->name);
sys_free(mc);
}
/* Load a MachCode into a JSCodeRegister (materializes JSValues, needs ctx) */
JSCodeRegister *JS_LoadMachCode(JSContext *ctx, MachCode *mc, JSValue env) {
JSCodeRegister *code = js_mallocz_rt(sizeof(JSCodeRegister));
code->arity = mc->arity;
code->nr_close_slots = mc->nr_close_slots;
code->nr_slots = mc->nr_slots;
code->entry_point = mc->entry_point;
code->instr_count = mc->instr_count;
code->instructions = mc->instructions; /* transfer ownership */
/* Materialize cpool: raw -> JSValue */
code->cpool_count = mc->cpool_count;
code->cpool = mach_materialize_cpool(ctx, mc->cpool, mc->cpool_count);
/* Recursively load nested functions */
code->func_count = mc->func_count;
if (mc->func_count > 0) {
code->functions = js_malloc_rt(mc->func_count * sizeof(JSCodeRegister *));
for (uint32_t i = 0; i < mc->func_count; i++)
code->functions[i] = JS_LoadMachCode(ctx, mc->functions[i], env);
} else {
code->functions = NULL;
}
/* Intern function name */
code->name = mc->name ? js_key_new(ctx, mc->name) : JS_NULL;
/* Link: resolve GETNAME to GETENV/GETINTRINSIC */
mach_link_code(ctx, code, env);
return code;
}
/* Free a JSCodeRegister and all nested functions */ /* Free a JSCodeRegister and all nested functions */
static void js_free_code_register(JSCodeRegister *code) { static void js_free_code_register(JSCodeRegister *code) {
if (!code) return; if (!code) return;
@@ -32457,7 +32534,6 @@ done:
============================================================ */ ============================================================ */
typedef struct MachGenState { typedef struct MachGenState {
JSContext *ctx;
cJSON *instructions; cJSON *instructions;
cJSON *data; cJSON *data;
cJSON *functions; cJSON *functions;
@@ -33570,7 +33646,6 @@ static void mach_gen_statement (MachGenState *s, cJSON *stmt) {
static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) { static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) {
MachGenState s = {0}; MachGenState s = {0};
s.ctx = parent->ctx;
s.instructions = cJSON_CreateArray (); s.instructions = cJSON_CreateArray ();
s.data = parent->data; s.data = parent->data;
s.functions = parent->functions; s.functions = parent->functions;
@@ -33734,12 +33809,11 @@ static cJSON *mach_gen_program (MachGenState *s, cJSON *ast) {
return result; return result;
} }
char *JS_Mcode (JSContext *ctx, const char *ast_json) { char *JS_Mcode (const char *ast_json) {
cJSON *ast = cJSON_Parse (ast_json); cJSON *ast = cJSON_Parse (ast_json);
if (!ast) return NULL; if (!ast) return NULL;
MachGenState s = {0}; MachGenState s = {0};
s.ctx = ctx;
s.instructions = cJSON_CreateArray (); s.instructions = cJSON_CreateArray ();
s.errors = NULL; s.errors = NULL;
s.has_error = 0; s.has_error = 0;
@@ -34929,12 +35003,13 @@ static void dump_register_code(JSContext *ctx, JSCodeRegister *code, int indent)
/* Dump MACH bytecode to stdout for debugging. Takes AST JSON. */ /* Dump MACH bytecode to stdout for debugging. Takes AST JSON. */
void JS_DumpMach(JSContext *ctx, const char *ast_json, JSValue env) { void JS_DumpMach(JSContext *ctx, const char *ast_json, JSValue env) {
JSCodeRegister *code = JS_CompileMach(ctx, ast_json, env); MachCode *mc = JS_CompileMach(ast_json);
if (!code) { if (!mc) {
printf("=== MACH Bytecode ===\nFailed to compile\n=== End MACH Bytecode ===\n"); printf("=== MACH Bytecode ===\nFailed to compile\n=== End MACH Bytecode ===\n");
return; return;
} }
JSCodeRegister *code = JS_LoadMachCode(ctx, mc, env);
printf("=== MACH Bytecode ===\n"); printf("=== MACH Bytecode ===\n");
dump_register_code(ctx, code, 0); dump_register_code(ctx, code, 0);
printf("=== End MACH Bytecode ===\n"); printf("=== End MACH Bytecode ===\n");
@@ -34942,11 +35017,12 @@ void JS_DumpMach(JSContext *ctx, const char *ast_json, JSValue env) {
/* Compile and execute MACH bytecode. Takes AST JSON. */ /* Compile and execute MACH bytecode. Takes AST JSON. */
JSValue JS_RunMach(JSContext *ctx, const char *ast_json, JSValue env) { JSValue JS_RunMach(JSContext *ctx, const char *ast_json, JSValue env) {
JSCodeRegister *code = JS_CompileMach(ctx, ast_json, env); MachCode *mc = JS_CompileMach(ast_json);
if (!code) { if (!mc) {
return JS_ThrowSyntaxError(ctx, "failed to compile AST to MACH bytecode"); return JS_ThrowSyntaxError(ctx, "failed to compile AST to MACH bytecode");
} }
JSCodeRegister *code = JS_LoadMachCode(ctx, mc, env);
JSValue result = JS_CallRegisterVM(ctx, code, ctx->global_obj, 0, NULL, env, JS_NULL); JSValue result = JS_CallRegisterVM(ctx, code, ctx->global_obj, 0, NULL, env, JS_NULL);
return result; return result;
} }

View File

@@ -1219,29 +1219,42 @@ CellModule *cell_module_from_bytecode (JSContext *ctx, JSFunctionBytecode *main_
CellModule *JS_CompileModule (JSContext *ctx, const char *input, size_t input_len, const char *filename); CellModule *JS_CompileModule (JSContext *ctx, const char *input, size_t input_len, const char *filename);
/* Parse source code and return AST as JSON string. /* Parse source code and return AST as JSON string.
Returns malloc'd JSON string (caller must free), or NULL on error. */ Returns malloc'd JSON string (caller must free), or NULL on error.
char *JS_AST (JSContext *ctx, const char *source, size_t len, const char *filename); No JSContext needed — pure string transformation. */
char *JS_AST (const char *source, size_t len, const char *filename);
/* Tokenize source code and return token array as JSON string. /* Tokenize source code and return token array as JSON string.
Returns malloc'd JSON string (caller must free), or NULL on error. */ Returns malloc'd JSON string (caller must free), or NULL on error.
char *JS_Tokenize (JSContext *ctx, const char *source, size_t len, const char *filename); No JSContext needed — pure string transformation. */
char *JS_Tokenize (const char *source, size_t len, const char *filename);
/* Compile AST JSON directly to register bytecode. /* Compiled bytecode (context-free, serializable) */
Returns JSCodeRegister* (caller should not free — managed by context), or NULL on error. */ typedef struct MachCode MachCode;
struct JSCodeRegister *JS_CompileMach(JSContext *ctx, const char *ast_json, JSValue env);
/* Compile AST JSON to context-free MachCode.
Returns MachCode* (caller must free with JS_FreeMachCode), or NULL on error. */
MachCode *JS_CompileMach(const char *ast_json);
/* Free a compiled MachCode tree. */
void JS_FreeMachCode(MachCode *mc);
/* Load compiled MachCode into a JSContext, materializing JSValues.
Returns JSCodeRegister* linked and ready for execution. */
struct JSCodeRegister *JS_LoadMachCode(JSContext *ctx, MachCode *mc, JSValue env);
/* Dump MACH bytecode to stdout for debugging. Takes AST JSON. /* Dump MACH bytecode to stdout for debugging. Takes AST JSON.
Internally generates MCODE and links to binary bytecode. */ Internally compiles, loads, and dumps binary bytecode. */
void JS_DumpMach (JSContext *ctx, const char *ast_json, JSValue env); void JS_DumpMach (JSContext *ctx, const char *ast_json, JSValue env);
/* Compile and execute MACH bytecode. Takes AST JSON. /* Compile and execute MACH bytecode. Takes AST JSON.
Internally generates MCODE and links to binary bytecode. Internally compiles, loads, and executes.
Returns result of execution, or JS_EXCEPTION on error. */ Returns result of execution, or JS_EXCEPTION on error. */
JSValue JS_RunMach (JSContext *ctx, const char *ast_json, JSValue env); JSValue JS_RunMach (JSContext *ctx, const char *ast_json, JSValue env);
/* Compile AST JSON to MCODE JSON (string-based IR). /* Compile AST JSON to MCODE JSON (string-based IR).
Returns malloc'd JSON string, or NULL on error. Caller must free. */ Returns malloc'd JSON string, or NULL on error. Caller must free.
char *JS_Mcode (JSContext *ctx, const char *ast_json); No JSContext needed — pure string transformation. */
char *JS_Mcode (const char *ast_json);
/* Parse and execute MCODE JSON directly via the MCODE interpreter. /* Parse and execute MCODE JSON directly via the MCODE interpreter.
Returns result of execution, or JS_EXCEPTION on error. */ Returns result of execution, or JS_EXCEPTION on error. */

View File

@@ -2190,7 +2190,7 @@ TEST(cell_module_string_constant) {
TEST(tokenize_unterminated_string) { TEST(tokenize_unterminated_string) {
const char *src = "var x = \"hello"; const char *src = "var x = \"hello";
char *json = JS_Tokenize(ctx, src, strlen(src), "<test>"); char *json = JS_Tokenize(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL"); ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL");
ASSERT_ERROR_MSG_CONTAINS(json, "unterminated string"); ASSERT_ERROR_MSG_CONTAINS(json, "unterminated string");
free(json); free(json);
@@ -2199,7 +2199,7 @@ TEST(tokenize_unterminated_string) {
TEST(tokenize_unterminated_template) { TEST(tokenize_unterminated_template) {
const char *src = "var x = `hello"; const char *src = "var x = `hello";
char *json = JS_Tokenize(ctx, src, strlen(src), "<test>"); char *json = JS_Tokenize(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL"); ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL");
ASSERT_ERROR_MSG_CONTAINS(json, "unterminated template"); ASSERT_ERROR_MSG_CONTAINS(json, "unterminated template");
free(json); free(json);
@@ -2208,7 +2208,7 @@ TEST(tokenize_unterminated_template) {
TEST(tokenize_unterminated_block_comment) { TEST(tokenize_unterminated_block_comment) {
const char *src = "var x /* comment"; const char *src = "var x /* comment";
char *json = JS_Tokenize(ctx, src, strlen(src), "<test>"); char *json = JS_Tokenize(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL"); ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL");
ASSERT_ERROR_MSG_CONTAINS(json, "unterminated block comment"); ASSERT_ERROR_MSG_CONTAINS(json, "unterminated block comment");
free(json); free(json);
@@ -2217,7 +2217,7 @@ TEST(tokenize_unterminated_block_comment) {
TEST(tokenize_malformed_hex) { TEST(tokenize_malformed_hex) {
const char *src = "var x = 0x"; const char *src = "var x = 0x";
char *json = JS_Tokenize(ctx, src, strlen(src), "<test>"); char *json = JS_Tokenize(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL"); ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL");
ASSERT_ERROR_MSG_CONTAINS(json, "malformed hex"); ASSERT_ERROR_MSG_CONTAINS(json, "malformed hex");
free(json); free(json);
@@ -2226,7 +2226,7 @@ TEST(tokenize_malformed_hex) {
TEST(tokenize_malformed_binary) { TEST(tokenize_malformed_binary) {
const char *src = "var x = 0b"; const char *src = "var x = 0b";
char *json = JS_Tokenize(ctx, src, strlen(src), "<test>"); char *json = JS_Tokenize(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL"); ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL");
ASSERT_ERROR_MSG_CONTAINS(json, "malformed binary"); ASSERT_ERROR_MSG_CONTAINS(json, "malformed binary");
free(json); free(json);
@@ -2235,7 +2235,7 @@ TEST(tokenize_malformed_binary) {
TEST(tokenize_malformed_exponent) { TEST(tokenize_malformed_exponent) {
const char *src = "var x = 1e+"; const char *src = "var x = 1e+";
char *json = JS_Tokenize(ctx, src, strlen(src), "<test>"); char *json = JS_Tokenize(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL"); ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL");
ASSERT_ERROR_MSG_CONTAINS(json, "no digits after exponent"); ASSERT_ERROR_MSG_CONTAINS(json, "no digits after exponent");
free(json); free(json);
@@ -2244,7 +2244,7 @@ TEST(tokenize_malformed_exponent) {
TEST(tokenize_valid_no_errors) { TEST(tokenize_valid_no_errors) {
const char *src = "var x = 42"; const char *src = "var x = 42";
char *json = JS_Tokenize(ctx, src, strlen(src), "<test>"); char *json = JS_Tokenize(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL"); ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL");
ASSERT_NO_ERRORS(json); ASSERT_NO_ERRORS(json);
free(json); free(json);
@@ -2257,7 +2257,7 @@ TEST(tokenize_valid_no_errors) {
TEST(ast_missing_identifier_after_var) { TEST(ast_missing_identifier_after_var) {
const char *src = "var = 1"; const char *src = "var = 1";
char *json = JS_AST(ctx, src, strlen(src), "<test>"); char *json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_AST returned NULL"); ASSERT_MSG(json != NULL, "JS_AST returned NULL");
ASSERT_ERROR_MSG_CONTAINS(json, "expected identifier"); ASSERT_ERROR_MSG_CONTAINS(json, "expected identifier");
free(json); free(json);
@@ -2266,7 +2266,7 @@ TEST(ast_missing_identifier_after_var) {
TEST(ast_missing_initializer_def) { TEST(ast_missing_initializer_def) {
const char *src = "def x"; const char *src = "def x";
char *json = JS_AST(ctx, src, strlen(src), "<test>"); char *json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_AST returned NULL"); ASSERT_MSG(json != NULL, "JS_AST returned NULL");
ASSERT_ERROR_MSG_CONTAINS(json, "missing initializer"); ASSERT_ERROR_MSG_CONTAINS(json, "missing initializer");
free(json); free(json);
@@ -2275,7 +2275,7 @@ TEST(ast_missing_initializer_def) {
TEST(ast_recovery_continues_after_error) { TEST(ast_recovery_continues_after_error) {
const char *src = "var = 1; var y = 2"; const char *src = "var = 1; var y = 2";
char *json = JS_AST(ctx, src, strlen(src), "<test>"); char *json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_AST returned NULL"); ASSERT_MSG(json != NULL, "JS_AST returned NULL");
ASSERT_HAS_ERRORS(json, 1); ASSERT_HAS_ERRORS(json, 1);
/* Check that 'y' statement is present in the AST */ /* Check that 'y' statement is present in the AST */
@@ -2286,7 +2286,7 @@ TEST(ast_recovery_continues_after_error) {
TEST(ast_valid_no_errors) { TEST(ast_valid_no_errors) {
const char *src = "var x = 1; var y = 2"; const char *src = "var x = 1; var y = 2";
char *json = JS_AST(ctx, src, strlen(src), "<test>"); char *json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_AST returned NULL"); ASSERT_MSG(json != NULL, "JS_AST returned NULL");
ASSERT_NO_ERRORS(json); ASSERT_NO_ERRORS(json);
free(json); free(json);
@@ -2299,7 +2299,7 @@ TEST(ast_valid_no_errors) {
TEST(ast_sem_assign_to_const) { TEST(ast_sem_assign_to_const) {
const char *src = "def x = 5; x = 3"; const char *src = "def x = 5; x = 3";
char *json = JS_AST(ctx, src, strlen(src), "<test>"); char *json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_AST returned NULL"); ASSERT_MSG(json != NULL, "JS_AST returned NULL");
ASSERT_ERROR_MSG_CONTAINS(json, "cannot assign to constant"); ASSERT_ERROR_MSG_CONTAINS(json, "cannot assign to constant");
free(json); free(json);
@@ -2308,7 +2308,7 @@ TEST(ast_sem_assign_to_const) {
TEST(ast_sem_assign_to_arg) { TEST(ast_sem_assign_to_arg) {
const char *src = "function(x) { x = 5; }"; const char *src = "function(x) { x = 5; }";
char *json = JS_AST(ctx, src, strlen(src), "<test>"); char *json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_AST returned NULL"); ASSERT_MSG(json != NULL, "JS_AST returned NULL");
ASSERT_ERROR_MSG_CONTAINS(json, "cannot assign to constant"); ASSERT_ERROR_MSG_CONTAINS(json, "cannot assign to constant");
free(json); free(json);
@@ -2317,7 +2317,7 @@ TEST(ast_sem_assign_to_arg) {
TEST(ast_sem_redeclare_const) { TEST(ast_sem_redeclare_const) {
const char *src = "def x = 1; def x = 2"; const char *src = "def x = 1; def x = 2";
char *json = JS_AST(ctx, src, strlen(src), "<test>"); char *json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_AST returned NULL"); ASSERT_MSG(json != NULL, "JS_AST returned NULL");
ASSERT_ERROR_MSG_CONTAINS(json, "cannot redeclare constant"); ASSERT_ERROR_MSG_CONTAINS(json, "cannot redeclare constant");
free(json); free(json);
@@ -2326,7 +2326,7 @@ TEST(ast_sem_redeclare_const) {
TEST(ast_sem_break_outside_loop) { TEST(ast_sem_break_outside_loop) {
const char *src = "break"; const char *src = "break";
char *json = JS_AST(ctx, src, strlen(src), "<test>"); char *json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_AST returned NULL"); ASSERT_MSG(json != NULL, "JS_AST returned NULL");
ASSERT_ERROR_MSG_CONTAINS(json, "outside of loop"); ASSERT_ERROR_MSG_CONTAINS(json, "outside of loop");
free(json); free(json);
@@ -2335,7 +2335,7 @@ TEST(ast_sem_break_outside_loop) {
TEST(ast_sem_continue_outside_loop) { TEST(ast_sem_continue_outside_loop) {
const char *src = "continue"; const char *src = "continue";
char *json = JS_AST(ctx, src, strlen(src), "<test>"); char *json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_AST returned NULL"); ASSERT_MSG(json != NULL, "JS_AST returned NULL");
ASSERT_ERROR_MSG_CONTAINS(json, "outside of loop"); ASSERT_ERROR_MSG_CONTAINS(json, "outside of loop");
free(json); free(json);
@@ -2344,7 +2344,7 @@ TEST(ast_sem_continue_outside_loop) {
TEST(ast_sem_break_inside_loop_ok) { TEST(ast_sem_break_inside_loop_ok) {
const char *src = "while (true) { break; }"; const char *src = "while (true) { break; }";
char *json = JS_AST(ctx, src, strlen(src), "<test>"); char *json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_AST returned NULL"); ASSERT_MSG(json != NULL, "JS_AST returned NULL");
ASSERT_NO_ERRORS(json); ASSERT_NO_ERRORS(json);
free(json); free(json);
@@ -2353,7 +2353,7 @@ TEST(ast_sem_break_inside_loop_ok) {
TEST(ast_sem_increment_const) { TEST(ast_sem_increment_const) {
const char *src = "def x = 1; x++"; const char *src = "def x = 1; x++";
char *json = JS_AST(ctx, src, strlen(src), "<test>"); char *json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_AST returned NULL"); ASSERT_MSG(json != NULL, "JS_AST returned NULL");
ASSERT_ERROR_MSG_CONTAINS(json, "cannot assign to constant"); ASSERT_ERROR_MSG_CONTAINS(json, "cannot assign to constant");
free(json); free(json);
@@ -2362,7 +2362,7 @@ TEST(ast_sem_increment_const) {
TEST(ast_sem_shadow_var_ok) { TEST(ast_sem_shadow_var_ok) {
const char *src = "var array = []; array"; const char *src = "var array = []; array";
char *json = JS_AST(ctx, src, strlen(src), "<test>"); char *json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_AST returned NULL"); ASSERT_MSG(json != NULL, "JS_AST returned NULL");
ASSERT_NO_ERRORS(json); ASSERT_NO_ERRORS(json);
free(json); free(json);
@@ -2371,7 +2371,7 @@ TEST(ast_sem_shadow_var_ok) {
TEST(ast_sem_var_assign_ok) { TEST(ast_sem_var_assign_ok) {
const char *src = "var x = 1; x = x + 1"; const char *src = "var x = 1; x = x + 1";
char *json = JS_AST(ctx, src, strlen(src), "<test>"); char *json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_AST returned NULL"); ASSERT_MSG(json != NULL, "JS_AST returned NULL");
ASSERT_NO_ERRORS(json); ASSERT_NO_ERRORS(json);
free(json); free(json);
@@ -2380,7 +2380,7 @@ TEST(ast_sem_var_assign_ok) {
TEST(ast_sem_nested_function_scope) { TEST(ast_sem_nested_function_scope) {
const char *src = "var x = 1; function f(x) { return x + 1; }"; const char *src = "var x = 1; function f(x) { return x + 1; }";
char *json = JS_AST(ctx, src, strlen(src), "<test>"); char *json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(json != NULL, "JS_AST returned NULL"); ASSERT_MSG(json != NULL, "JS_AST returned NULL");
ASSERT_NO_ERRORS(json); ASSERT_NO_ERRORS(json);
free(json); free(json);
@@ -2393,21 +2393,23 @@ TEST(ast_sem_nested_function_scope) {
TEST(mach_compile_basic) { TEST(mach_compile_basic) {
const char *src = "var x = 1; x = x + 1"; const char *src = "var x = 1; x = x + 1";
char *ast_json = JS_AST(ctx, src, strlen(src), "<test>"); char *ast_json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL"); ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL");
struct JSCodeRegister *code = JS_CompileMach(ctx, ast_json, JS_NULL); MachCode *mc = JS_CompileMach(ast_json);
free(ast_json); free(ast_json);
ASSERT_MSG(code != NULL, "JS_CompileMach returned NULL"); ASSERT_MSG(mc != NULL, "JS_CompileMach returned NULL");
JS_FreeMachCode(mc);
return 1; return 1;
} }
TEST(mach_compile_function) { TEST(mach_compile_function) {
const char *src = "function f(x) { return x + 1 }"; const char *src = "function f(x) { return x + 1 }";
char *ast_json = JS_AST(ctx, src, strlen(src), "<test>"); char *ast_json = JS_AST(src, strlen(src), "<test>");
ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL"); ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL");
struct JSCodeRegister *code = JS_CompileMach(ctx, ast_json, JS_NULL); MachCode *mc = JS_CompileMach(ast_json);
free(ast_json); free(ast_json);
ASSERT_MSG(code != NULL, "JS_CompileMach returned NULL"); ASSERT_MSG(mc != NULL, "JS_CompileMach returned NULL");
JS_FreeMachCode(mc);
return 1; return 1;
} }