3 Commits

Author SHA1 Message Date
John Alanbrook
8cf98d8a9e Merge branch 'mcode2' into mach 2026-02-06 15:14:40 -06:00
John Alanbrook
3c38e828e5 context free tokenizing, parsing, compiling 2026-02-06 15:14:18 -06:00
John Alanbrook
0a45394689 fix crash related to allocating in context heap 2026-02-06 12:43:19 -06:00
4 changed files with 349 additions and 309 deletions

View File

@@ -399,33 +399,15 @@ int cell_init(int argc, char **argv)
script = (char *)script_or_file;
}
JSRuntime *rt = JS_NewRuntime();
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);
char *json = JS_AST(script, strlen(script), filename);
if (json) {
int has_errors = print_json_errors(json);
printf("%s\n", json);
free(json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return has_errors ? 1 : 0;
} else {
printf("Failed to parse AST\n");
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
@@ -460,33 +442,15 @@ int cell_init(int argc, char **argv)
script = (char *)script_or_file;
}
JSRuntime *rt = JS_NewRuntime();
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);
char *json = JS_Tokenize(script, strlen(script), filename);
if (json) {
int has_errors = print_json_errors(json);
printf("%s\n", json);
free(json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return has_errors ? 1 : 0;
} else {
printf("Failed to tokenize\n");
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
@@ -521,51 +485,31 @@ int cell_init(int argc, char **argv)
script = (char *)script_or_file;
}
JSRuntime *rt = JS_NewRuntime();
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);
char *ast_json = JS_AST(script, strlen(script), filename);
if (!ast_json) {
printf("Failed to parse AST\n");
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
if (print_json_errors(ast_json)) {
free(ast_json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
char *mcode_json = JS_Mcode(ctx, ast_json);
char *mcode_json = JS_Mcode(ast_json);
free(ast_json);
if (!mcode_json) {
printf("Failed to generate MCODE\n");
JS_FreeContext(ctx); JS_FreeRuntime(rt); free(allocated_script);
free(allocated_script);
return 1;
}
printf("%s\n", mcode_json);
free(mcode_json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return 0;
}
@@ -592,40 +536,35 @@ int cell_init(int argc, char **argv)
script = (char *)script_or_file;
}
JSRuntime *rt = JS_NewRuntime();
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);
char *ast_json = JS_AST(script, strlen(script), filename);
if (!ast_json) {
printf("Failed to parse AST\n");
JS_FreeContext(parse_ctx); JS_FreeRuntime(rt); free(allocated_script);
free(allocated_script);
return 1;
}
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;
}
char *mcode_json = JS_Mcode(parse_ctx, ast_json);
char *mcode_json = JS_Mcode(ast_json);
free(ast_json);
JS_FreeContext(parse_ctx);
if (!mcode_json) {
printf("Failed to generate MCODE\n");
JS_FreeRuntime(rt); free(allocated_script);
free(allocated_script);
return 1;
}
if (print_json_errors(mcode_json)) {
free(mcode_json); JS_FreeRuntime(rt); free(allocated_script);
free(mcode_json); free(allocated_script);
return 1;
}
/* 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);
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;
}
JSRuntime *rt = JS_NewRuntime();
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);
char *ast_json = JS_AST(script, strlen(script), filename);
if (!ast_json) {
printf("Failed to parse AST\n");
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
if (print_json_errors(ast_json)) {
free(ast_json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
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);
free(ast_json);
@@ -745,37 +679,32 @@ int cell_init(int argc, char **argv)
script = (char *)script_or_file;
}
JSRuntime *rt = JS_NewRuntime();
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);
char *ast_json = JS_AST(script, strlen(script), filename);
if (!ast_json) {
printf("Failed to parse AST\n");
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
if (print_json_errors(ast_json)) {
free(ast_json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
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);
free(ast_json);

View File

@@ -27857,7 +27857,6 @@ JSValue js_math_cycles_use (JSContext *ctx) {
============================================================ */
typedef struct ASTParseState {
JSContext *ctx;
const char *filename;
const uint8_t *buf_start;
const uint8_t *buf_ptr;
@@ -27870,23 +27869,42 @@ typedef struct ASTParseState {
int has_error;
union {
struct {
JSValue str;
const char *str;
size_t len;
} str;
struct {
JSValue val;
double val;
} num;
struct {
JSValue str;
const char *str;
size_t len;
BOOL has_escape;
BOOL is_reserved;
} ident;
struct {
JSValue body;
JSValue flags;
const char *body;
size_t body_len;
const char *flags;
size_t flags_len;
} regexp;
} token_u;
} 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_assign_expr (ASTParseState *s);
static cJSON *ast_parse_statement (ASTParseState *s);
@@ -28046,7 +28064,8 @@ redo:
}
p++;
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;
case '\'':
case '\"': {
@@ -28065,7 +28084,8 @@ redo:
p++;
/* Store the string content without quotes */
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;
case '\r':
if (p[1] == '\n') p++;
@@ -28137,7 +28157,8 @@ redo:
}
}
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.is_reserved = FALSE;
s->token_val = TOK_IDENT;
@@ -28222,7 +28243,7 @@ redo:
numstr[p - start] = '\0';
double val = strtod (numstr, NULL);
sys_free (numstr);
s->token_u.num.val = JS_NewFloat64 (s->ctx, val);
s->token_u.num.val = val;
} break;
case '*':
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) {
ast_error (s, start, "unterminated template literal");
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 {
p++;
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;
case '\'':
@@ -28376,11 +28399,13 @@ static int tokenize_next (ASTParseState *s) {
if (p >= s->buf_end) {
ast_error (s, start, "unterminated string literal");
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 {
p++;
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;
case '\r':
@@ -28460,7 +28485,8 @@ static int tokenize_next (ASTParseState *s) {
}
}
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.is_reserved = FALSE;
s->token_val = TOK_IDENT;
@@ -28544,7 +28570,7 @@ static int tokenize_next (ASTParseState *s) {
numstr[p - start] = '\0';
double val = strtod (numstr, NULL);
sys_free (numstr);
s->token_u.num.val = JS_NewFloat64 (s->ctx, val);
s->token_u.num.val = val;
} break;
case '*':
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) {
case TOK_NUMBER: {
node = ast_node (s, "number", start);
double d = JS_VALUE_GET_FLOAT64 (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);
}
double d = s->token_u.num.val;
/* Store original text representation */
size_t len = s->buf_ptr - start;
char *text = sys_malloc (len + 1);
@@ -28657,9 +28680,7 @@ static cJSON *ast_parse_primary (ASTParseState *s) {
case TOK_STRING: {
node = ast_node (s, "text", start);
const char *str = JS_ToCString (s->ctx, s->token_u.str.str);
cJSON_AddStringToObject (node, "value", str ? str : "");
JS_FreeCString (s->ctx, str);
cjson_add_strn (node, "value", s->token_u.str.str, s->token_u.str.len);
ast_node_end (s, node, s->buf_ptr);
ast_next_token (s);
} break;
@@ -28681,9 +28702,7 @@ static cJSON *ast_parse_primary (ASTParseState *s) {
if (!has_expr) {
/* Simple template — unchanged behavior */
node = ast_node (s, "text", start);
const char *str = JS_ToCString (s->ctx, s->token_u.str.str);
cJSON_AddStringToObject (node, "value", str ? str : "");
JS_FreeCString (s->ctx, str);
cjson_add_strn (node, "value", s->token_u.str.str, s->token_u.str.len);
ast_node_end (s, node, s->buf_ptr);
ast_next_token (s);
} else {
@@ -28747,9 +28766,7 @@ static cJSON *ast_parse_primary (ASTParseState *s) {
node = ast_parse_arrow_function (s);
} else {
node = ast_node (s, "name", start);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str);
cJSON_AddStringToObject (node, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
cjson_add_strn (node, "name", s->token_u.ident.str, s->token_u.ident.len);
ast_node_end (s, node, s->buf_ptr);
ast_next_token (s);
}
@@ -28845,9 +28862,7 @@ static cJSON *ast_parse_primary (ASTParseState *s) {
while (s->token_val != ')' && s->token_val != TOK_EOF) {
if (s->token_val == TOK_IDENT) {
cJSON *param = ast_node (s, "name", s->token_ptr);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str);
cJSON_AddStringToObject (param, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
cjson_add_strn (param, "name", s->token_u.ident.str, s->token_u.ident.len);
ast_node_end (s, param, s->buf_ptr);
ast_next_token (s);
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_AddItemToObject (new_node, "left", node);
if (s->token_val == TOK_IDENT) {
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str);
cJSON_AddStringToObject (new_node, "right", str ? str : "");
JS_FreeCString (s->ctx, str);
cjson_add_strn (new_node, "right", s->token_u.ident.str, s->token_u.ident.len);
ast_next_token (s);
} else {
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 */
cJSON *new_node = ast_node (s, "?.", start);
cJSON_AddItemToObject (new_node, "left", node);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str);
cJSON_AddStringToObject (new_node, "right", str ? str : "");
JS_FreeCString (s->ctx, str);
cjson_add_strn (new_node, "right", s->token_u.ident.str, s->token_u.ident.len);
ast_next_token (s);
ast_node_end (s, new_node, s->buf_ptr);
node = new_node;
@@ -29363,9 +29374,7 @@ static cJSON *ast_parse_function_inner (ASTParseState *s, BOOL is_expr) {
/* Optional function name */
if (s->token_val == TOK_IDENT) {
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str);
cJSON_AddStringToObject (node, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
cjson_add_strn (node, "name", s->token_u.ident.str, s->token_u.ident.len);
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) {
const uint8_t *param_ptr = s->token_ptr;
cJSON *param = ast_node (s, "name", param_ptr);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str);
cJSON_AddStringToObject (param, "name", str ? str : "");
cjson_add_strn (param, "name", s->token_u.ident.str, s->token_u.ident.len);
/* Check for duplicate parameter name */
{
char *tmp_name = sys_malloc (s->token_u.ident.len + 1);
memcpy (tmp_name, s->token_u.ident.str, s->token_u.ident.len);
tmp_name[s->token_u.ident.len] = '\0';
cJSON *prev;
cJSON_ArrayForEach (prev, params) {
const char *prev_name = cJSON_GetStringValue (cJSON_GetObjectItem (prev, "name"));
if (prev_name && str && strcmp (prev_name, str) == 0) {
ast_error (s, param_ptr, "duplicate parameter name '%s'", str);
if (prev_name && strcmp (prev_name, tmp_name) == 0) {
ast_error (s, param_ptr, "duplicate parameter name '%s'", tmp_name);
break;
}
}
JS_FreeCString (s->ctx, str);
sys_free (tmp_name);
}
ast_node_end (s, param, s->buf_ptr);
ast_next_token (s);
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) {
/* Single parameter without parens: x => ... */
cJSON *param = ast_node (s, "name", s->token_ptr);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str);
cJSON_AddStringToObject (param, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
cjson_add_strn (param, "name", s->token_u.ident.str, s->token_u.ident.len);
ast_node_end (s, param, s->buf_ptr);
cJSON_AddItemToArray (params, param);
ast_next_token (s);
@@ -29461,18 +29472,22 @@ static cJSON *ast_parse_arrow_function (ASTParseState *s) {
if (s->token_val == TOK_IDENT) {
const uint8_t *param_ptr = s->token_ptr;
cJSON *param = ast_node (s, "name", param_ptr);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str);
cJSON_AddStringToObject (param, "name", str ? str : "");
cjson_add_strn (param, "name", s->token_u.ident.str, s->token_u.ident.len);
/* Check for duplicate parameter name */
{
char *tmp_name = sys_malloc (s->token_u.ident.len + 1);
memcpy (tmp_name, s->token_u.ident.str, s->token_u.ident.len);
tmp_name[s->token_u.ident.len] = '\0';
cJSON *prev;
cJSON_ArrayForEach (prev, params) {
const char *prev_name = cJSON_GetStringValue (cJSON_GetObjectItem (prev, "name"));
if (prev_name && str && strcmp (prev_name, str) == 0) {
ast_error (s, param_ptr, "duplicate parameter name '%s'", str);
if (prev_name && strcmp (prev_name, tmp_name) == 0) {
ast_error (s, param_ptr, "duplicate parameter name '%s'", tmp_name);
break;
}
}
JS_FreeCString (s->ctx, str);
sys_free (tmp_name);
}
ast_node_end (s, param, s->buf_ptr);
ast_next_token (s);
@@ -29587,8 +29602,11 @@ static cJSON *ast_parse_statement (ASTParseState *s) {
const uint8_t *var_ptr = s->token_ptr;
node = ast_node (s, kind_name, start);
cJSON *left = ast_node (s, "name", s->token_ptr);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str);
cJSON_AddStringToObject (left, "name", str ? str : "");
cjson_add_strn (left, "name", s->token_u.ident.str, s->token_u.ident.len);
/* 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);
cJSON_AddItemToObject (node, "left", left);
ast_next_token (s);
@@ -29602,9 +29620,9 @@ static cJSON *ast_parse_statement (ASTParseState *s) {
cJSON_AddBoolToObject (node, "pop", 1);
} else if (is_def) {
/* 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);
cJSON_AddItemToArray (decls, node);
decl_count++;
@@ -29772,9 +29790,7 @@ static cJSON *ast_parse_statement (ASTParseState *s) {
node = ast_node (s, "break", start);
ast_next_token (s);
if (s->token_val == TOK_IDENT && !s->got_lf) {
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str);
cJSON_AddStringToObject (node, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
cjson_add_strn (node, "name", s->token_u.ident.str, s->token_u.ident.len);
ast_next_token (s);
}
ast_expect_semi (s);
@@ -29785,9 +29801,7 @@ static cJSON *ast_parse_statement (ASTParseState *s) {
node = ast_node (s, "continue", start);
ast_next_token (s);
if (s->token_val == TOK_IDENT && !s->got_lf) {
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str);
cJSON_AddStringToObject (node, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
cjson_add_strn (node, "name", s->token_u.ident.str, s->token_u.ident.len);
ast_next_token (s);
}
ast_expect_semi (s);
@@ -29816,9 +29830,7 @@ static cJSON *ast_parse_statement (ASTParseState *s) {
if (s->token_val == '(') {
ast_next_token (s);
if (s->token_val == TOK_IDENT) {
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str);
cJSON_AddStringToObject (catch_node, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
cjson_add_strn (catch_node, "name", s->token_u.ident.str, s->token_u.ident.len);
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 == ':') {
/* Labeled statement */
node = ast_node (s, "label", start);
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str);
cJSON_AddStringToObject (node, "name", str ? str : "");
JS_FreeCString (s->ctx, str);
cjson_add_strn (node, "name", s->token_u.ident.str, s->token_u.ident.len);
ast_next_token (s); /* skip identifier */
ast_next_token (s); /* skip colon */
cJSON *stmt = ast_parse_statement (s);
@@ -30540,11 +30550,10 @@ static void ast_semantic_check (cJSON *ast, cJSON **errors_out,
*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;
memset (&s, 0, sizeof (s));
s.ctx = ctx;
s.filename = filename;
s.buf_start = (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);
sys_free (text);
/* Store parsed number */
double d = JS_VALUE_GET_FLOAT64 (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);
}
double d = s->token_u.num.val;
cJSON_AddNumberToObject (tok, "number", d);
} break;
case TOK_STRING:
case TOK_TEMPLATE: {
const char *str = JS_ToCString (s->ctx, s->token_u.str.str);
cJSON_AddStringToObject (tok, "value", str ? str : "");
JS_FreeCString (s->ctx, str);
cjson_add_strn (tok, "value", s->token_u.str.str, s->token_u.str.len);
} break;
case TOK_IDENT: {
const char *str = JS_ToCString (s->ctx, s->token_u.ident.str);
cJSON_AddStringToObject (tok, "value", str ? str : "");
JS_FreeCString (s->ctx, str);
cjson_add_strn (tok, "value", s->token_u.ident.str, s->token_u.ident.len);
} break;
case TOK_ERROR: {
/* Store the raw source text as value */
@@ -30673,11 +30675,10 @@ static cJSON *build_token_object (ASTParseState *s) {
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;
memset (&s, 0, sizeof (s));
s.ctx = ctx;
s.filename = filename;
s.buf_start = (const uint8_t *)source;
s.buf_ptr = (const uint8_t *)source;
@@ -30731,23 +30732,52 @@ typedef struct MachVarInfo {
int is_closure; /* 1 if captured by a nested function */
} MachVarInfo;
/* ---- Compile-time constant pool entry ---- */
/* Stores raw data during compilation; converted to JSValues when loading into context */
typedef enum { MACH_CP_INT, MACH_CP_FLOAT, MACH_CP_STR } MachCPType;
typedef struct {
MachCPType type;
union {
int32_t ival; /* integer constant */
double fval; /* float constant */
char *str; /* owned C string */
};
} 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 ---- */
typedef struct MachCompState {
JSContext *ctx;
/* Instruction buffer (growable) */
MachInstr32 *code;
int code_count;
int code_capacity;
/* Constant pool */
JSValue *cpool;
/* Constant pool (raw entries, no GC objects) */
MachCPoolEntry *cpool;
int cpool_count;
int cpool_capacity;
/* Nested functions */
JSCodeRegister **functions;
MachCode **functions;
int func_count;
int func_capacity;
@@ -30810,56 +30840,77 @@ static void mach_free_reg_to(MachCompState *cs, int saved) {
cs->freereg = saved;
}
/* Add a constant to the pool, return its index */
static int mach_cpool_add(MachCompState *cs, JSValue val) {
/* Check for duplicates (simple linear scan) */
/* Add an integer constant to the pool, return its index */
static int mach_cpool_add_int(MachCompState *cs, int32_t val) {
for (int i = 0; i < cs->cpool_count; i++) {
JSValue existing = cs->cpool[i];
/* Compare by tag and value */
if (JS_IsInt(val) && JS_IsInt(existing)) {
if (JS_VALUE_GET_INT(val) == JS_VALUE_GET_INT(existing))
return i;
} else if (JS_VALUE_IS_TEXT(val) && JS_VALUE_IS_TEXT(existing)) {
const char *a = JS_ToCString(cs->ctx, val);
const char *b = JS_ToCString(cs->ctx, existing);
int eq = a && b && strcmp(a, b) == 0;
JS_FreeCString(cs->ctx, a);
JS_FreeCString(cs->ctx, b);
if (eq) return i;
MachCPoolEntry *e = &cs->cpool[i];
if (e->type == MACH_CP_INT && e->ival == 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(JSValue));
cs->cpool = sys_realloc(cs->cpool, new_cap * sizeof(MachCPoolEntry));
cs->cpool_capacity = new_cap;
}
cs->cpool[cs->cpool_count] = 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++;
}
/* Add a string constant, return its cpool index */
static int mach_cpool_add_str(MachCompState *cs, const char *str) {
/* Check for existing identical string first (before allocating) */
/* Check for existing identical string */
for (int i = 0; i < cs->cpool_count; i++) {
JSValue existing = cs->cpool[i];
if (JS_VALUE_IS_TEXT(existing)) {
const char *s = JS_ToCString(cs->ctx, existing);
int eq = s && strcmp(s, str) == 0;
JS_FreeCString(cs->ctx, s);
if (eq) return i;
MachCPoolEntry *e = &cs->cpool[i];
if (e->type == MACH_CP_STR && strcmp(e->str, str) == 0)
return i;
}
}
JSValue val = JS_NewString(cs->ctx, str);
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(JSValue));
cs->cpool = sys_realloc(cs->cpool, new_cap * sizeof(MachCPoolEntry));
cs->cpool_capacity = new_cap;
}
cs->cpool[cs->cpool_count] = val;
char *dup = sys_malloc(strlen(str) + 1);
memcpy(dup, str, strlen(str) + 1);
cs->cpool[cs->cpool_count] = (MachCPoolEntry){ .type = MACH_CP_STR, .str = dup };
return cs->cpool_count++;
}
/* Convert compile-time cpool entries to JSValue array for JSCodeRegister.
Caller takes ownership of the returned array. Frees the raw entries.
Strings are interned into stone memory (no GC allocation). */
static JSValue *mach_materialize_cpool(JSContext *ctx, MachCPoolEntry *entries, int count) {
if (count == 0) { sys_free(entries); return NULL; }
JSValue *cpool = js_malloc_rt(count * sizeof(JSValue));
for (int i = 0; i < count; i++) {
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);
break;
}
}
return cpool;
}
/* Add a variable */
static void mach_add_var(MachCompState *cs, const char *name, int slot, int is_const) {
if (cs->var_count >= cs->var_capacity) {
@@ -30885,10 +30936,10 @@ static int mach_find_var(MachCompState *cs, const char *name) {
}
/* 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) {
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->functions[cs->func_count] = fn;
@@ -30957,12 +31008,11 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
mach_emit(cs, MACH_AsBx(MACH_LOADI, dest, (int16_t)ival));
} else {
/* Large number: use constant pool */
JSValue val;
int ki;
if (dval == (double)(int32_t)dval)
val = JS_NewInt32(cs->ctx, (int32_t)dval);
ki = mach_cpool_add_int(cs, (int32_t)dval);
else
val = JS_NewFloat64(cs->ctx, dval);
int ki = mach_cpool_add(cs, val);
ki = mach_cpool_add_float(cs, dval);
mach_emit(cs, MACH_ABx(MACH_LOADK, dest, ki));
}
} else {
@@ -31398,7 +31448,6 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
if (dest < 0) dest = mach_reserve_reg(cs);
/* Compile nested function */
MachCompState child = {0};
child.ctx = cs->ctx;
child.parent = cs;
child.scopes = cs->scopes;
child.freereg = 1; /* slot 0 = this */
@@ -31441,10 +31490,11 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
/* Implicit return null */
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_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->nr_slots = child.maxreg;
fn_code->nr_close_slots = fn_ncs ? (int)cJSON_GetNumberValue(fn_ncs) : 0;
@@ -31458,10 +31508,11 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
cJSON *fname = cJSON_GetObjectItem(node, "name");
if (fname && cJSON_IsString(fname)) {
fn_code->name = JS_NewString(cs->ctx, cJSON_GetStringValue(fname));
fn_code->name = JS_CellStone(cs->ctx, fn_code->name);
const char *ns = cJSON_GetStringValue(fname);
fn_code->name = sys_malloc(strlen(ns) + 1);
strcpy(fn_code->name, ns);
} else {
fn_code->name = JS_NULL;
fn_code->name = NULL;
}
/* Free child var table (not code/cpool, those are owned by fn_code now) */
@@ -31734,7 +31785,7 @@ static void mach_link_code(JSContext *ctx, JSCodeRegister *code, JSValue env) {
/* ---- 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");
if (!stmts || !cJSON_IsArray(stmts)) return NULL;
@@ -31758,8 +31809,9 @@ static JSCodeRegister *mach_compile_program(MachCompState *cs, cJSON *ast, JSVal
cJSON *prog_scope = mach_find_scope_record(cs->scopes, 0);
cJSON *ncs_node = prog_scope ? cJSON_GetObjectItem(prog_scope, "nr_close_slots") : NULL;
/* Build JSCodeRegister */
JSCodeRegister *code = js_mallocz_rt(sizeof(JSCodeRegister));
/* Build MachCode */
MachCode *code = sys_malloc(sizeof(MachCode));
memset(code, 0, sizeof(MachCode));
code->arity = 0;
code->nr_slots = cs->maxreg;
code->nr_close_slots = ncs_node ? (int)cJSON_GetNumberValue(ncs_node) : 0;
@@ -31770,27 +31822,23 @@ static JSCodeRegister *mach_compile_program(MachCompState *cs, cJSON *ast, JSVal
code->cpool = cs->cpool;
code->func_count = cs->func_count;
code->functions = cs->functions;
code->name = JS_NULL;
code->name = NULL;
return code;
}
/* Public API: compile AST JSON to JSCodeRegister */
JSCodeRegister *JS_CompileMach(JSContext *ctx, const char *ast_json, JSValue env) {
/* Public API: compile AST JSON to MachCode (context-free) */
MachCode *JS_CompileMach(const char *ast_json) {
cJSON *ast = cJSON_Parse(ast_json);
if (!ast) return NULL;
MachCompState cs = {0};
cs.ctx = ctx;
cs.freereg = 1; /* slot 0 = this */
cs.maxreg = 1;
JSCodeRegister *code = mach_compile_program(&cs, ast, env);
MachCode *code = mach_compile_program(&cs, ast);
/* Link: resolve GETNAME to GETINTRINSIC/GETENV */
if (code) mach_link_code(ctx, code, env);
/* Free var table (code/cpool/functions are owned by JSCodeRegister now) */
/* Free var table (code/cpool/functions are owned by MachCode now) */
for (int i = 0; i < cs.var_count; i++)
sys_free(cs.vars[i].name);
sys_free(cs.vars);
@@ -31799,6 +31847,55 @@ JSCodeRegister *JS_CompileMach(JSContext *ctx, const char *ast_json, JSValue env
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 */
static void js_free_code_register(JSCodeRegister *code) {
if (!code) return;
@@ -32439,7 +32536,6 @@ done:
============================================================ */
typedef struct MachGenState {
JSContext *ctx;
cJSON *instructions;
cJSON *data;
cJSON *functions;
@@ -33591,7 +33687,6 @@ static void mach_gen_statement (MachGenState *s, cJSON *stmt) {
static cJSON *mach_gen_function (MachGenState *parent, cJSON *func_node) {
MachGenState s = {0};
s.ctx = parent->ctx;
s.instructions = cJSON_CreateArray ();
s.data = parent->data;
s.functions = parent->functions;
@@ -33813,12 +33908,11 @@ static cJSON *mach_gen_program (MachGenState *s, cJSON *ast) {
return result;
}
char *JS_Mcode (JSContext *ctx, const char *ast_json) {
char *JS_Mcode (const char *ast_json) {
cJSON *ast = cJSON_Parse (ast_json);
if (!ast) return NULL;
MachGenState s = {0};
s.ctx = ctx;
s.instructions = cJSON_CreateArray ();
s.errors = NULL;
s.has_error = 0;
@@ -35008,12 +35102,13 @@ static void dump_register_code(JSContext *ctx, JSCodeRegister *code, int indent)
/* Dump MACH bytecode to stdout for debugging. Takes AST JSON. */
void JS_DumpMach(JSContext *ctx, const char *ast_json, JSValue env) {
JSCodeRegister *code = JS_CompileMach(ctx, ast_json, env);
if (!code) {
MachCode *mc = JS_CompileMach(ast_json);
if (!mc) {
printf("=== MACH Bytecode ===\nFailed to compile\n=== End MACH Bytecode ===\n");
return;
}
JSCodeRegister *code = JS_LoadMachCode(ctx, mc, env);
printf("=== MACH Bytecode ===\n");
dump_register_code(ctx, code, 0);
printf("=== End MACH Bytecode ===\n");
@@ -35021,11 +35116,12 @@ void JS_DumpMach(JSContext *ctx, const char *ast_json, JSValue env) {
/* Compile and execute MACH bytecode. Takes AST JSON. */
JSValue JS_RunMach(JSContext *ctx, const char *ast_json, JSValue env) {
JSCodeRegister *code = JS_CompileMach(ctx, ast_json, env);
if (!code) {
MachCode *mc = JS_CompileMach(ast_json);
if (!mc) {
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);
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);
/* Parse source code and return AST as JSON string.
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);
Returns malloc'd JSON string (caller must free), or NULL on error.
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.
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);
Returns malloc'd JSON string (caller must free), or NULL on error.
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.
Returns JSCodeRegister* (caller should not free — managed by context), or NULL on error. */
struct JSCodeRegister *JS_CompileMach(JSContext *ctx, const char *ast_json, JSValue env);
/* Compiled bytecode (context-free, serializable) */
typedef struct MachCode MachCode;
/* 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.
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);
/* 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. */
JSValue JS_RunMach (JSContext *ctx, const char *ast_json, JSValue env);
/* Compile AST JSON to MCODE JSON (string-based IR).
Returns malloc'd JSON string, or NULL on error. Caller must free. */
char *JS_Mcode (JSContext *ctx, const char *ast_json);
Returns malloc'd JSON string, or NULL on error. Caller must free.
No JSContext needed — pure string transformation. */
char *JS_Mcode (const char *ast_json);
/* Parse and execute MCODE JSON directly via the MCODE interpreter.
Returns result of execution, or JS_EXCEPTION on error. */

View File

@@ -2190,7 +2190,7 @@ TEST(cell_module_string_constant) {
TEST(tokenize_unterminated_string) {
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_ERROR_MSG_CONTAINS(json, "unterminated string");
free(json);
@@ -2199,7 +2199,7 @@ TEST(tokenize_unterminated_string) {
TEST(tokenize_unterminated_template) {
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_ERROR_MSG_CONTAINS(json, "unterminated template");
free(json);
@@ -2208,7 +2208,7 @@ TEST(tokenize_unterminated_template) {
TEST(tokenize_unterminated_block_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_ERROR_MSG_CONTAINS(json, "unterminated block comment");
free(json);
@@ -2217,7 +2217,7 @@ TEST(tokenize_unterminated_block_comment) {
TEST(tokenize_malformed_hex) {
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_ERROR_MSG_CONTAINS(json, "malformed hex");
free(json);
@@ -2226,7 +2226,7 @@ TEST(tokenize_malformed_hex) {
TEST(tokenize_malformed_binary) {
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_ERROR_MSG_CONTAINS(json, "malformed binary");
free(json);
@@ -2235,7 +2235,7 @@ TEST(tokenize_malformed_binary) {
TEST(tokenize_malformed_exponent) {
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_ERROR_MSG_CONTAINS(json, "no digits after exponent");
free(json);
@@ -2244,7 +2244,7 @@ TEST(tokenize_malformed_exponent) {
TEST(tokenize_valid_no_errors) {
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_NO_ERRORS(json);
free(json);
@@ -2257,7 +2257,7 @@ TEST(tokenize_valid_no_errors) {
TEST(ast_missing_identifier_after_var) {
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_ERROR_MSG_CONTAINS(json, "expected identifier");
free(json);
@@ -2266,7 +2266,7 @@ TEST(ast_missing_identifier_after_var) {
TEST(ast_missing_initializer_def) {
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_ERROR_MSG_CONTAINS(json, "missing initializer");
free(json);
@@ -2275,7 +2275,7 @@ TEST(ast_missing_initializer_def) {
TEST(ast_recovery_continues_after_error) {
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_HAS_ERRORS(json, 1);
/* Check that 'y' statement is present in the AST */
@@ -2286,7 +2286,7 @@ TEST(ast_recovery_continues_after_error) {
TEST(ast_valid_no_errors) {
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_NO_ERRORS(json);
free(json);
@@ -2299,7 +2299,7 @@ TEST(ast_valid_no_errors) {
TEST(ast_sem_assign_to_const) {
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_ERROR_MSG_CONTAINS(json, "cannot assign to constant");
free(json);
@@ -2308,7 +2308,7 @@ TEST(ast_sem_assign_to_const) {
TEST(ast_sem_assign_to_arg) {
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_ERROR_MSG_CONTAINS(json, "cannot assign to constant");
free(json);
@@ -2317,7 +2317,7 @@ TEST(ast_sem_assign_to_arg) {
TEST(ast_sem_redeclare_const) {
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_ERROR_MSG_CONTAINS(json, "cannot redeclare constant");
free(json);
@@ -2326,7 +2326,7 @@ TEST(ast_sem_redeclare_const) {
TEST(ast_sem_break_outside_loop) {
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_ERROR_MSG_CONTAINS(json, "outside of loop");
free(json);
@@ -2335,7 +2335,7 @@ TEST(ast_sem_break_outside_loop) {
TEST(ast_sem_continue_outside_loop) {
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_ERROR_MSG_CONTAINS(json, "outside of loop");
free(json);
@@ -2344,7 +2344,7 @@ TEST(ast_sem_continue_outside_loop) {
TEST(ast_sem_break_inside_loop_ok) {
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_NO_ERRORS(json);
free(json);
@@ -2353,7 +2353,7 @@ TEST(ast_sem_break_inside_loop_ok) {
TEST(ast_sem_increment_const) {
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_ERROR_MSG_CONTAINS(json, "cannot assign to constant");
free(json);
@@ -2362,7 +2362,7 @@ TEST(ast_sem_increment_const) {
TEST(ast_sem_shadow_var_ok) {
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_NO_ERRORS(json);
free(json);
@@ -2371,7 +2371,7 @@ TEST(ast_sem_shadow_var_ok) {
TEST(ast_sem_var_assign_ok) {
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_NO_ERRORS(json);
free(json);
@@ -2380,7 +2380,7 @@ TEST(ast_sem_var_assign_ok) {
TEST(ast_sem_nested_function_scope) {
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_NO_ERRORS(json);
free(json);
@@ -2393,21 +2393,23 @@ TEST(ast_sem_nested_function_scope) {
TEST(mach_compile_basic) {
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");
struct JSCodeRegister *code = JS_CompileMach(ctx, ast_json, JS_NULL);
MachCode *mc = JS_CompileMach(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;
}
TEST(mach_compile_function) {
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");
struct JSCodeRegister *code = JS_CompileMach(ctx, ast_json, JS_NULL);
MachCode *mc = JS_CompileMach(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;
}