Phase 2 abgeschlossen: Bytecode, TBVM, Runtime-Scheibe, tbc run

- Sema zum Lowering-Pass umgebaut: typisiertes HIR (Slots, explizite
  Konvertierungsknoten) als Codegen-Eingabe; BYREF verlangt exakten Typ
- Bytecode-Feindesign umgesetzt: monomorpher Opcode-Satz,
  .tbc-Container (Formatversion 1) mit eigenem Writer/Reader
- Codegenerator HIR -> Bytecode (Fixup-Listen, keine globalen Passes)
- TBVM-Interpreter: Kontrollfluss, GOSUB-Stack je Frame, BYREF/BYVAL,
  STATIC, DEF FN, DATA/READ/RESTORE, ON [LOCAL] ERROR/RESUME/ERR/ERL,
  Breakpoints/Einzelschritt/Inspektion, STOP fortsetzbar
- Runtime-Scheibe: Host-Trait (Konsole/Capture), Builtin-Tabelle,
  Konvertierungsmatrix, PRINT-Formatierung/Druckzonen, Stringfunktionen
- tbc run/build/check mit Exit-Codes nach Entscheidung D6
- Korpus-Harness (byte-genauer Vergleich) + 3 neue Korpusdateien
  (konvertierung, fehlerbehandlung, byref); 137 Tests gruen
- Benchmarks: Einzelmodul 1,2 ms / Projekt 49.760 Zeilen 124 ms
  (Budgets eingehalten), VM ~5 Mio Schleifeniterationen/s
- Doku fortgeschrieben (tbvm-design, sprachreferenz, PLAN);
  verlagerte Punkte als explizite Aufgaben in Phase 3
- OpenSpec-Change phase-2-bytecode-vm (27/27 Tasks)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-02 11:28:07 +02:00
parent da23d52036
commit f7e57b0bd8
42 changed files with 9225 additions and 484 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-02

View File

@@ -0,0 +1,129 @@
# Design — Phase 2: Bytecode und VM
## Context
Motivation siehe proposal.md („Why"). Ausgangslage: `tb-frontend` liefert
AST + Diagnosen, wirft aber alle Sema-Ergebnisse (Typen, Slots,
Konstanten) weg; `tb-vm` ist ein Stub. Rahmenbedingungen aus PLAN.md und
docs/tbvm-design.md: Instant-Compile (Single-Pass, keine
Optimierungspasses), Interpreter-Tempo in Lua-Klasse, Werte-Modell als
Tagged Enum mit `Rc` (kein GC — der Dialekt kennt keine Zyklen),
`step()`-basierte, unterbrechbare VM. Entscheidungen dieses Dokuments
wurden am 2026-09-02 in einer Explore-Session getroffen und bestätigt.
## Goals / Non-Goals
**Goals:**
- Durchgängige Pipeline Quelltext → HIR → Bytecode → Ausführung mit
byte-genauer Korpus-Ausgabe.
- Debugger-taugliche VM-Architektur, ohne dass der Debugger selbst
(Phase 5) schon gebaut wird.
- Builtin-ABI, die Phase 3 nur noch „auffüllt".
**Non-Goals:**
- Mehrmodul-Linking und `COMMON`-Verknüpfung über Moduleinheiten (die
Prozedurtabelle ist modul-qualifiziert angelegt, verknüpft wird ab
Phase 5 mit `.MAK`-Projekten).
- Datei-E/A, Ereigniszustellung (Hooks über Tick-Flag-Wort genügen),
`tbc build --exe`, Optimierungspasses jeder Art.
## Decisions
### D1 — Typisiertes HIR als Sema-Ausgabe (statt Doppel-Resolution oder Side-Tables)
Die Sema wird vom reinen Checker zum Lowering-Pass: Ausgabe ist ein
eigener HIR-Baum (Namen → Slots, Typen an jedem Ausdruck, Konstanten
gefaltet, implizite Konvertierungen als explizite Knoten). Verworfen:
(a) Codegen resolved selbst nochmal — dupliziert die Typisierungslogik,
Divergenz-Bugs; (b) Side-Tables am AST — der AST hat keine Node-IDs,
Pointer-Keys sind fragil. Das HIR hält das Single-Pass-Budget ein (alle
Pässe linear, keine globale Analyse) und macht den Codegen zum dummen
Tree-Walk.
### D2 — Monomorpher Opcode-Satz, Konvertierungen als eigene Opcodes
Da das HIR jede Konvertierung materialisiert, sind alle Arithmetik-,
Vergleichs- und Konvertierungs-Opcodes typrein (`ADD_I16`, `ADD_F64`,
`CONV_I16_F64`, `STR_EQ` …). Kein Tag-Dispatch in heißen Pfaden; die
Konvertierungsmatrix lebt an genau einer Stelle (HIR-Erzeugung), der
Interpreter bleibt eine enge `match`-Schleife. Preis: breiter
Opcode-Satz (~150200) — Fleiß, keine Komplexität. Encoding: Opcode
1 Byte; Slots u16; Konstantenpool-Index u16 (Wide-Variante bei Bedarf);
Sprünge u32-absolut innerhalb der Prozedur (fixup-freundlich).
### D3 — Host-Trait mit blockierenden Aufrufen (statt Yield-Modell)
Alle Konsolenwirkungen laufen über ein `Host`-Trait (Ausgabe, Zeile
lesen, Abbruchprüfung). Host-Calls dürfen blockieren und sind aus
VM-Sicht atomar; Ticks (Breakpoints, Einzelschritt, Abbruch) greifen an
Anweisungsgrenzen. Verworfen: VM „yieldet" bei E/A und der Einbetter
erfüllt Requests — debuggerfreundlicher, aber deutlich komplexer; für
den IDE-Debugger reicht die Anweisungsgranularität. Implementierungen:
Konsolen-Host (`tbc run`), Capture-Host (Test-Harness), später
Zellenpuffer-Host (Phase 3/5).
### D4 — Fehlerbehandlung: Handler-Zustand modul-global + pro Frame
`ON ERROR GOTO` setzt den modulweiten Handler, `ON LOCAL ERROR` einen
Frame-lokalen, der ihn verdeckt. Beim Fehler: aktiven Handler suchen
(erst Frame-Kette nach LOCAL-Handlern, sonst Modul-Handler), Stack bis
zum Handler-Frame abwickeln, gemerkten Anweisungs-Offset + Frame für
`RESUME` sichern, `ERR`/`ERL` setzen. Fehler bei aktiver Behandlung →
sofortiger Abbruch (keine Kaskade). Grundlage ist die Original-Hilfe
(Topics `ON ERROR`, `RESUME`); ein Emulator-Spike ist nur nötig, falls
die Doku Lücken lässt.
### D5 — GOSUB-Stack pro Frame
`GOSUB` legt die Rücksprungadresse auf einen separaten Stack im
aktuellen Frame (kein Call-Frame); `RETURN` bedient zuerst diesen
Stack. Prozedurende mit nicht-leerem GOSUB-Stack verwirft ihn (wie beim
Vorbild beim Verlassen der Prozedur).
### D6 — Runner-Verhalten `STOP`/`CONT`
`STOP` außerhalb der IDE terminiert mit Meldung + Exit-Code ≠ 0; `END`/
Programmende → Exit-Code 0. `CONT` ist Direktmodus-Konzept der IDE
(Phase 5) — die `step()`-Architektur liefert Suspend/Resume dort gratis.
### D7 — `.tbc` mit eigenem Writer/Reader (kein serde)
Format bleibt stabil, dokumentiert und unabhängig von Crate-Versionen;
Abschnittstabelle erlaubt spätere Abschnitte (FORMS ab Phase 4) ohne
Versionsbruch. Prozedurtabelle trägt von Beginn an die Modulkennung.
### D8 — Benchmarks als früher Bestandteil, nicht als Abschluss
Das Compile-Budget-Harness (generiertes ~50k-Zeilen-Projekt) entsteht
früh in der Phase, damit Architekturfehler (z. B. quadratische
Fixup-Listen) sofort auffallen. Criterion-Benches für VM-Durchsatz
(Schleifen/Strings); Ergebnisse werden in docs/tbvm-design.md
festgehalten. Ein Dispatch-Mikrobenchmark vorab wurde verworfen
(vorzeitige Optimierung — gemessen wird am echten Interpreter).
## Risks / Trade-offs
- [Sema-Umbau destabilisiert Phase-1-Ergebnis] → Diagnostik-Verhalten
ist durch die 33 Frontend-Tests + Korpus fixiert; Umbau gilt erst als
fertig, wenn alle Bestandstests grün sind.
- [Opcode-Breite macht Interpreter-Match unübersichtlich] → Opcodes
systematisch gruppieren (Makro/Tabelle generiert Emit + Dispatch
konsistent); Dokumentationspflicht in tbvm-design.md.
- [Konvertierungsmatrix enthält falsch rekonstruierte Zellen] →
jede Zelle mit Fundstelle in der Original-Hilfe belegen; unklare
Zellen als `TODO verify` im Korpus markieren (Konvention besteht).
- [PRINT-Zahlformatierung (SINGLE/DOUBLE-Stellen) weicht ab] →
byte-genaue Korpustests decken Randfälle ab; Formatierungsregeln aus
der Original-Hilfe ableiten, Abweichungen dokumentieren.
- [Blockierende Host-Calls verhindern Abbruch während INPUT] →
akzeptiert für Phase 2 (Abbruchprüfung obliegt dem Host); die
Ereignisschleifen-Architektur (Phase 4) kann den Host asynchroner
gestalten, ohne die VM zu ändern.
## Migration Plan
Rein additiv (neue Crate-Inhalte, neue Sema-Ausgabe neben bestehender
Diagnostik). Kein Rollback-Plan nötig; PLAN.md-Haken und
docs/tbvm-design.md-Fortschreibung erfolgen beim Abschluss der Phase.
## Open Questions
- Exakte Ergebnistyp-Regel für `^` sowie Stellenzahl der
SINGLE/DOUBLE-Textdarstellung: wird beim Ausformulieren der Matrix
aus der Original-Hilfe übernommen; ändert weder Ansatz noch
Task-Zuschnitt.
- Ob `ERL` beim Vorbild auch Zeilennummern aus `GOSUB`-Zielen o. ä.
Sonderfällen meldet: bei Doku-Lücke entscheidet ein kurzer
Emulator-Check; betrifft nur eine Matrix-/Handlerzelle.

View File

@@ -0,0 +1,85 @@
# Phase 2 — Bytecode und VM (`tb-vm`)
## Why
Das Sprach-Frontend (Phase 1) liefert einen typgeprüften AST, aber es gibt
keine Ausführungsschicht: `tb-vm` besteht aus leeren Stub-Modulen, der
Kompatibilitäts-Testkorpus ist nur eine ruhende Verhaltensspezifikation.
Phase 2 macht aus dem Frontend einen Compiler mit lauffähigem Ergebnis —
Bytecode-Erzeugung, serialisierbares Kompilat (`.tbc`) und die TBVM als
Interpreter — und löst damit den Plan-Meilenstein ein: der Konsolen-Korpus
läuft per `tbc run` mit byte-genau korrekter Ausgabe.
## What Changes
- **Typisiertes HIR als Sema-Ausgabe** (Entscheidung 2026-09-02, Explore-
Session): Die semantische Analyse behält ihr Wissen — sie liefert neben
Diagnostics einen abgesenkten, typisierten Zwischenbaum (Namen → Slots,
Typen explizit, Konstanten gefaltet, implizite Konvertierungen als
explizite Knoten). Der Codegen wird ein einfacher Tree-Walk.
- **Bytecode-Feindesign** (Eingangsaufgabe aus Phase 0): monomorpher,
typisierter Opcode-Satz; Zahlenkonvertierungs-Matrix (implizite Casts,
Banker's Rounding, Überlauf → Fehler 6) als Tabelle in
docs/tbvm-design.md und als Korpustest verankert.
- **`.tbc`-Container**: Serialisierung mit eigenem Writer/Reader (Magic,
Formatversion, Abschnittstabelle: CONSTS, TYPES, PROCS, CODE, DATA,
LINES); Prozedurtabelle von Beginn an modul-qualifiziert.
- **TBVM-Interpreter**: Ausdrücke, Kontrollfluss (`IF`, `SELECT CASE`,
Schleifen, `GOTO`/`GOSUB`), Prozeduraufrufe mit BYREF-Semantik,
`DEF FN`, `DATA`/`READ`/`RESTORE`.
- **Fehlerbehandlung**: `ON ERROR GOTO` (modulweit), `ON LOCAL ERROR`
(prozedurlokal), `RESUME [0|NEXT|label]`, `ERR`/`ERL`, Propagation die
Aufrufkette hoch, Fehler im Handler fatal.
- **Unterbrechbarkeit**: `step()`-basierte VM, Tick-Prüfung an
Anweisungsgrenzen (ein Flag-Wort), Breakpoints, Einzelschritt,
Variableninspektion — Grundlage für den IDE-Debugger (Phase 5).
- **Runner-Verhalten** (Entscheidung 2026-09-02): `STOP` außerhalb der IDE
terminiert mit Meldung und Exit-Code ≠ 0, `END` mit Exit-Code 0;
`CONT` bleibt IDE-Konzept (Phase 5).
- **Konsolen-Basisbibliothek (vorgezogene Phase-3-Scheibe)**: Builtin-ABI
(Dispatch-Tabelle in `tb-runtime`), `Host`-Trait für Konsolen-E/A
(blockierende Host-Calls, kein Yield-Modell), PRINT-Zahlenformatierung
und Druckzonen sowie die vom Korpus benötigten Stringfunktionen.
- **Test-Harness**: `cargo test` führt jede Korpusdatei per `tbc run` aus
und vergleicht byte-genau gegen `.out`.
- **Benchmarks** in `benches/`: Compile-Budget (~50k Zeilen < 1 s, Modul
< 50 ms) und VM-Durchsatz; Ergebnisse in docs/tbvm-design.md.
**Non-Goals:** Mehrmodul-Linking/`COMMON`-Verknüpfung über Moduleinheiten
(kommt mit Projekten, Phase 5); Datei-E/A, Ereignissteuerung, `tbc build
--exe` (spätere Phasen); vollständige Standardbibliothek (Phase 3).
## Capabilities
### New Capabilities
- `bytecode-kompilat`: Codegen HIR → Bytecode, Opcode-Satz,
`.tbc`-Format und Serialisierung, Instant-Compile-Budget.
- `vm-ausfuehrung`: Interpreter-Semantik — Ausdrücke inkl.
Zahlenkonvertierungs-Matrix, Kontrollfluss, Prozeduraufrufe/BYREF,
`GOSUB`, `DEF FN`, `DATA`/`READ`, Runner-Verhalten (`STOP`/`END`) und
Unterbrechbarkeit (Ticks, Breakpoints, Einzelschritt).
- `vm-fehlerbehandlung`: `ON [LOCAL] ERROR`, `RESUME`, `ERR`/`ERL`,
Handler-Scoping und Fehlerpropagation.
- `konsolen-basisbibliothek`: `Host`-Trait, Builtin-ABI,
PRINT-Formatierung/Druckzonen, Korpus-Stringfunktionen.
### Modified Capabilities
- `sprach-frontend`: Die Sema SHALL zusätzlich zum Diagnostik-Ergebnis
ein typisiertes HIR liefern (neue Ausgabe-Anforderung des Frontends).
- `kompat-testkorpus`: Neuer Laufzeitvergleich — jede Korpusdatei läuft
per `tbc run`, Ausgabe wird byte-genau gegen `.out` geprüft.
## Impact
- `crates/tb-frontend`: Sema-Umbau (HIR-Ausgabe), neues HIR-Modul.
- `crates/tb-vm`: `bytecode.rs`, `codegen.rs`, `interp.rs` von Stub zu
Vollimplementierung; neue Benchmarks.
- `crates/tb-runtime`: Builtin-Dispatch-Tabelle, PRINT-Formatierung,
Stringfunktionen (Scheibe).
- `crates/tb-cli`: `tbc run` (Kompilieren + Ausführen), Konsolen-`Host`.
- `tests/compat`: Harness + neue Korpusdateien (u. a. Konvertierungs-
matrix, Fehlerbehandlung); `docs/tbvm-design.md` wird fortgeschrieben
(Opcode-Satz, Matrix, Benchmark-Ergebnisse), PLAN.md-Haken folgen bei
Abschluss.

View File

@@ -0,0 +1,64 @@
## Purpose
Das Bytecode-Kompilat ist das serialisierbare Ergebnis der Übersetzung:
Der Codegenerator senkt das typisierte HIR in einen monomorphen
Opcode-Satz ab, der `.tbc`-Container hält es als stabiles, dokumentiertes
Format, und das Instant-Compile-Budget sichert den schnellen
Edit-Run-Zyklus.
## ADDED Requirements
### Requirement: Monomorpher Opcode-Satz
Der Codegenerator SHALL jedes HIR-Konstrukt in typisierte, monomorphe
Opcodes übersetzen: Arithmetik, Vergleiche und Konvertierungen tragen den
Operandentyp im Opcode (kein Tag-Dispatch über Werte zur Laufzeit);
implizite Konvertierungen erscheinen als explizite Konvertierungs-Opcodes
an den vom HIR bestimmten Stellen. Der Opcode-Satz SHALL in
docs/tbvm-design.md vollständig dokumentiert sein.
#### Scenario: Gemischter Ausdruck wird monomorph
- **WHEN** `d# = i% + 1.5#` übersetzt wird (INTEGER-Variable, DOUBLE-Ziel)
- **THEN** enthält der Bytecode einen Konvertierungs-Opcode INTEGER→DOUBLE und eine DOUBLE-Addition, keinen generischen Additions-Opcode
#### Scenario: Namen sind zur Laufzeit aufgelöst
- **WHEN** eine Variable oder Prozedur im Bytecode referenziert wird
- **THEN** geschieht das über Slot- bzw. Tabellenindizes, nicht über Namens-Lookups
### Requirement: `.tbc`-Containerformat
Das Kompilat SHALL als `.tbc`-Datei serialisierbar und wieder ladbar sein:
Magic `TBC\0`, Formatversion, Abschnittstabelle mit den Abschnitten
CONSTS (deduplizierter Konstantenpool), TYPES (TYPE-Layouts), PROCS
(modul-qualifizierte Prozedurtabelle mit Signatur, Locals-Anzahl,
Code-Offset), CODE, DATA (`READ`/`RESTORE`-Segment) und LINES
(Zeilentabelle). Laden und erneutes Serialisieren MUST verlustfrei sein;
eine unbekannte Formatversion MUST mit einer klaren Fehlermeldung
abgewiesen werden.
#### Scenario: Roundtrip
- **WHEN** ein kompiliertes Modul als `.tbc` geschrieben und wieder geladen wird
- **THEN** ist das geladene Kompilat funktional identisch (gleiche Ausführung, gleiche Zeilenzuordnung)
#### Scenario: Unbekannte Version
- **WHEN** eine `.tbc`-Datei mit höherer Formatversion geladen wird
- **THEN** wird das Laden mit einer Meldung abgelehnt, die die Version nennt
### Requirement: Zeilentabelle für Fehlerortung
Der Bytecode SHALL jede Anweisung ihrem Ursprung (Moduldatei, Zeile)
zuordnen, sodass Laufzeitfehlermeldungen, `ERL`, Breakpoints und
Einzelschritt die Quellzeile exakt benennen können.
#### Scenario: Fehler nennt Zeile
- **WHEN** in Zeile 42 eines Programms ein Laufzeitfehler ohne Handler auftritt
- **THEN** nennt die Fehlermeldung Zeile 42
### Requirement: Instant-Compile-Budget
Die Übersetzung (Lexen bis Bytecode) SHALL ohne globale Analysepasses
auskommen; Vorwärtsreferenzen werden über Fixups aufgelöst. Ein
Benchmark MUST das Budget nachweisbar machen: ein Projekt von ~50.000
Zeilen kompiliert in unter 1 s, ein einzelnes Modul in unter 50 ms
(Release-Build, Referenzrechner); die Messwerte werden in
docs/tbvm-design.md festgehalten.
#### Scenario: Budget wird gemessen
- **WHEN** die Benchmarks in `benches/` laufen
- **THEN** wird die Kompilierzeit für das ~50k-Zeilen-Projekt und für ein Einzelmodul ausgewiesen und gegen das Budget verglichen

View File

@@ -0,0 +1,27 @@
## ADDED Requirements
### Requirement: Laufzeitvergleich über den Korpus
Ein automatischer Test SHALL jede Korpusdatei `tests/compat/*.bas`
kompilieren, über die VM mit Capture-Host ausführen und die Ausgabe
byte-genau (inklusive nachgestellter Leerzeichen und LF-Zeilenenden)
gegen die zugehörige `.out`-Datei vergleichen. Bei Abweichung MUST der
Test Datei, erste abweichende Zeile sowie Soll und Ist nennen.
#### Scenario: Korpus läuft mit korrekter Ausgabe
- **WHEN** die Laufzeit-Testsuite läuft
- **THEN** stimmt die Ausgabe jeder Korpusdatei byte-genau mit ihrer `.out` überein
#### Scenario: Abweichung wird benannt
- **WHEN** ein Korpusprogramm eine abweichende Ausgabe erzeugt
- **THEN** schlägt der Test fehl und nennt Datei, Zeilennummer, Soll- und Ist-Zeile
### Requirement: Korpusabdeckung der Phase-2-Semantik
Der Korpus SHALL um Referenzprogramme mit dokumentierter Sollausgabe für
die Zahlenkonvertierungs-Matrix (Rundung, Ganzzahldivision, logische
Operatoren, Überlauf-Randfälle über Fehlerbehandlung), die
Fehlerbehandlung (`ON ERROR`, `RESUME`-Varianten, `ERR`/`ERL`) sowie
Prozeduraufrufe mit BYREF-/BYVAL-Wirkung erweitert werden.
#### Scenario: Konvertierungsmatrix als Korpustest
- **WHEN** die Laufzeit-Testsuite läuft
- **THEN** existiert ein Korpusprogramm, das die Matrix-Randfälle ausgibt, und seine Ausgabe entspricht der dokumentierten Sollausgabe

View File

@@ -0,0 +1,66 @@
## Purpose
Die Konsolen-Basisbibliothek ist die in Phase 2 vorgezogene Scheibe der
Laufzeitbibliothek: die Builtin-Aufrufschnittstelle der VM, ein
`Host`-Abstraktion für Konsolen-E/A sowie PRINT-Formatierung und die vom
Testkorpus benötigten Stringfunktionen.
## ADDED Requirements
### Requirement: Builtin-Aufrufschnittstelle
Die VM SHALL Bibliotheksfunktionen und -anweisungen über eine indizierte
Dispatch-Tabelle aufrufen (Argumente auf dem Operandenstack, Index im
Opcode); die Tabelle lebt in der Laufzeitbibliothek und ist ohne
Änderung am Opcode-Satz erweiterbar (Phase 3 füllt sie auf).
#### Scenario: Builtin über Tabelle
- **WHEN** `PRINT LEN("abc")` ausgeführt wird
- **THEN** wird `LEN` über seinen Tabellenindex aufgerufen und liefert 3
### Requirement: Host-Abstraktion für Konsolen-E/A
Alle Konsolenwirkungen der VM SHALL über eine austauschbare
Host-Schnittstelle laufen (Textausgabe, zeilenweises Lesen für `INPUT`,
Abbruchprüfung). Host-Aufrufe dürfen blockieren; die Abbruchprüfung
(Strg+Untbr) obliegt dem Host. `tbc run` verwendet einen Konsolen-Host
(stdout/stdin); Tests MUST einen Capture-Host verwenden können, der die
Ausgabe byte-genau aufzeichnet.
#### Scenario: Capture-Host im Test
- **WHEN** ein Korpusprogramm im Test-Harness läuft
- **THEN** entsteht die komplette Ausgabe im Capture-Host und ist byte-genau vergleichbar
### Requirement: PRINT-Formatierung
`PRINT` SHALL Zahlen im Format des Vorbilds ausgeben: führendes
Leerzeichen für nicht-negative bzw. `-` für negative Werte,
nachgestelltes Leerzeichen (auch am Zeilenende); Zahlendarstellung je
Typ wie beim Vorbild (u. a. kein Dezimalpunkt bei ganzzahligen Werten,
signifikante Stellen je nach SINGLE/DOUBLE). `;` schließt direkt an,
`,` springt zur nächsten 14-Zeichen-Druckzone (belegt ein Element die
Zone ganz, zur übernächsten); ohne abschließendes `;`/`,` folgt ein
Zeilenumbruch.
#### Scenario: Zahlen mit Vorzeichenspalte
- **WHEN** `PRINT 1; -2; 3.5` ausgeführt wird
- **THEN** lautet die Ausgabe ` 1 -2 3.5 ` mit Zeilenumbruch
#### Scenario: Druckzonen
- **WHEN** `PRINT "12345678901234", "x"` ausgeführt wird
- **THEN** beginnt `x` in Spalte 29 (Zone nach der belegten übersprungen)
### Requirement: Korpus-Stringfunktionen
Die Bibliothek SHALL mindestens folgende Funktionen mit der Semantik des
Vorbilds bereitstellen (Zeichen = Unicode-Codepoints, dokumentierte
UTF-8-Abweichung): `LEN`, `LEFT$`, `RIGHT$`, `MID$` (Funktion),
`INSTR`, `UCASE$`, `LCASE$`, `STRING$`, `SPACE$`, `CHR$`, `ASC`,
`STR$`, `VAL`. Randfälle folgen dem Vorbild, u. a.: `VAL` liest das
führende Zahlenpräfix und ignoriert Leerraum; `STR$` stellt
nicht-negativen Zahlen ein Leerzeichen voran; `ASC("")` löst Fehler 5
„Illegal function call" aus.
#### Scenario: Korpusdatei strings.bas
- **WHEN** `tests/compat/strings.bas` ausgeführt wird
- **THEN** entspricht die Ausgabe byte-genau `strings.out`
#### Scenario: VAL mit Restzeichen
- **WHEN** `VAL(" 12.5abc")` ausgewertet wird
- **THEN** ist das Ergebnis 12.5

View File

@@ -0,0 +1,19 @@
## ADDED Requirements
### Requirement: Typisiertes HIR als Sema-Ausgabe
Die semantische Analyse SHALL neben den Diagnosen einen typisierten,
abgesenkten Zwischenbaum (HIR) liefern, der die Codegen-Eingabe bildet:
Namen sind zu Slot-/Tabellenindizes aufgelöst, jeder Ausdrucksknoten
trägt seinen Ergebnistyp, `CONST`-Werte sind gefaltet, `DEFtype`-Regeln
angewandt und implizite Konvertierungen als explizite
Konvertierungsknoten materialisiert. Bei Diagnosen mit Fehlercharakter
darf kein HIR für das betroffene Konstrukt garantiert werden; ein
diagnose-freies Modul MUST ein vollständiges HIR ergeben.
#### Scenario: Konvertierung wird materialisiert
- **WHEN** `d# = i% + 1.5#` semantisch analysiert wird
- **THEN** enthält das HIR einen expliziten Konvertierungsknoten INTEGER→DOUBLE unter dem Additionsknoten und die Zuweisung trägt den Zieltyp DOUBLE
#### Scenario: Diagnose-freies Modul liefert HIR
- **WHEN** ein Korpusmodul ohne Diagnosen analysiert wird
- **THEN** liefert das Frontend ein HIR, aus dem der Codegen ohne erneute Namens- oder Typauflösung Bytecode erzeugen kann

View File

@@ -0,0 +1,115 @@
## Purpose
Die TBVM führt das Bytecode-Kompilat mit der exakten Semantik des
Dialekts aus: Ausdrücke mit der Zahlenkonvertierungs-Matrix des Vorbilds,
Kontrollfluss, Prozeduraufrufe mit BYREF, `GOSUB`, unterbrechbare
`step()`-Ausführung als Grundlage für Debugger und Ereignisschleife.
## ADDED Requirements
### Requirement: Zahlenkonvertierungs-Matrix
Die VM SHALL implizite und explizite Zahlenkonvertierungen nach der in
docs/tbvm-design.md dokumentierten Matrix ausführen, insbesondere:
Gleitkomma→Ganzzahl rundet kaufmännisch zur nächsten geraden Zahl
(Banker's Rounding); Bereichsüberschreitung löst Fehler 6 „Overflow"
aus; `/` liefert stets Gleitkomma, `\` und `MOD` konvertieren ihre
Operanden vorab zu Ganzzahlen; logische Operatoren (`AND`, `OR`, `XOR`,
`EQV`, `IMP`) arbeiten bitweise auf zu Ganzzahlen konvertierten
Operanden; CURRENCY rechnet als Festkomma ×10 000. Die Matrix SHALL
durch ein Korpusprogramm mit dokumentierter Sollausgabe abgedeckt sein.
#### Scenario: Banker's Rounding
- **WHEN** `PRINT CINT(0.5); CINT(1.5); CINT(2.5)` ausgeführt wird
- **THEN** lautet die Ausgabe ` 0 2 2 `
#### Scenario: Überlauf bei Konvertierung
- **WHEN** einer INTEGER-Variablen der Wert 40000 zugewiesen wird
- **THEN** tritt Laufzeitfehler 6 „Overflow" auf
#### Scenario: Ganzzahldivision rundet Operanden vor
- **WHEN** `PRINT 7.5 \ 2` ausgeführt wird
- **THEN** wird 7.5 zuerst zu 8 gerundet und die Ausgabe ist ` 4 `
### Requirement: Kontrollfluss
Die VM SHALL `IF`/`ELSEIF`/`ELSE`, `SELECT CASE` (Werte, Bereiche,
`IS`-Vergleiche, `CASE ELSE`), `FOR`/`NEXT` (inkl. `STEP`, null
Durchläufen bei unpassender Grenze), `DO`/`LOOP` (alle vier
Bedingungsformen), `WHILE`/`WEND`, `GOTO`, `ON expr GOTO/GOSUB` und
`EXIT FOR/DO` mit der Semantik des Vorbilds ausführen.
#### Scenario: FOR ohne Durchlauf
- **WHEN** `FOR i% = 3 TO 1: PRINT i%: NEXT` ausgeführt wird
- **THEN** wird der Schleifenkörper nicht betreten
#### Scenario: Korpus Kontrollfluss
- **WHEN** `tests/compat/kontrollfluss.bas` ausgeführt wird
- **THEN** entspricht die Ausgabe byte-genau `kontrollfluss.out`
### Requirement: GOSUB/RETURN neben Prozeduraufrufen
`GOSUB` SHALL die Rücksprungadresse auf einem GOSUB-Stack im aktuellen
Frame ablegen (kein eigener Frame); `RETURN` kehrt dorthin zurück,
`RETURN label` an das benannte Ziel. Ein `RETURN` ohne anhängigen
`GOSUB` MUST Fehler 3 „RETURN without GOSUB" auslösen.
#### Scenario: RETURN ohne GOSUB
- **WHEN** ein Programm `RETURN` ohne vorangehendes `GOSUB` ausführt
- **THEN** tritt Laufzeitfehler 3 auf
### Requirement: Prozeduraufrufe mit BYREF-Semantik
SUB-/FUNCTION-Aufrufe SHALL eigene Frames mit Locals-Slots verwenden.
Variablen, Arrayelemente und UDT-Felder werden BYREF übergeben
(Änderungen im Rumpf wirken auf das Argument zurück); ein geklammertes
Argument oder ein Ausdruck wird als temporärer Wert übergeben (BYVAL-
Wirkung). `STATIC`-Prozeduren behalten ihre Locals zwischen Aufrufen;
rekursive Aufrufe nicht-statischer Prozeduren erhalten frische Locals.
`DEF FN`-Funktionen werten im Modulkontext aus (Zugriff auf
Modulvariablen), ihre Parameter wirken lokal.
#### Scenario: BYREF wirkt zurück
- **WHEN** `SUB Inc(x%) : x% = x% + 1 : END SUB` mit `Inc n%` aufgerufen wird
- **THEN** ist `n%` danach um 1 erhöht
#### Scenario: Klammern erzwingen Wertübergabe
- **WHEN** dieselbe SUB mit `Inc (n%)` aufgerufen wird
- **THEN** bleibt `n%` unverändert
### Requirement: DATA/READ/RESTORE
`READ` SHALL die `DATA`-Konstanten des Moduls in Deklarationsreihenfolge
konsumieren und in den Zieltyp konvertieren; `RESTORE [label]` setzt den
Lesezeiger auf den Anfang bzw. auf die erste `DATA`-Anweisung ab dem
Label. Lesen hinter dem letzten Element MUST Fehler 4 „Out of DATA"
auslösen; eine nicht konvertierbare Konstante MUST Fehler 13 „Type
mismatch" auslösen.
#### Scenario: Out of DATA
- **WHEN** ein Programm mehr Werte liest als `DATA` bereitstellt
- **THEN** tritt Laufzeitfehler 4 auf
### Requirement: Unterbrechbare step()-Ausführung
Die VM SHALL als zustandsbehaftete, vom Einbetter getriebene Struktur
ausführbar sein: An Anweisungsgrenzen prüft sie ein Flag-Wort und kann
anhalten (Breakpoint auf (Modul, Zeile), Einzelschritt, Abbruchwunsch).
Im angehaltenen Zustand MUST der Einbetter Variableninhalte (Skalare,
Arrayelemente, UDT-Felder) inspizieren und die Ausführung fortsetzen
können. Die Tick-Prüfung erfolgt nur an Anweisungsgrenzen, nicht pro
Opcode.
#### Scenario: Breakpoint hält an
- **WHEN** ein Breakpoint auf Zeile 10 gesetzt ist und die Ausführung Zeile 10 erreicht
- **THEN** hält die VM vor der Anweisung an und meldet Modul und Zeile
#### Scenario: Inspektion im Halt
- **WHEN** die VM an einem Breakpoint steht
- **THEN** liefert die Inspektions-API den aktuellen Wert einer benannten Variablen
### Requirement: Programmende im Runner
Außerhalb der IDE SHALL `END` (und das Erreichen des Programmendes) den
Prozess mit Exit-Code 0 beenden; `STOP` SHALL mit der Meldung
`STOP in line n` (bzw. Modul/Zeile) und einem Exit-Code ungleich 0
terminieren (Projektentscheidung 2026-09-02). `SYSTEM` verhält sich wie
`END`. `CONT` existiert nur als IDE-Konzept (Phase 5) und ist kein
Programmbestandteil.
#### Scenario: STOP im Runner
- **WHEN** ein per `tbc run` gestartetes Programm `STOP` in Zeile 7 ausführt
- **THEN** endet der Prozess mit Exit-Code ungleich 0 und die Meldung nennt Zeile 7

View File

@@ -0,0 +1,71 @@
## Purpose
Die BASIC-Fehlersemantik (`ON ERROR GOTO`, `ON LOCAL ERROR`, `RESUME`,
`ERR`/`ERL`) ist Teil des Kompatibilitätsvertrags: Programme des Vorbilds
verlassen sich auf Handler-Scoping, Propagation und Wiederaufnahme exakt
wie dokumentiert.
## ADDED Requirements
### Requirement: Handler-Scoping nach Vorbild
`ON ERROR GOTO label` SHALL einen modulweiten Handler aktivieren (auch
wenn die Anweisung in einer Prozedur steht); `ON LOCAL ERROR GOTO label`
SHALL einen prozedurlokalen Handler aktivieren, der den modulweiten für
die Dauer des Prozedurlaufs verdeckt. `ON [LOCAL] ERROR GOTO 0`
deaktiviert den jeweiligen Handler. Tritt ein Fehler in einer Prozedur
ohne zuständigen Handler auf, SHALL er die Aufrufkette hochpropagieren,
bis ein aktiver Handler gefunden ist; ohne Handler bricht das Programm
mit der Fehlermeldung und der Quellzeile ab.
#### Scenario: Modulweiter Handler fängt Prozedurfehler
- **WHEN** das Hauptprogramm `ON ERROR GOTO Fehler` gesetzt hat und eine gerufene SUB Fehler 6 auslöst
- **THEN** springt die Ausführung in den Handler `Fehler` und `ERR` ist 6
#### Scenario: Lokaler Handler verdeckt modulweiten
- **WHEN** eine Prozedur `ON LOCAL ERROR GOTO L` gesetzt hat und in ihr ein Fehler auftritt
- **THEN** behandelt `L` den Fehler, nicht der modulweite Handler
#### Scenario: Ohne Handler bricht das Programm ab
- **WHEN** Fehler 9 ohne aktiven Handler auftritt
- **THEN** endet das Programm mit der Meldung „Subscript out of range" und der Quellzeile
### Requirement: ERR und ERL
Beim Eintritt in einen Handler SHALL `ERR` den Fehlercode liefern und
`ERL` die zuletzt vor dem Fehler ausgeführte **numerische** Zeilennummer
(0, wenn keine numerischen Zeilennummern durchlaufen wurden —
alphanumerische Labels zählen nicht). `ERROR n` SHALL Fehler `n`
künstlich auslösen und dieselbe Maschinerie durchlaufen.
#### Scenario: ERL bei Label-Programm
- **WHEN** ein Programm ohne numerische Zeilennummern einen Fehler behandelt
- **THEN** liefert `ERL` 0
#### Scenario: ERROR-Anweisung
- **WHEN** `ERROR 53` bei aktivem Handler ausgeführt wird
- **THEN** wird der Handler mit `ERR` = 53 betreten
### Requirement: RESUME-Semantik
Im Handler SHALL `RESUME` (bzw. `RESUME 0`) die fehlerauslösende
Anweisung wiederholen, `RESUME NEXT` mit der Anweisung danach
fortfahren und `RESUME label` an das Ziel springen. `RESUME` außerhalb
einer aktiven Fehlerbehandlung MUST Fehler 20 „RESUME without error"
auslösen. Nach `RESUME` gilt die Fehlerbehandlung als abgeschlossen
(`ERR` wird zurückgesetzt).
#### Scenario: RESUME NEXT
- **WHEN** ein Handler nach Fehler 11 (`Division by zero`) `RESUME NEXT` ausführt
- **THEN** wird die auf die Division folgende Anweisung ausgeführt
#### Scenario: RESUME ohne Fehler
- **WHEN** `RESUME` außerhalb eines Handlers ausgeführt wird
- **THEN** tritt Laufzeitfehler 20 auf
### Requirement: Fehler im Handler ist fatal
Tritt ein Fehler auf, während die Fehlerbehandlung aktiv ist (zwischen
Handler-Eintritt und `RESUME`), SHALL das Programm ohne erneute
Handler-Suche mit der Fehlermeldung abbrechen (keine Fehlerkaskaden,
Verhalten des Vorbilds).
#### Scenario: Kein Kaskadieren
- **WHEN** im aktiven Handler selbst Fehler 6 auftritt
- **THEN** bricht das Programm mit „Overflow" ab, auch wenn ein Handler registriert ist

View File

@@ -0,0 +1,121 @@
# Tasks — Phase 2: Bytecode und VM
## 1. Feindesign-Dokumentation (Eingangsaufgabe aus Phase 0)
- [x] 1.1 Zahlenkonvertierungs-Matrix (implizite Casts, Banker's Rounding,
Überlauf, `/` vs. `\`/`MOD`, logische Operatoren, CURRENCY, `^`-Regel,
SINGLE/DOUBLE-Textdarstellung) als Tabelle in docs/tbvm-design.md
schreiben; jede Zelle mit Fundstelle in der Original-Hilfe belegen,
unklare Zellen als `TODO verify` markieren — verifizierbar: Abschnitt
existiert, keine Zelle ohne Beleg oder TODO-Marke
- [x] 1.2 Opcode-Satz vollständig in docs/tbvm-design.md ausformulieren
(Gruppen, Encoding, Operanden) und `STOP`/`CONT`-Runner-Entscheidung
sowie Fehlerbehandlungs-Scoping (design.md D4D6) dort festhalten —
verifizierbar: „Offene Punkte"-Abschnitt in tbvm-design.md ist leer
## 2. Frontend: Typisiertes HIR
- [x] 2.1 HIR-Datenmodell in `tb-frontend` definieren (Slots, typisierte
Ausdrücke, explizite Konvertierungsknoten, Prozedur-/Modulstruktur,
Quellzeilen-Anker) — verifizierbar: `cargo check` und Doku-Kommentare
je Knotentyp
- [x] 2.2 Sema zum Lowering-Pass umbauen: `analyze_source` liefert
zusätzlich HIR; Konstantenfaltung, `DEFtype`, implizite
Konvertierungen materialisieren — verifizierbar: alle 33 bestehenden
Frontend-Tests bleiben grün
- [x] 2.3 HIR-Tests: diagnose-freie Korpusmodule liefern vollständiges HIR;
Konvertierungsknoten-Szenario (`d# = i% + 1.5`) als Unit-Test —
verifizierbar: neue Tests in `crates/tb-frontend/tests` grün
## 3. Bytecode und Serialisierung
- [x] 3.1 Opcode-Definitionen und Modulstruktur (Konstantenpool, Typtabelle,
modul-qualifizierte Prozedurtabelle, DATA-Segment, Zeilentabelle) in
`tb-vm::bytecode` implementieren — verifizierbar: Unit-Tests für
Encoding/Decoding einzelner Opcodes
- [x] 3.2 `.tbc`-Writer/Reader (Magic, Formatversion, Abschnittstabelle)
implementieren — verifizierbar: Roundtrip-Test (schreiben → laden →
identisch) und Ablehnungstest für unbekannte Formatversion
## 4. Codegenerator
- [x] 4.1 Ausdrucks-Codegen (Literale, Slots, Arithmetik, Vergleiche,
logische Operatoren, Konvertierungsknoten, String-Konkatenation) —
verifizierbar: Unit-Tests HIR-Ausdruck → erwartete Opcode-Folge
- [x] 4.2 Kontrollfluss-Codegen (`IF`, `SELECT CASE`, `FOR`, `DO`, `WHILE`,
`GOTO`/`GOSUB`/`ON GOTO`, `EXIT`, Labels/Zeilennummern) mit
Fixup-Listen für Vorwärtsziele — verifizierbar: Unit-Tests je
Konstrukt
- [x] 4.3 Prozedur-Codegen (SUB/FUNCTION/`DEF FN`, BYREF-/BYVAL-Argumente,
`STATIC`, Frames/Locals), `DIM`/Arrays/UDTs, `DATA`-Segment und
Zeilentabelle emittieren — verifizierbar: Unit-Tests und
`cargo test -p tb-vm` grün
## 5. Interpreter (TBVM)
- [x] 5.1 VM-Grundgerüst: Werte-Modell, Operandenstack, Frames,
`step()`-Schleife mit Tick-Flag-Wort an Anweisungsgrenzen —
verifizierbar: Ausdrucks-Programme laufen in Unit-Tests
- [x] 5.2 Kontrollfluss und `GOSUB`-Stack pro Frame inkl. Fehler 3
(„RETURN without GOSUB") — verifizierbar: kontrollfluss.bas läuft im
Capture-Host mit korrekter Ausgabe
- [x] 5.3 Prozeduraufrufe (BYREF-Rückwirkung, Klammer-BYVAL, `STATIC`,
Rekursion, `DEF FN` im Modulkontext) — verifizierbar: Unit-Tests der
Spec-Szenarien aus vm-ausfuehrung
- [x] 5.4 `DATA`/`READ`/`RESTORE` inkl. Fehler 4 und 13 — verifizierbar:
Unit-Tests der Spec-Szenarien
- [x] 5.5 Fehlerbehandlung: Handler-Zustand (modul-global + Frame-lokal),
Unwinding, `RESUME`-Varianten, `ERR`/`ERL`, `ERROR n`, Fehler im
Handler fatal — verifizierbar: alle Szenarien aus vm-fehlerbehandlung
als Tests grün
- [x] 5.6 Unterbrechbarkeit: Breakpoints auf (Modul, Zeile), Einzelschritt,
Variableninspektion im Halt, Abbruchwunsch — verifizierbar:
Unit-Tests treiben die VM per `step()` und prüfen Halt/Inspektion
- [x] 5.7 Programmende: `END`/`SYSTEM`/Programmende vs. `STOP` mit Meldung
und Exit-Code-Unterscheidung — verifizierbar: Runner-Test prüft
Exit-Codes und Meldung
## 6. Konsolen-Basisbibliothek (`tb-runtime`-Scheibe)
- [x] 6.1 `Host`-Trait definieren; Konsolen-Host und Capture-Host
implementieren — verifizierbar: Unit-Test zeichnet Ausgabe byte-genau
auf
- [x] 6.2 Builtin-Dispatch-Tabelle + `CALL_BUILTIN`-ABI (Argumente auf dem
Stack) in VM und Runtime verdrahten — verifizierbar: `LEN`-Aufruf
läuft über die Tabelle (Spec-Szenario)
- [x] 6.3 PRINT-Formatierung: Zahlendarstellung je Typ, Vorzeichenspalte,
nachgestelltes Leerzeichen, `;`/`,`-Semantik, 14-Zeichen-Druckzonen —
verifizierbar: printzahlen.bas und printzonen.bas byte-genau korrekt
- [x] 6.4 Korpus-Stringfunktionen (`LEN`, `LEFT$`, `RIGHT$`, `MID$`,
`INSTR`, `UCASE$`, `LCASE$`, `STRING$`, `SPACE$`, `CHR$`, `ASC`,
`STR$`, `VAL`) mit Randfällen des Vorbilds — verifizierbar:
strings.bas byte-genau korrekt plus Randfall-Unit-Tests
(`VAL`-Präfixlesen, `STR$`-Leerzeichen, `ASC("")` → Fehler 5)
## 7. CLI und Test-Harness
- [x] 7.1 `tbc run <datei.bas>`: kompilieren, `.tbc` in Memory ausführen,
Konsolen-Host; Diagnosen bei Compile-Fehler, Exit-Codes nach D6 —
verifizierbar: `tbc run tests/compat/hello.bas` druckt `Hallo, Welt!`
- [x] 7.2 Harness in `tests/`: alle `tests/compat/*.bas` kompilieren, im
Capture-Host ausführen, byte-genau gegen `.out` vergleichen; bei
Abweichung Datei/Zeile/Soll/Ist nennen — verifizierbar: `cargo test`
führt den kompletten Korpus aus
- [x] 7.3 Korpus erweitern: Konvertierungsmatrix-Programm, Fehlerbehandlung
(`ON ERROR`/`RESUME`/`ERR`/`ERL`), BYREF/BYVAL-Programm mit
dokumentierter Sollausgabe (Belege bzw. `TODO verify`-Konvention) —
verifizierbar: neue `.bas`/`.out`-Paare laufen im Harness grün
## 8. Benchmarks und Abschluss
- [x] 8.1 Compile-Budget-Harness früh aufsetzen: Generator für
~50k-Zeilen-Projekt + Criterion-Bench; Budgetvergleich (< 1 s Projekt,
< 50 ms Modul) — verifizierbar: `cargo bench` weist die Zeiten aus
- [x] 8.2 VM-Durchsatz-Benches (Schleifen, Strings); Ergebnisse und
Benchmark-Stand in docs/tbvm-design.md „Performance" festhalten —
verifizierbar: Abschnitt enthält Messwerte mit Datum
- [x] 8.3 Meilenstein-Verifikation und Doku-Abschluss: kompletter Korpus
grün via `cargo test`, docs/sprachreferenz.md um Phase-2-Festlegungen
ergänzt (Rundung, `STOP`-Verhalten), PLAN.md-Phase-2-Haken gesetzt —
verifizierbar: alle Checkboxen der Phase in PLAN.md abgehakt,
Testsuite grün