Zum Inhalt

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 :reset ausfü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 xquery(TypeOf{Name=x, SessionCtx})
  • :why xquery(Why{…}) (gibt Dependency/Decision chain zurück)
  • :bytecode lastquery(Disasm{proto=lastProto}) (oder Host disasm)
  • :resetcmd(ReplReset{mode=soft|hard})
  • :trace cmd oncmd(SetTrace{domain=Commands, enabled=true})
  • :memory@intrinsics Introspection 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.)