Compare commits
39 Commits
syntax
...
024d796ca4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
024d796ca4 | ||
|
|
ea185dbffd | ||
|
|
6571262af0 | ||
|
|
77ae133747 | ||
|
|
142a2d518b | ||
|
|
5b65c64fe5 | ||
|
|
e985fa5fe1 | ||
|
|
160ade2410 | ||
|
|
e2bc5948c1 | ||
|
|
8cf98d8a9e | ||
|
|
3c38e828e5 | ||
|
|
af2d296f40 | ||
|
|
0a45394689 | ||
|
|
32885a422f | ||
|
|
8959e53303 | ||
|
|
8a9a02b131 | ||
|
|
f9d68b2990 | ||
|
|
017a57b1eb | ||
|
|
ff8c68d01c | ||
|
|
9212003401 | ||
|
|
f9f8a4db42 | ||
|
|
8db95c654b | ||
|
|
63feabed5d | ||
|
|
c814c0e1d8 | ||
|
|
bead0c48d4 | ||
|
|
98dcab4ba7 | ||
|
|
ae44ce7b4b | ||
|
|
1c38699b5a | ||
|
|
9a70a12d82 | ||
|
|
a8a271e014 | ||
|
|
91761c03e6 | ||
|
|
5a479cc765 | ||
|
|
97a003e025 | ||
|
|
20f14abd17 | ||
|
|
19ba184fec | ||
|
|
7909b11f6b | ||
|
|
27229c675c | ||
|
|
64d234ee35 | ||
|
|
e861d73eec |
364
source/cell.c
364
source/cell.c
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "cell.h"
|
||||
#include "cell_internal.h"
|
||||
#include "cJSON.h"
|
||||
|
||||
#define ENGINE "internal/engine.cm"
|
||||
#define CELL_SHOP_DIR ".cell"
|
||||
@@ -104,6 +105,35 @@ 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);
|
||||
if (!root) return 0;
|
||||
cJSON *errors = cJSON_GetObjectItemCaseSensitive(root, "errors");
|
||||
if (!cJSON_IsArray(errors) || cJSON_GetArraySize(errors) == 0) {
|
||||
cJSON_Delete(root);
|
||||
return 0;
|
||||
}
|
||||
const char *filename = "<unknown>";
|
||||
cJSON *fname = cJSON_GetObjectItemCaseSensitive(root, "filename");
|
||||
if (cJSON_IsString(fname))
|
||||
filename = fname->valuestring;
|
||||
cJSON *e;
|
||||
cJSON_ArrayForEach(e, errors) {
|
||||
const char *msg = cJSON_GetStringValue(
|
||||
cJSON_GetObjectItemCaseSensitive(e, "message"));
|
||||
cJSON *line = cJSON_GetObjectItemCaseSensitive(e, "line");
|
||||
cJSON *col = cJSON_GetObjectItemCaseSensitive(e, "column");
|
||||
if (msg && cJSON_IsNumber(line) && cJSON_IsNumber(col))
|
||||
fprintf(stderr, "%s:%d:%d: error: %s\n",
|
||||
filename, (int)line->valuedouble, (int)col->valuedouble, msg);
|
||||
else if (msg)
|
||||
fprintf(stderr, "%s: error: %s\n", filename, msg);
|
||||
}
|
||||
cJSON_Delete(root);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Get the core path for use by scripts
|
||||
const char* cell_get_core_path(void) {
|
||||
return core_path;
|
||||
@@ -369,32 +399,18 @@ 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);
|
||||
free(allocated_script);
|
||||
return has_errors ? 1 : 0;
|
||||
} else {
|
||||
printf("Failed to parse AST\n");
|
||||
free(allocated_script);
|
||||
return 1;
|
||||
}
|
||||
|
||||
JS_FreeContext(ctx);
|
||||
JS_FreeRuntime(rt);
|
||||
free(allocated_script);
|
||||
return json ? 0 : 1;
|
||||
}
|
||||
|
||||
/* Check for --tokenize flag to output token array JSON */
|
||||
@@ -426,35 +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");
|
||||
char *json = JS_Tokenize(script, strlen(script), filename);
|
||||
if (json) {
|
||||
int has_errors = print_json_errors(json);
|
||||
printf("%s\n", json);
|
||||
free(json);
|
||||
free(allocated_script);
|
||||
return has_errors ? 1 : 0;
|
||||
} else {
|
||||
printf("Failed to tokenize\n");
|
||||
free(allocated_script);
|
||||
return 1;
|
||||
}
|
||||
JSContext *ctx = JS_NewContext(rt);
|
||||
if (!ctx) {
|
||||
printf("Failed to create JS context\n");
|
||||
JS_FreeRuntime(rt);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
char *json = JS_Tokenize(ctx, script, strlen(script), filename);
|
||||
if (json) {
|
||||
printf("%s\n", json);
|
||||
free(json);
|
||||
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 {
|
||||
printf("Failed to tokenize\n");
|
||||
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 json ? 0 : 1;
|
||||
return JS_IsException(result) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* Check for --mach flag to output machine code JSON */
|
||||
/* 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;
|
||||
@@ -483,114 +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;
|
||||
}
|
||||
|
||||
char *mach_json = JS_Mach(ctx, ast_json);
|
||||
free(ast_json);
|
||||
|
||||
if (mach_json) {
|
||||
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;
|
||||
if (print_json_errors(ast_json)) {
|
||||
free(ast_json);
|
||||
free(allocated_script);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
char *mach_json = JS_Mach(ctx, ast_json);
|
||||
JS_DumpMach(ctx, ast_json, JS_NULL);
|
||||
free(ast_json);
|
||||
|
||||
if (mach_json) {
|
||||
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;
|
||||
@@ -619,46 +693,35 @@ int cell_init(int argc, char **argv)
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* Parse to AST */
|
||||
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;
|
||||
}
|
||||
|
||||
/* Generate machine code */
|
||||
char *mach_json = JS_Mach(ctx, ast_json);
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
@@ -667,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);
|
||||
|
||||
8972
source/quickjs.c
8972
source/quickjs.c
File diff suppressed because it is too large
Load Diff
@@ -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. */
|
||||
|
||||
312
source/suite.c
312
source/suite.c
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include "quickjs.h"
|
||||
#include "cJSON.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
@@ -2132,6 +2133,286 @@ TEST(cell_module_string_constant) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
ERROR RECOVERY TESTS - Helper macros
|
||||
============================================================================ */
|
||||
|
||||
#define ASSERT_HAS_ERRORS(json_str, min_count) do { \
|
||||
cJSON *_root = cJSON_Parse(json_str); \
|
||||
ASSERT_MSG(_root != NULL, "failed to parse JSON output"); \
|
||||
cJSON *_errs = cJSON_GetObjectItem(_root, "errors"); \
|
||||
if (!_errs || !cJSON_IsArray(_errs) || cJSON_GetArraySize(_errs) < (min_count)) { \
|
||||
printf("[line %d: expected at least %d error(s), got %d] ", __LINE__, (min_count), \
|
||||
_errs && cJSON_IsArray(_errs) ? cJSON_GetArraySize(_errs) : 0); \
|
||||
cJSON_Delete(_root); \
|
||||
return 0; \
|
||||
} \
|
||||
cJSON_Delete(_root); \
|
||||
} while(0)
|
||||
|
||||
#define ASSERT_NO_ERRORS(json_str) do { \
|
||||
cJSON *_root = cJSON_Parse(json_str); \
|
||||
ASSERT_MSG(_root != NULL, "failed to parse JSON output"); \
|
||||
cJSON *_errs = cJSON_GetObjectItem(_root, "errors"); \
|
||||
if (_errs && cJSON_IsArray(_errs) && cJSON_GetArraySize(_errs) > 0) { \
|
||||
cJSON *_first = cJSON_GetArrayItem(_errs, 0); \
|
||||
const char *_msg = cJSON_GetStringValue(cJSON_GetObjectItem(_first, "message")); \
|
||||
printf("[line %d: expected no errors, got: %s] ", __LINE__, _msg ? _msg : "?"); \
|
||||
cJSON_Delete(_root); \
|
||||
return 0; \
|
||||
} \
|
||||
cJSON_Delete(_root); \
|
||||
} while(0)
|
||||
|
||||
#define ASSERT_ERROR_MSG_CONTAINS(json_str, substring) do { \
|
||||
cJSON *_root = cJSON_Parse(json_str); \
|
||||
ASSERT_MSG(_root != NULL, "failed to parse JSON output"); \
|
||||
cJSON *_errs = cJSON_GetObjectItem(_root, "errors"); \
|
||||
int _found = 0; \
|
||||
if (_errs && cJSON_IsArray(_errs)) { \
|
||||
cJSON *_e; \
|
||||
cJSON_ArrayForEach(_e, _errs) { \
|
||||
const char *_msg = cJSON_GetStringValue(cJSON_GetObjectItem(_e, "message")); \
|
||||
if (_msg && strstr(_msg, (substring))) { _found = 1; break; } \
|
||||
} \
|
||||
} \
|
||||
if (!_found) { \
|
||||
printf("[line %d: no error containing '%s'] ", __LINE__, (substring)); \
|
||||
cJSON_Delete(_root); \
|
||||
return 0; \
|
||||
} \
|
||||
cJSON_Delete(_root); \
|
||||
} while(0)
|
||||
|
||||
/* ============================================================================
|
||||
TOKENIZER ERROR TESTS
|
||||
============================================================================ */
|
||||
|
||||
TEST(tokenize_unterminated_string) {
|
||||
const char *src = "var x = \"hello";
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(tokenize_unterminated_template) {
|
||||
const char *src = "var x = `hello";
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(tokenize_unterminated_block_comment) {
|
||||
const char *src = "var x /* comment";
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(tokenize_malformed_hex) {
|
||||
const char *src = "var x = 0x";
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(tokenize_malformed_binary) {
|
||||
const char *src = "var x = 0b";
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(tokenize_malformed_exponent) {
|
||||
const char *src = "var x = 1e+";
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(tokenize_valid_no_errors) {
|
||||
const char *src = "var x = 42";
|
||||
char *json = JS_Tokenize(src, strlen(src), "<test>");
|
||||
ASSERT_MSG(json != NULL, "JS_Tokenize returned NULL");
|
||||
ASSERT_NO_ERRORS(json);
|
||||
free(json);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
PARSER ERROR TESTS
|
||||
============================================================================ */
|
||||
|
||||
TEST(ast_missing_identifier_after_var) {
|
||||
const char *src = "var = 1";
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ast_missing_initializer_def) {
|
||||
const char *src = "def x";
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ast_recovery_continues_after_error) {
|
||||
const char *src = "var = 1; var y = 2";
|
||||
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 */
|
||||
ASSERT_MSG(strstr(json, "\"y\"") != NULL, "recovery failed: 'y' not in AST");
|
||||
free(json);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ast_valid_no_errors) {
|
||||
const char *src = "var x = 1; var y = 2";
|
||||
char *json = JS_AST(src, strlen(src), "<test>");
|
||||
ASSERT_MSG(json != NULL, "JS_AST returned NULL");
|
||||
ASSERT_NO_ERRORS(json);
|
||||
free(json);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
AST SEMANTIC ERROR TESTS
|
||||
============================================================================ */
|
||||
|
||||
TEST(ast_sem_assign_to_const) {
|
||||
const char *src = "def x = 5; x = 3";
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ast_sem_assign_to_arg) {
|
||||
const char *src = "function(x) { x = 5; }";
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ast_sem_redeclare_const) {
|
||||
const char *src = "def x = 1; def x = 2";
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ast_sem_break_outside_loop) {
|
||||
const char *src = "break";
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ast_sem_continue_outside_loop) {
|
||||
const char *src = "continue";
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ast_sem_break_inside_loop_ok) {
|
||||
const char *src = "while (true) { break; }";
|
||||
char *json = JS_AST(src, strlen(src), "<test>");
|
||||
ASSERT_MSG(json != NULL, "JS_AST returned NULL");
|
||||
ASSERT_NO_ERRORS(json);
|
||||
free(json);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ast_sem_increment_const) {
|
||||
const char *src = "def x = 1; x++";
|
||||
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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ast_sem_shadow_var_ok) {
|
||||
const char *src = "var array = []; array";
|
||||
char *json = JS_AST(src, strlen(src), "<test>");
|
||||
ASSERT_MSG(json != NULL, "JS_AST returned NULL");
|
||||
ASSERT_NO_ERRORS(json);
|
||||
free(json);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ast_sem_var_assign_ok) {
|
||||
const char *src = "var x = 1; x = x + 1";
|
||||
char *json = JS_AST(src, strlen(src), "<test>");
|
||||
ASSERT_MSG(json != NULL, "JS_AST returned NULL");
|
||||
ASSERT_NO_ERRORS(json);
|
||||
free(json);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ast_sem_nested_function_scope) {
|
||||
const char *src = "var x = 1; function f(x) { return x + 1; }";
|
||||
char *json = JS_AST(src, strlen(src), "<test>");
|
||||
ASSERT_MSG(json != NULL, "JS_AST returned NULL");
|
||||
ASSERT_NO_ERRORS(json);
|
||||
free(json);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
CODEGEN TESTS (updated for new direct AST-to-bytecode compiler)
|
||||
============================================================================ */
|
||||
|
||||
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");
|
||||
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;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
MAIN TEST RUNNER
|
||||
============================================================================ */
|
||||
@@ -2348,6 +2629,37 @@ int run_c_test_suite(JSContext *ctx)
|
||||
RUN_TEST(cell_module_roundtrip_execute);
|
||||
RUN_TEST(cell_module_string_constant);
|
||||
|
||||
printf("\nTokenizer Errors:\n");
|
||||
RUN_TEST(tokenize_unterminated_string);
|
||||
RUN_TEST(tokenize_unterminated_template);
|
||||
RUN_TEST(tokenize_unterminated_block_comment);
|
||||
RUN_TEST(tokenize_malformed_hex);
|
||||
RUN_TEST(tokenize_malformed_binary);
|
||||
RUN_TEST(tokenize_malformed_exponent);
|
||||
RUN_TEST(tokenize_valid_no_errors);
|
||||
|
||||
printf("\nParser Errors:\n");
|
||||
RUN_TEST(ast_missing_identifier_after_var);
|
||||
RUN_TEST(ast_missing_initializer_def);
|
||||
RUN_TEST(ast_recovery_continues_after_error);
|
||||
RUN_TEST(ast_valid_no_errors);
|
||||
|
||||
printf("\nAST Semantic Errors:\n");
|
||||
RUN_TEST(ast_sem_assign_to_const);
|
||||
RUN_TEST(ast_sem_assign_to_arg);
|
||||
RUN_TEST(ast_sem_redeclare_const);
|
||||
RUN_TEST(ast_sem_break_outside_loop);
|
||||
RUN_TEST(ast_sem_continue_outside_loop);
|
||||
RUN_TEST(ast_sem_break_inside_loop_ok);
|
||||
RUN_TEST(ast_sem_increment_const);
|
||||
RUN_TEST(ast_sem_shadow_var_ok);
|
||||
RUN_TEST(ast_sem_var_assign_ok);
|
||||
RUN_TEST(ast_sem_nested_function_scope);
|
||||
|
||||
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);
|
||||
printf("=================================\n\n");
|
||||
|
||||
39
tests/demo.ce
Normal file
39
tests/demo.ce
Normal 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")
|
||||
@@ -1 +0,0 @@
|
||||
var x = 0; outer: { x = 1; break outer; x = 2 }; x
|
||||
@@ -1 +0,0 @@
|
||||
typeof 5
|
||||
@@ -1 +0,0 @@
|
||||
void 0
|
||||
@@ -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
336
vm_test/syntax.txt
Normal 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
|
||||
@@ -1 +0,0 @@
|
||||
try { 1 } catch(e) { 2 }
|
||||
@@ -1 +0,0 @@
|
||||
var x = 1; try { throw 0 } catch(e) { x = 2 } finally { x = x + 1 }; x
|
||||
@@ -1 +0,0 @@
|
||||
var x = 1; try { x = 2 } finally { x = 3 }; x
|
||||
@@ -1 +0,0 @@
|
||||
try { throw "err" } catch(e) { e }
|
||||
Reference in New Issue
Block a user