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>
288 lines
14 KiB
Markdown
288 lines
14 KiB
Markdown
## 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.
|