Jade/JDL: REPL & Script Mode – Design Rationale (Spec-synchron)¶
Status: empfohlen (non-normativ), spec-synchron zu den vorhandenen Dokumenten
Ziel: Das komplette “Warum und Wie” hinter dem REPL/Script-Mode-Ansatz aus unserem Gespräch – inkl. Abgleich mit den bestehenden Specs.
Nicht-Ziel: Dieses Dokument führt keine neue Sprachsemantik ein. Wo die Specs Lücken haben, wird das als offen markiert und es werden minimale, leicht implementierbare Optionen vorgeschlagen.
0. Kontext: Warum REPL/Script Mode überhaupt ein Thema ist¶
- JDL ist konzeptionell “neu” (Types/Refinements/Policies/Arenas/Effects). Ohne Tooling wirkt das für viele wie ein Bootcamp.
- Eine REPL ist nicht nur “eval”, sondern Interaktion: erklären, inspizieren, messen, debuggen.
- Wenn du die REPL auf denselben Mechanismen aufbaust wie die VM/Stdlib/Compiler (Queries/Commands, Proto/Frame, Intrinsics), wird sie automatisch zu einer Produktivitätskonsole für dich und Nutzer.
1. Spec-Abgleich (was wir nicht verletzen dürfen)¶
1.1 Proto/Frame als Ausführungseinheit¶
Quelle: 05-vm-instruction-set.md
- Funktionen werden als VM-Artefakt kompiliert (Proto); Call erzeugt einen Frame, Return beendet ihn.
Konsequenz für REPL/Script Mode:
- Jede REPL-Eingabe (Chunk) wird zu einem Proto kompiliert und über einen Frame ausgeführt.
- Script Mode ist dann nur ein Spezialfall: “ein Proto aus einer Datei / einem Modul”.
1.2 JadeValue nur an Grenzen, Registerfile intern als Byte-Layout¶
Quelle: 11-jadevalue-und-register-layout.md
- JadeValue ist Boundary-Container (Call/Return/FFI/Intrinsics).
- Innerhalb eines Frames: Registerfile als Byte-Buffer mit compile-time Layout.
Konsequenz:
- REPL druckt Ergebnisse über den Boundary-Return (JadeValue) oder über eine explizite Print/Format-Intrinsic.
- REPL-Inspektion von Registern/Bytecode kann sich auf Proto-Daten (Layout) stützen, ohne Runtime-Boxing zu erzwingen.
1.3 Arenas/Scopes und Copy-on-exit¶
Quelle: 03-runtime.md
- with Storage.arena[A] { ... } bildet einen temporären Scope; am Ende: reset (Pointer-Reset, kein Traversal).
- Werte, die den Scope verlassen, werden in die umgebende Storage kopiert (oder sind verboten, wenn nicht kopierbar).
Konsequenz:
- Eine REPL-Eingabe ist sinnvollerweise “ein Scope”: temporäre Werte sterben nach dem Chunk.
- Persistente Bindings (Environment) liegen außerhalb (Session-Storage).
- “Export” in das Environment entspricht einem Arena-Crossing und nutzt dieselben Regeln (Copy/retain/error).
1.4 Intrinsics als stabile VM↔Stdlib Schnittstelle¶
Quelle: 08-intrinsics-vm-api.md und spec-namespaces-und-module.md (@intrinsics)
- Intrinsics sind der stabile, minimale Satz VM-Services (Handles, Scheduling, Introspection, FFI, …).
- Namespaces reservieren @intrinsics als compiler-provided Namespace.
Konsequenz:
- REPL-Funktionen (z.B. :memory, :scheduler, Handle-Checks) können direkt auf vorhandene Introspection-Intrinsics aufsetzen.
- Die genaue syntaktische Oberfläche (direkt @intrinsics::... vs Stdlib-Wrapper) ist in den Specs nicht überall konsistent dokumentiert → offen, aber das Prinzip bleibt.
1.5 Subsystem-Kapselung: keine direkten Zustandszugriffe¶
Quelle: 09-subsystem-kapselung-und-zustandsintegritaet.md (Architekturprinzip), jade-compiler-db-spec.md, 06-generator-und-code-emission.md
- Subsysteme besitzen eigenen State; Interaktion über definierte Schnittstellen.
- Generator arbeitet read-only gegen Query-APIs.
Konsequenz:
- REPL-Meta-Commands laufen über Queries (CompilerDb/Query-System) und Commands (Runtime/Ports), nicht über “direkt in Tabellen greifen”.
- Das ist exakt die gleiche Arbeitsweise, die du für Stdlib/VM-Entwicklung sowieso brauchst.
1.6 Runtime Introspection nur opt-in: reified¶
Quelle: 04-qualifier-effekte-konversionen-wire.md
- Runtime-TypeInfo auf Typparameter nur mit reified; niemals implizit.
Konsequenz:
- Eine REPL kann Introspection anbieten, ohne die Runtime generell teuer zu machen:
- Default: keine reified metadata.
- Opt-in: reified oder REPL-Schalter, der den nächsten Chunk/Typ reifiziert (nur in der Session).
2. Kerngedanke: REPL ist keine “Sonder-VM”, sondern ein Frontend über Proto/Frame¶
2.1 Warum viele Sprachen main() haben (und warum REPL anders ist)¶
- Ein OS-Prozess braucht einen Startpunkt (bei ELF z.B.
_start), daraus wird eine Sprachkonvention (main). - Für eine VM gilt: du brauchst einen ersten Ausführungskontext (Root-Frame).
- REPL/Script Mode brauchen nicht zwingend
main, sondern einen “Chunk/Module init” Mechanismus.
2.2 Das Proto/Frame Modell bleibt unverändert¶
- Proto: Code + ConstPool + Register-Layout + DebugTables (+ optional Drop-Pläne).
- Frame: pc + regs + caller (+ optional unwind state).
- REPL liefert nur “neue Protos” in kleinen Portionen.
3. Meta-Commands statt “repl::inspect()” als normales JDL¶
3.1 Warum Meta-Commands außerhalb von JDL bleiben sollten¶
Meta-Commands sind Tooling-Steuerung, keine Programmlogik. Wenn du sie als normale Funktionen maskierst, passieren typische Probleme:
- Namespace-Kollisionen: User kann (bewusst/unbewusst)
repl::inspectüberschreiben oder shadowen. - Fehlerresistenz: Wenn ein Chunk syntaktisch/typisch kaputt ist, willst du trotzdem
:resetausführen können. - Security/Scope: Du willst nicht, dass “normale Programme” CompilerDb inspizieren oder Sessions resetten können.
Darum ist die REPL-Lösung:
- Zeilenbeginn : (oder ähnliches) ist kein gültiges JDL und wird im REPL-Host abgefangen.
- Meta-Commands sind REPL-only; sie existieren nicht als Sprachfeature.
3.2 Meta-Commands als Query/Command Dispatcher¶
Meta-Commands werden auf zwei Arten ausgeführt:
- Queries: read-only (z.B. :type, :why, :bytecode, :query cache)
- Commands: side-effects (z.B. :reset, :trace on, :reify on, Breakpoints)
Das passt zu deinen Subsystem-Regeln: Tooling steuert das System über dieselben offiziellen Schnittstellen.
4. Script Mode und REPL-Chunks: “implizite Protos” (offen, aber minimal)¶
4.1 Was die Specs dazu sagen¶
Es gibt in den vorhandenen Specs keine vollständige, normative Definition für:
- “Top-Level Statements”
- “Module init Proto”
- “Entry-Mechanismus ohne main()”
Das ist normal. Es ist ein offener Designpunkt.
4.2 Minimaler, spec-kompatibler Vorschlag¶
- Script Mode: kompiliere ein Modul und erzeuge ein implizites Proto
__module_init__(Name frei, nur intern) für Top-Level-Ausführung. - REPL: jede Eingabe wird zu einem impliziten Proto
__repl_chunk_N__.
Wichtig: Das ist nur eine Implementation Convention. Sie kollidiert nicht mit dem VM-Modell, weil es am Ende “nur Protos” sind.
4.3 Namensraum-Integration (spec-kompatibel)¶
Nutze :: und die Namespace-Identität aus spec-namespaces-und-module.md.
Minimal (Vorschlag):
- Ein reservierter Session-Namespace repl:: (oder @repl::, falls du reservierte Namespaces bevorzugst).
- Jeder Chunk erweitert semantisch dieses Environment (Bindings).
Wenn du Reservierung willst:
- Das Namespace-Spec hat bereits “reserved namespaces” (z.B. @intrinsics). Ein REPL-spezifisches Reserved Namespace ist offen und müsste dort ergänzt werden.
5. Persistenz: REPL Environment ist langlebig, Frames sind kurzlebig¶
5.1 Was “persistieren” muss¶
Zwischen Chunks muss überleben:
- Bindings (Namen → Werte/Handles)
- neue Typen/Protokolle (Type Engine interned IDs)
- ggf. letzte Ergebnisse (_)
- geladene Module/Imports
5.2 Was nicht persistiert¶
- Frame-Registerfile
- temporäre Zwischenergebnisse
- temporäre Arena-Allokationen des Chunks
5.3 Speicherpolitik (aus 03-runtime.md abgeleitet)¶
Empfehlung: “jede Eingabe ist ein Scope”
- Pro Chunk existiert eine Chunk-Arena (oder Mark/Rewind-Region).
- Nach Ausführung: reset.
- Beim Export in Env gelten Arena-Crossing-Regeln:
- Value: trivial
- Ref/Handle: retain/own transfer nach Policy
- Arena-bound: Copy-to-parent oder Fehler, wenn nicht kopierbar
Das führt zu einer REPL, die nicht leakt und nicht langsam wird.
6. Reified Introspection: opt-in statt Dauerteuer¶
6.1 Was reified garantiert¶
Aus 04-qualifier-effekte-konversionen-wire.md:
- TypeInfo auf Typparameter nur mit reified, nie implizit.
6.2 REPL-Workflow, der dazu passt¶
- Default: keine reified metadata.
- Meta-Command
:reify on(Session/next chunk) setzt eine Compile-Option: - Der nächste Chunk oder bestimmte Typen werden reified kompiliert.
:inspect x:- wenn reified metadata vorhanden → tiefe introspection
- sonst → begrenzte Sicht (z.B. TypeId/Policy über Intrinsics) + Hinweis “reify required”
So bleibt die Runtime billig, aber Debugging bleibt möglich.
7. Meta-Command Set: minimal nützlich, später mächtig¶
7.1 MVP Meta-Commands (klein, aber produktiv)¶
:help:reset(Env leeren; optional soft/hard):env(Bindings):type <expr|name>(TypeOf):why <expr|name>(Reasoning aus Query-Graph):bytecode last(Disasm des letzten Protos)
Diese Liste ist absichtlich klein: Sie liefert “Geländer” für Lernkurve und Debug.
7.2 “Oh nett.” Features, die direkt aus Specs entstehen¶
:cost last(aus Command-Trace: allocs, RC pressure, arena resets, crossings):trace cmd on/off(zeigt Commands: StoragePush, JME alloc, release…):bytecode last --annotate(Reg types/policies/lifetimes aus Proto-Tables):query cache/:query deps ...(CompilerDb Transparenz)
Das ist kein Spielzeug: es hilft Nutzern, die Semantik und Kosten wirklich zu verstehen.
8. Wie Meta-Commands intern ausgeführt werden (ohne Sprach-Sonderfälle)¶
8.1 Grundfluss der REPL¶
1) Eingabezeile lesen
2) Wenn : → meta-dispatch (kein Compile/Eval nötig)
3) Sonst:
- parse chunk
- type-check (Query-System/Type Engine)
- lower + proto build (Generator)
- VM eval (Frame)
- Ergebnis drucken
- lastProto/lastValue speichern
8.2 Mapping Meta-Command → Query/Command (Beispiele)¶
:type x→query(TypeOf{Name=x, SessionCtx}):why x→query(Why{…})(gibt Dependency/Decision chain zurück):bytecode last→query(Disasm{proto=lastProto})(oder Host disasm):reset→cmd(ReplReset{mode=soft|hard}):trace cmd on→cmd(SetTrace{domain=Commands, enabled=true}):memory→@intrinsicsIntrospection oder Runtime query, je nachdem wie du es modellierst
9. Offene Punkte (bewusst markieren, minimal entscheiden)¶
9.1 Top-Level Semantik (Script Mode)¶
Offen: - sind Top-Level Statements erlaubt? - wie verhalten sich Imports vs Ausführung?
Minimaler, stabiler Weg:
- Datei/Modul hat optionales __module_init__ Proto, das nur Top-Level Statements enthält.
- Imports sind Compile-Time; Ausführung ist explizit (Script Mode) oder implizit (REPL chunk).
9.2 Reserved Namespace für REPL¶
Offen:
- gibt es ein reserved repl::/@repl::?
Minimal:
- Implementierung nutzt repl:: als gewöhnlichen Namespace in der Session, aber dokumentiert es als “reserved by convention” bis es formalisiert ist.
9.3 Intrinsics-Naming Oberfläche¶
Aktuelle Specs zeigen:
- Intrinsics API Dokument benutzt jdl.vm_* Schreibweise,
- Namespace-Spec reserviert @intrinsics.
Interpretationsfrei festhalten:
- Semantik: Intrinsics existieren als compiler-provided interface.
- Syntax: Oberfläche muss konsolidiert werden; REPL/Stdlib kann bis dahin Wrapper anbieten.
10. Warum wir genau so darauf gekommen sind (Herleitung in einem Satz)¶
Weil das Projekt bereits die Bausteine für eine “Interactive Runtime Console” enthält:
- Proto/Frame macht Ausführung klein und wiederholbar,
- Arenas machen “jede Eingabe ist ein Scope” billig und deterministisch,
- Intrinsics liefern stabile Runtime-Services,
- CompilerDb/Queries machen Reasoning/Explainability möglich,
- reified macht Runtime Introspection explizit und kontrollierbar,
- und Meta-Commands verhindern, dass Tooling die Sprache verunreinigt.
11. Praktischer Anschluss: Minimal-MVP Dokument¶
Dieses Dokument erklärt das Warum.
Der konkrete “kleinste vertikale Schnitt” zum Bauen steht im separaten Plan:
jade-minimal-repl-mvp.md
(Die beiden Dokumente zusammen ergeben: Rationale + Build Plan.)