← Commit history

refactor(ashvm): consume shared hearth/front lexer and pipeline; drop old lexer, @import and REPL; add run/check commands

lennoxrose committed on Oct 18, 2026 at 00:37 UTC

db1d620parentb893228

25 files changed+228−217

hearth/ashvm/Makefilemodified

+13−9
@@ -24,13 +24,12 @@ GC ?= 0
2424 ifeq ($(GC),1)
2525 GC_FLAGS = -DASH_GC
2626 endif
27-CFLAGS = -Wall -Wextra -std=c11 -O2 -Isrc -I../pyre/src -fopenmp $(GC_FLAGS)
28-LDFLAGS = -lm ../../bin/extensions/pyre/libpyre.a -fopenmp -ldl -lvulkan -lstdc++ $(CUDA_LIBS)
27+CFLAGS = -Wall -Wextra -std=c11 -O2 -Isrc -I../front/src -I../pyre/src -fopenmp $(GC_FLAGS)
28+LDFLAGS = -lm ../../bin/libashfront.a ../../bin/extensions/pyre/libpyre.a -fopenmp -ldl -lvulkan -lstdc++ $(CUDA_LIBS)
2929
3030 ASHVM_SRC = \
3131 src/app/C/main.c \
32- src/lexer/C/lexer.c \
33- src/lexer/C/lexer_import.c \
32+ src/app/C/repl.c \
3433 src/value/C/hashmap.c \
3534 src/value/C/value.c \
3635 src/vm/C/api.c \
@@ -42,12 +41,10 @@ ASHVM_SRC = \
4241 src/compiler/C/calls.c \
4342 src/compiler/C/constants.c \
4443 src/compiler/C/expr.c \
45- src/compiler/C/import_paths.c \
4644 src/compiler/C/loop_stack.c \
4745 src/compiler/C/state.c \
4846 src/compiler/C/stmt.c \
4947 src/compiler/C/stmt_control.c \
50- src/compiler/C/stmt_import.c \
5148 src/compiler/C/module_state.c \
5249 src/compiler/C/prescan.c \
5350 src/builtins/C/builtins.c \
@@ -67,7 +64,7 @@ ASHVM_OBJ = $(foreach s,$(ASHVM_SRC),$(call obj_of,$(s)))
6764 ASHVM_DEP = $(foreach s,$(ASHVM_SRC),$(call dep_of,$(s)))
6865
6966 # -MMD -MP: emit a per-object .d file listing the headers it includes, so
70-# editing a shared header (e.g. lexer.h, value.h, chunk.h) correctly
67+# editing a shared header (e.g. value.h, value.h, chunk.h) correctly
7168 # triggers a rebuild of every .o that includes it, not just files whose
7269 # own .c changed. Without this, make's only dependency is "the .c is newer
7370 # than the .o" -- a header-only edit silently produces a binary built from
@@ -89,7 +86,14 @@ all: $(BIN)
8986 ../../bin/extensions/pyre/libpyre.a:
9087 $(MAKE) -C ../pyre lib
9188
92-$(BIN): $(ASHVM_OBJ) ../../bin/extensions/pyre/libpyre.a
89+# The shared front end (lexer, parser, type checker, lowering) is the only frontend ashvm has;
90+# front's own Makefile decides whether the library is up to date.
91+../../bin/libashfront.a: FORCE
92+ $(MAKE) -C ../front
93+
94+FORCE:
95+
96+$(BIN): $(ASHVM_OBJ) ../../bin/libashfront.a ../../bin/extensions/pyre/libpyre.a
9397 mkdir -p ../../bin
9498 $(CC) $(CFLAGS) -o $(BIN) $(ASHVM_OBJ) $(LDFLAGS)
9599
@@ -100,4 +104,4 @@ clean:
100104 find src -name '*.d' -delete
101105 rm -f $(BIN)
102106
103-.PHONY: all clean
107+.PHONY: all clean FORCE

hearth/ashvm/src/app/C/main.cmodified

+23−97
@@ -1,112 +1,38 @@
11 #include <stdio.h>
22 #include <stdlib.h>
33 #include <string.h>
4-#include <setjmp.h>
4+#include "app/H/repl.h"
55 #include "compiler/H/compiler.h"
66 #include "vm/H/vm.h"
77 #include "diagnostics/H/diagnostics.h"
8+#include "jdriver/H/jdriver.h"
89
9-static char *read_file(const char *path) {
10- FILE *file = fopen(path, "rb");
11- if (!file) { fprintf(stderr, "could not open file: %s\n", path); exit(1); }
12- fseek(file, 0L, SEEK_END);
13- long size = ftell(file);
14- rewind(file);
15- char *buffer = malloc(size + 1);
16- fread(buffer, 1, size, file);
17- buffer[size] = '\0';
18- fclose(file);
19- return buffer;
10+void ashvm_execute_ir(char *ir) {
11+ diagnostics_set_source(ir, "<internal ir>");
12+ vm_run(compile(ir));
2013 }
2114
22-static void count_braces(const char *line, int *depth, int *in_string) {
23- for (const char *p = line; *p; p++) {
24- if (*in_string) {
25- if (*p == '\\' && p[1] != '\0') { p++; continue; }
26- if (*p == '"') *in_string = 0;
27- continue;
28- }
29- if (*p == '"') { *in_string = 1; continue; }
30- if (*p == '{') (*depth)++;
31- if (*p == '}') (*depth)--;
32- }
33-}
34-
35-static int is_blank(const char *s) {
36- for (; *s; s++) if (*s != ' ' && *s != '\t' && *s != '\n' && *s != '\r') return 0;
37- return 1;
38-}
39-
40-static jmp_buf repl_recovery;
41-
42-static void run_repl(void) {
43- static Chunk repl_chunk;
44- chunk_init(&repl_chunk);
45- vm_set_repl_recovery(&repl_recovery);
46-
47- printf("Ash REPL (ashvm) -- variables and functions persist between lines.\n");
48- printf("Numeric/string/array/map only (no closures over REPL-level captures across lines).\n");
49- printf("Type 'exit' to quit. Runtime errors are caught automatically; syntax errors will end the session.\n\n");
50-
51- int first_call = 1;
52- char buffer[16384];
53-
54- for (;;) {
55- buffer[0] = '\0';
56- int depth = 0;
57- int in_string = 0;
58- int has_content = 0;
59-
60- for (;;) {
61- printf(depth == 0 ? "ashvm> " : "... ");
62- fflush(stdout);
63-
64- char line[2048];
65- if (!fgets(line, sizeof(line), stdin)) { printf("\n"); return; }
66-
67- if (depth == 0 && !has_content) {
68- char trimmed[2048];
69- strncpy(trimmed, line, sizeof(trimmed) - 1);
70- trimmed[sizeof(trimmed) - 1] = '\0';
71- size_t l = strlen(trimmed);
72- while (l > 0 && (trimmed[l-1] == '\n' || trimmed[l-1] == '\r')) trimmed[--l] = '\0';
73- if (strcmp(trimmed, "exit") == 0) return;
74- }
75-
76- if (!is_blank(line)) has_content = 1;
77- count_braces(line, &depth, &in_string);
78-
79- strncat(buffer, line, sizeof(buffer) - strlen(buffer) - 1);
80-
81- if (depth <= 0) { depth = 0; break; }
82- }
83-
84- if (!has_content) continue;
85-
86- int this_is_first = first_call;
87- first_call = 0;
88-
89- if (setjmp(repl_recovery) != 0) {
90- continue;
91- }
92-
93- int start_offset;
94- compile_repl_line(buffer, &repl_chunk, &start_offset);
95-
96- vm_repl_checkpoint();
97- vm_run_from(&repl_chunk, start_offset, this_is_first);
98- }
15+static int usage(const char *prog) {
16+ fprintf(stderr, "usage: %s interactive REPL\n", prog);
17+ fprintf(stderr, " %s run [--wrap-int] <file.ash> run a program\n", prog);
18+ fprintf(stderr, " %s check <file.ash> type-check without running\n", prog);
19+ return 2;
9920 }
10021
10122 int main(int argc, char *argv[]) {
102- if (argc < 2) {
103- run_repl();
23+ if (argc < 2) return repl_run();
24+
25+ if (strcmp(argv[1], "run") == 0) {
26+ int wrap_int = argc == 4 && strcmp(argv[2], "--wrap-int") == 0;
27+ if (argc != 3 + wrap_int) return usage(argv[0]);
28+ char *ir = jdriver_compile_file(argv[2 + wrap_int], wrap_int);
29+ if (!ir) return 1;
30+ ashvm_execute_ir(ir);
10431 return 0;
10532 }
106- char *source = read_file(argv[1]);
107- diagnostics_set_source(source, argv[1]);
108- Chunk *main_chunk = compile(source, argv[1]);
109- vm_run(main_chunk);
110- free(source);
111- return 0;
33+ if (strcmp(argv[1], "check") == 0) {
34+ if (argc != 3) return usage(argv[0]);
35+ return jdriver_check_file(argv[2]);
36+ }
37+ return usage(argv[0]);
11238 }

hearth/ashvm/src/app/C/repl.cadded

+115−0
@@ -0,0 +1,115 @@
1+#define _DEFAULT_SOURCE
2+#include <fcntl.h>
3+#include <stdio.h>
4+#include <stdlib.h>
5+#include <string.h>
6+#include <sys/wait.h>
7+#include <unistd.h>
8+
9+#include "app/H/repl.h"
10+#include "jdriver/H/jdriver.h"
11+#include "jrepl/H/jrepl.h"
12+
13+// Tracks { } nesting across lines, ignoring braces inside string and char literals.
14+static void count_braces(const char *line, int *depth, char *quote) {
15+ for (const char *p = line; *p; p++) {
16+ if (*quote) {
17+ if (*p == '\\' && p[1] != '\0') { p++; continue; }
18+ if (*p == *quote) *quote = 0;
19+ continue;
20+ }
21+ if (*p == '"' || *p == '\'') { *quote = *p; continue; }
22+ if (*p == '/' && p[1] == '/') break;
23+ if (*p == '{') (*depth)++;
24+ if (*p == '}') (*depth)--;
25+ }
26+}
27+
28+static int is_blank(const char *s) {
29+ for (; *s; s++) if (*s != ' ' && *s != '\t' && *s != '\n' && *s != '\r') return 0;
30+ return 1;
31+}
32+
33+// Runs `source` in a child. Everything printed before JREPL_MARKER (the replayed history) is dropped.
34+// Returns 1 when the child compiled and ran to completion.
35+static int run_in_child(const char *source, int quiet) {
36+ int fds[2];
37+ if (pipe(fds) != 0) { perror("pipe"); return 0; }
38+ fflush(stdout);
39+ fflush(stderr);
40+ pid_t pid = fork();
41+ if (pid < 0) { perror("fork"); return 0; }
42+ if (pid == 0) {
43+ close(fds[0]);
44+ dup2(fds[1], 1);
45+ close(fds[1]);
46+ if (quiet) {
47+ int null = open("/dev/null", O_WRONLY);
48+ if (null >= 0) { dup2(null, 2); close(null); }
49+ }
50+ char *ir = jdriver_compile_source("./Repl.ash", source, 0);
51+ if (!ir) exit(1);
52+ ashvm_execute_ir(ir);
53+ exit(0);
54+ }
55+ close(fds[1]);
56+ char *held = NULL;
57+ size_t held_len = 0;
58+ int seen = 0;
59+ char chunk[4096];
60+ ssize_t n;
61+ while ((n = read(fds[0], chunk, sizeof(chunk))) > 0) {
62+ if (seen) { fwrite(chunk, 1, (size_t)n, stdout); continue; }
63+ held = realloc(held, held_len + (size_t)n + 1);
64+ memcpy(held + held_len, chunk, (size_t)n);
65+ held_len += (size_t)n;
66+ held[held_len] = '\0';
67+ char *m = strstr(held, JREPL_MARKER "\n");
68+ if (m) {
69+ m += strlen(JREPL_MARKER "\n");
70+ fwrite(m, 1, held_len - (size_t)(m - held), stdout);
71+ seen = 1;
72+ }
73+ }
74+ close(fds[0]);
75+ free(held);
76+ int status = 0;
77+ waitpid(pid, &status, 0);
78+ fflush(stdout);
79+ return WIFEXITED(status) && WEXITSTATUS(status) == 0;
80+}
81+
82+int repl_run(void) {
83+ JRepl *session = jrepl_new();
84+ printf("Ash REPL (ashvm) -- Java-like Ash. Imports, types, methods and statements persist.\n");
85+ printf("A line without a trailing ';' or '}' is evaluated and printed. Type 'exit' to quit.\n");
86+ printf("State is rebuilt by replaying earlier lines, so file writes and stdin reads repeat.\n\n");
87+
88+ char buffer[16384];
89+ for (;;) {
90+ buffer[0] = '\0';
91+ int depth = 0, has_content = 0;
92+ char quote = 0;
93+ for (;;) {
94+ printf(depth == 0 ? "ashvm> " : "... ");
95+ fflush(stdout);
96+ char line[2048];
97+ if (!fgets(line, sizeof(line), stdin)) { printf("\n"); return 0; }
98+ if (depth == 0 && !has_content && (strcmp(line, "exit\n") == 0 || strcmp(line, "exit") == 0)) return 0;
99+ if (!is_blank(line)) has_content = 1;
100+ count_braces(line, &depth, &quote);
101+ strncat(buffer, line, sizeof(buffer) - strlen(buffer) - 1);
102+ if (depth <= 0) break;
103+ }
104+ if (!has_content) continue;
105+
106+ JReplKind kind = jrepl_classify(buffer);
107+ if (kind == JREPL_EXPRESSION) {
108+ // a bare expression is printed; if that does not compile (a void call), run it as a statement
109+ if (run_in_child(jrepl_source(session, buffer, kind, 1), 1)) { jrepl_commit(session, buffer, kind, 1); continue; }
110+ if (run_in_child(jrepl_source(session, buffer, kind, 0), 0)) jrepl_commit(session, buffer, kind, 0);
111+ continue;
112+ }
113+ if (run_in_child(jrepl_source(session, buffer, kind, 0), 0)) jrepl_commit(session, buffer, kind, 0);
114+ }
115+}

hearth/ashvm/src/app/D/main.dmodified

+6−3
@@ -1,8 +1,11 @@
1-src/app/O/main.o: src/app/C/main.c src/compiler/H/compiler.h \
2- src/vm/H/chunk.h src/value/H/value.h src/vm/H/vm.h \
3- src/diagnostics/H/diagnostics.h
1+src/app/O/main.o: src/app/C/main.c src/app/H/repl.h \
2+ src/compiler/H/compiler.h src/vm/H/chunk.h src/value/H/value.h \
3+ src/vm/H/vm.h src/diagnostics/H/diagnostics.h \
4+ ../front/src/jdriver/H/jdriver.h
5+src/app/H/repl.h:
46 src/compiler/H/compiler.h:
57 src/vm/H/chunk.h:
68 src/value/H/value.h:
79 src/vm/H/vm.h:
810 src/diagnostics/H/diagnostics.h:
11+../front/src/jdriver/H/jdriver.h:

hearth/ashvm/src/app/D/repl.dadded

+5−0
@@ -0,0 +1,5 @@
1+src/app/O/repl.o: src/app/C/repl.c src/app/H/repl.h \
2+ ../front/src/jdriver/H/jdriver.h ../front/src/jrepl/H/jrepl.h
3+src/app/H/repl.h:
4+../front/src/jdriver/H/jdriver.h:
5+../front/src/jrepl/H/jrepl.h:

hearth/ashvm/src/app/H/repl.hadded

+12−0
@@ -0,0 +1,12 @@
1+#ifndef ASH_VM_REPL_H
2+#define ASH_VM_REPL_H
3+
4+// Interactive session for Java-like Ash. Each accepted line is replayed through the shared front
5+// end (hearth/front/src/jrepl) and executed in a child process, so a failing line cannot damage
6+// the session. Returns the process exit status.
7+int repl_run(void);
8+
9+// Compiles the internal IR text from hearth/front and runs it on the VM. Exits on a compile error.
10+void ashvm_execute_ir(char *ir);
11+
12+#endif

hearth/ashvm/src/compiler/C/stmt.cmodified

+19−23
@@ -5,12 +5,10 @@
55 #include "diagnostics/H/diagnostics.h"
66 #include "compiler/H/loop_stack.h"
77
8-// statement() is shared between top-level parsing (compile()'s own loop,
9-// and stmt_import.c's parse_imported_file_body()) and here, inside a
10-// forge/given/during/attempt body -- block_depth distinguishes the two so
11-// the "only forge/local at an imported file's top level" guard in
12-// statement() only fires for genuinely top-level statements, never for an
13-// ordinary yield/given/etc. nested inside an imported function's own body.
8+// statement() is shared between top-level parsing (compile()'s own loop) and
9+// here, inside a forge/given/during/attempt body -- block_depth distinguishes
10+// the two so the "only forge/local at the unit's top level" guard in
11+// statement() only fires for genuinely top-level statements.
1412 static int block_depth = 0;
1513
1614 void block(void) {
@@ -281,9 +279,6 @@ static void raise_statement(void) {
281279 }
282280
283281 void statement(void) {
284- if (current.type == TOKEN_IMPORT) { advance_token(); compile_import_stmt(); return; }
285- imports_close_block();
286-
287282 const char *ns; int ns_len;
288283 get_import_namespace(&ns, &ns_len);
289284 if (ns_len > 0 && block_depth == 0 && current.type != TOKEN_FORGE && current.type != TOKEN_LOCAL) {
@@ -324,28 +319,29 @@ void statement(void) {
324319
325320 static Chunk main_chunk;
326321
327-Chunk *compile(const char *source, const char *source_path) {
322+// The IR is one unit of top-level `forge` and `local` declarations (see
323+// ir/H/ir_lex.h) that lives in a single module namespace; the program runs by
324+// calling its `start` function. The lowering owns the unit, so there is no
325+// import machinery: only this one namespace exists.
326+#define IR_NAMESPACE "g"
327+
328+Chunk *compile(const char *source) {
328329 vm_function_count = 0;
329330 local_count = 0;
330331 chunk_init(&main_chunk);
331332 chunk = &main_chunk;
333+ set_import_namespace(IR_NAMESPACE, (int)strlen(IR_NAMESPACE));
332334 prescan_functions(source);
333335 lexer_init(source);
334336 advance_token();
335- imports_init(source_path);
336337 while (current.type != TOKEN_EOF) statement();
337338 check_all_functions_defined();
339+ set_import_namespace(NULL, 0);
340+ int start_fn = find_function(IR_NAMESPACE ".start", (int)strlen(IR_NAMESPACE ".start"));
341+ if (start_fn == -1) { fprintf(stderr, "error: program has no entry point\n"); exit(1); }
342+ emit_op(OP_CALL);
343+ emit((uint8_t)start_fn);
344+ emit(0);
345+ emit_op(OP_POP);
338346 return &main_chunk;
339347 }
340-
341-// Appends compiled bytecode to an EXISTING chunk without resetting locals or
342-// function definitions -- used by the REPL so state persists between lines.
343-Chunk *compile_repl_line(const char *source, Chunk *target_chunk, int *out_start_offset) {
344- chunk = target_chunk;
345- *out_start_offset = chunk->count;
346- prescan_functions(source);
347- lexer_init(source);
348- advance_token();
349- while (current.type != TOKEN_EOF) statement();
350- return chunk;
351-}

hearth/ashvm/src/compiler/D/calls.dmodified

+5−4
@@ -1,14 +1,15 @@
11 src/compiler/O/calls.o: src/compiler/C/calls.c src/compiler/H/internal.h \
22 src/compiler/H/state.h src/compiler/H/compiler.h src/vm/H/chunk.h \
3- src/value/H/value.h src/lexer/H/lexer.h src/compiler/H/constants.h \
4- src/compiler/H/module_state.h src/builtins/H/builtins.h \
5- src/diagnostics/H/diagnostics.h src/compiler/H/loop_stack.h
3+ src/value/H/value.h ../front/src/ir/H/ir_lex.h \
4+ src/compiler/H/constants.h src/compiler/H/module_state.h \
5+ src/builtins/H/builtins.h src/diagnostics/H/diagnostics.h \
6+ src/compiler/H/loop_stack.h
67 src/compiler/H/internal.h:
78 src/compiler/H/state.h:
89 src/compiler/H/compiler.h:
910 src/vm/H/chunk.h:
1011 src/value/H/value.h:
11-src/lexer/H/lexer.h:
12+../front/src/ir/H/ir_lex.h:
1213 src/compiler/H/constants.h:
1314 src/compiler/H/module_state.h:
1415 src/builtins/H/builtins.h:

hearth/ashvm/src/compiler/D/constants.dmodified

+2−2
@@ -1,14 +1,14 @@
11 src/compiler/O/constants.o: src/compiler/C/constants.c \
22 src/compiler/H/constants.h src/value/H/value.h src/compiler/H/internal.h \
33 src/compiler/H/state.h src/compiler/H/compiler.h src/vm/H/chunk.h \
4- src/lexer/H/lexer.h src/compiler/H/module_state.h \
4+ ../front/src/ir/H/ir_lex.h src/compiler/H/module_state.h \
55 src/diagnostics/H/diagnostics.h
66 src/compiler/H/constants.h:
77 src/value/H/value.h:
88 src/compiler/H/internal.h:
99 src/compiler/H/state.h:
1010 src/compiler/H/compiler.h:
1111 src/vm/H/chunk.h:
12-src/lexer/H/lexer.h:
12+../front/src/ir/H/ir_lex.h:
1313 src/compiler/H/module_state.h:
1414 src/diagnostics/H/diagnostics.h:

hearth/ashvm/src/compiler/D/expr.dmodified

+4−3
@@ -1,13 +1,14 @@
11 src/compiler/O/expr.o: src/compiler/C/expr.c src/compiler/H/internal.h \
22 src/compiler/H/state.h src/compiler/H/compiler.h src/vm/H/chunk.h \
3- src/value/H/value.h src/lexer/H/lexer.h src/compiler/H/constants.h \
4- src/compiler/H/module_state.h src/diagnostics/H/diagnostics.h
3+ src/value/H/value.h ../front/src/ir/H/ir_lex.h \
4+ src/compiler/H/constants.h src/compiler/H/module_state.h \
5+ src/diagnostics/H/diagnostics.h
56 src/compiler/H/internal.h:
67 src/compiler/H/state.h:
78 src/compiler/H/compiler.h:
89 src/vm/H/chunk.h:
910 src/value/H/value.h:
10-src/lexer/H/lexer.h:
11+../front/src/ir/H/ir_lex.h:
1112 src/compiler/H/constants.h:
1213 src/compiler/H/module_state.h:
1314 src/diagnostics/H/diagnostics.h:

hearth/ashvm/src/compiler/D/loop_stack.dmodified

+4−4
@@ -1,9 +1,9 @@
11 src/compiler/O/loop_stack.o: src/compiler/C/loop_stack.c \
2- src/compiler/H/loop_stack.h src/lexer/H/lexer.h src/compiler/H/state.h \
3- src/compiler/H/compiler.h src/vm/H/chunk.h src/value/H/value.h \
4- src/diagnostics/H/diagnostics.h
2+ src/compiler/H/loop_stack.h ../front/src/ir/H/ir_lex.h \
3+ src/compiler/H/state.h src/compiler/H/compiler.h src/vm/H/chunk.h \
4+ src/value/H/value.h src/diagnostics/H/diagnostics.h
55 src/compiler/H/loop_stack.h:
6-src/lexer/H/lexer.h:
6+../front/src/ir/H/ir_lex.h:
77 src/compiler/H/state.h:
88 src/compiler/H/compiler.h:
99 src/vm/H/chunk.h:

hearth/ashvm/src/compiler/D/module_state.dmodified

+4−3
@@ -1,15 +1,16 @@
11 src/compiler/O/module_state.o: src/compiler/C/module_state.c \
22 src/compiler/H/module_state.h src/compiler/H/internal.h \
33 src/compiler/H/state.h src/compiler/H/compiler.h src/vm/H/chunk.h \
4- src/value/H/value.h src/lexer/H/lexer.h src/compiler/H/constants.h \
5- src/vm/H/state.h src/diagnostics/H/diagnostics.h
4+ src/value/H/value.h ../front/src/ir/H/ir_lex.h \
5+ src/compiler/H/constants.h src/vm/H/state.h \
6+ src/diagnostics/H/diagnostics.h
67 src/compiler/H/module_state.h:
78 src/compiler/H/internal.h:
89 src/compiler/H/state.h:
910 src/compiler/H/compiler.h:
1011 src/vm/H/chunk.h:
1112 src/value/H/value.h:
12-src/lexer/H/lexer.h:
13+../front/src/ir/H/ir_lex.h:
1314 src/compiler/H/constants.h:
1415 src/vm/H/state.h:
1516 src/diagnostics/H/diagnostics.h:

hearth/ashvm/src/compiler/D/prescan.dmodified

+2−2
@@ -1,14 +1,14 @@
11 src/compiler/O/prescan.o: src/compiler/C/prescan.c \
22 src/compiler/H/internal.h src/compiler/H/state.h \
33 src/compiler/H/compiler.h src/vm/H/chunk.h src/value/H/value.h \
4- src/lexer/H/lexer.h src/compiler/H/constants.h \
4+ ../front/src/ir/H/ir_lex.h src/compiler/H/constants.h \
55 src/compiler/H/module_state.h src/diagnostics/H/diagnostics.h
66 src/compiler/H/internal.h:
77 src/compiler/H/state.h:
88 src/compiler/H/compiler.h:
99 src/vm/H/chunk.h:
1010 src/value/H/value.h:
11-src/lexer/H/lexer.h:
11+../front/src/ir/H/ir_lex.h:
1212 src/compiler/H/constants.h:
1313 src/compiler/H/module_state.h:
1414 src/diagnostics/H/diagnostics.h:

hearth/ashvm/src/compiler/D/state.dmodified

+2−2
@@ -1,9 +1,9 @@
11 src/compiler/O/state.o: src/compiler/C/state.c src/compiler/H/state.h \
22 src/compiler/H/compiler.h src/vm/H/chunk.h src/value/H/value.h \
3- src/lexer/H/lexer.h src/diagnostics/H/diagnostics.h
3+ ../front/src/ir/H/ir_lex.h src/diagnostics/H/diagnostics.h
44 src/compiler/H/state.h:
55 src/compiler/H/compiler.h:
66 src/vm/H/chunk.h:
77 src/value/H/value.h:
8-src/lexer/H/lexer.h:
8+../front/src/ir/H/ir_lex.h:
99 src/diagnostics/H/diagnostics.h:

hearth/ashvm/src/compiler/D/stmt.dmodified

+4−4
@@ -1,14 +1,14 @@
11 src/compiler/O/stmt.o: src/compiler/C/stmt.c src/compiler/H/internal.h \
22 src/compiler/H/state.h src/compiler/H/compiler.h src/vm/H/chunk.h \
3- src/value/H/value.h src/lexer/H/lexer.h src/compiler/H/constants.h \
4- src/compiler/H/module_state.h src/diagnostics/H/diagnostics.h \
5- src/compiler/H/loop_stack.h
3+ src/value/H/value.h ../front/src/ir/H/ir_lex.h \
4+ src/compiler/H/constants.h src/compiler/H/module_state.h \
5+ src/diagnostics/H/diagnostics.h src/compiler/H/loop_stack.h
66 src/compiler/H/internal.h:
77 src/compiler/H/state.h:
88 src/compiler/H/compiler.h:
99 src/vm/H/chunk.h:
1010 src/value/H/value.h:
11-src/lexer/H/lexer.h:
11+../front/src/ir/H/ir_lex.h:
1212 src/compiler/H/constants.h:
1313 src/compiler/H/module_state.h:
1414 src/diagnostics/H/diagnostics.h:

hearth/ashvm/src/compiler/D/stmt_control.dmodified

+2−2
@@ -1,15 +1,15 @@
11 src/compiler/O/stmt_control.o: src/compiler/C/stmt_control.c \
22 src/compiler/H/internal.h src/compiler/H/state.h \
33 src/compiler/H/compiler.h src/vm/H/chunk.h src/value/H/value.h \
4- src/lexer/H/lexer.h src/compiler/H/constants.h \
4+ ../front/src/ir/H/ir_lex.h src/compiler/H/constants.h \
55 src/compiler/H/module_state.h src/diagnostics/H/diagnostics.h \
66 src/builtins/H/builtins.h src/compiler/H/loop_stack.h
77 src/compiler/H/internal.h:
88 src/compiler/H/state.h:
99 src/compiler/H/compiler.h:
1010 src/vm/H/chunk.h:
1111 src/value/H/value.h:
12-src/lexer/H/lexer.h:
12+../front/src/ir/H/ir_lex.h:
1313 src/compiler/H/constants.h:
1414 src/compiler/H/module_state.h:
1515 src/diagnostics/H/diagnostics.h:

hearth/ashvm/src/compiler/H/compiler.hmodified

+2−2
@@ -17,7 +17,7 @@ typedef struct {
1717 extern VMFunction vm_functions[MAX_VM_FUNCS];
1818 extern int vm_function_count;
1919
20-Chunk *compile(const char *source, const char *source_path);
21-Chunk *compile_repl_line(const char *source, Chunk *target_chunk, int *out_start_offset);
20+// Compiles the internal IR unit produced by hearth/front (jdriver). Exits with a diagnostic on error.
21+Chunk *compile(const char *source);
2222
2323 #endif

hearth/ashvm/src/compiler/H/internal.hmodified

+0−10
@@ -34,11 +34,6 @@ int try_fuse_condition(TokenType terminator);
3434
3535 // compiler_stmt.c
3636 void block(void);
37-// statement() itself (compiler_stmt.c's dispatcher) also needs to be
38-// callable from compiler_stmt_import.c's parse_imported_file_body(),
39-// which drives the same top-level "while not EOF, parse a statement"
40-// loop for a recursively parsed imported file -- so it's declared here
41-// rather than staying static to compiler_stmt.c.
4237 void statement(void);
4338
4439 // compiler_stmt_control.c -- the statement kinds involved enough to
@@ -50,9 +45,4 @@ void compile_attempt_stmt(void);
5045 void compile_during_stmt(void);
5146 void compile_each_stmt(void);
5247
53-// compiler_stmt_import.c
54-void compile_import_stmt(void);
55-void imports_close_block(void);
56-void imports_init(const char *entry_file_path);
57-
5848 #endif

hearth/ashvm/src/compiler/H/loop_stack.hmodified

+1−1
@@ -1,6 +1,6 @@
11 #ifndef ASH_VM_COMPILER_LOOP_STACK_H
22 #define ASH_VM_COMPILER_LOOP_STACK_H
3-#include "lexer/H/lexer.h"
3+#include "ir/H/ir_lex.h"
44
55 // stop/next need to know which loop they belong to, and loops nest --
66 // they're pure "emit now, patch later" jump bookkeeping (same primitive

hearth/ashvm/src/compiler/H/state.hmodified

+1−1
@@ -1,7 +1,7 @@
11 #ifndef ASH_VM_COMPILER_STATE_H
22 #define ASH_VM_COMPILER_STATE_H
33 #include "compiler/H/compiler.h"
4-#include "lexer/H/lexer.h"
4+#include "ir/H/ir_lex.h"
55
66 // Shared parser/emitter state for compiler_expr.c and compiler_stmt.c --
77 // not part of the public API (compiler.h). Mirrors the same

hearth/ashvm/src/vm/C/api.cmodified

+0−24
@@ -5,15 +5,6 @@
55 #include "vm/H/gc.h"
66 #include "compiler/H/compiler.h"
77
8-void vm_set_repl_recovery(jmp_buf *jb) {
9- repl_recovery_jmp = jb;
10-}
11-
12-void vm_repl_checkpoint(void) {
13- repl_saved_frame_count = frame_count;
14- repl_saved_stack_top = stack_top;
15-}
16-
178 void vm_run(Chunk *main_chunk) {
189 stack_top = stack;
1910 frame_count = 1;
@@ -23,21 +14,6 @@ void vm_run(Chunk *main_chunk) {
2314 vm_execute(0);
2415 }
2516
26-// Runs bytecode starting at start_offset within the SAME persistent chunk,
27-// used by the REPL: on the first call, resets the stack/frames like vm_run;
28-// on later calls, keeps the existing stack/frames (so variables persist) and
29-// just resumes execution at the newly-appended bytecode's start.
30-void vm_run_from(Chunk *chunk, int start_offset, int is_first_call) {
31- if (is_first_call) {
32- stack_top = stack;
33- frame_count = 1;
34- frames[0].chunk = chunk;
35- frames[0].slots = stack;
36- }
37- frames[0].ip = chunk->code + start_offset;
38- vm_execute(0);
39-}
40-
4117 VMValue vm_call_function_sync(int func_idx, VMValue *args, int argc) {
4218 for (int i = 0; i < argc; i++) *stack_top++ = args[i];
4319 int caller_frame_count = frame_count;

hearth/ashvm/src/vm/C/errors.cmodified

+2−12
@@ -21,20 +21,15 @@ void vm_runtime_error(const char *fmt, ...) {
2121
2222 if (try_depth > 0) {
2323 longjmp(try_handlers[try_depth - 1].jmp, 1);
24- } else if (repl_recovery_jmp != NULL) {
25- diagnostics_report("error", error_message, current_pos, current_poslen);
26- frame_count = repl_saved_frame_count;
27- stack_top = repl_saved_stack_top;
28- longjmp(*repl_recovery_jmp, 1);
2924 } else {
3025 diagnostics_report("error", error_message, current_pos, current_poslen);
3126 exit(1);
3227 }
3328 }
3429
3530 // `raise expr;` -- any value (a string is what built-in errors raise; a map
36-// gives a structured error). Mirrors vm_runtime_error's three-way dispatch
37-// (attempt handler / REPL recovery / hard exit) exactly, but hands back
31+// gives a structured error). Mirrors vm_runtime_error's two-way dispatch
32+// (attempt handler / hard exit) exactly, but hands back
3833 // the exact raised VMValue via `raised_value` instead of funneling it
3934 // through the fixed-size error_message buffer -- see state.h.
4035 void vm_raise_value(VMValue msg) {
@@ -44,11 +39,6 @@ void vm_raise_value(VMValue msg) {
4439
4540 if (try_depth > 0) {
4641 longjmp(try_handlers[try_depth - 1].jmp, 1);
47- } else if (repl_recovery_jmp != NULL) {
48- diagnostics_report("error", text, current_pos, current_poslen);
49- frame_count = repl_saved_frame_count;
50- stack_top = repl_saved_stack_top;
51- longjmp(*repl_recovery_jmp, 1);
5242 } else {
5343 diagnostics_report("error", text, current_pos, current_poslen);
5444 exit(1);

hearth/ashvm/src/vm/C/state.cmodified

+0−3
@@ -14,9 +14,6 @@ char error_message[512];
1414 VMValue raised_value;
1515 int has_raised_value = 0;
1616
17-jmp_buf *repl_recovery_jmp = NULL;
18-int repl_saved_frame_count;
19-VMValue *repl_saved_stack_top;
2017
2118 const char *current_pos = NULL;
2219 int current_poslen = 0;

hearth/ashvm/src/vm/H/state.hmodified

+0−3
@@ -46,9 +46,6 @@ extern char error_message[512];
4646 extern VMValue raised_value;
4747 extern int has_raised_value;
4848
49-extern jmp_buf *repl_recovery_jmp;
50-extern int repl_saved_frame_count;
51-extern VMValue *repl_saved_stack_top;
5249
5350 // Source position of the instruction currently executing, captured once per
5451 // DISPATCH() (the single point every instruction passes through) so

hearth/ashvm/src/vm/H/vm.hmodified

+0−3
@@ -4,9 +4,6 @@
44 #include "vm/H/chunk.h"
55
66 void vm_run(Chunk *main_chunk);
7-void vm_run_from(Chunk *chunk, int start_offset, int is_first_call);
8-void vm_set_repl_recovery(jmp_buf *jb);
9-void vm_repl_checkpoint(void);
107
118 VMValue vm_call_function_sync(int func_idx, VMValue *args, int argc);
129 VMValue vm_call_closure_sync(VMClosureObj *c, VMValue *args, int argc);