faster mach bytecode generation
This commit is contained in:
2
Makefile
2
Makefile
@@ -58,7 +58,7 @@ static:
|
||||
# Bootstrap: build cell from scratch using meson (only needed once)
|
||||
# Also installs core scripts to ~/.cell/core
|
||||
bootstrap:
|
||||
meson setup build_bootstrap -Dbuildtype=debug -Db_sanitize=address
|
||||
meson setup build_bootstrap -Dbuildtype=debugoptimized
|
||||
meson compile -C build_bootstrap
|
||||
cp build_bootstrap/cell .
|
||||
cp build_bootstrap/libcell_runtime.dylib .
|
||||
|
||||
@@ -105,15 +105,11 @@ static char* load_core_file(const char *filename, size_t *out_size) {
|
||||
return data;
|
||||
}
|
||||
|
||||
static int print_json_errors(const char *json) {
|
||||
if (!json) return 0;
|
||||
cJSON *root = cJSON_Parse(json);
|
||||
static int print_tree_errors(cJSON *root) {
|
||||
if (!root) return 0;
|
||||
cJSON *errors = cJSON_GetObjectItemCaseSensitive(root, "errors");
|
||||
if (!cJSON_IsArray(errors) || cJSON_GetArraySize(errors) == 0) {
|
||||
cJSON_Delete(root);
|
||||
if (!cJSON_IsArray(errors) || cJSON_GetArraySize(errors) == 0)
|
||||
return 0;
|
||||
}
|
||||
const char *filename = "<unknown>";
|
||||
cJSON *fname = cJSON_GetObjectItemCaseSensitive(root, "filename");
|
||||
if (cJSON_IsString(fname))
|
||||
@@ -130,10 +126,18 @@ static int print_json_errors(const char *json) {
|
||||
else if (msg)
|
||||
fprintf(stderr, "%s: error: %s\n", filename, msg);
|
||||
}
|
||||
cJSON_Delete(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int print_json_errors(const char *json) {
|
||||
if (!json) return 0;
|
||||
cJSON *root = cJSON_Parse(json);
|
||||
if (!root) return 0;
|
||||
int result = print_tree_errors(root);
|
||||
cJSON_Delete(root);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get the core path for use by scripts
|
||||
const char* cell_get_core_path(void) {
|
||||
return core_path;
|
||||
@@ -399,17 +403,13 @@ int cell_init(int argc, char **argv)
|
||||
script = (char *)script_or_file;
|
||||
}
|
||||
|
||||
char *json = JS_AST(script, strlen(script), filename);
|
||||
if (json) {
|
||||
int has_errors = print_json_errors(json);
|
||||
cJSON *root = cJSON_Parse(json);
|
||||
free(json);
|
||||
if (root) {
|
||||
char *pretty = cJSON_Print(root);
|
||||
cJSON_Delete(root);
|
||||
printf("%s\n", pretty);
|
||||
free(pretty);
|
||||
}
|
||||
cJSON *ast = JS_ASTTree(script, strlen(script), filename);
|
||||
if (ast) {
|
||||
int has_errors = print_tree_errors(ast);
|
||||
char *pretty = cJSON_Print(ast);
|
||||
cJSON_Delete(ast);
|
||||
printf("%s\n", pretty);
|
||||
free(pretty);
|
||||
free(allocated_script);
|
||||
return has_errors ? 1 : 0;
|
||||
} else {
|
||||
@@ -647,15 +647,15 @@ int cell_init(int argc, char **argv)
|
||||
script = (char *)script_or_file;
|
||||
}
|
||||
|
||||
char *ast_json = JS_AST(script, strlen(script), filename);
|
||||
if (!ast_json) {
|
||||
cJSON *ast = JS_ASTTree(script, strlen(script), filename);
|
||||
if (!ast) {
|
||||
printf("Failed to parse AST\n");
|
||||
free(allocated_script);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (print_json_errors(ast_json)) {
|
||||
free(ast_json);
|
||||
if (print_tree_errors(ast)) {
|
||||
cJSON_Delete(ast);
|
||||
free(allocated_script);
|
||||
return 1;
|
||||
}
|
||||
@@ -663,18 +663,18 @@ int cell_init(int argc, char **argv)
|
||||
JSRuntime *rt = JS_NewRuntime();
|
||||
if (!rt) {
|
||||
printf("Failed to create JS runtime\n");
|
||||
free(ast_json); free(allocated_script);
|
||||
cJSON_Delete(ast); 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);
|
||||
cJSON_Delete(ast); JS_FreeRuntime(rt); free(allocated_script);
|
||||
return 1;
|
||||
}
|
||||
|
||||
JS_DumpMach(ctx, ast_json, JS_NULL);
|
||||
free(ast_json);
|
||||
JS_DumpMachTree(ctx, ast, JS_NULL);
|
||||
cJSON_Delete(ast);
|
||||
|
||||
JS_FreeContext(ctx);
|
||||
JS_FreeRuntime(rt);
|
||||
@@ -711,15 +711,15 @@ int cell_init(int argc, char **argv)
|
||||
script = (char *)script_or_file;
|
||||
}
|
||||
|
||||
char *ast_json = JS_AST(script, strlen(script), filename);
|
||||
if (!ast_json) {
|
||||
cJSON *ast = JS_ASTTree(script, strlen(script), filename);
|
||||
if (!ast) {
|
||||
printf("Failed to parse AST\n");
|
||||
free(allocated_script);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (print_json_errors(ast_json)) {
|
||||
free(ast_json);
|
||||
if (print_tree_errors(ast)) {
|
||||
cJSON_Delete(ast);
|
||||
free(allocated_script);
|
||||
return 1;
|
||||
}
|
||||
@@ -727,18 +727,18 @@ int cell_init(int argc, char **argv)
|
||||
JSRuntime *rt = JS_NewRuntime();
|
||||
if (!rt) {
|
||||
printf("Failed to create JS runtime\n");
|
||||
free(ast_json); free(allocated_script);
|
||||
cJSON_Delete(ast); 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);
|
||||
cJSON_Delete(ast); JS_FreeRuntime(rt); free(allocated_script);
|
||||
return 1;
|
||||
}
|
||||
|
||||
JSValue result = JS_RunMach(ctx, ast_json, JS_NULL);
|
||||
free(ast_json);
|
||||
JSValue result = JS_RunMachTree(ctx, ast, JS_NULL);
|
||||
cJSON_Delete(ast);
|
||||
|
||||
int exit_code = 0;
|
||||
if (JS_IsException(result)) {
|
||||
|
||||
@@ -31153,7 +31153,7 @@ static void ast_semantic_check (cJSON *ast, cJSON **errors_out,
|
||||
*intrinsics_out = intr_arr;
|
||||
}
|
||||
|
||||
char *JS_AST (const char *source, size_t len, const char *filename) {
|
||||
cJSON *JS_ASTTree (const char *source, size_t len, const char *filename) {
|
||||
ASTParseState s;
|
||||
memset (&s, 0, sizeof (s));
|
||||
|
||||
@@ -31205,10 +31205,14 @@ char *JS_AST (const char *source, size_t len, const char *filename) {
|
||||
cJSON_AddItemToObject (ast, "errors", sem_errors);
|
||||
}
|
||||
|
||||
/* Convert to JSON string */
|
||||
return ast;
|
||||
}
|
||||
|
||||
char *JS_AST (const char *source, size_t len, const char *filename) {
|
||||
cJSON *ast = JS_ASTTree (source, len, filename);
|
||||
if (!ast) return NULL;
|
||||
char *json = cJSON_PrintUnformatted (ast);
|
||||
cJSON_Delete (ast);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -33021,8 +33025,7 @@ static MachCode *mach_compile_program(MachCompState *cs, cJSON *ast) {
|
||||
}
|
||||
|
||||
/* Public API: compile AST JSON to MachCode (context-free) */
|
||||
MachCode *JS_CompileMach(const char *ast_json) {
|
||||
cJSON *ast = cJSON_Parse(ast_json);
|
||||
MachCode *JS_CompileMachTree(cJSON *ast) {
|
||||
if (!ast) return NULL;
|
||||
|
||||
MachCompState cs = {0};
|
||||
@@ -33036,6 +33039,13 @@ MachCode *JS_CompileMach(const char *ast_json) {
|
||||
sys_free(cs.vars[i].name);
|
||||
sys_free(cs.vars);
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
MachCode *JS_CompileMach(const char *ast_json) {
|
||||
cJSON *ast = cJSON_Parse(ast_json);
|
||||
if (!ast) return NULL;
|
||||
MachCode *code = JS_CompileMachTree(ast);
|
||||
cJSON_Delete(ast);
|
||||
return code;
|
||||
}
|
||||
@@ -37386,9 +37396,9 @@ 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) {
|
||||
MachCode *mc = JS_CompileMach(ast_json);
|
||||
/* Dump MACH bytecode to stdout for debugging. Takes AST cJSON tree. */
|
||||
void JS_DumpMachTree(JSContext *ctx, cJSON *ast, JSValue env) {
|
||||
MachCode *mc = JS_CompileMachTree(ast);
|
||||
if (!mc) {
|
||||
printf("=== MACH Bytecode ===\nFailed to compile\n=== End MACH Bytecode ===\n");
|
||||
return;
|
||||
@@ -37400,9 +37410,19 @@ void JS_DumpMach(JSContext *ctx, const char *ast_json, JSValue env) {
|
||||
printf("=== End MACH Bytecode ===\n");
|
||||
}
|
||||
|
||||
/* Compile and execute MACH bytecode. Takes AST JSON. */
|
||||
JSValue JS_RunMach(JSContext *ctx, const char *ast_json, JSValue env) {
|
||||
MachCode *mc = JS_CompileMach(ast_json);
|
||||
void JS_DumpMach(JSContext *ctx, const char *ast_json, JSValue env) {
|
||||
cJSON *ast = cJSON_Parse(ast_json);
|
||||
if (!ast) {
|
||||
printf("=== MACH Bytecode ===\nFailed to parse\n=== End MACH Bytecode ===\n");
|
||||
return;
|
||||
}
|
||||
JS_DumpMachTree(ctx, ast, env);
|
||||
cJSON_Delete(ast);
|
||||
}
|
||||
|
||||
/* Compile and execute MACH bytecode. Takes AST cJSON tree. */
|
||||
JSValue JS_RunMachTree(JSContext *ctx, cJSON *ast, JSValue env) {
|
||||
MachCode *mc = JS_CompileMachTree(ast);
|
||||
if (!mc) {
|
||||
return JS_ThrowSyntaxError(ctx, "failed to compile AST to MACH bytecode");
|
||||
}
|
||||
@@ -37411,3 +37431,13 @@ JSValue JS_RunMach(JSContext *ctx, const char *ast_json, JSValue env) {
|
||||
JSValue result = JS_CallRegisterVM(ctx, code, ctx->global_obj, 0, NULL, env, JS_NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
JSValue JS_RunMach(JSContext *ctx, const char *ast_json, JSValue env) {
|
||||
cJSON *ast = cJSON_Parse(ast_json);
|
||||
if (!ast) {
|
||||
return JS_ThrowSyntaxError(ctx, "failed to parse AST JSON");
|
||||
}
|
||||
JSValue result = JS_RunMachTree(ctx, ast, env);
|
||||
cJSON_Delete(ast);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1218,37 +1218,43 @@ CellModule *cell_module_from_bytecode (JSContext *ctx, JSFunctionBytecode *main_
|
||||
Returns allocated CellModule (caller must free with cell_module_free), or NULL on error. */
|
||||
CellModule *JS_CompileModule (JSContext *ctx, const char *input, size_t input_len, const char *filename);
|
||||
|
||||
/* Parse source code and return AST as cJSON tree.
|
||||
Caller must call cJSON_Delete() on result. */
|
||||
struct cJSON *JS_ASTTree (const char *source, size_t 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.
|
||||
No JSContext needed — pure string transformation. */
|
||||
Returns malloc'd JSON string (caller must free), or NULL on error. */
|
||||
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.
|
||||
No JSContext needed — pure string transformation. */
|
||||
Returns malloc'd JSON string (caller must free), or NULL on error. */
|
||||
char *JS_Tokenize (const char *source, size_t len, const char *filename);
|
||||
|
||||
/* 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. */
|
||||
/* Compile AST cJSON tree to context-free MachCode. */
|
||||
MachCode *JS_CompileMachTree(struct cJSON *ast);
|
||||
|
||||
/* Compile AST JSON string to context-free MachCode. */
|
||||
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. */
|
||||
/* Load compiled MachCode into a JSContext, materializing JSValues. */
|
||||
struct JSCodeRegister *JS_LoadMachCode(JSContext *ctx, MachCode *mc, JSValue env);
|
||||
|
||||
/* Dump MACH bytecode to stdout for debugging. Takes AST JSON.
|
||||
Internally compiles, loads, and dumps binary bytecode. */
|
||||
/* Dump MACH bytecode to stdout. Takes AST cJSON tree. */
|
||||
void JS_DumpMachTree (JSContext *ctx, struct cJSON *ast, JSValue env);
|
||||
|
||||
/* Dump MACH bytecode to stdout. Takes AST JSON string. */
|
||||
void JS_DumpMach (JSContext *ctx, const char *ast_json, JSValue env);
|
||||
|
||||
/* Compile and execute MACH bytecode. Takes AST JSON.
|
||||
Internally compiles, loads, and executes.
|
||||
Returns result of execution, or JS_EXCEPTION on error. */
|
||||
/* Compile and execute MACH bytecode from AST cJSON tree. */
|
||||
JSValue JS_RunMachTree (JSContext *ctx, struct cJSON *ast, JSValue env);
|
||||
|
||||
/* Compile and execute MACH bytecode from AST JSON string. */
|
||||
JSValue JS_RunMach (JSContext *ctx, const char *ast_json, JSValue env);
|
||||
|
||||
/* Compile AST JSON to MCODE JSON (string-based IR).
|
||||
|
||||
@@ -2393,22 +2393,22 @@ TEST(ast_sem_nested_function_scope) {
|
||||
|
||||
TEST(mach_compile_basic) {
|
||||
const char *src = "var x = 1; x = x + 1";
|
||||
char *ast_json = JS_AST(src, strlen(src), "<test>");
|
||||
ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL");
|
||||
MachCode *mc = JS_CompileMach(ast_json);
|
||||
free(ast_json);
|
||||
ASSERT_MSG(mc != NULL, "JS_CompileMach returned NULL");
|
||||
cJSON *ast = JS_ASTTree(src, strlen(src), "<test>");
|
||||
ASSERT_MSG(ast != NULL, "JS_ASTTree returned NULL");
|
||||
MachCode *mc = JS_CompileMachTree(ast);
|
||||
cJSON_Delete(ast);
|
||||
ASSERT_MSG(mc != NULL, "JS_CompileMachTree 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(src, strlen(src), "<test>");
|
||||
ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL");
|
||||
MachCode *mc = JS_CompileMach(ast_json);
|
||||
free(ast_json);
|
||||
ASSERT_MSG(mc != NULL, "JS_CompileMach returned NULL");
|
||||
cJSON *ast = JS_ASTTree(src, strlen(src), "<test>");
|
||||
ASSERT_MSG(ast != NULL, "JS_ASTTree returned NULL");
|
||||
MachCode *mc = JS_CompileMachTree(ast);
|
||||
cJSON_Delete(ast);
|
||||
ASSERT_MSG(mc != NULL, "JS_CompileMachTree returned NULL");
|
||||
JS_FreeMachCode(mc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user