kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 5891d2801fc96a50e2f725ab300076e622704cd4
parent a24bb437a9063b0d9f51a75282495f5d191b67dc
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 21 May 2026 10:47:12 -0700

Update FRONTEND plan

Diffstat:
Mdoc/FRONTEND.md | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 66 insertions(+), 0 deletions(-)

diff --git a/doc/FRONTEND.md b/doc/FRONTEND.md @@ -40,6 +40,72 @@ When the REPL calls `cfree_frontend_compile` for the second snippet, it passes a This requires minimal changes to `CG` and `ObjBuilder`, preserves `CfreeCgSym == ObjSymId`, and guarantees monotonic uniqueness across the session. +### 3. REPL Expression Modes +`expr` and bare REPL input should become frontend compile modes, not +driver-side source string rewriting. Today `driver/dbg.c` fabricates a C +translation unit that defines a uniquely named zero-argument thunk, compiles it, +looks up the thunk symbol, and calls it. That works for C syntax only and forces +the driver to recover declarations from DWARF or JIT symbol metadata. + +In the persistent frontend model, `dbg` should keep the language-specific +frontend instance alive and call the same `compile` entrypoint with an input +kind describing how the source text should be interpreted: + +```c +typedef enum CfreeFrontendInputKind { + CFREE_FRONTEND_INPUT_TRANSLATION_UNIT, + CFREE_FRONTEND_INPUT_REPL_TOPLEVEL, + CFREE_FRONTEND_INPUT_REPL_EXPR, + CFREE_FRONTEND_INPUT_REPL_BLOCK, +} CfreeFrontendInputKind; + +typedef struct CfreeFrontendCompileOptions { + CfreeCodeOptions code; + CfreeDiagnosticOptions diagnostics; + const void *language_options; + CfreeFrontendInputKind input_kind; + const char *repl_entry_name; /* for REPL_EXPR / REPL_BLOCK */ +} CfreeFrontendCompileOptions; +``` + +- **`jit { ... }`** uses `CFREE_FRONTEND_INPUT_REPL_TOPLEVEL`: compile + language top-level declarations/statements into a fresh object while keeping + frontend state. +- **`expr ...` and raw bare input** use `CFREE_FRONTEND_INPUT_REPL_EXPR`: the + frontend wraps the text as a zero-argument function named `repl_entry_name` + and returns an integer-compatible value (`uint64_t` / `i64`) for the debugger + to print. +- **`expr { ... }`** uses `CFREE_FRONTEND_INPUT_REPL_BLOCK`: the frontend wraps + the body as a zero-argument function and lets the language define whether the + block must contain an explicit `return`. + +Each frontend owns its own wrapper spelling. C can lower an expression as: + +```c +unsigned long long __cfree_dbg_expr_1(void) { + return (unsigned long long)(USER_EXPR); +} +``` + +Toy can lower the same REPL request as: + +```toy +fn __cfree_dbg_expr_1(): i64 { + return USER_EXPR as i64; +} +``` + +Because the wrapper is compiled by the persistent frontend, it naturally sees +macros, typedefs, functions, globals, and Toy declarations entered earlier in +the session. `dbg` only needs to generate the unique entry name, compile the +fresh object, append it to the JIT image, look up the entry symbol, and execute +it through `CfreeJitSession`. + +DWARF recovery remains useful for inspecting preexisting objects and external +debug info, but it should not be the primary mechanism for normal REPL +expressions. The persistent frontend has strictly better source-level context +for anything typed during the session. + ### Alternative Discussed: DWARF Recovery *Tradeoffs of recovering from DWARF instead of a stateful frontend:* We *could* keep the frontend single-shot and recover types/symbols by reading DWARF from the JIT session (like LLDB does).