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:
287
openspec/changes/phase-3-runtime-bildschirm/design.md
Normal file
287
openspec/changes/phase-3-runtime-bildschirm/design.md
Normal file
@@ -0,0 +1,287 @@
|
||||
## 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 4–6 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.
|
||||
Reference in New Issue
Block a user