Jade Minimal REPL MVP (Bootstrapping Plan)¶
Status: empfohlen (non-normativ)
Ziel: Das absolute Minimum, das Jade implementieren muss, damit du im Terminal nicht nur “Text” eintippst, sondern einen echten REPL-Loop hast, der:
- Code annimmt,
- (minimal) kompiliert,
- in der VM ausführt,
- ein Ergebnis ausgibt,
- und wenigstens einen nützlichen Meta-Command kann.
Dieses Dokument ist bewusst brutal pragmatisch. Es ist nicht “die Sprache fertig”, sondern der kleinste vertikale Schnitt durch den Stack.
0) Definition: “REPL Command absetzen”¶
Es gibt drei sinnvolle Minimalstufen:
1) REPL-Hülle (Meta-Commands only)
Du kannst :help tippen und bekommst output. Kein Compiler, keine VM.
2) Echter Eval-Pfad (1+1 → 2)
Du tippst Ausdruck, Jade erzeugt ein Proto und führt es in der VM aus.
3) Jade-Feeling (Tooling zeigt internals)
Du kannst :bytecode last (oder :cost last) und bekommst etwas, das nach VM/Compiler riecht.
Wenn du “zumindest einen REPL Command” im Sinne von “Jade macht was” meinst, brauchst du mindestens Stufe (2).
Wenn du “es hebt sich ab” willst: Stufe (3).
1) MVP Zielbild: Stufe (3) in klein¶
MVP Output, den du im Terminal erreichen willst:
Das ist klein, aber es bestätigt: - es gibt einen Compilerpfad (selbst minimal), - es gibt Protos, - es gibt Frames, - es gibt VM-Ausführung, - Meta-Commands funktionieren.
2) Minimaler Stack: Komponenten und Verantwortlichkeiten¶
2.1 REPL Shell (Host, D)¶
Muss können:
- Zeile lesen (multiline optional später)
- Wenn Zeile mit : beginnt → Meta-Command parse + dispatch
- Sonst → als Chunk “Code” behandeln
State der REPL:
- Env (Bindings), v0: leer oder nur _ (last result)
- lastProto (für :bytecode last)
- optional: lastValue
2.2 Parser (minimal)¶
v0: Expression-Parser nur für:
- Integer literal (i64)
- + (optional -, *, /)
- Klammern optional
Wenn du wirklich minimal sein willst: int (+ int)* reicht.
2.3 Type Engine (hart minimal)¶
v0: nur ein Typ:
- i64
Keine Refinements, keine Protocols, keine Conversions.
Der Type Engine Anteil ist “konstant”: alles ist i64.
Warum trotzdem Type Engine erwähnen?
Weil du später nicht wieder rausmigrieren willst. Schon v0 sollte eine “TypeOf(expr)” Funktion haben, auch wenn sie immer i64 zurückgibt.
2.4 Codegen (Proto Builder)¶
Du willst früh in dein echtes Modell reinlaufen: Proto + Bytecode.
Minimaler Proto:
- code[] (Instruktionsarray)
- regCount
- constPool optional (v0: nicht nötig, weil LoadI immediates trägt)
- debug optional (pc→source) später
2.5 VM (minimal)¶
Frame:
- pc
- regs[] (nur i64 slots, v0)
- proto*
Dispatch loop für 3–4 Opcodes.
3) Minimal Instruction Set (Bootstrapping Subset)¶
Für “1+1” brauchst du:
LoadI dst, immAdd dst, a, bReturn src
Optional (aber schnell nützlich):
- Sub, Mul, Div
- Neg
- Jmp, JmpIf (für später: if, while)
3.1 Bytecode Encoding (v0)¶
Mach es stumpf und debug-freundlich:
- Ein struct pro Opcode mit tag + Operanden.
- Keine Bitpacking-Spielchen, bis du wirklich speed brauchst.
Beispiel:
enum Op : ubyte { LoadI, Add, Return }
struct Instr {
Op op;
ushort a, b, c; // regs
long imm; // nur für LoadI verwendet
}
4) Minimaler Ablauf pro Chunk¶
4.1 Ohne Bindings¶
1) parse expr
2) type-check (trivial)
3) emit bytecode:
- eval in regs
4) proto speichern als lastProto
5) vm.run(proto) -> value
6) value ausgeben
7) Env["_"] = value (optional)
4.2 Mit Bindings (kleines Upgrade)¶
Du willst REPL-typisch:
Minimal dafür:
- EnvSet(name, value) und EnvGet(name) als Host-Operationen (v0)
- Der Codegen ersetzt x durch EnvGet("x")
Du kannst das erstmal ohne Intrinsics lösen: der REPL-Host erkennt let und mutiert Env direkt.
Später kann das in VM-Intrinsics überführt werden, aber v0 soll laufen.
5) Ein Meta-Command, der sofort Mehrwert liefert¶
5.1 :bytecode last (empfohlen als erstes)¶
Warum: Minimal zu implementieren, sofort “Jade VM”-Feeling.
Implementation:
- REPL speichert lastProto
- Disassembler iteriert code[] und druckt Instruktionen
5.2 :help, :reset, :env (Support)¶
:helplistet Commands:resetleert Env und lastProto:envzeigt Bindings (später mehr)
6) D-spezifische Minimal-Style Rules (damit du nicht gleich Mist baust)¶
Schon ab v0 lohnt sich:
- VM Hot Path:
@nogc nothrow - keine Exceptions im VM core
@safedefault,@trustednur für kleine Cast/Pointer Stellen (bei v0 fast nicht nötig)- starke Typen für Indizes:
RegIndex,PcIndex(optional, aber gut)
7) Module-Layout (damit es wächst ohne Chaos)¶
Vorschlag:
jade/repl/shell.d(input loop, meta commands)env.d(bindings)jade/parse/expr.d(minimal parser)jade/te/te.d(TypeOf minimal)jade/codegen/emit.d(AST -> bytecode)proto.d(proto struct)jade/vm/vm.d(run loop)disasm.d(for :bytecode)
8) Tests (minimal, aber entscheidend)¶
Du willst nicht debuggen, ob 1+1 wirklich 2 ist. Du willst es testen.
- parse:
"1+2+3"AST korrekt - codegen+vm:
"1+1" -> 2 - meta:
:bytecode lastenthält opcodes in Reihenfolge - env:
let x=3; x+2 -> 5
9) Nächste sinnvolle Inkremente (nach MVP)¶
Wenn MVP steht, dann:
1) :type <expr> (auch wenn nur i64)
2) :why <expr> stub (zeigt “i64 by default”)
3) if (Jmp/JmpIf)
4) while (Jmp backedge)
5) erst dann: JME handles, arenas, storage commands
10) Warum dieses MVP “richtig” ist¶
Weil es: - dein Proto/Frame Modell von Anfang an benutzt, - VM und Tooling früh sichtbar macht, - keine großen Subsysteme voraussetzt, - und trotzdem exakt der Pfad ist, auf dem du später weiterbauen willst.
Du bekommst ein echtes Feedback-Loop-System: tippen → proto → frame → exec → inspect.
Und das ist der einzige Weg, wie ein Sprachprojekt nicht in Spezifikations-Beton erstarrt.