Phase 3 (Kern): Laufzeitbibliothek, Bildschirm und Datei-E/A

Setzt den OpenSpec-Change phase-3-runtime-bildschirm um (77/77 Aufgaben).
Abdeckung laut Inventar: 195 implementiert, 37 offen, 53 Non-Feature.

Vollstaendigkeits-Inventar
- docs/inventar.md mit 285 Eintraegen aus den Themenlisten von
  bas7advr.hlp und qb45advr.hlp, je mit Status und Fundstelle
- crates/tb-frontend/tests/inventar.rs haelt die Tabelle in beide
  Richtungen gegen den Code; der Abdeckungsstand kann nicht veralten

Bruchschritt (Puffer, Host, Korpus)
- Cell/TextScreen ziehen ratatui-frei nach tb-runtime::screen; tb-ui
  behaelt Farbabbildung, Widget und den neuen Terminal-Host
- Host wechselt vom Zeichenstrom auf Anzeige des Zellenpuffers plus
  Ereignisse (Taste, Groessenaenderung, Abbruch)
- Korpusvergleich auf getrimmten Bildschirm-Snapshot; die acht
  bestehenden Sollausgaben blieben dabei unveraendert

Groessenunabhaengigkeit
- 80x25 ist nirgends mehr eine feste Grenze; Groessenaenderungen waehrend
  der Ausfuehrung werden zugestellt (Inhalt oben links erhalten, Cursor
  und VIEW PRINT geklemmt)
- Korpusnachweis: dasselbe Programm bei 80x25 und 120x40 mit je eigener
  Sollausgabe, plus ein Programm mit Groessenwechsel mitten im Lauf

Bibliothek
- Breite Unicode-Zeichen belegen zwei Zellen (Cursor, POS, Randumbruch)
- Bildschirm: CLS, COLOR, LOCATE, WIDTH, VIEW PRINT, SCREEN, CSRLIN, POS
- Tastatur: INKEY$, INPUT$, Funktionstasten-Makros (KEY n / LIST / ON)
- PRINT USING, LPRINT USING, FORMAT$, SetFormatCC
- Mathematik mit kompatiblem PRNG (gleiche Saat, gleiche Folge)
- Datum und Zeit mit Serienwerten ab 1899
- Finanzmathematik: FV#, PV#, Pmt#, IPmt#, PPmt#, NPer#, Rate#, NPV#,
  IRR#, MIRR#, SLN#, SYD#, DDB#
- Datei-E/A: sequenziell, RANDOM (Recordpuffer und UDT-Variablen),
  BINARY, FIELD/LSET/RSET, Statusfunktionen, Dateisystem, MK$/CV
- System: ENVIRON, FRE, CLEAR, TRON/TROFF, STACK, ERDEV, ERR-Anweisung

Altlasten aus Phase 2
- ON ERROR GOTO auf Modulebene ist aus Prozeduren ansprechbar
  (prozeduruebergreifender Fixup im Codegenerator)
- DATA behaelt seinen Rohtext (Gross-/Kleinschreibung, innerer Leerraum)
- Die vier TODO-verify-Zellen der Konvertierungsmatrix sind aufgeloest

Vom Inventar aufgedeckte Fehler
- Zwoelf Non-Features wies der Compiler entgegen der Phase-1-Spec nicht
  ab (CALLS, SSEG, POINT, VIEW, COM, PEN, STRIG, STICK, die
  String*-Routinen, LINE und OPEN "COMn:") -- geschlossen
- Drei Gruppen fehlten im urspruenglichen Umfang: Finanzmathematik,
  Record-Konvertierung, Rest der Dateisystemfunktionen
- bas7advr.hlp allein ist keine vollstaendige Quelle; das Inventar
  bildet die Vereinigung mit qb45advr.hlp

Neue Changes
- phase-3-isam: schliesst Phase 3 ab (Speicherschicht redb)
- phase-3-ortszeit: zieht die UTC-Abweichung zurueck

Neue Abhaengigkeit: unicode-width.
Dokumentiert: sprachreferenz.md und tbvm-design.md sind TODO-frei,
docs/bibliothek.md neu, PLAN.md fortgeschrieben.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-04 06:54:19 +02:00
parent a6f005d023
commit 52ccbb5848
94 changed files with 10421 additions and 792 deletions

View File

@@ -41,11 +41,17 @@ enum Value {
BYREF-Parameter als Referenz-Slots (Handle auf Variablen-Slot).
- GOSUB: **kein** Frame — Rücksprungadresse auf separatem GOSUB-Stack im
aktuellen Frame (RETURN prüft diesen zuerst).
- **Fehlerbehandlung:** Pro Frame ein Handler-Zustand (`ON ERROR GOTO x`).
Laufzeitfehler → VM sucht aktiven Handler im aktuellen Modulkontext
(Vorbild: Handler sind modul-/prozedurlokal, TODO: exakte Scoping-Regel
testen), setzt `ERR`/`ERL`, springt. `RESUME` nutzt gemerkten
Anweisungs-Offset.
- **Fehlerbehandlung:** Zwei Ebenen (Scoping geklärt 2026-09-04).
`ON LOCAL ERROR GOTO x` setzt einen Handler im aktuellen Frame; sein
Sprungziel muss im selben Rumpf liegen. `ON ERROR GOTO x` **ohne**
`LOCAL` setzt den modulweiten Handler — auch aus einer Prozedur heraus,
und sein Sprungziel liegt dann im Modulrumpf (der Codegenerator löst es
über Prozedur 0 auf, die zuerst übersetzt wird).
Ein Laufzeitfehler sucht zuerst lokale Handler die Aufrufkette hinauf,
dann den modulweiten; beim modulweiten wird bis Frame 0 abgewickelt,
weshalb `RESUME NEXT` dort hinter dem Prozeduraufruf fortsetzt. Der
Handler setzt `ERR`/`ERL`; `RESUME` nutzt den gemerkten
Anweisungs-Offset. Ein Fehler im Handler ist fatal (keine Kaskaden).
## Ausführungsmodell / Unterbrechbarkeit
@@ -136,6 +142,20 @@ Windows 11, `cargo bench -p tb-vm`):**
- DOUBLE-Schleife: ≈ 5,2 Mio Iterationen/s
- SUB-Aufrufe mit BYREF: ≈ 5,2 Mio Aufrufe/s
- String-Lasten (MID$/INSTR/Verkettung): ≈ 0,8 Mio Runden/s
**Nachmessung nach Phase 3 (2026-09-04, macOS, Apple Silicon,
`cargo bench -p tb-vm`)** — die Zustellpunkte des neuen `Host` (Anzeige
nur bei geändertem Bildschirm, Ereignisabholung alle 1024 Anweisungen)
kosten nichts Messbares:
- Compile-Budget: Einzelmodul 508 Zeilen **0,77 ms**; Projekt 49 760
Zeilen **53 ms** (≈ 950 000 Zeilen/s)
- VM-Durchsatz: INTEGER-Schleife **11,0 Mio/s**, DOUBLE **12,2 Mio/s**,
SUB-Aufrufe **10,7 Mio/s**, String-Lasten **2,4 Mio Runden/s**
(Die höheren Zahlen gegenüber 2026-09-02 gehen auf die andere Maschine
zurück, nicht auf eine Optimierung — gemessen wurde beide Male derselbe
Benchmark.)
- Einordnung: bei ≈ 13 Instruktionen je Schleifeniteration entspricht
das ≈ 60 Mio Instruktionen/s — Vorbild-Programme auf 1992er-Hardware
werden um weit mehr als den Faktor 100 übertroffen; für 80×25-
@@ -145,14 +165,18 @@ Windows 11, `cargo bench -p tb-vm`):**
## Zahlenkonvertierungs-Matrix (Feindesign, 2026-09-02)
Grundlage: Original-Hilfe (Topics „Arithmetic Operators", „CINT/CLNG",
„MOD", „Numeric Types"). Zellen ohne belastbare Fundstelle sind mit
`TODO verify` markiert; der Korpustest `konvertierung.bas` verankert die
Matrix ausführbar.
„MOD", „Numeric Types"). Vier Punkte sind dort **nicht** geregelt; sie
wurden am 2026-09-04 entschieden und sind unten als *Projektentscheidung*
gekennzeichnet — sie gelten damit als bewusste Festlegung, nicht als
rekonstruiertes Vorbildverhalten. Der Korpustest `konvertierung.bas`
verankert die Matrix ausführbar.
**Rangfolge für Promotion:** INTEGER < LONG < CURRENCY < SINGLE < DOUBLE.
Ausnahme: CURRENCY gemischt mit SINGLE oder DOUBLE ergibt **DOUBLE**
(nicht SINGLE), damit keine Festkomma-Präzision in f32 verloren geht
(`TODO verify` gegen PDS-Hilfe; plausibelste Lesart).
(nicht SINGLE). *Projektentscheidung 2026-09-04:* Die Original-Hilfe
regelt diesen Fall nicht. CURRENCY trägt 19 signifikante Stellen, SINGLE
nur 7 — eine Promotion nach SINGLE zerstörte genau die Genauigkeit, für
die es den Typ gibt. DOUBLE (16 Stellen) ist die nächstbeste Wahl.
**Ergebnistyp binärer Operatoren** (l op r; T = promoteter Typ):
@@ -161,7 +185,7 @@ Ausnahme: CURRENCY gemischt mit SINGLE oder DOUBLE ergibt **DOUBLE**
| `+ - *` | T; gleiche Typen bleiben erhalten | Ganzzahl-/CURRENCY-Überlauf → Fehler 6 (auch INTEGER+INTEGER promotet **nicht**) |
| `/` | DOUBLE, wenn ein Operand DOUBLE oder CURRENCY, sonst SINGLE | Division durch 0 → Fehler 11 |
| `\` `MOD` | INTEGER, wenn beide Operanden INTEGER, sonst LONG | Operanden werden **vorher** kaufmännisch (banker's) auf Ganzzahl gerundet; außerhalb LONG → Fehler 6; Divisor 0 → Fehler 11; `MOD`-Vorzeichen wie Dividend |
| `^` | DOUBLE, wenn ein Operand DOUBLE oder CURRENCY, sonst SINGLE (`TODO verify`) | Rechnung intern in DOUBLE; 0 ^ negativ → Fehler 11; negative Basis mit nicht-ganzem Exponent → Fehler 5 |
| `^` | DOUBLE, wenn ein Operand DOUBLE oder CURRENCY, sonst SINGLE (*Projektentscheidung 2026-09-04*) | Rechnung intern in DOUBLE; 0 ^ negativ → Fehler 11; negative Basis mit nicht-ganzem Exponent → Fehler 5 |
| `= <> < <= > >=` | INTEGER (1/0) | Vergleich im promoteten Typ; Strings codepoint-weise |
| `NOT AND OR XOR EQV IMP` | INTEGER, wenn alle Operanden INTEGER, sonst LONG | bitweise; Operanden vorher banker's-gerundet auf Ganzzahl, außerhalb LONG → Fehler 6 |
| unäres `-` | Typ des Operanden | (32768) INTEGER → Fehler 6 |
@@ -188,13 +212,21 @@ via `CINT`/`CLNG`/`CSNG`/`CDBL`/`CCUR` — identische Semantik):
- INTEGER/LONG: dezimal ohne Dezimalpunkt.
- SINGLE: bis 7 signifikante Stellen, kürzeste Darstellung, keine
führende Null vor dem Dezimalpunkt (`.5`), keine nachgestellten
Nullen; außerhalb des Festformat-Bereichs Exponentialform `mE±xx`
(exakte Schwelle `TODO verify`; umgesetzt: Exponentialform, wenn der
Dezimalexponent < 7 oder ≥ 7 ist).
Nullen; außerhalb des Festformat-Bereichs Exponentialform `mE±xx`.
*Projektentscheidung 2026-09-04:* Die Original-Hilfe nennt keine
Schwelle. Gewählt ist der Dezimalexponent < 7 oder ≥ 7 — damit bleibt
jeder Wert im Festformat, der sich mit den 7 signifikanten Stellen des
Typs verlustfrei schreiben lässt (`9999999` fest, `1E+07`
exponentiell; `.0000001` fest, `1E-08` exponentiell).
- DOUBLE: wie SINGLE mit bis 16 signifikanten Stellen und `D±xx` als
Exponentmarke.
- CURRENCY: Festformat mit bis zu 4 Nachkommastellen, nachgestellte
Nullen entfallen (`TODO verify`).
Nullen entfallen (`2.5000@``2.5`, `1.10@``1.1`).
*Projektentscheidung 2026-09-04:* Die Original-Hilfe nennt kein
Ausgabeformat. Gewählt ist dieselbe Regel wie bei den übrigen
Zahlentypen — nachgestellte Nullen entfallen —, damit `PRINT` über alle
Typen einheitlich wirkt. Wer feste Nachkommastellen braucht, nutzt
`PRINT USING`.
- `PRINT` stellt nicht-negativen Zahlen ein Leerzeichen, negativen ein
`-` voran und hängt stets ein Leerzeichen an; `STR$` nur das führende
Leerzeichen/`-`, kein nachgestelltes.
@@ -266,6 +298,9 @@ als Vergleichs-/Sprungkette abgesenkt.
## Offene Punkte
Keine — die Punkte aus Phase 0 (Opcode-Satz, Konvertierungsmatrix,
`STOP`/`CONT` im Runner) sind oben ausgearbeitet; verbleibende
Detailunsicherheiten sind als `TODO verify` direkt an den betroffenen
Zellen markiert und über den Testkorpus abgesichert.
`STOP`/`CONT` im Runner) sind oben ausgearbeitet. Die vier Stellen, zu
denen die Original-Hilfe schweigt, sind am 2026-09-04 als
*Projektentscheidung* festgelegt, an Ort und Stelle begründet und über
`tests/compat/konvertierung.bas` ausführbar verankert. Sollte je eine
belastbare Fundstelle auftauchen, die ihnen widerspricht, gilt sie —
die Entscheidungen sind Lückenfüller, keine Setzungen gegen das Vorbild.