29 Commits

Author SHA1 Message Date
John Alanbrook
024d796ca4 add asan error vm stacktrace 2026-02-06 21:49:53 -06:00
John Alanbrook
ea185dbffd rm typeof 2026-02-06 21:26:45 -06:00
John Alanbrook
6571262af0 mach disrupt support 2026-02-06 21:09:18 -06:00
John Alanbrook
77ae133747 Merge branch 'mcode2' into mach 2026-02-06 20:45:57 -06:00
John Alanbrook
142a2d518b Merge branch 'stacktrace' into mach 2026-02-06 20:44:43 -06:00
John Alanbrook
5b65c64fe5 stack traces 2026-02-06 20:44:38 -06:00
John Alanbrook
e985fa5fe1 disrupt/disruption; remove try/catch 2026-02-06 18:40:56 -06:00
John Alanbrook
160ade2410 smarter gc malloc for large allocations 2026-02-06 18:38:23 -06:00
John Alanbrook
e2bc5948c1 fix functions and closures in mach 2026-02-06 18:30:26 -06:00
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
af2d296f40 use new parser info 2026-02-06 12:45:25 -06:00
John Alanbrook
0a45394689 fix crash related to allocating in context heap 2026-02-06 12:43:19 -06:00
John Alanbrook
32885a422f bring in mcode 2026-02-06 04:24:14 -06:00
John Alanbrook
8959e53303 Merge branch 'newsyn' into mcode2 2026-02-06 03:55:56 -06:00
John Alanbrook
8a9a02b131 Merge branch 'newsyn' into mach 2026-02-06 03:54:38 -06:00
John Alanbrook
f9d68b2990 fix if/else, chained assignment 2026-02-06 03:54:25 -06:00
John Alanbrook
017a57b1eb use new parser information 2026-02-06 03:44:44 -06:00
John Alanbrook
ff8c68d01c mcode and mcode interpreter 2026-02-06 03:31:31 -06:00
John Alanbrook
9212003401 cannot set unbound 2026-02-06 03:24:01 -06:00
John Alanbrook
f9f8a4db42 Merge branch 'newsyn' into mach 2026-02-06 03:10:14 -06:00
John Alanbrook
8db95c654b more info in AST parser 2026-02-06 03:00:46 -06:00
John Alanbrook
63feabed5d mach vm 2026-02-06 02:50:48 -06:00
John Alanbrook
c814c0e1d8 rm new; rm void 2026-02-06 02:12:19 -06:00
John Alanbrook
bead0c48d4 Merge branch 'mcode' into newsyn 2026-02-06 02:02:46 -06:00
John Alanbrook
98dcab4ba7 comprehensive syntax test; fix multiple default args 2026-02-06 02:02:17 -06:00
John Alanbrook
ae44ce7b4b mcode and mach 2026-02-06 01:56:26 -06:00
John Alanbrook
1c38699b5a fix scope resolution 2026-02-06 01:41:03 -06:00
John Alanbrook
9a70a12d82 object literal 2026-02-05 21:41:34 -06:00
14 changed files with 5298 additions and 4004 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,39 +442,165 @@ 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;
}
}
/* Check for --mach flag to output machine code JSON */
/* Check for --mcode flag to output MCODE JSON IR */
if (argc >= 3 && strcmp(argv[1], "--mcode") == 0) {
const char *script_or_file = argv[2];
char *script = NULL;
char *allocated_script = NULL;
const char *filename = "<eval>";
struct stat st;
if (stat(script_or_file, &st) == 0 && S_ISREG(st.st_mode)) {
FILE *f = fopen(script_or_file, "r");
if (!f) {
printf("Failed to open file: %s\n", script_or_file);
return 1;
}
allocated_script = malloc(st.st_size + 1);
if (!allocated_script) {
fclose(f);
printf("Failed to allocate memory for script\n");
return 1;
}
size_t read_size = fread(allocated_script, 1, st.st_size, f);
fclose(f);
allocated_script[read_size] = '\0';
script = allocated_script;
filename = script_or_file;
} else {
script = (char *)script_or_file;
}
char *ast_json = JS_AST(script, strlen(script), filename);
if (!ast_json) {
printf("Failed to parse AST\n");
free(allocated_script);
return 1;
}
if (print_json_errors(ast_json)) {
free(ast_json);
free(allocated_script);
return 1;
}
char *mcode_json = JS_Mcode(ast_json);
free(ast_json);
if (!mcode_json) {
printf("Failed to generate MCODE\n");
free(allocated_script);
return 1;
}
printf("%s\n", mcode_json);
free(mcode_json);
free(allocated_script);
return 0;
}
/* Check for --run-mcode flag to execute via MCODE interpreter */
if (argc >= 3 && strcmp(argv[1], "--run-mcode") == 0) {
const char *script_or_file = argv[2];
char *script = NULL;
char *allocated_script = NULL;
const char *filename = "<eval>";
struct stat st;
if (stat(script_or_file, &st) == 0 && S_ISREG(st.st_mode)) {
FILE *f = fopen(script_or_file, "r");
if (!f) { printf("Failed to open file: %s\n", script_or_file); return 1; }
allocated_script = malloc(st.st_size + 1);
if (!allocated_script) { fclose(f); printf("Failed to allocate memory\n"); return 1; }
size_t read_size = fread(allocated_script, 1, st.st_size, f);
fclose(f);
allocated_script[read_size] = '\0';
script = allocated_script;
filename = script_or_file;
} else {
script = (char *)script_or_file;
}
char *ast_json = JS_AST(script, strlen(script), filename);
if (!ast_json) {
printf("Failed to parse AST\n");
free(allocated_script);
return 1;
}
if (print_json_errors(ast_json)) {
free(ast_json); free(allocated_script);
return 1;
}
char *mcode_json = JS_Mcode(ast_json);
free(ast_json);
if (!mcode_json) {
printf("Failed to generate MCODE\n");
free(allocated_script);
return 1;
}
if (print_json_errors(mcode_json)) {
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; }
JSValue result = JS_CallMcode(ctx, mcode_json);
free(mcode_json);
if (JS_IsException(result)) {
JSValue exc = JS_GetException(ctx);
const char *str = JS_ToCString(ctx, exc);
if (str) { printf("Error: %s\n", str); JS_FreeCString(ctx, str); }
cJSON *stack = JS_GetStack(ctx);
if (stack) {
int n = cJSON_GetArraySize(stack);
for (int i = 0; i < n; i++) {
cJSON *fr = cJSON_GetArrayItem(stack, i);
const char *fn = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "function"));
const char *file = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "file"));
int line = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "line"));
int col = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "column"));
printf(" at %s (%s:%d:%d)\n", fn ? fn : "<anonymous>", file ? file : "<unknown>", line, col);
}
cJSON_Delete(stack);
}
JS_FreeValue(ctx, exc);
} else if (!JS_IsNull(result)) {
const char *str = JS_ToCString(ctx, result);
if (str) { printf("%s\n", str); JS_FreeCString(ctx, str); }
}
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return JS_IsException(result) ? 1 : 0;
}
/* Check for --mach flag to dump MACH bytecode */
if (argc >= 3 && strcmp(argv[1], "--mach") == 0) {
const char *script_or_file = argv[2];
char *script = NULL;
@@ -521,144 +629,42 @@ 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 *mach_json = JS_Mach(ctx, ast_json);
free(ast_json);
if (mach_json) {
if (print_json_errors(mach_json)) {
free(mach_json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
printf("%s\n", mach_json);
free(mach_json);
} else {
printf("Failed to generate machine code\n");
}
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return mach_json ? 0 : 1;
}
/* Check for --vmcode flag to dump linked register VM bytecode */
if (argc >= 3 && strcmp(argv[1], "--vmcode") == 0) {
const char *script_or_file = argv[2];
char *script = NULL;
char *allocated_script = NULL;
const char *filename = "<eval>";
struct stat st;
if (stat(script_or_file, &st) == 0 && S_ISREG(st.st_mode)) {
FILE *f = fopen(script_or_file, "r");
if (!f) {
printf("Failed to open file: %s\n", script_or_file);
return 1;
}
allocated_script = malloc(st.st_size + 1);
if (!allocated_script) {
fclose(f);
printf("Failed to allocate memory for script\n");
return 1;
}
size_t read_size = fread(allocated_script, 1, st.st_size, f);
fclose(f);
allocated_script[read_size] = '\0';
script = allocated_script;
filename = script_or_file;
} else {
script = (char *)script_or_file;
}
JSRuntime *rt = JS_NewRuntime();
if (!rt) {
printf("Failed to create JS runtime\n");
free(allocated_script);
free(ast_json); 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);
free(ast_json); JS_FreeRuntime(rt); free(allocated_script);
return 1;
}
char *ast_json = JS_AST(ctx, 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 *mach_json = JS_Mach(ctx, ast_json);
JS_DumpMach(ctx, ast_json, JS_NULL);
free(ast_json);
if (mach_json) {
if (print_json_errors(mach_json)) {
free(mach_json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
JS_DumpRegisterMach(ctx, mach_json, JS_NULL);
free(mach_json);
} else {
printf("Failed to generate machine code\n");
}
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return mach_json ? 0 : 1;
return 0;
}
/* Check for --mach-run flag to generate and run machine code through register VM */
/* Check for --mach-run flag to compile and run through MACH VM */
if (argc >= 3 && strcmp(argv[1], "--mach-run") == 0) {
const char *script_or_file = argv[2];
char *script = NULL;
@@ -687,62 +693,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;
}
JSContext *ctx = JS_NewContext(rt);
if (!ctx) {
printf("Failed to create JS context\n");
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
/* Parse to AST */
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;
}
/* Generate machine code */
char *mach_json = JS_Mach(ctx, ast_json);
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);
if (!mach_json) {
printf("Failed to generate machine code\n");
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
if (print_json_errors(mach_json)) {
free(mach_json);
JS_FreeContext(ctx);
JS_FreeRuntime(rt);
free(allocated_script);
return 1;
}
/* Execute through register VM */
JSValue result = JS_IntegrateRegister(ctx, mach_json, JS_NULL);
free(mach_json);
int exit_code = 0;
if (JS_IsException(result)) {
JSValue exc = JS_GetException(ctx);
@@ -751,9 +730,22 @@ int cell_init(int argc, char **argv)
printf("Error: %s\n", err_str);
JS_FreeCString(ctx, err_str);
}
cJSON *stack = JS_GetStack(ctx);
if (stack) {
int n = cJSON_GetArraySize(stack);
for (int i = 0; i < n; i++) {
cJSON *fr = cJSON_GetArrayItem(stack, i);
const char *fn = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "function"));
const char *file = cJSON_GetStringValue(cJSON_GetObjectItem(fr, "file"));
int line = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "line"));
int col = (int)cJSON_GetNumberValue(cJSON_GetObjectItem(fr, "column"));
printf(" at %s (%s:%d:%d)\n", fn ? fn : "<anonymous>", file ? file : "<unknown>", line, col);
}
cJSON_Delete(stack);
}
JS_FreeValue(ctx, exc);
exit_code = 1;
} else if (!JS_IsNull(result)) {
/* Print result */
const char *str = JS_ToCString(ctx, result);
if (str) {
printf("%s\n", str);

File diff suppressed because it is too large Load Diff

View File

@@ -1219,26 +1219,51 @@ 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);
/* Generate register-based machine code from AST JSON.
Returns malloc'd JSON string (caller must free), or NULL on error. */
char *JS_Mach (JSContext *ctx, const char *ast_json);
/* Compiled bytecode (context-free, serializable) */
typedef struct MachCode MachCode;
/* Link and execute register-based machine code.
mach_json: JSON output from JS_Mach
env: stone record for external variable resolution (or JS_NULL)
/* 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 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 compiles, loads, and executes.
Returns result of execution, or JS_EXCEPTION on error. */
JSValue JS_IntegrateRegister (JSContext *ctx, const char *mach_json, JSValue env);
JSValue JS_RunMach (JSContext *ctx, const char *ast_json, JSValue env);
/* Dump linked register VM bytecode to stdout for debugging.
Links mach_json and prints the resulting bytecode with resolved labels. */
void JS_DumpRegisterMach (JSContext *ctx, const char *mach_json, JSValue env);
/* Compile AST JSON to MCODE JSON (string-based IR).
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. */
JSValue JS_CallMcode (JSContext *ctx, const char *mcode_json);
/* Get stack trace as cJSON array of frame objects.
Returns NULL if no register VM frame is active.
Caller must call cJSON_Delete() on the result. */
struct cJSON *JS_GetStack (JSContext *ctx);
/* Integrate a CellModule with an environment and execute.
Returns callable function value, 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);
@@ -2388,102 +2388,28 @@ TEST(ast_sem_nested_function_scope) {
}
/* ============================================================================
CODEGEN/SEMANTIC ERROR TESTS
CODEGEN TESTS (updated for new direct AST-to-bytecode compiler)
============================================================================ */
TEST(mach_assign_to_const) {
const char *src = "def x = 1; x = 2";
char *ast_json = JS_AST(ctx, src, strlen(src), "<test>");
ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL");
char *mach_json = JS_Mach(ctx, ast_json);
free(ast_json);
ASSERT_MSG(mach_json != NULL, "JS_Mach returned NULL");
ASSERT_ERROR_MSG_CONTAINS(mach_json, "cannot assign to constant");
free(mach_json);
return 1;
}
TEST(mach_assign_to_arg) {
const char *src = "function f(x) { x = 5 }";
char *ast_json = JS_AST(ctx, src, strlen(src), "<test>");
ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL");
char *mach_json = JS_Mach(ctx, ast_json);
free(ast_json);
ASSERT_MSG(mach_json != NULL, "JS_Mach returned NULL");
ASSERT_ERROR_MSG_CONTAINS(mach_json, "cannot assign to function argument");
free(mach_json);
return 1;
}
TEST(mach_redeclare_const) {
const char *src = "def x = 1; def x = 2";
char *ast_json = JS_AST(ctx, src, strlen(src), "<test>");
ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL");
char *mach_json = JS_Mach(ctx, ast_json);
free(ast_json);
ASSERT_MSG(mach_json != NULL, "JS_Mach returned NULL");
ASSERT_ERROR_MSG_CONTAINS(mach_json, "cannot redeclare constant");
free(mach_json);
return 1;
}
TEST(mach_break_outside_loop) {
const char *src = "break";
char *ast_json = JS_AST(ctx, src, strlen(src), "<test>");
ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL");
char *mach_json = JS_Mach(ctx, ast_json);
free(ast_json);
ASSERT_MSG(mach_json != NULL, "JS_Mach returned NULL");
ASSERT_ERROR_MSG_CONTAINS(mach_json, "outside of loop");
free(mach_json);
return 1;
}
TEST(mach_continue_outside_loop) {
const char *src = "continue";
char *ast_json = JS_AST(ctx, src, strlen(src), "<test>");
ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL");
char *mach_json = JS_Mach(ctx, ast_json);
free(ast_json);
ASSERT_MSG(mach_json != NULL, "JS_Mach returned NULL");
ASSERT_ERROR_MSG_CONTAINS(mach_json, "outside of loop");
free(mach_json);
return 1;
}
TEST(mach_assign_unbound_var) {
const char *src = "x = 42";
char *ast_json = JS_AST(ctx, src, strlen(src), "<test>");
ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL");
char *mach_json = JS_Mach(ctx, ast_json);
free(ast_json);
ASSERT_MSG(mach_json != NULL, "JS_Mach returned NULL");
ASSERT_ERROR_MSG_CONTAINS(mach_json, "not declared");
free(mach_json);
return 1;
}
TEST(mach_shadow_is_ok) {
const char *src = "var array = []; array";
char *ast_json = JS_AST(ctx, src, strlen(src), "<test>");
ASSERT_MSG(ast_json != NULL, "JS_AST returned NULL");
char *mach_json = JS_Mach(ctx, ast_json);
free(ast_json);
ASSERT_MSG(mach_json != NULL, "JS_Mach returned NULL");
ASSERT_NO_ERRORS(mach_json);
free(mach_json);
return 1;
}
TEST(mach_valid_no_errors) {
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");
char *mach_json = JS_Mach(ctx, ast_json);
MachCode *mc = JS_CompileMach(ast_json);
free(ast_json);
ASSERT_MSG(mach_json != NULL, "JS_Mach returned NULL");
ASSERT_NO_ERRORS(mach_json);
free(mach_json);
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(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");
JS_FreeMachCode(mc);
return 1;
}
@@ -2730,15 +2656,9 @@ int run_c_test_suite(JSContext *ctx)
RUN_TEST(ast_sem_var_assign_ok);
RUN_TEST(ast_sem_nested_function_scope);
printf("\nCodegen Errors:\n");
RUN_TEST(mach_assign_to_const);
RUN_TEST(mach_assign_to_arg);
RUN_TEST(mach_redeclare_const);
RUN_TEST(mach_break_outside_loop);
RUN_TEST(mach_continue_outside_loop);
RUN_TEST(mach_assign_unbound_var);
RUN_TEST(mach_shadow_is_ok);
RUN_TEST(mach_valid_no_errors);
printf("\nCodegen:\n");
RUN_TEST(mach_compile_basic);
RUN_TEST(mach_compile_function);
printf("\n=================================\n");
printf("Results: %d passed, %d failed\n", tests_passed, tests_failed);

39
tests/demo.ce Normal file
View File

@@ -0,0 +1,39 @@
function safe_add(a, b) {
return a + b
} disruption {
print("disruption caught in safe_add")
}
function inner() {
disrupt
}
function outer() {
inner()
} disruption {
print("disruption caught in outer — from inner()")
}
// Test 1: explicit disrupt with handler
function test_explicit() {
disrupt
} disruption {
print("test 1: explicit disrupt handled")
}
test_explicit()
// Test 2: type error disrupt (number + function)
safe_add(1, print)
// Test 3: unwinding — inner disrupts, outer catches
outer()
// Test 4: disrupt from inside disruption clause
function test_nested() {
disrupt
} disruption {
print("test 4: first disruption")
}
test_nested()
print("done")

View File

@@ -1 +0,0 @@
var x = 0; outer: { x = 1; break outer; x = 2 }; x

View File

@@ -1 +0,0 @@
typeof 5

View File

@@ -1 +0,0 @@
void 0

View File

@@ -1 +0,0 @@
var r; switch(2) { case 1: r = 1; break; case 2: r = 2; break }; r

336
vm_test/syntax.txt Normal file
View File

@@ -0,0 +1,336 @@
/* comprehensive syntax test - exercises all AST paths */
// --- variables and constants ---
var a
var b = 1
var c = 2, d = 3
def PI = 3.14159
def MSG = "hello"
// --- number literals ---
var n_int = 42
var n_float = 3.14
var n_hex = 0xFF
var n_octal = 0o77
var n_binary = 0b1010
var n_underscore = 1_000_000
var n_exp = 1e10
var n_exp_neg = 2.5e-3
// --- string literals ---
var s_single = 'single'
var s_double = "double"
var s_escape = "tab\there\nnewline\\slash\"quote"
var s_unicode = "\u0041\u0042"
// --- template literals ---
var t_basic = `hello ${b} world`
var t_nested = `a ${b + c} b ${d}`
var t_expr = `result: ${b > 0 ? "yes" : "no"}`
// --- array literals ---
var arr_empty = []
var arr_simple = [1, 2, 3]
var arr_mixed = [1, "two", true, null]
var arr_nested = [[1, 2], [3, [4, 5]]]
// --- record/object literals ---
var rec_empty = {}
var rec_simple = {x: 1, y: 2}
var rec_nested = {a: {b: {c: 3}}}
var shorthand_var = 10
var rec_shorthand = {shorthand_var}
var rec_computed = {["key" + "1"]: 100}
var rec_method = {
greet() { return "hi" }
}
var rec_mixed = {
name: "test",
value: 42,
nested: {inner: true},
items: [1, 2, 3]
}
// --- all binary arithmetic operators ---
var arith_add = 1 + 2
var arith_sub = 5 - 3
var arith_mul = 4 * 5
var arith_div = 10 / 3
var arith_mod = 10 % 3
var arith_pow = 2 ** 8
// --- comparison operators ---
var cmp_lt = 1 < 2
var cmp_gt = 2 > 1
var cmp_lte = 1 <= 1
var cmp_gte = 2 >= 2
var cmp_eq = 1 == 1
var cmp_neq = 1 != 2
// --- logical operators ---
var log_and = true && false
var log_or = false || true
var log_not = !false
// --- bitwise operators ---
var bit_and = 0xFF & 0x0F
var bit_or = 0xF0 | 0x0F
var bit_xor = 0xFF ^ 0x0F
var bit_not = ~0
var bit_shl = 1 << 8
var bit_shr = 256 >> 4
var bit_shru = -1 >>> 0
// --- nullish coalescing ---
var nullish = null ?? "default"
// --- ternary operator ---
var tern = b > 0 ? "pos" : "neg"
var tern_nested = b > 0 ? (b > 10 ? "big" : "small") : "neg"
// --- comma operator ---
var comma_result = (1, 2, 3)
// --- unary operators ---
var unary_pos = +b
var unary_neg = -b
// --- increment/decrement ---
var inc_val = 0
inc_val++
inc_val--
;++inc_val
;--inc_val
// --- all compound assignments ---
var ca = 10
ca += 5
ca -= 3
ca *= 2
ca /= 4
ca %= 3
ca **= 2
ca <<= 1
ca >>= 1
ca >>>= 0
ca &= 0xFF
ca |= 0x01
ca ^= 0x10
ca &&= true
ca ||= false
ca ??= 1
// --- chained assignment ---
var ch1, ch2
ch1 = ch2 = 42
// --- property access ---
var obj = {a: {b: {c: 1}}, items: [10, 20, 30]}
var dot_access = obj.a
var dot_chain = obj.a.b.c
var bracket_access = obj["a"]
var bracket_dynamic = obj["it" + "ems"]
var bracket_index = obj.items[1]
// --- optional chaining ---
var opt_obj = {x: {y: 1}}
var opt_prop = opt_obj?.x
var opt_deep = opt_obj?.x?.y
var opt_null = null?.foo
var opt_bracket = opt_obj?.["x"]
var opt_call_obj = {f: function() { return 1 }}
var opt_call = opt_call_obj?.f?.()
// --- function declarations ---
function add(a, b) {
return a + b
}
function no_params() {
return 42
}
function with_default(x = 10, y = 20) {
return x + y
}
// --- function expressions ---
var func_expr = function(x) { return x * 2 }
var func_named = function multiply(x, y) { return x * y }
// --- arrow functions (all forms) ---
var arrow_no_param = () => 0
var arrow_one = x => x + 1
var arrow_multi = (a, b) => a + b
var arrow_block = (a, b) => { return a + b }
var arrow_default = (x = 1, y = 2) => x + y
var arrow_default_expr = (x = 1 + 2, y = arr_simple[0]) => x + y
// --- closures ---
function make_counter() {
var count = 0
return function() {
count += 1
return count
}
}
function outer_fn() {
var x = 10
function middle() {
var y = 20
function inner() {
return x + y
}
return inner()
}
return middle()
}
// --- this in methods ---
var counter = {
val: 0,
inc() { this.val += 1 },
get() { return this.val }
}
// --- if/else/else-if ---
var if_result
if (b > 0) {
if_result = "positive"
} else if (b == 0) {
if_result = "zero"
} else {
if_result = "negative"
}
// --- while loop ---
var w = 0
while (w < 5) {
w += 1
}
// --- do-while loop ---
var dw = 0
do {
dw += 1
} while (dw < 3)
// --- for loop ---
var f_sum = 0
for (var i = 0; i < 10; i++) {
f_sum += i
}
// --- for loop with break ---
var fb = 0
for (var j = 0; j < 100; j++) {
if (j == 5) break
fb = j
}
// --- for loop with continue ---
var fc = 0
for (var k = 0; k < 10; k++) {
if (k % 2 == 0) continue
fc += k
}
// --- labeled break ---
outer: for (var x = 0; x < 3; x++) {
for (var y = 0; y < 3; y++) {
if (y == 1) break outer
}
}
// --- labeled continue ---
var lc = 0
loop: for (var m = 0; m < 3; m++) {
for (var n = 0; n < 3; n++) {
if (n == 1) continue loop
lc += 1
}
}
// --- disrupt and disruption ---
function disrupt_test() {
disrupt
}
function disruption_test() {
var x = 1
} disruption {
var y = 2
}
function disrupt_with_disruption() {
disrupt
} disruption {
var handled = true
}
// --- delete operator ---
var del_obj = {a: 1, b: 2}
delete del_obj.a
// --- in operator ---
var in_result = "b" in del_obj
// --- go statement ---
function async_task() { return 1 }
function caller() { go async_task() }
// --- IIFE ---
var iife = (function() { return 99 })()
// --- recursive function ---
function factorial(n) {
if (n <= 1) return 1
return n * factorial(n - 1)
}
// --- mutually recursive functions ---
function is_even(n) {
if (n == 0) return true
return is_odd(n - 1)
}
function is_odd(n) {
if (n == 0) return false
return is_even(n - 1)
}
// --- block scoping ---
var block_val = 1
{
var block_val = 2
}
// --- nested blocks ---
{
var nb = 1
{
var nb2 = nb + 1
{
var nb3 = nb2 + 1
}
}
}
// --- empty statement ---
;
// --- parenthesized expression for precedence ---
var prec = (1 + 2) * (3 + 4)
// --- complex expressions ---
var complex = arr_simple[0] + rec_simple.x * (b > 0 ? 2 : 1)
// --- operator precedence chain ---
var prec_chain = 1 + 2 * 3 ** 2 - 4 / 2 % 3
// --- regex literals ---
var re = /hello/
var re_flags = /world/gi
// --- line comment at end ---
var end = 1 // done

View File

@@ -1 +0,0 @@
try { 1 } catch(e) { 2 }

View File

@@ -1 +0,0 @@
var x = 1; try { throw 0 } catch(e) { x = 2 } finally { x = x + 1 }; x

View File

@@ -1 +0,0 @@
var x = 1; try { x = 2 } finally { x = 3 }; x

View File

@@ -1 +0,0 @@
try { throw "err" } catch(e) { e }