Files
TerminalBasic/openspec/changes/phase-3-runtime-bildschirm/design.md
Chili Palmer 52ccbb5848 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>
2026-09-04 06:54:19 +02:00

288 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Context
Siehe proposal.md — Why. Technisch entscheidend ist der Ist-Zustand
dreier Stellen:
- `tb-ui::screen::TextScreen` (385 Zeilen) trägt die Bildschirmsemantik
bereits vollständig: `cls`, `set_color`, `locate`, `csrlin`, `pos`,
`view_print`, `cell`, `print`, `scroll_up`, `resize`. Ratatui kommt
darin an genau zwei Stellen vor: `basic_color()` (4 Zeilen) und
`impl Widget for &TextScreen` (~35 Zeilen).
- `tb-runtime::host::Host` ist ein Stream-Trait (`write(&str)`,
`read_line()`). `tb-runtime` und `tb-ui` sind beide Blätter im
Abhängigkeitsgraphen und treffen sich erst in `tb-cli`/`tb-ide`.
- Rund 50 dokumentierte Elemente besitzen im Frontend eine Signatur in
`builtin_fn`/`builtin_stmt`, senken aber auf `HStmtKind::Unsupported`
bzw. `HExpr::Unsupported` ab → Opcode `0x05` → Laufzeitfehler 73.
Zwei Entscheidungen vom 2026-09-03 rahmen die Arbeit: Das Windowing wird
— wie beim Vorbild — vom erzeugten Programm mitgebracht und nicht von der
IDE, sodass Forms und `PRINT` in denselben Zellenpuffer zeichnen. Die
Forms-Engine selbst bleibt dabei am von PLAN.md vorgesehenen Ort
`tb-ui::forms`.
## Goals / Non-Goals
**Goals:**
- Der Zellenpuffer liegt dort, wo sowohl die Laufzeitbibliothek als auch
die spätere Forms-Engine in `tb-ui` ihn erreichen, ohne dass die
Ausführungsschicht eine Terminal-Bibliothek zieht.
- Bildschirmverhalten ist ohne Terminal automatisiert prüfbar.
- Die Abdeckung misst sich selbst statt gepflegt zu werden.
- Die Umstellung der acht bestehenden Golden Files geschieht in einem
nachvollziehbaren, einmaligen Schritt.
**Non-Goals:**
- Optimierung der Bildschirmausgabe (Diff-Minimierung, Teilaktualisierung)
— erst wenn Messungen sie verlangen.
- Ein Rendering-Backend jenseits des Terminals.
- Die Forms-Engine selbst (Phase 4, `tb-ui::forms`); dieser Change
liefert ihr nur die Zeichenfläche.
## Decisions
### D1: Zellenpuffer nach `tb-runtime::screen`, ratatui-frei
`Cell` und `TextScreen` ziehen unverändert nach `tb-runtime::screen`.
`basic_color()` und `impl Widget` bleiben in `tb-ui` und arbeiten künftig
auf dem Runtime-Typ.
*Warum:* `PRINT`, `LOCATE`, `CLS`, `COLOR`, `CSRLIN`, `POS` und die
Funktionsform von `SCREEN` sind Elemente der Laufzeitbibliothek. Sie
müssen den Puffer erreichen, ohne dass `tb-runtime` eine
Terminal-Bibliothek zieht — sonst braucht jeder VM- und Bibliothekstest
ein Terminal. Das trägt die Entscheidung allein.
*Verhältnis zur Forms-Engine:* Forms bleibt laut PLAN.md in
`tb-ui::forms` (Entscheidung 2026-09-03). Das passt zur Richtung: `tb-ui`
hängt künftig an `tb-runtime` und zeichnet von oben in denselben Puffer
wie `PRINT` — ein `MSGBOX` über einer Konsolenausgabe überdeckt deren
Zellen und gibt sie beim Schließen zurück. Läge der Puffer weiterhin in
`tb-ui`, wäre genau diese gemeinsame Fläche für die Bibliothek
unerreichbar. Umgekehrt entsteht kein Zyklus: die Ausführungsschicht
erreicht die Forms-Engine über die Host-Schnittstelle (D2), die sie
selbst deklariert und `tb-ui` implementiert.
*Alternativen:*
- `tb-runtime` hängt an `tb-ui`: null Doppelung, aber ratatui und
crossterm landen in jedem VM-Test. Verworfen.
- Semantik bleibt in `tb-ui`, jeder Host führt einen eigenen Puffer:
dieselbe Scroll-/Umbruch-/Doppelzellenlogik dreimal. Verworfen.
*Aufwand:* Der Umzug ist mechanisch — betroffen sind der `use`-Block, die
zwei ratatui-Stellen und die Modulzuordnung. Die Semantik selbst wird
nicht angefasst; erweitert wird sie nur um die Doppelzellen (D4).
### D2: `Host` wird Bildschirm- statt Stream-Schnittstelle
`write`/`read_line` weichen einer Schnittstelle, die den Zellenpuffer zur
Anzeige übergibt und Ereignisse (Tastendruck, Größenänderung, Abbruch)
zurückliefert. Die Bibliothek schreibt nicht mehr in den Host, sondern in
den Puffer; der Host stellt dar.
*Warum:* `LOCATE 5,10`, `COLOR 14,1`, `CSRLIN` und besonders
`SCREEN(r,c)` — das ein Zeichen vom Bildschirm *zurückliest* — sind über
einen Zeichenstrom nicht ausdrückbar. Das ist ein Zwang, keine Präferenz.
*Wann dargestellt wird:* Der Host wird an denselben Tick-Grenzen
aufgerufen, an denen die VM heute schon die Abbruchprüfung macht, sowie
vor jeder blockierenden Eingabe. Kein zusätzlicher Mechanismus.
*Blockieren bleibt erlaubt* (Entscheidung D3 aus Phase 2 bleibt gültig):
`INPUT` und `INPUT$` blockieren im Host. Erst die Ereignisschleife der
Phase 4 bringt kooperative Zustellpunkte.
*Alternativen:*
- Stream-Host behalten und Bildschirmwirkungen als ANSI-Escapes
hindurchreichen: `CSRLIN`/`POS`/`SCREEN()` bräuchten trotzdem einen
Schattenpuffer, und der Capture-Host müsste ANSI parsen. Verworfen.
### D3: Korpusvergleich als getrimmter Snapshot
Die `.out`-Datei hält das Textbild des Puffers bis zur letzten belegten
Zeile und Spalte. Eine zweite Ebene mit Farbattributen kommt nur hinzu,
wenn das Programm `COLOR` verwendet.
*Warum getrimmt:* Ein voller 80×25-Snapshot für `hello.bas` wären 2000
Zeichen für zwei Zeichen Signal, und jeder Diff zeigte Rauschen. Getrimmt
bleiben die bestehenden acht Sollausgaben inhaltlich fast unverändert —
`PRINT`-only-Programme ergeben dasselbe Textbild wie bisher der Stream.
*Warum Attribute optional:* Programme ohne `COLOR` erzeugen sonst eine
zweite Ebene aus lauter identischen Werten.
*Umstellung:* Die acht Golden Files werden einmalig neu erzeugt und der
Diff wird geprüft — erwartet wird, dass sich nur Zeilen ändern, die
vorher auf `\r`/Umbruchverhalten beruhten. Ein unerwarteter Diff ist ein
Befund, kein Anpassungsgrund.
*Alternativen:*
- Zwei Harnesses (Stream für Alt-Tests, Snapshot für neue): dauerhaft
doppelte Infrastruktur für einen einmaligen Umstellungsschmerz.
Verworfen.
- Voller Snapshot: siehe oben. Verworfen.
### D4: Doppelzellen über Darstellungsbreite
Breite Zeichen belegen zwei Zellen: die erste trägt das Zeichen, die
zweite ist als Fortsetzung markiert. Neue Abhängigkeit `unicode-width`
in `tb-runtime`.
*Warum eine Abhängigkeit:* Die Breitentabelle folgt einem
Unicode-Annex und ändert sich mit jeder Unicode-Version; sie
nachzubauen hieße, sie dauerhaft nachzupflegen.
*Konsequenzen, die die Spec festhält:* Cursor rückt um zwei Spalten vor;
`POS` zählt die Anfangsspalte; ein breites Zeichen in der letzten Spalte
wird vollständig umgebrochen und die Spalte bleibt leer; `LOCATE` auf
eine Fortsetzungszelle wirkt auf den Zeichenanfang. Beim Überschreiben
einer Hälfte muss die andere geleert werden, sonst entstehen
Geisterzeichen.
### D4a: 80×25 ist nirgends eine Konstante
Anforderung des Projektinhabers (2026-09-03), Priorität für das ganze
Programm: 80×25 ist stets Synonym für „voller Bildschirm", nie eine feste
Grenze. Moderne Terminals haben keine feste Größe, und sie ändert sich
während der Ausführung.
*Konsequenz für die Umsetzung:* Kein Zweig darf gegen 80 oder 25
vergleichen. Die Abmessungen kommen ausschließlich aus dem Puffer
(`cols()`/`rows()`), abgeleitet aus der Darstellungsfläche. Betroffen
sind `CLS` (voller aktueller Bildschirm), der voreingestellte
Scrollbereich, die Umbruchspalte, die Scrollzeile sowie die
Grenzprüfungen von `LOCATE` und der Funktionsform von `SCREEN`.
*Größenänderung zur Laufzeit* ist damit kein Sonderfall, sondern der
Normalfall: Das Größenänderungsereignis kommt über den Host (D2), der
Puffer wird angepasst, Inhalt bleibt oben links, Cursor und
`VIEW PRINT`-Bereich werden geklemmt. Ein Bereich, der durch die
Verkleinerung leer würde, fällt auf den vollen Bildschirm zurück, statt
ungültig zu bleiben.
*Das Mindestmaß bleibt eine Darstellungsschranke:* Ist die Fläche kleiner
als 80×25, zeigt die Darstellungsschicht den Hinweis „Terminal zu klein"
(bestehende Entscheidung 2026-09-02). Das ist eine untere Schranke der
Anzeige, keine Bildschirmgröße für das Programm.
*Prüfbarkeit:* Der Test-Host bekommt seine Größe je Korpusprogramm
explizit gesetzt, ohne stillschweigenden Vorgabewert. Mindestens ein
Bildschirmprogramm läuft bei zwei Größen mit je eigener Sollausgabe —
eine hartkodierte 80 oder 25 fällt damit sofort auf. Ein weiteres
durchläuft eine Größenänderung mitten in der Ausführung.
### D4b: Das Inventar geht der Umfangsschätzung vor
Die Erhebung in Aufgabe 1.1 (2026-09-03) hat gezeigt, dass die
ursprüngliche Umfangsliste dieses Changes drei ganze Gruppen nicht
enthielt: die 13 finanzmathematischen Funktionen, die 14
Record-Konvertierungsfunktionen und einen Rest an Dateisystem- und
Systemelementen. Sie hat außerdem zwei Elemente widerlegt, die
`phase-3-isam` fälschlich führte (`CHECKPOINT`, `TEXTCOMP`).
*Konsequenz für die Arbeitsweise:* Maßgeblich ist das Inventar, nicht die
Aufzählung im Proposal. Wo beide auseinandergehen, wird das Proposal
nachgezogen. Aufzählungen in Proposal und Specs sind Mindestumfang.
*Konsequenz für die Quelle:* `bas7advr.hlp/TOPIC_LIST.html` ist
nachweislich unvollständig — `SGN`, `SQR`, `SPACE$`, `STRING$`, `CSRLIN`,
`BEEP` und `DATE$` als Funktion fehlen dort. Das Inventar bildet die
Vereinigungsmenge aus dieser Liste, `qb45advr.hlp/TOPIC_LIST.html` und
`docs/sprachreferenz.md`; jeder Eintrag nennt seine Fundstelle. Die
Zählung einer einzelnen Themenliste ist kein Vollständigkeitsnachweis.
### D5: Inventar als Markdown-Tabelle mit Abgleichstest
`docs/inventar.md` trägt eine Pipe-Tabelle mit festen Spalten (Name, Art,
Gruppe, Status, Fundstelle). Ein Test parst sie und hält sie gegen
`builtin_fn`/`builtin_stmt`, die `Unsupported`-Marker und
`banned_feature`.
*Warum Markdown und nicht TOML/YAML:* Der Plan nennt „maschinenlesbar",
aber keinen Konsumenten außer der Messbarkeit selbst; Zielpfad ist
ausdrücklich `docs/inventar.md`, und das IDE-Hilfesystem (Phase 5)
rendert `docs/`-Markdown. Eine Pipe-Tabelle ist mit einem Split
maschinenlesbar. Ein zweites Format bräuchte einen Generator, um wieder
bei Markdown zu landen.
*Der Test ist der eigentliche Punkt:* Ohne ihn verrottet der Status. Mit
ihm kann das Inventar nicht falsch werden, ohne dass die Suite rot wird.
### D6: `Unsupported`-Marker bleiben, aber nur noch für Phase 4
Die Marker verschwinden nicht — Ereignissteuerung, `SETUEVENT`, `MSGBOX`
und `INPUTBOX$` behalten sie bis Phase 4. Der Abgleichstest aus D5 stellt
sicher, dass genau die im Inventar als `offen` geführten Elemente noch
Marker tragen.
### D7: Reihenfolge der Umsetzung
```
1. Inventar (Sollseite) -> misst alles Folgende
2. Puffer-Umzug + Host + Snapshot -> BREAKING, zusammen in einem Schritt
3. Bildschirmanweisungen -> braucht 2
4. Strings/Format/Datum/Mathematik -> unabhaengig, jederzeit
5. Datei-E/A -> unabhaengig von 2/3
6. Altlasten Phase 2 -> unabhaengig
```
Schritt 2 ist der einzige Bruch und wird bewusst als eine Einheit
umgesetzt: Puffer-Umzug, `Host`-Neuschnitt und Snapshot-Umstellung hängen
so eng zusammen, dass ein Zwischenzustand die Suite ohnehin rot ließe.
Schritte 46 können vorgezogen werden, falls Schritt 2 sich zieht.
## Risks / Trade-offs
- **Der `Host`-Bruch legt kurzzeitig die gesamte Suite lahm** → Schritt 2
als eine Einheit umsetzen, nicht in Teilschritten; erst wenn Puffer,
Host und Harness zusammen stehen, wird gemessen.
- **Neu erzeugte Golden Files zementieren einen Fehler** → Der Diff der
acht Dateien wird einzeln geprüft. Erwartung: `PRINT`-only-Programme
ändern sich nicht. Jede Abweichung wird begründet oder ist ein Bug.
- **Doppelzellen brechen bestehende Annahmen** (`POS` = Zeichenzahl,
Zelle = Zeichen) → Korpustest mit CJK/Emoji inklusive Umbruch am
rechten Rand, bevor die übrige Bildschirmarbeit darauf aufsetzt.
- **`RND`-Kompatibilität ist Rekonstruktionsarbeit** — die Zahlenfolge
des Vorbilds ist nur über die Dokumentation und Beobachtung greifbar →
Algorithmus dokumentieren; gelingt die exakte Folge nicht, wird die
Abweichung in der Sprachreferenz festgehalten statt still
abzuweichen.
- **UTF-32-Records machen Dateien binär inkompatibel zum Vorbild** →
bewusst entschieden (2026-09-02); Preis ist, dass Originaldateien nicht
gelesen werden können. Muss in der Sprachreferenz stehen.
- **`SHELL`, `FILES`, Satzsperren und `LPRINT` sind plattformabhängig** →
jede Abweichung wird in der Sprachreferenz unter „Abweichungen"
benannt; stilles Abweichen ist laut Guiding Principle ein Bug.
- **Das Inventar aus der Original-Hilfe zu erheben ist der stillste
Aufwandstreiber** → als eigener erster Schritt geführt, damit sein
Umfang sichtbar wird, statt sich in die Implementierungsarbeit zu
mischen.
## Migration Plan
1. Inventar anlegen (rein additiv, kein Bruch).
2. Bruchschritt in einem Zug: `TextScreen` nach `tb-runtime::screen`,
`tb-ui` auf Adapterrolle, `Host` neu, Harness auf Snapshot, acht
Golden Files neu erzeugen und Diff prüfen.
3. Alles Weitere additiv — jedes neue Element wechselt im Inventar von
`offen` auf `implementiert`, der Abgleichstest hält beides zusammen.
Rücknahme: Schritt 2 ist ein zusammenhängender Commit und als Ganzes
rücknehmbar. Danach gibt es keinen weiteren Bruch.
## Open Questions
- Umfang der `KEY n`-Funktionstasten-Makros (`KEY LIST`/`ON`/`OFF`): Die
Anweisung steht im Scope; welche Makro-Semantik das Vorbild genau
zeigt, ist bei der Umsetzung aus der Original-Hilfe zu klären und in
docs/sprachreferenz.md festzuhalten. Berührt weder Puffer noch Host
noch Aufgabenschnitt.
- Ersatzwert für `FRE` auf einer Plattform ohne DOS-Speichermodell: bei
der Umsetzung zu wählen und zu dokumentieren.
- Konkrete Gestalt der Schnittstelle, über die die Forms-Engine in
`tb-ui` später eigene Zeichenbereiche im Puffer belegt und wieder
freigibt: erst in Phase 4 zu klären, wenn die Steuerelemente stehen.
Ändert weder Puffer noch Host noch Aufgabenschnitt dieses Changes.