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:
2
openspec/changes/phase-3-isam/.openspec.yaml
Normal file
2
openspec/changes/phase-3-isam/.openspec.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-03
|
||||
209
openspec/changes/phase-3-isam/design.md
Normal file
209
openspec/changes/phase-3-isam/design.md
Normal file
@@ -0,0 +1,209 @@
|
||||
## Context
|
||||
|
||||
Siehe proposal.md — Why. Technisch relevant ist der Zustand nach
|
||||
`phase-3-runtime-bildschirm`:
|
||||
|
||||
- Die Dateitabelle, die Recordpuffer und die UTF-32-Serialisierung fester
|
||||
Textfelder existieren (Capability `datei-eio`) und sind die Grundlage,
|
||||
auf der ISAM die Sätze ablegt.
|
||||
- Die Laufzeitfehler 81–89 sind in `tb_runtime::errors` bereits
|
||||
vorhanden; dieser Change liefert ihre Auslöser.
|
||||
- Vom ISAM-Sprachumfang existiert heute nur die Parser-Klausel
|
||||
`OPEN … FOR ISAM` (`parser.rs`). Die ISAM-Anweisungen haben weder
|
||||
Grammatik noch Signatur — sie enden in „unbekannter Bezeichner".
|
||||
- Das Sprachinventar (`docs/inventar.md`) führt die ISAM-Elemente mit
|
||||
Status `offen` und ist der maßgebliche Umfangsnachweis.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Der volle dokumentierte ISAM-Sprachumfang läuft, nicht eine Auswahl.
|
||||
- Die Cursor-Semantik des Vorbilds ist exakt abgebildet — sie ist der
|
||||
Teil, den keine Fremdbibliothek liefert.
|
||||
- Speicher, Indizes und Transaktionen kommen aus einer erprobten
|
||||
Bibliothek statt aus Eigenbau.
|
||||
- Phase 3 ist mit diesem Change abgeschlossen.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Leistungsoptimierung des Satzzugriffs — ISAM-Programme des Vorbilds
|
||||
arbeiten auf Datenmengen, die jede heutige Speicherschicht mühelos
|
||||
trägt.
|
||||
- Nebenläufiger Zugriff mehrerer Prozesse auf dieselbe Datenbankdatei
|
||||
über die Satzsperren aus `datei-eio` hinaus.
|
||||
- Lesen von Datenbankdateien des Vorbilds.
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1: `redb` als Speicherschicht
|
||||
|
||||
Die Datenbankdatei ist eine `redb`-Datei. Sätze und Indexeinträge liegen
|
||||
darin als Key-Value-Paare; `redb` liefert B-Baum, Transaktionen und
|
||||
Crash-Sicherheit.
|
||||
|
||||
*Warum nicht Eigenbau:* Seitenverwaltung, B-Baum und crash-sichere
|
||||
Transaktionen sind gelöste Probleme mit langen Fehlerkatalogen. Der
|
||||
projektspezifische Teil ist die ISAM-Semantik, nicht der Baum darunter.
|
||||
|
||||
*Warum nicht SQLite:* Zöge eine C-Bibliothek in jedes von `tbc build
|
||||
--exe` erzeugte Binary. Der eigentliche Grund ist aber semantisch: ISAM
|
||||
hat einen *stabilen, benannten Cursor*, der Änderungen überlebt und sich
|
||||
vorwärts wie rückwärts bewegt — ein SQL-Cursor ist ein
|
||||
Ergebnismengen-Iterator. Die Abbildung wäre aufwendiger als der Gewinn.
|
||||
Die für Stufe 2 geplante SQL-Erweiterung bleibt davon unberührt; sie ist
|
||||
eine eigene Sprachebene und kann später ihren eigenen Speicher wählen.
|
||||
|
||||
*Was `redb` nicht liefert und wir bauen:* Sekundärindizes,
|
||||
Savepoints innerhalb einer Transaktion, die Cursor-Semantik.
|
||||
|
||||
### D2: Datenmodell — Sätze und Indizes als getrennte Tabellen
|
||||
|
||||
Je Tabelle eine Satztabelle `satz-id → Satzbytes` und je Index eine
|
||||
Tabelle `Schlüsselbytes → satz-id`. Satz-IDs sind monoton vergebene
|
||||
Zahlen und werden nie wiederverwendet.
|
||||
|
||||
*Warum getrennt:* `SETINDEX` wechselt die Durchlaufordnung zur Laufzeit;
|
||||
mit Indextabellen ist das ein Wechsel der zu lesenden Tabelle, ohne die
|
||||
Sätze anzufassen. `MOVEPREVIOUS` und die `SEEK`-Familie sind dann
|
||||
Bereichsabfragen auf einer geordneten Tabelle.
|
||||
|
||||
*Warum IDs nicht wiederverwendet werden:* Der Cursor merkt sich eine
|
||||
Satz-ID. Würde sie nach `DELETE` neu vergeben, könnte ein Cursor still
|
||||
auf einen fremden Satz zeigen.
|
||||
|
||||
### D3: Schlüsselkodierung muss ordnungserhaltend sein
|
||||
|
||||
Indexschlüssel werden so kodiert, dass die Byte-Reihenfolge der Kodierung
|
||||
der fachlichen Reihenfolge entspricht — Zahlen mit vorzeichenrichtiger
|
||||
Kodierung fester Breite, Texte nach der in D4 festgelegten Ordnung,
|
||||
mehrspaltige Schlüssel als Verkettung mit eindeutigem Trenner.
|
||||
Absteigende Spalten werden durch Invertierung der Bytes abgebildet.
|
||||
|
||||
*Warum das die kritische Stelle ist:* `redb` ordnet nach Bytes. Jede
|
||||
Ordnungsverletzung in der Kodierung äußert sich als falsche
|
||||
Satzreihenfolge — ein Fehler, der erst bei bestimmten Datenwerten
|
||||
sichtbar wird. Ein Eigenschaftstest über zufällige Wertepaare
|
||||
(Kodierungsvergleich = fachlicher Vergleich) gehört deshalb zur
|
||||
Umsetzung.
|
||||
|
||||
*Mehrspaltige Präfixsuche* fällt aus dieser Kodierung ab: Suche mit
|
||||
weniger Werten als Spalten ist eine Bereichsabfrage über das
|
||||
Schlüsselpräfix.
|
||||
|
||||
### D4: Sortierordnung explizit festlegen
|
||||
|
||||
Texte werden nach Unicode-Codepoints geordnet, Groß- und Kleinschreibung
|
||||
unterschieden. Es ist dieselbe Ordnung, die der Vergleichsoperator des
|
||||
Dialekts auf Strings anwendet.
|
||||
|
||||
`TEXTCOMP` gibt es in der Original-Hilfe nicht (Befund aus Aufgabe 1.1,
|
||||
2026-09-03); die Ordnung braucht deshalb keine eigene Vergleichsfunktion,
|
||||
sondern muss nur mit dem Stringvergleich der Sprache übereinstimmen.
|
||||
|
||||
*Warum nicht sprachabhängige Sortierung:* Sie machte die Reihenfolge
|
||||
plattform- und locale-abhängig und damit die Korpustests unreproduzierbar.
|
||||
Das Vorbild ordnete nach Zeichencodes einer festen Codepage; Codepoints
|
||||
sind die direkte Entsprechung unter der bereits getroffenen
|
||||
Unicode-Entscheidung. Die Ordnung wird in docs/sprachreferenz.md
|
||||
festgehalten, weil sie beobachtbar ist.
|
||||
|
||||
### D5: Cursor als Wert, nicht als Bibliotheks-Iterator
|
||||
|
||||
Der Cursor je Dateinummer ist ein eigener Zustand: aktiver Index,
|
||||
zuletzt besuchte Satz-ID und ein Kennzeichen „unpositioniert". Jede
|
||||
Bewegung ist eine frische Bereichsabfrage ab dem gemerkten Schlüssel.
|
||||
|
||||
*Warum keine gehaltenen Iteratoren:* Der ISAM-Cursor überlebt
|
||||
Satzänderungen und Transaktionsgrenzen; ein gehaltener Iterator über eine
|
||||
Transaktion tut das nicht. Ein Cursor als Wert bildet außerdem
|
||||
„unpositioniert" (Fehler 85) direkt ab, statt es aus einem
|
||||
Iteratorzustand ableiten zu müssen.
|
||||
|
||||
*Preis:* Jede Bewegung kostet eine Suche statt eines Schritts. Bei den
|
||||
Datenmengen dieser Programme irrelevant; siehe Non-Goals.
|
||||
|
||||
### D6: Savepoints als Rücknahmeprotokoll
|
||||
|
||||
`BEGINTRANS` öffnet eine Schreibtransaktion. Innerhalb davon führt die
|
||||
Laufzeit ein Protokoll der Änderungen; die Funktion `SAVEPOINT` merkt
|
||||
sich eine Position darin und liefert deren Kennung, `ROLLBACK` mit dieser
|
||||
Kennung spielt das Protokoll bis dorthin rückwärts ab. `ROLLBACK ALL` und
|
||||
`COMMITTRANS` nutzen die Transaktion der Bibliothek direkt.
|
||||
|
||||
*Warum ein eigenes Protokoll:* Verschachtelte Rücknahmepunkte gehören
|
||||
nicht zum Funktionsumfang von `redb`. Das Protokoll ist der kleinste
|
||||
Weg dorthin und bleibt auf den Umfang einer laufenden Transaktion
|
||||
begrenzt.
|
||||
|
||||
### D7: Fehler 89 und `SETMEM` bekommen eine echte Grenze
|
||||
|
||||
`SETMEM` ist eine Funktion: sie ändert die Obergrenze des ISAM-Puffers um
|
||||
den übergebenen Betrag und liefert die danach verfügbare Größe.
|
||||
Überschreiten der Grenze löst Fehler 89 aus. Ohne Grenze wäre der Code nie
|
||||
erreichbar und die Anweisung eine Attrappe.
|
||||
|
||||
*Abweichung:* Die Bedeutung in Bytes entspricht nicht dem
|
||||
DOS-Speichermodell des Vorbilds und wird in docs/sprachreferenz.md
|
||||
dokumentiert.
|
||||
|
||||
### D8: Reihenfolge der Umsetzung
|
||||
|
||||
```
|
||||
1. Frontend: Grammatik + Signaturen -> Elemente werden benannt
|
||||
2. Speicher: Datei, Satztabelle, Schluesselkodierung (+ Eigenschaftstest)
|
||||
3. Indizes: CREATEINDEX/SETINDEX/GETINDEX$/DELETEINDEX
|
||||
4. Cursor: MOVE-Familie, SEEK-Familie, EOF
|
||||
5. Saetze: INSERT/RETRIEVE/UPDATE/DELETE, Eindeutigkeit
|
||||
6. Transaktionen: BEGINTRANS/COMMITTRANS/ROLLBACK[ ALL]/SAVEPOINT
|
||||
7. Rest: SETMEM, DELETETABLE, BOF, Formatversion
|
||||
```
|
||||
|
||||
Schritt 1 zuerst, weil er allein schon den Guiding-Principle-Verstoß
|
||||
behebt: ISAM-Programme werden ab da namentlich abgewiesen statt mit
|
||||
„unbekannter Bezeichner". Schritt 2 vor allem anderen, weil die
|
||||
Schlüsselkodierung jede spätere Ordnung bestimmt.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Fehlerhafte Schlüsselkodierung fällt erst bei bestimmten Daten auf**
|
||||
→ Eigenschaftstest über zufällige Wertepaare je Spaltentyp:
|
||||
Byte-Vergleich der Kodierung MUSS dem fachlichen Vergleich entsprechen.
|
||||
Läuft vor allem Weiteren.
|
||||
- **Der genaue ISAM-Sprachumfang stammt aus der Original-Hilfe und ist
|
||||
bislang nur aus zweiter Hand belegt** → Das Inventar aus
|
||||
`phase-3-runtime-bildschirm` ist die maßgebliche Liste; findet die
|
||||
Erhebung dort weitere Elemente, gehören sie in diesen Change. Die
|
||||
Aufzählung im Proposal ist ein Mindestumfang, keine Obergrenze.
|
||||
- **Cursorverhalten an den Rändern ist die häufigste Fehlerquelle**
|
||||
(`EOF` nach `MOVENEXT` am Ende, Cursor nach `DELETE`, nach `SETINDEX`,
|
||||
nach `ROLLBACK`) → je ein Korpustest pro Randfall, nicht ein
|
||||
Sammeltest.
|
||||
- **Neue Abhängigkeit in `tb-runtime`, die bisher keine hatte** →
|
||||
bewusst; die Alternative wäre ein eigener transaktionaler B-Baum.
|
||||
Die Abhängigkeit bleibt auf das ISAM-Modul beschränkt.
|
||||
- **Das Rücknahmeprotokoll kann bei langen Transaktionen wachsen** →
|
||||
begrenzt auf die laufende Transaktion; `COMMITTRANS` beendet sie. Falls
|
||||
eine Grenze nötig wird, ist Fehler 89 der dafür vorgesehene Ausgang.
|
||||
- **Eigenes Dateiformat heißt: keine Vorbild-Datenbanken lesbar** →
|
||||
bewusst entschieden, dieselbe Linie wie UTF-32-Records; steht in
|
||||
docs/dateiformate.md und in der Sprachreferenz.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Rein additiv — es gibt heute keine ISAM-Funktionalität und keine
|
||||
Datenbankdateien im Feld. Die einzige sichtbare Änderung an bestehendem
|
||||
Verhalten: `OPEN … FOR ISAM` löst nicht mehr Laufzeitfehler 73 aus,
|
||||
sondern arbeitet.
|
||||
|
||||
Die Datenbankdatei trägt von der ersten Fassung an eine Formatversion,
|
||||
damit spätere Änderungen abgewiesen statt fehlinterpretiert werden.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Genaue Argumentformen einzelner ISAM-Anweisungen (etwa die Schreibweise
|
||||
der Spaltenliste bei `CREATEINDEX`): bei der Umsetzung aus der
|
||||
Original-Hilfe zu übernehmen. Berührt weder Datenmodell noch
|
||||
Aufgabenschnitt.
|
||||
- Konkreter Vorgabewert der `SETMEM`-Grenze: bei der Umsetzung zu wählen
|
||||
und zu dokumentieren.
|
||||
104
openspec/changes/phase-3-isam/proposal.md
Normal file
104
openspec/changes/phase-3-isam/proposal.md
Normal file
@@ -0,0 +1,104 @@
|
||||
# Phase 3 (Abschluss) — ISAM-Datenbankunterstützung
|
||||
|
||||
## Why
|
||||
|
||||
Die Leitplanke „Vollständigkeit ist das Soll" und die Entscheidung vom
|
||||
2026-09-02 legen fest: ISAM wird implementiert, es ist kein Non-Feature.
|
||||
Heute existiert davon nur eine Parser-Klausel — `OPEN … FOR ISAM typname
|
||||
tabellenname AS #n` wird erkannt und auf den „nicht verfügbar"-Marker
|
||||
abgesenkt. Die eigentlichen ISAM-Anweisungen und -Funktionen
|
||||
(`CREATEINDEX`, `SETINDEX`, `INSERT`, `RETRIEVE`, `UPDATE`, `DELETE`, die
|
||||
`MOVE`- und `SEEK`-Familien, die Transaktionsanweisungen) besitzen im
|
||||
Frontend **keine Signatur**. Sie enden damit in „unbekannter Bezeichner"
|
||||
bzw. einem generischen Syntaxfehler — genau der Fall, den der Guiding
|
||||
Principle als Bug definiert: ein dokumentiertes Feature wird still und
|
||||
ohne Namensnennung abgewiesen.
|
||||
|
||||
Die Laufzeitfehler 81–89 des ISAM-Bereichs sind bereits als
|
||||
`tb_runtime::errors` implementiert und warten auf ihre Auslöser.
|
||||
|
||||
Dieser Change schließt Phase 3 ab. Der vorausgehende Change
|
||||
`phase-3-runtime-bildschirm` liefert die Datei-E/A-Grundlage
|
||||
(Dateitabelle, Recordpuffer, UTF-32-Festtextfelder), auf der ISAM
|
||||
aufsetzt, sowie das Inventar, in dem die ISAM-Elemente ihren Status
|
||||
führen.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **ISAM-Elemente erhalten Frontend-Signaturen**: Sämtliche in der
|
||||
Original-Hilfe dokumentierten ISAM-Anweisungen und -Funktionen werden
|
||||
in Grammatik und Signaturprüfung aufgenommen, damit ihre Verwendung
|
||||
namentlich diagnostiziert statt als unbekannter Bezeichner behandelt
|
||||
wird. Den maßgeblichen Umfang bestimmt das Sprachinventar aus
|
||||
`phase-3-runtime-bildschirm`; nach heutigem Stand umfasst er
|
||||
mindestens `CREATEINDEX`, `DELETEINDEX`, `SETINDEX`, `GETINDEX$`,
|
||||
`INSERT`, `RETRIEVE`, `UPDATE`, `DELETE`, `DELETETABLE`, `MOVEFIRST`,
|
||||
`MOVELAST`, `MOVENEXT`, `MOVEPREVIOUS`, `SEEKEQ`, `SEEKGT`, `SEEKGE`,
|
||||
`BEGINTRANS`, `COMMITTRANS`, `ROLLBACK`/`ROLLBACK ALL`, `SAVEPOINT`
|
||||
(Funktion), `SETMEM` (Funktion), `BOF` sowie das ISAM-Verhalten von
|
||||
`EOF`, `DELETE`, `LOF` und `LOC`.
|
||||
- **ISAM-Datenbanken und Tabellen**: `OPEN … FOR ISAM typname
|
||||
tabellenname AS #n` bindet eine Tabelle einer Datenbankdatei an eine
|
||||
Dateinummer; das Satzlayout ergibt sich aus dem benannten
|
||||
benutzerdefinierten Typ. Tabellen entstehen beim ersten Öffnen,
|
||||
`DELETETABLE` entfernt sie.
|
||||
- **Indizes**: benannte Indizes über eine oder mehrere Spalten, eindeutig
|
||||
oder mehrdeutig, mit auf- und absteigender Ordnung; `SETINDEX` wählt
|
||||
den aktiven Index und bestimmt damit die Satzreihenfolge, `GETINDEX$`
|
||||
liefert ihn zurück.
|
||||
- **Satzoperationen und Cursor**: `INSERT`, `RETRIEVE`, `UPDATE`,
|
||||
`DELETE` arbeiten auf dem aktuellen Satz; die `MOVE`-Familie bewegt den
|
||||
Cursor in der Ordnung des aktiven Index, die `SEEK`-Familie positioniert
|
||||
über Schlüsselvergleiche (gleich, größer, größer-gleich). `EOF` und
|
||||
`BOF` melden das Überlaufen des Cursors am Ende bzw. am Anfang.
|
||||
- **Transaktionen**: `BEGINTRANS`, `COMMITTRANS`, `ROLLBACK` und
|
||||
`ROLLBACK ALL` mit Rücknahme aller Änderungen seit Transaktionsbeginn
|
||||
bzw. seit einem Sicherungspunkt; `SAVEPOINT` ist eine **Funktion** und
|
||||
liefert die Kennung des gesetzten Sicherungspunkts.
|
||||
- **Vergleichsreihenfolge**: Die Sortierordnung der Indizes wird unter
|
||||
Unicode festgelegt und dokumentiert.
|
||||
- **ISAM-Fehlersemantik**: Die vorhandenen Fehlercodes 81–89 werden von
|
||||
den entsprechenden Situationen ausgelöst (ungültiger Name, Tabelle
|
||||
nicht gefunden, Index nicht gefunden, ungültige Spalte, kein aktueller
|
||||
Satz, Verstoß gegen einen eindeutigen Index, ungültige Operation auf
|
||||
NULL-Index, inkonsistente Datenbank, zu wenig Puffer).
|
||||
- **Eigenes Datenbankdateiformat**: Die Datenbankdatei ist bewusst nicht
|
||||
binärkompatibel zum Vorbild — dieselbe Linie wie bei den UTF-32-
|
||||
Records. Das Format wird in `docs/dateiformate.md` beschrieben.
|
||||
|
||||
**Non-Goals:** Ein Konvertierungswerkzeug für Datenbankdateien des
|
||||
Vorbilds; die eingebettete SQL-Erweiterung samt `WHILE SELECT`
|
||||
(Stufe 2, Ideenspeicher); Mehrprozess-Zugriff über
|
||||
Betriebssystemgrenzen hinweg jenseits der Satzsperren aus `datei-eio`.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `isam-datenbank`: indizierter Satzzugriff — Datenbank- und
|
||||
Tabellenbindung an Dateinummern, Indexverwaltung, Cursorbewegung und
|
||||
Schlüsselsuche, Satzoperationen, Transaktionen, Vergleichsreihenfolge
|
||||
und die ISAM-Fehlersemantik (Codes 81–89).
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `sprach-frontend`: Die Grammatik und die Signaturprüfung SHALL die
|
||||
ISAM-Anweisungen und -Funktionen umfassen, sodass ihre Verwendung
|
||||
namentlich diagnostiziert wird statt als unbekannter Bezeichner.
|
||||
|
||||
## Impact
|
||||
|
||||
- `crates/tb-frontend`: neue Anweisungsgrammatik und Signaturen für die
|
||||
ISAM-Elemente; die bisherige `Unsupported`-Absenkung von `OPEN … FOR
|
||||
ISAM` entfällt.
|
||||
- `crates/tb-runtime`: neues ISAM-Modul (Speicher, Indizes, Cursor,
|
||||
Transaktionen) auf der Dateitabelle und den Recordpuffern aus
|
||||
`phase-3-runtime-bildschirm`.
|
||||
- `crates/tb-vm`: neue Builtin-Indizes für die ISAM-Anweisungen.
|
||||
- `tests/compat`: neue Korpusprogramme für Indexordnung, Schlüsselsuche,
|
||||
Eindeutigkeitsverletzung, Cursorenden und Transaktionsrücknahme.
|
||||
- `docs/`: `docs/dateiformate.md` um das Datenbankdateiformat erweitert;
|
||||
`docs/bibliothek.md` um die ISAM-Elemente; `docs/inventar.md` wechselt
|
||||
deren Status auf `implementiert`; `docs/sprachreferenz.md` hält die
|
||||
Sortierordnung und die Inkompatibilität des Dateiformats fest.
|
||||
- PLAN.md: Phase 3 gilt mit diesem Change als abgeschlossen.
|
||||
187
openspec/changes/phase-3-isam/specs/isam-datenbank/spec.md
Normal file
187
openspec/changes/phase-3-isam/specs/isam-datenbank/spec.md
Normal file
@@ -0,0 +1,187 @@
|
||||
## Purpose
|
||||
Die ISAM-Unterstützung bietet indizierten Satzzugriff auf Tabellen einer
|
||||
Datenbankdatei: Sätze werden über benannte Indizes geordnet durchlaufen
|
||||
und über Schlüsselvergleiche gesucht, Änderungen laufen in Transaktionen
|
||||
und Verstöße melden die ISAM-Fehlercodes des Vorbilds.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Datenbank- und Tabellenbindung an eine Dateinummer
|
||||
`OPEN datenbank$ FOR ISAM typname tabellenname AS #n` SHALL eine Tabelle
|
||||
einer Datenbankdatei an eine Dateinummer binden. Das Satzlayout SHALL
|
||||
sich aus dem benannten benutzerdefinierten Typ ergeben; dessen Felder
|
||||
sind die Spalten der Tabelle. Existiert die Tabelle noch nicht, SHALL sie
|
||||
mit diesem Layout angelegt werden; existiert sie mit abweichendem Layout,
|
||||
MUST Laufzeitfehler 88 „ISAM - Database inconsistent" auftreten. Ein
|
||||
ungültiger Tabellen- oder Datenbankname MUST Fehler 81 „ISAM - Invalid
|
||||
name" auslösen. `CLOSE` SHALL die Bindung lösen und ausstehende
|
||||
Änderungen festschreiben. `DELETETABLE` SHALL eine Tabelle samt ihren
|
||||
Indizes entfernen; eine nicht vorhandene Tabelle MUST Fehler 82 „ISAM -
|
||||
Table not found" auslösen.
|
||||
|
||||
#### Scenario: Tabelle wird beim ersten Öffnen angelegt
|
||||
- **WHEN** eine Datenbank ohne die Tabelle `Kunden` mit `OPEN "db" FOR ISAM KundeTyp "Kunden" AS #1` geöffnet wird
|
||||
- **THEN** entsteht die Tabelle mit den Feldern des Typs und die Dateinummer ist nutzbar
|
||||
|
||||
#### Scenario: Abweichendes Satzlayout
|
||||
- **WHEN** eine bestehende Tabelle mit einem Typ geöffnet wird, dessen Felder nicht zu ihren Spalten passen
|
||||
- **THEN** tritt Laufzeitfehler 88 auf
|
||||
|
||||
#### Scenario: Entfernte Tabelle
|
||||
- **WHEN** `DELETETABLE` für eine nicht vorhandene Tabelle aufgerufen wird
|
||||
- **THEN** tritt Laufzeitfehler 82 auf
|
||||
|
||||
### Requirement: Indexverwaltung
|
||||
`CREATEINDEX` SHALL einen benannten Index über eine oder mehrere Spalten
|
||||
anlegen, wahlweise eindeutig oder mehrdeutig und je Spalte auf- oder
|
||||
absteigend geordnet. `DELETEINDEX` SHALL einen Index entfernen.
|
||||
`SETINDEX` SHALL den aktiven Index einer Dateinummer setzen und damit die
|
||||
Reihenfolge bestimmen, in der Sätze durchlaufen werden; `GETINDEX$` SHALL
|
||||
den Namen des aktiven Index liefern. Ein unbekannter Indexname MUST
|
||||
Laufzeitfehler 83 „ISAM - Index not found" auslösen, ein unbekannter
|
||||
Spaltenname Fehler 84 „ISAM - Invalid column". Nach `SETINDEX` MUST der
|
||||
Cursor als unpositioniert gelten.
|
||||
|
||||
#### Scenario: Aktiver Index bestimmt die Reihenfolge
|
||||
- **WHEN** ein Index über die Spalte `Name` gesetzt ist und die Sätze mit `MOVEFIRST` und `MOVENEXT` durchlaufen werden
|
||||
- **THEN** erscheinen sie in aufsteigender Reihenfolge der Spalte `Name`
|
||||
|
||||
#### Scenario: Unbekannter Index
|
||||
- **WHEN** `SETINDEX #1, "GibtsNicht"` ausgeführt wird
|
||||
- **THEN** tritt Laufzeitfehler 83 auf
|
||||
|
||||
#### Scenario: Index über unbekannte Spalte
|
||||
- **WHEN** `CREATEINDEX` eine Spalte nennt, die der Satztyp nicht führt
|
||||
- **THEN** tritt Laufzeitfehler 84 auf
|
||||
|
||||
### Requirement: Cursorbewegung in der Ordnung des aktiven Index
|
||||
`MOVEFIRST`, `MOVELAST`, `MOVENEXT` und `MOVEPREVIOUS` SHALL den Cursor
|
||||
in der Ordnung des aktiven Index bewegen. Läuft der Cursor über das Ende
|
||||
hinaus, SHALL `EOF` für diese Dateinummer wahr werden; läuft er über den
|
||||
Anfang hinaus, SHALL `BOF` wahr werden. In beiden Fällen SHALL der Cursor
|
||||
als unpositioniert gelten. Eine Bewegung ohne aktiven Index SHALL der
|
||||
Einfügereihenfolge folgen.
|
||||
|
||||
#### Scenario: Cursor läuft über das Ende hinaus
|
||||
- **WHEN** nach dem letzten Satz `MOVENEXT` ausgeführt wird
|
||||
- **THEN** liefert `EOF` für diese Dateinummer wahr
|
||||
|
||||
#### Scenario: Rückwärts über den Anfang hinaus
|
||||
- **WHEN** auf dem ersten Satz `MOVEPREVIOUS` ausgeführt wird
|
||||
- **THEN** liefert `BOF` für diese Dateinummer wahr
|
||||
|
||||
### Requirement: Schlüsselsuche
|
||||
`SEEKEQ`, `SEEKGT` und `SEEKGE` SHALL den Cursor auf den ersten Satz
|
||||
setzen, dessen Schlüssel im aktiven Index gleich, größer bzw.
|
||||
größer-gleich dem angegebenen Wert ist. Die Zahl der Schlüsselwerte MUST
|
||||
höchstens der Spaltenzahl des aktiven Index entsprechen; ein
|
||||
mehrspaltiger Index SHALL mit einem Präfix der Spalten durchsucht werden
|
||||
können. Findet die Suche keinen Satz, SHALL `EOF` wahr werden und der
|
||||
Cursor unpositioniert bleiben. Eine Suche ohne aktiven Index MUST
|
||||
Laufzeitfehler 83 auslösen.
|
||||
|
||||
#### Scenario: Suche nach größerem Schlüssel
|
||||
- **WHEN** Sätze mit den Schlüsseln 10, 20 und 30 vorliegen und `SEEKGT #1, 20` ausgeführt wird
|
||||
- **THEN** steht der Cursor auf dem Satz mit Schlüssel 30
|
||||
|
||||
#### Scenario: Erfolglose Suche
|
||||
- **WHEN** `SEEKGT #1, 30` bei größtem Schlüssel 30 ausgeführt wird
|
||||
- **THEN** liefert `EOF` wahr
|
||||
|
||||
#### Scenario: Suche über Präfix eines mehrspaltigen Index
|
||||
- **WHEN** der aktive Index über `Nachname` und `Vorname` geht und mit nur einem Wert gesucht wird
|
||||
- **THEN** positioniert die Suche auf dem ersten Satz mit passendem `Nachname`
|
||||
|
||||
### Requirement: Satzoperationen auf dem aktuellen Satz
|
||||
`INSERT` SHALL einen Satz aus einer Variablen des Tabellentyps einfügen,
|
||||
`RETRIEVE` den aktuellen Satz in eine solche Variable lesen, `UPDATE` ihn
|
||||
aus einer solchen Variablen überschreiben und `DELETE` ihn entfernen.
|
||||
`RETRIEVE`, `UPDATE` und `DELETE` ohne positionierten Cursor MUST
|
||||
Laufzeitfehler 85 „ISAM - No current record" auslösen. Ein `INSERT` oder
|
||||
`UPDATE`, das einen als eindeutig angelegten Index verletzen würde, MUST
|
||||
Laufzeitfehler 86 „ISAM - Duplicate value for unique index" auslösen und
|
||||
den Satzbestand unverändert lassen. Nach `DELETE` SHALL der Cursor als
|
||||
unpositioniert gelten. Alle Indizes der Tabelle SHALL nach jeder
|
||||
Satzoperation die neue Ordnung widerspiegeln.
|
||||
|
||||
#### Scenario: Einfügen und Zurücklesen
|
||||
- **WHEN** ein Satz eingefügt, per `SEEKEQ` gesucht und mit `RETRIEVE` gelesen wird
|
||||
- **THEN** stimmen alle Feldwerte mit den eingefügten überein
|
||||
|
||||
#### Scenario: Operation ohne aktuellen Satz
|
||||
- **WHEN** nach `SETINDEX` ohne Positionierung `RETRIEVE #1, k` ausgeführt wird
|
||||
- **THEN** tritt Laufzeitfehler 85 auf
|
||||
|
||||
#### Scenario: Verstoß gegen eindeutigen Index
|
||||
- **WHEN** ein Satz eingefügt wird, dessen Schlüssel in einem eindeutigen Index bereits vorkommt
|
||||
- **THEN** tritt Laufzeitfehler 86 auf und der Satzbestand bleibt unverändert
|
||||
|
||||
#### Scenario: Index folgt der Änderung
|
||||
- **WHEN** die indizierte Spalte eines Satzes per `UPDATE` geändert wird
|
||||
- **THEN** erscheint der Satz beim nächsten Durchlauf an seiner neuen Position in der Indexordnung
|
||||
|
||||
### Requirement: Transaktionen mit Rücknahme
|
||||
`BEGINTRANS` SHALL eine Transaktion eröffnen, `COMMITTRANS` alle
|
||||
Änderungen seit ihrem Beginn festschreiben und `ROLLBACK` sie
|
||||
zurücknehmen. `SAVEPOINT` SHALL als **Funktion** einen Sicherungspunkt
|
||||
setzen und dessen Kennung liefern; `ROLLBACK` mit einer solchen Kennung
|
||||
SHALL auf diesen Punkt zurücksetzen, ohne die Transaktion aufzugeben.
|
||||
`ROLLBACK ALL` SHALL alle Änderungen seit `BEGINTRANS` zurücknehmen und
|
||||
die Transaktion beenden. Nach jeder Rücknahme MUST der Satzbestand
|
||||
einschließlich aller Indizes dem Zustand zum Bezugspunkt entsprechen und
|
||||
der Cursor als unpositioniert gelten.
|
||||
|
||||
#### Scenario: Rücknahme stellt den Bestand wieder her
|
||||
- **WHEN** nach `BEGINTRANS` Sätze eingefügt und gelöscht werden und dann `ROLLBACK` folgt
|
||||
- **THEN** entspricht der Bestand beim erneuten Durchlauf dem Zustand vor `BEGINTRANS`
|
||||
|
||||
#### Scenario: Rücknahme auf einen Sicherungspunkt
|
||||
- **WHEN** innerhalb einer Transaktion `s = SAVEPOINT` ausgewertet, danach geändert und mit `ROLLBACK s` zurückgesetzt wird
|
||||
- **THEN** bleiben die Änderungen vor dem Sicherungspunkt erhalten und die danach sind zurückgenommen
|
||||
|
||||
#### Scenario: ROLLBACK ALL beendet die Transaktion
|
||||
- **WHEN** nach mehreren Sicherungspunkten `ROLLBACK ALL` ausgeführt wird
|
||||
- **THEN** entspricht der Bestand dem Zustand vor `BEGINTRANS` und die Transaktion ist beendet
|
||||
|
||||
### Requirement: Vergleichsreihenfolge für Indizes
|
||||
Textspalten SHALL in Indizes nach einer festgelegten, dokumentierten
|
||||
Sortierordnung geordnet werden. Diese Ordnung MUST dieselbe sein, die der
|
||||
Vergleichsoperator des Dialekts auf Strings anwendet, damit ein Programm
|
||||
die Satzreihenfolge vorhersagen kann. Die gewählte Ordnung MUST in
|
||||
docs/sprachreferenz.md festgehalten sein, einschließlich der Behandlung
|
||||
von Groß- und Kleinschreibung und von Zeichen jenseits von ASCII.
|
||||
|
||||
#### Scenario: Indexordnung entspricht dem Stringvergleich
|
||||
- **WHEN** zwei Texte als Schlüssel indiziert und dieselben Werte mit `<` verglichen werden
|
||||
- **THEN** entspricht ihre Reihenfolge im Index dem Ergebnis des Vergleichs
|
||||
|
||||
### Requirement: Pufferverwaltung mit benanntem Fehler
|
||||
`SETMEM` SHALL als **Funktion** die für ISAM verfügbare Puffergröße um
|
||||
den übergebenen Betrag ändern und die danach verfügbare Größe liefern.
|
||||
Reicht
|
||||
der verfügbare Puffer für eine Operation nicht aus, MUST Laufzeitfehler 89
|
||||
„ISAM - Insufficient ISAM buffers" auftreten statt eines generischen
|
||||
Fehlers oder eines stillen Abbruchs. Weicht die Bedeutung von `SETMEM`
|
||||
mangels DOS-Speichermodell vom Vorbild ab, MUST die Abweichung in
|
||||
docs/sprachreferenz.md dokumentiert sein.
|
||||
|
||||
#### Scenario: Pufferfehler wird benannt
|
||||
- **WHEN** eine Operation den verfügbaren ISAM-Puffer überschreitet
|
||||
- **THEN** tritt Laufzeitfehler 89 auf
|
||||
|
||||
### Requirement: Eigenes Datenbankdateiformat
|
||||
Die Datenbankdatei SHALL ein eigenes, in docs/dateiformate.md
|
||||
beschriebenes Format verwenden und ist bewusst nicht binärkompatibel zu
|
||||
Datenbankdateien des Vorbilds — dieselbe Linie wie bei den
|
||||
UTF-32-Festtextfeldern. Das Format SHALL eine Formatversion tragen; eine
|
||||
unbekannte Version MUST mit einem benannten Fehler abgewiesen werden
|
||||
statt fehlinterpretiert zu werden. Eine strukturell beschädigte Datei
|
||||
MUST Laufzeitfehler 88 auslösen.
|
||||
|
||||
#### Scenario: Unbekannte Formatversion
|
||||
- **WHEN** eine Datenbankdatei mit einer höheren Formatversion geöffnet wird
|
||||
- **THEN** wird sie mit einem benannten Fehler abgewiesen und nicht gelesen
|
||||
|
||||
#### Scenario: Beschädigte Datei
|
||||
- **WHEN** eine strukturell beschädigte Datenbankdatei geöffnet wird
|
||||
- **THEN** tritt Laufzeitfehler 88 auf
|
||||
27
openspec/changes/phase-3-isam/specs/sprach-frontend/spec.md
Normal file
27
openspec/changes/phase-3-isam/specs/sprach-frontend/spec.md
Normal file
@@ -0,0 +1,27 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: ISAM-Anweisungen und -Funktionen in Grammatik und Signaturprüfung
|
||||
Der Parser SHALL die ISAM-Anweisungen und -Funktionen des Vorbilds als
|
||||
eigene Konstrukte erkennen, und die Semantik SHALL für jede eine Signatur
|
||||
führen (Argumentanzahl und -typen). Der maßgebliche Umfang ergibt sich
|
||||
aus dem Sprachinventar; er umfasst mindestens `CREATEINDEX`,
|
||||
`DELETEINDEX`, `SETINDEX`, `GETINDEX$`, `INSERT`, `RETRIEVE`, `UPDATE`,
|
||||
`DELETE`, `DELETETABLE`, `MOVEFIRST`, `MOVELAST`, `MOVENEXT`,
|
||||
`MOVEPREVIOUS`, `SEEKEQ`, `SEEKGT`, `SEEKGE`, `BEGINTRANS`,
|
||||
`COMMITTRANS`, `ROLLBACK`, `ROLLBACK ALL`, `SAVEPOINT`, `SETMEM` und
|
||||
`BOF`. Eine fehlerhafte Verwendung MUST eine Diagnose erzeugen, die
|
||||
das Element namentlich nennt; ein ISAM-Element MUST NOT als unbekannter
|
||||
Bezeichner oder mit einem generischen Syntaxfehler abgewiesen werden
|
||||
(Guiding Principle).
|
||||
|
||||
#### Scenario: ISAM-Anweisung wird als solche erkannt
|
||||
- **WHEN** ein Modul `SETINDEX #1, "NachName"` enthält
|
||||
- **THEN** entsteht keine Diagnose „unbekannter Bezeichner" und die Anweisung wird als ISAM-Anweisung erfasst
|
||||
|
||||
#### Scenario: Falsche Argumentanzahl wird namentlich gemeldet
|
||||
- **WHEN** ein Modul `SEEKGT #1` ohne Schlüsselwert enthält
|
||||
- **THEN** nennt die Diagnose `SEEKGT` und die erwartete Argumentanzahl
|
||||
|
||||
#### Scenario: Typprüfung des Satzarguments
|
||||
- **WHEN** `RETRIEVE #1, x$` mit einem String statt einer Variablen des Tabellentyps geprüft wird
|
||||
- **THEN** wird „Type mismatch" mit Quelltextposition gemeldet
|
||||
78
openspec/changes/phase-3-isam/tasks.md
Normal file
78
openspec/changes/phase-3-isam/tasks.md
Normal file
@@ -0,0 +1,78 @@
|
||||
## 1. Umfang festlegen
|
||||
|
||||
- [ ] 1.1 ISAM-Abschnitt der Original-Hilfe auswerten und alle Anweisungen, Funktionen und Klauseln gegen die ISAM-Einträge in `docs/inventar.md` abgleichen; verifiziert dadurch, dass jedes gefundene Element im Inventar mit Status `offen` steht und im Change notiert ist, falls es über die Aufzählung im Proposal hinausgeht
|
||||
- [ ] 1.2 Argumentformen je Element aus der Original-Hilfe festhalten (insbesondere die Schreibweise der Spaltenliste bei `CREATEINDEX`); verifiziert durch die Notiz im Change, die jede Signatur mit ihrer Quelle nennt
|
||||
|
||||
## 2. Frontend: Grammatik und Signaturen
|
||||
|
||||
- [ ] 2.1 ISAM-Anweisungen in die Parser-Grammatik aufnehmen; verifiziert durch Frontend-Test, dass `SETINDEX #1, "Name"` diagnose-frei parst
|
||||
- [ ] 2.2 Signaturen (Argumentanzahl und -typen) für alle ISAM-Elemente in der Semantik führen; verifiziert durch Frontend-Test, dass `SEEKGT #1` ohne Schlüsselwert eine Diagnose erzeugt, die `SEEKGT` namentlich nennt
|
||||
- [ ] 2.3 Satzargumente gegen den Tabellentyp prüfen; verifiziert durch Frontend-Test, dass `RETRIEVE #1, x$` „Type mismatch" meldet
|
||||
- [ ] 2.4 `Unsupported`-Absenkung von `OPEN … FOR ISAM` entfernen und durch die reguläre Absenkung ersetzen; verifiziert dadurch, dass ein ISAM-Programm nicht mehr mit Laufzeitfehler 73 endet
|
||||
- [ ] 2.5 Prüfen, dass kein ISAM-Element mehr als unbekannter Bezeichner behandelt wird; verifiziert durch Frontend-Test über ein Programm, das jedes Element einmal verwendet, ohne Diagnose „unbekannter Bezeichner"
|
||||
|
||||
## 3. Speicherschicht
|
||||
|
||||
- [ ] 3.1 `redb` als Abhängigkeit aufnehmen, auf das ISAM-Modul in `tb-runtime` beschränkt; verifiziert dadurch, dass `cargo build -p tb-runtime` durchläuft
|
||||
- [ ] 3.2 Datenbankdatei mit Formatversion anlegen und öffnen; verifiziert durch Test, dass eine Datei mit höherer Formatversion mit benanntem Fehler abgewiesen und eine strukturell beschädigte Datei mit Laufzeitfehler 88 abgelehnt wird
|
||||
- [ ] 3.3 Satztabelle `satz-id → Satzbytes` mit monoton vergebenen, nie wiederverwendeten IDs; verifiziert durch Test, dass eine nach `DELETE` neu vergebene ID nicht vorkommt
|
||||
- [ ] 3.4 Satzserialisierung auf den Recordpuffern und der UTF-32-Festtextkodierung aus `datei-eio` aufsetzen; verifiziert durch Roundtrip-Test über alle Feldtypen des Satztyps
|
||||
- [ ] 3.5 Ordnungserhaltende Schlüsselkodierung je Spaltentyp (Zahlen vorzeichenrichtig fester Breite, Text nach Codepoints, mehrspaltig verkettet mit eindeutigem Trenner, absteigend durch Byte-Invertierung); verifiziert durch Eigenschaftstest über zufällige Wertepaare, dass der Byte-Vergleich der Kodierung dem fachlichen Vergleich entspricht
|
||||
- [ ] 3.6 Sortierordnung (Unicode-Codepoints, Groß-/Kleinschreibung unterschieden) in docs/sprachreferenz.md festhalten; verifiziert durch den Abschnittseintrag
|
||||
- [ ] 3.7 Datenbankdateiformat in docs/dateiformate.md beschreiben inklusive Formatversion und bewusster Inkompatibilität zum Vorbild; verifiziert durch den Abschnittseintrag
|
||||
|
||||
## 4. Tabellen
|
||||
|
||||
- [ ] 4.1 `OPEN … FOR ISAM typname tabellenname AS #n` an die Dateitabelle binden, Satzlayout aus dem benannten Typ ableiten; verifiziert durch Korpustest, dass die Dateinummer danach nutzbar ist
|
||||
- [ ] 4.2 Tabelle beim ersten Öffnen anlegen; verifiziert durch Korpustest gegen eine Datenbank ohne diese Tabelle
|
||||
- [ ] 4.3 Layoutabgleich bei bestehender Tabelle mit Fehler 88 bei Abweichung; verifiziert durch Korpustest mit unpassendem Typ
|
||||
- [ ] 4.4 Ungültige Datenbank- und Tabellennamen mit Fehler 81 abweisen; verifiziert durch Korpustest
|
||||
- [ ] 4.5 `CLOSE` löst die Bindung und schreibt ausstehende Änderungen fest; verifiziert durch Korpustest, dass die Sätze nach erneutem Öffnen vorhanden sind
|
||||
- [ ] 4.6 `DELETETABLE` entfernt Tabelle samt Indizes, Fehler 82 bei fehlender Tabelle; verifiziert durch Korpustest für beide Fälle
|
||||
|
||||
## 5. Indizes
|
||||
|
||||
- [ ] 5.1 Indextabellen `Schlüsselbytes → satz-id` je Index anlegen; verifiziert durch Test, dass die Einträge in Schlüsselreihenfolge stehen
|
||||
- [ ] 5.2 `CREATEINDEX` mit einer und mit mehreren Spalten, eindeutig und mehrdeutig, auf- und absteigend; verifiziert durch Korpustest je Variante
|
||||
- [ ] 5.3 `DELETEINDEX`; verifiziert durch Korpustest, dass der Index danach nicht mehr gesetzt werden kann
|
||||
- [ ] 5.4 `SETINDEX` setzt den aktiven Index und den Cursor auf unpositioniert, `GETINDEX$` liefert ihn zurück; verifiziert durch Korpustest
|
||||
- [ ] 5.5 Fehler 83 bei unbekanntem Index und Fehler 84 bei unbekannter Spalte; verifiziert durch Korpustest für beide Fälle
|
||||
|
||||
## 6. Cursor
|
||||
|
||||
- [ ] 6.1 Cursor je Dateinummer als Wert führen (aktiver Index, letzte Satz-ID, Kennzeichen unpositioniert); verifiziert durch Unit-Test, dass der Cursor eine Satzänderung überlebt
|
||||
- [ ] 6.2 `MOVEFIRST`, `MOVELAST`, `MOVENEXT`, `MOVEPREVIOUS` in der Ordnung des aktiven Index; verifiziert durch Korpustest, dass ein Durchlauf die Sätze in Indexreihenfolge liefert
|
||||
- [ ] 6.3 Bewegung ohne aktiven Index folgt der Einfügereihenfolge; verifiziert durch Korpustest
|
||||
- [ ] 6.4 `EOF` am Ende und `BOF` am Anfang, Cursor danach jeweils unpositioniert; verifiziert durch je einen Korpustest für `MOVENEXT` am Ende und `MOVEPREVIOUS` am Anfang
|
||||
- [ ] 6.5 `SEEKEQ`, `SEEKGT`, `SEEKGE` inklusive Präfixsuche über einen mehrspaltigen Index; verifiziert durch Korpustest mit den Schlüsseln 10, 20, 30 und einer Präfixsuche
|
||||
- [ ] 6.6 Erfolglose Suche setzt `EOF` und lässt den Cursor unpositioniert; verifiziert durch Korpustest
|
||||
- [ ] 6.7 Suche ohne aktiven Index löst Fehler 83 aus; verifiziert durch Korpustest
|
||||
|
||||
## 7. Satzoperationen
|
||||
|
||||
- [ ] 7.1 `INSERT` und `RETRIEVE`; verifiziert durch Korpustest, dass ein eingefügter Satz per `SEEKEQ` gefunden und feldgleich gelesen wird
|
||||
- [ ] 7.2 `UPDATE` und `DELETE`, Cursor nach `DELETE` unpositioniert; verifiziert durch Korpustest
|
||||
- [ ] 7.3 Fehler 85 bei `RETRIEVE`/`UPDATE`/`DELETE` ohne positionierten Cursor; verifiziert durch je einen Korpustest
|
||||
- [ ] 7.4 Eindeutigkeitsprüfung mit Fehler 86 und unverändertem Satzbestand; verifiziert durch Korpustest, der nach dem Fehler den Bestand nachzählt
|
||||
- [ ] 7.5 Alle Indizes nach jeder Satzoperation nachführen; verifiziert durch Korpustest, dass ein Satz nach Änderung der indizierten Spalte an neuer Position im Durchlauf erscheint
|
||||
|
||||
## 8. Transaktionen
|
||||
|
||||
- [ ] 8.1 `BEGINTRANS`/`COMMITTRANS` auf einer Schreibtransaktion; verifiziert durch Korpustest, dass die Änderungen nach erneutem Öffnen vorhanden sind
|
||||
- [ ] 8.2 Rücknahmeprotokoll der Änderungen innerhalb einer Transaktion; verifiziert durch Unit-Test, dass jede Satzoperation einen Protokolleintrag erzeugt
|
||||
- [ ] 8.3 `ROLLBACK ALL` stellt Sätze und Indizes wieder her, beendet die Transaktion und setzt den Cursor auf unpositioniert; verifiziert durch Korpustest mit Einfügen, Löschen und anschließendem Durchlauf
|
||||
- [ ] 8.4 `SAVEPOINT` als Funktion (liefert Kennung) und `ROLLBACK kennung`; verifiziert durch Korpustest, dass Änderungen vor dem Punkt erhalten bleiben
|
||||
|
||||
## 9. Übrige Elemente
|
||||
|
||||
- [ ] 9.2 `SETMEM` als Funktion mit echter Puffergrenze und Fehler 89 beim Überschreiten; verifiziert durch Korpustest, der die Grenze niedrig setzt und den Fehler auslöst
|
||||
- [ ] 9.3 Vorgabewert und Bedeutung von `SETMEM` sowie die Abweichung vom DOS-Speichermodell in docs/sprachreferenz.md festhalten; verifiziert durch den Abschnittseintrag
|
||||
- [ ] 9.4 Indexordnung gegen den Stringvergleich des Dialekts prüfen; verifiziert durch Korpustest, dass Indexreihenfolge und `<`-Vergleich übereinstimmen
|
||||
|
||||
## 10. Abschluss
|
||||
|
||||
- [ ] 10.1 ISAM-Einträge in `docs/inventar.md` auf `implementiert` setzen; verifiziert dadurch, dass der Abgleichstest aus `phase-3-runtime-bildschirm` grün bleibt
|
||||
- [ ] 10.2 `docs/bibliothek.md` um die ISAM-Elemente erweitern; verifiziert dadurch, dass jedes umgesetzte Element dort einen Eintrag hat
|
||||
- [ ] 10.3 Korpus-ISAM-Programme in einem temporären Arbeitsverzeichnis ausführen; verifiziert dadurch, dass nach `cargo test` `git status` sauber ist
|
||||
- [ ] 10.4 Vollständige Suite auf allen Zielplattformen ausführen; verifiziert dadurch, dass `cargo test --workspace` grün ist
|
||||
- [ ] 10.5 PLAN.md Phase 3 als abgeschlossen markieren; verifiziert durch den aktualisierten Planabschnitt
|
||||
2
openspec/changes/phase-3-ortszeit/.openspec.yaml
Normal file
2
openspec/changes/phase-3-ortszeit/.openspec.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-04
|
||||
180
openspec/changes/phase-3-ortszeit/design.md
Normal file
180
openspec/changes/phase-3-ortszeit/design.md
Normal file
@@ -0,0 +1,180 @@
|
||||
## Context
|
||||
|
||||
Siehe proposal.md — Why. Der Ist-Zustand ist eng umrissen:
|
||||
|
||||
- `tb-runtime::datetime` rechnet vollständig selbst: Kalenderumrechnung
|
||||
(Howard Hinnant), serielle Datumswerte ab dem 30.12.1899, `WEEKDAY`,
|
||||
Textformen. Diese Rechnung ist getestet und bleibt.
|
||||
- Der einzige Punkt, an dem die Zeitzone eine Rolle spielt, ist
|
||||
`jetzt_sekunden()` — Sekunden seit der Unix-Epoche. Alles Weitere
|
||||
(`jetzt_serial`, `DATE$`, `TIME$`, `TIMER`, `NOW`) baut darauf auf.
|
||||
- `tb-runtime` hat bislang eine einzige Abhängigkeit mit fachlichem
|
||||
Gewicht: `unicode-width`.
|
||||
|
||||
Damit ist die Aufgabe klein: **eine Zahl** — der UTC-Versatz zu einem
|
||||
Zeitpunkt — kommt künftig von außen.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Zeitfunktionen liefern das, was die Uhr des Rechners zeigt.
|
||||
- Sommerzeit wird zum jeweiligen Zeitpunkt berücksichtigt.
|
||||
- Die Berührungsfläche zur Bibliothek bleibt eine Funktion, damit sie
|
||||
austauschbar ist und die getestete Kalenderrechnung unberührt bleibt.
|
||||
- Zeitverhalten bleibt automatisiert prüfbar, ohne von der Zone des
|
||||
Testrechners abzuhängen.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Zeitzonenbehaftete Datentypen im Dialekt — das Vorbild kennt nur
|
||||
Wanduhrzeit.
|
||||
- Eine Sprachanbindung an die Bibliothek (Zonennamen, Umrechnung zwischen
|
||||
Zonen). Das ist der Stufe-2-Eintrag im PLAN, nicht dieser Change.
|
||||
- Historische Zonendaten jenseits dessen, was die Systemdatenbank
|
||||
hergibt.
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1: `jiff` als Zeitzonenbibliothek
|
||||
|
||||
*Gewählt:* `jiff` — sie liest die Zeitzonendatenbank des Systems und
|
||||
liefert den Versatz zu einem Zeitpunkt ohne Einschränkung.
|
||||
|
||||
*Alternativen:*
|
||||
|
||||
| Bibliothek | Bewertung |
|
||||
|---|---|
|
||||
| `jiff` | Für genau diese Frage gebaut; Systemzonendatenbank; keine Sicherheitsauflage. **Gewählt.** |
|
||||
| `chrono` (≥ 0.4.20) | Gleichwertig sicher, seit 0.4.20 ohne `localtime_r` (Zonenermittlung über `iana-time-zone`, Datenbank über einen `tz-rs`-Zweig). Etablierter, bringt aber Serde-/Ökosystem-Anbindung mit, die hier niemand braucht. Tragfähige zweite Wahl. |
|
||||
| `time` | Der lokale Versatz ist auf Unix nur nach ausdrücklichem Opt-in in möglicherweise undefiniertes Verhalten verfügbar (`set_soundness`). **Verworfen** — das ist kein Preis für eine Uhrzeit. |
|
||||
| Eigenbau (`iana-time-zone` + TZif-Parser) | Hieße, die Zonendatenbank selbst zu lesen und dauerhaft nachzupflegen. Genau das, wofür man eine Bibliothek nimmt. **Verworfen.** |
|
||||
|
||||
*Warum die Wahl leicht revidierbar bleibt:* Die Bibliothek wird an genau
|
||||
einer Stelle aufgerufen (D2). Ein Wechsel zu `chrono` wäre ein Austausch
|
||||
dieser einen Funktion.
|
||||
|
||||
### D2: Berührungsfläche ist eine Funktion
|
||||
|
||||
```
|
||||
tb-runtime::datetime
|
||||
|
||||
utc_versatz(zeitpunkt: i64) -> Option<i32> // Sekunden, None = Zone unbekannt
|
||||
└── einziger Aufruf der Bibliothek
|
||||
|
||||
jetzt_sekunden() ─┐
|
||||
jetzt_serial() ├── rechnen weiter selbst
|
||||
civil_from_days() │
|
||||
weekday() … ─┘
|
||||
```
|
||||
|
||||
Die Kalenderrechnung sieht die Bibliothek nie. Sie bekommt nach wie vor
|
||||
Sekunden seit der Epoche — nur eben um den Versatz verschobene.
|
||||
|
||||
*Warum nicht die Bibliothek durchreichen:* Ihre Datums-/Zeittypen wären
|
||||
ein zweites Kalendermodell neben dem des Dialekts (der bei 1899 zählt,
|
||||
`WEEKDAY` = 1 für Sonntag, CURRENCY-nahe Serienwerte). Zwei Modelle
|
||||
nebeneinander wären eine dauerhafte Fehlerquelle; die vorhandene Rechnung
|
||||
ist getestet und bleibt maßgeblich.
|
||||
|
||||
### D3: Der Versatz gilt je Zeitpunkt, nicht je Programmlauf
|
||||
|
||||
`utc_versatz` bekommt den Zeitpunkt als Argument. Ein Programm, das über
|
||||
eine Zeitumstellung hinweg läuft, folgt ihr; ein Programm, das mit
|
||||
`DATE$ =` in einen anderen Teil des Jahres springt, bekommt den dort
|
||||
gültigen Versatz.
|
||||
|
||||
*Preis:* Eine Zonenabfrage je Zeitabfrage. Bei den Aufrufmengen eines
|
||||
Terminalprogramms belanglos; die Bibliothek hält die Zonendaten selbst
|
||||
vor.
|
||||
|
||||
### D4: Zonenquelle ist umschaltbar — damit Tests nicht von der Maschine abhängen
|
||||
|
||||
```rust
|
||||
pub enum Zeitzone {
|
||||
/// Zone des Rechners (Vorgabe).
|
||||
System,
|
||||
/// Fester Versatz in Sekunden — für Tests und den Korpus.
|
||||
Fest(i32),
|
||||
/// Zone nicht ermittelbar; es gilt UTC.
|
||||
Unbekannt,
|
||||
}
|
||||
```
|
||||
|
||||
`RtState` hält eine `Zeitzone`. Die Vorgabe ist `System`; schlägt die
|
||||
Ermittlung fehl, wird daraus `Unbekannt`. Tests und Korpusprogramme
|
||||
setzen `Fest(n)`.
|
||||
|
||||
*Warum nicht `TZ` in den Tests setzen:* Umgebungsvariablen sind
|
||||
prozessweit, Tests laufen nebenläufig, und `set_var` ist seit Rust 2024
|
||||
`unsafe`. Ein Zustand im `RtState` ist der offensichtliche Weg — er ist
|
||||
genau so weit sichtbar wie nötig.
|
||||
|
||||
*Korpus:* Das Harness bekommt eine Kopfzeilen-Direktive analog zu
|
||||
`' tb-screen:` und `' tb-tempdir`:
|
||||
|
||||
```text
|
||||
' tb-tz: +02:00
|
||||
```
|
||||
|
||||
Damit bleibt die Sollausgabe eines Zeitprogramms unabhängig davon, wo der
|
||||
Testrechner steht.
|
||||
|
||||
### D5: Rückfallebene ist sichtbar, nicht still
|
||||
|
||||
Ohne ermittelbare Zone gilt UTC — ein Abbruch wäre unverhältnismäßig, ein
|
||||
stiller Wechsel aber ein Verstoß gegen den Guiding Principle. Die Laufzeit
|
||||
führt den Zustand mit, und der Zustand ist abfragbar; die Sprachreferenz
|
||||
beschreibt ihn.
|
||||
|
||||
*Wie ein Programm es erfährt:* Über die vorhandene Systemabfrage
|
||||
`ENVIRON$` wäre es geraten. Stattdessen bekommt die Laufzeit einen
|
||||
benannten Zustand, den der Runner beim Start auf die Fehlerausgabe
|
||||
schreibt, wenn er `Unbekannt` ist — einmal, nicht je Abfrage. Ein
|
||||
Programm, das es programmatisch braucht, bekommt es in Stufe 2 über die
|
||||
Bibliotheksanbindung.
|
||||
|
||||
### D6: Reihenfolge der Umsetzung
|
||||
|
||||
```
|
||||
1. Abhaengigkeit + utc_versatz() -> isoliert testbar
|
||||
2. jetzt_sekunden/jetzt_serial umstellen -> DATE$/TIME$/TIMER/NOW folgen
|
||||
3. Zeitzone im RtState + Korpusdirektive -> Tests werden maschinenunabhaengig
|
||||
4. Doku: Abweichung zurueckziehen, PLAN Stufe 2
|
||||
```
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Erste schwere Abhängigkeit der Laufzeit** → bewusst entschieden
|
||||
(2026-09-04). Die Berührungsfläche ist eine Funktion (D2); ein Wechsel
|
||||
der Bibliothek bleibt ein lokaler Eingriff.
|
||||
- **Zeitabhängige Tests werden flatterig** → deshalb D4: kein Test und
|
||||
kein Korpusprogramm fragt die echte Zone ab. Was die Systemzone
|
||||
betrifft, wird über `Fest(n)` und `Unbekannt` geprüft.
|
||||
- **Zeitumstellung erzeugt mehrdeutige Wanduhrzeiten** (Rückstellung) und
|
||||
nicht existierende (Vorstellung) → betrifft nur die Richtung
|
||||
Wanduhr → Zeitpunkt. Die Zeitfunktionen dieses Dialekts gehen die
|
||||
andere Richtung (Zeitpunkt → Wanduhr) und sind davon nicht betroffen.
|
||||
`DATEVALUE`/`TIMEVALUE` lesen Wanduhrzeit und rechnen gar nicht um.
|
||||
`DATE$ =`/`TIME$ =` setzen einen Versatz gegen die aktuelle Wanduhrzeit
|
||||
— auch das ist eindeutig.
|
||||
- **Die Systemzonendatenbank kann fehlen** (schlanke Container) → D5.
|
||||
- **Bauzeit und Binärgröße wachsen** → für ein Werkzeug, dessen Programme
|
||||
Uhrzeiten anzeigen, ist eine falsche Uhrzeit der größere Preis.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Rein verhaltensändernd, ohne Formatbruch: `.tbc`-Dateien, Korpus-Golden-
|
||||
Files und Schnittstellen bleiben unberührt. Das einzige beobachtbare
|
||||
Verhalten, das sich ändert, ist der Wert von `DATE$`, `TIME$`, `TIMER`
|
||||
und `NOW` — und zwar zum Richtigen hin.
|
||||
|
||||
Rücknahme: Ein Zurücksetzen auf UTC wäre `Zeitzone::Unbekannt` als
|
||||
Vorgabe; die Abhängigkeit bliebe ungenutzt.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Ob die Bibliotheksanbindung in Stufe 2 (Zonennamen, Umrechnung zwischen
|
||||
Zonen als BASIC-Funktionen) auf derselben Bibliothek aufsetzt oder eine
|
||||
eigene Wahl trifft, entscheidet Stufe 2. Für diesen Change ohne
|
||||
Wirkung — hier wird nur der Versatz gebraucht.
|
||||
76
openspec/changes/phase-3-ortszeit/proposal.md
Normal file
76
openspec/changes/phase-3-ortszeit/proposal.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Ortszeit statt UTC
|
||||
|
||||
## Why
|
||||
|
||||
`DATE$`, `TIME$`, `TIMER` und `NOW` liefern heute koordinierte Weltzeit.
|
||||
Das ist keine Rekonstruktion des Vorbilds, sondern eine Notlösung: die
|
||||
Standardbibliothek von Rust kennt keine Zeitzonen, und in
|
||||
`phase-3-runtime-bildschirm` sollte die erste schwere Abhängigkeit der
|
||||
Laufzeit nicht nebenbei entstehen. Die Abweichung steht in
|
||||
docs/sprachreferenz.md.
|
||||
|
||||
Für ein BASIC zum Bauen von Terminal-Werkzeugen ist das ein echter
|
||||
Mangel: Ein Programm, das eine Uhr anzeigt, ein Protokoll schreibt oder
|
||||
einen Termin prüft, zeigt schlicht die falsche Zeit — je nach Zone um
|
||||
Stunden daneben. Das Vorbild lief auf der Ortszeit des Rechners; jedes
|
||||
übernommene Programm rechnet damit.
|
||||
|
||||
Der Projektinhaber hat am 2026-09-04 entschieden, dafür eine Bibliothek
|
||||
in die Laufzeit aufzunehmen. Dieser Change zieht die Abweichung zurück.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Zeitfunktionen liefern Ortszeit**: `DATE$`, `TIME$`, `TIMER` und
|
||||
`NOW` rechnen in der Zeitzone des Rechners statt in UTC. `TIMER` zählt
|
||||
Sekunden seit der **lokalen** Mitternacht.
|
||||
- **Sommerzeit wird berücksichtigt**: Der Versatz wird für den jeweiligen
|
||||
Zeitpunkt bestimmt, nicht einmalig beim Programmstart — ein Programm,
|
||||
das über einen Zeitumstellungspunkt hinweg läuft, folgt ihm.
|
||||
- **Neue Abhängigkeit in `tb-runtime`**: eine Zeitzonenbibliothek, deren
|
||||
Berührungsfläche bewusst auf eine einzige Frage beschränkt bleibt —
|
||||
„welcher UTC-Versatz gilt zu diesem Zeitpunkt?". Die Kalenderrechnung
|
||||
(serielle Datumswerte, `WEEKDAY`, Zerlegung) bleibt die vorhandene,
|
||||
getestete Eigenimplementierung in `tb-runtime::datetime`.
|
||||
- **Rückfallebene**: Lässt sich die Zeitzone des Rechners nicht
|
||||
ermitteln, gilt UTC. Das Programm läuft weiter; die Rückfallebene ist
|
||||
über eine Funktion der Laufzeit erkennbar und dokumentiert.
|
||||
- **`DATE$ =` / `TIME$ =` bleiben ein Programmversatz** (sie stellen
|
||||
nicht die Systemuhr) — sie wirken künftig auf die Ortszeit.
|
||||
- **Abweichung zurückziehen**: Der Eintrag „Uhr läuft in UTC" entfällt
|
||||
aus docs/sprachreferenz.md; an seine Stelle tritt die Beschreibung der
|
||||
Zonenermittlung samt Rückfallebene.
|
||||
- **Stufe 2 im PLAN**: Die Zeitzonenbibliothek wird in den Ideenspeicher
|
||||
„Runtime-Bibliotheken als BASIC-Bibliotheken" aufgenommen — neben
|
||||
crossterm —, damit BASIC-Programme später direkt auf Zonen zugreifen
|
||||
können (Zonennamen, Versatz zu einem Zeitpunkt, Umrechnung zwischen
|
||||
Zonen).
|
||||
|
||||
**Non-Goals:** Zeitzonenbehaftete Datentypen im Dialekt (das Vorbild
|
||||
kennt nur lokale Wanduhrzeit); eine Sprachanbindung an die Bibliothek
|
||||
(das ist der Stufe-2-Eintrag, nicht dieser Change); Änderungen an der
|
||||
Zählweise serieller Datumswerte oder an `DATEVALUE`/`TIMEVALUE`, die
|
||||
Wanduhrzeit lesen und daher keine Umrechnung brauchen.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `konsolen-basisbibliothek`: Die Datums- und Zeitfunktionen SHALL die
|
||||
Ortszeit des Rechners liefern statt UTC, einschließlich Sommerzeit und
|
||||
einer dokumentierten Rückfallebene.
|
||||
|
||||
## Impact
|
||||
|
||||
- `crates/tb-runtime/src/datetime.rs`: Versatzermittlung über die neue
|
||||
Bibliothek; `jetzt_serial`/`jetzt_sekunden` liefern Ortszeit. Die
|
||||
Kalenderrechnung bleibt unverändert.
|
||||
- `crates/tb-runtime/src/builtins.rs`: `DATE$`, `TIME$`, `TIMER`, `NOW`
|
||||
rechnen über die neue Funktion.
|
||||
- `Cargo.toml` (Workspace) und `crates/tb-runtime/Cargo.toml`: neue
|
||||
Abhängigkeit.
|
||||
- `tests/compat`: Korpusprogramm für die Zeitfunktionen, das ohne
|
||||
Abhängigkeit von der Zone des Testrechners prüfbar bleibt.
|
||||
- `docs/sprachreferenz.md`: Abweichung „Uhr läuft in UTC" entfällt;
|
||||
Zonenermittlung und Rückfallebene beschrieben.
|
||||
- `docs/bibliothek.md`: Abschnitt Datum/Zeit fortgeschrieben.
|
||||
- `PLAN.md`: Stufe-2-Eintrag um die Bibliothek erweitert.
|
||||
@@ -0,0 +1,58 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Zeitfunktionen liefern Ortszeit
|
||||
`DATE$`, `TIME$`, `TIMER` und `NOW` SHALL den Zeitpunkt in der Zeitzone
|
||||
des Rechners liefern, nicht in koordinierter Weltzeit. `TIMER` SHALL die
|
||||
Sekunden seit der **lokalen** Mitternacht zählen. Der maßgebliche Versatz
|
||||
SHALL für den jeweils abgefragten Zeitpunkt bestimmt werden, nicht einmal
|
||||
beim Programmstart — ein Programm, das über eine Zeitumstellung hinweg
|
||||
läuft, folgt ihr. Die Zerlegungsfunktionen (`DAY`, `MONTH`, `YEAR`,
|
||||
`WEEKDAY`, `HOUR`, `MINUTE`, `SECOND`) arbeiten unverändert auf seriellen
|
||||
Werten und rechnen selbst nicht um.
|
||||
|
||||
#### Scenario: Datum und Uhrzeit stimmen mit der Systemuhr überein
|
||||
- **WHEN** ein Programm `DATE$` und `TIME$` abfragt
|
||||
- **THEN** entsprechen sie der Ortszeit des Rechners zum Abfragezeitpunkt
|
||||
|
||||
#### Scenario: TIMER zählt ab lokaler Mitternacht
|
||||
- **WHEN** `TIMER` abgefragt wird
|
||||
- **THEN** entspricht der Wert den Sekunden seit der letzten lokalen Mitternacht, nicht seit der UTC-Mitternacht
|
||||
|
||||
#### Scenario: NOW und die Zerlegungsfunktionen passen zusammen
|
||||
- **WHEN** `n# = NOW` gebildet und `HOUR(n#)` ausgewertet wird
|
||||
- **THEN** liefert `HOUR(n#)` dieselbe Stunde, die auch `TIME$` nennt
|
||||
|
||||
#### Scenario: Sommerzeit wirkt zum jeweiligen Zeitpunkt
|
||||
- **WHEN** in einer Zone mit Sommerzeit ein Zeitpunkt vor und einer nach der Umstellung abgefragt wird
|
||||
- **THEN** unterscheiden sich die beiden Versätze um die Umstellungsspanne
|
||||
|
||||
### Requirement: Zonenermittlung mit Rückfallebene
|
||||
Die Laufzeit SHALL die Zeitzone des Rechners über die
|
||||
Betriebssystemeinstellung bestimmen. Lässt sie sich nicht ermitteln,
|
||||
SHALL UTC gelten und das Programm ohne Fehler weiterlaufen. Ob die
|
||||
Rückfallebene greift, MUST für ein Programm erkennbar sein und in
|
||||
docs/sprachreferenz.md beschrieben stehen; ein stiller Wechsel auf UTC
|
||||
MUST NOT vorkommen.
|
||||
|
||||
#### Scenario: Zone nicht ermittelbar
|
||||
- **WHEN** die Zeitzone des Rechners nicht bestimmt werden kann
|
||||
- **THEN** rechnen die Zeitfunktionen in UTC und das Programm läuft ohne Fehler weiter
|
||||
|
||||
#### Scenario: Rückfallebene ist erkennbar
|
||||
- **WHEN** ein Programm den Zonenzustand abfragt
|
||||
- **THEN** unterscheidet die Antwort erkennbar zwischen ermittelter Zone und Rückfall auf UTC
|
||||
|
||||
### Requirement: Programmversatz wirkt auf die Ortszeit
|
||||
`DATE$ = text$` und `TIME$ = text$` SHALL weiterhin nicht die Systemuhr
|
||||
stellen, sondern einen programmeigenen Versatz setzen. Dieser Versatz
|
||||
SHALL auf die **Ortszeit** wirken: nach `TIME$ = "10:20:30"` nennt
|
||||
`TIME$` diese Uhrzeit, und `NOW`, `HOUR` und `TIMER` stimmen damit
|
||||
überein.
|
||||
|
||||
#### Scenario: Gesetzte Uhrzeit wird zurückgelesen
|
||||
- **WHEN** `TIME$ = "10:20:30"` ausgeführt und danach `TIME$` abgefragt wird
|
||||
- **THEN** nennt `TIME$` `10:20:30`
|
||||
|
||||
#### Scenario: Versatz wirkt auf alle Zeitfunktionen
|
||||
- **WHEN** `DATE$ = "01-02-1993"` gesetzt und danach `YEAR(NOW)` ausgewertet wird
|
||||
- **THEN** liefert `YEAR(NOW)` 1993
|
||||
35
openspec/changes/phase-3-ortszeit/tasks.md
Normal file
35
openspec/changes/phase-3-ortszeit/tasks.md
Normal file
@@ -0,0 +1,35 @@
|
||||
## 1. Versatzermittlung
|
||||
|
||||
- [ ] 1.1 `jiff` als Workspace-Abhängigkeit aufnehmen und in `tb-runtime` einbinden; verifiziert dadurch, dass `cargo build -p tb-runtime` durchläuft und `cargo tree -p tb-runtime` die Bibliothek genau einmal zeigt
|
||||
- [ ] 1.2 `datetime::utc_versatz(zeitpunkt: i64) -> Option<i32>` als einzigen Aufrufpunkt der Bibliothek schreiben (Sekunden Versatz, `None` = Zone nicht ermittelbar); verifiziert durch Unit-Test, dass der Versatz für einen festen Zeitpunkt im gültigen Bereich −50400…50400 liegt
|
||||
- [ ] 1.3 Sommerzeit prüfen: für dieselbe Zone einen Zeitpunkt im Januar und einen im Juli abfragen; verifiziert durch Unit-Test, dass sich die Versätze in einer Zone mit Sommerzeit unterscheiden und in einer ohne gleich bleiben
|
||||
- [ ] 1.4 Sicherstellen, dass die Kalenderrechnung unberührt bleibt; verifiziert dadurch, dass `datetime.rs` außerhalb von `utc_versatz` keinen Bibliotheksaufruf enthält und die bestehenden `datetime`-Tests unverändert grün sind
|
||||
|
||||
## 2. Zonenzustand in der Laufzeit
|
||||
|
||||
- [ ] 2.1 `Zeitzone { System, Fest(i32), Unbekannt }` einführen und in `RtState` halten; Vorgabe `System`, bei fehlgeschlagener Ermittlung `Unbekannt`; verifiziert durch Unit-Test je Variante
|
||||
- [ ] 2.2 `jetzt_sekunden`/`jetzt_serial` auf den Zonenzustand umstellen; verifiziert durch Unit-Test, dass `Fest(3600)` genau eine Stunde gegenüber `Unbekannt` verschiebt
|
||||
- [ ] 2.3 `DATE$`, `TIME$`, `TIMER` und `NOW` über den Zonenzustand rechnen; verifiziert durch Test, dass `HOUR(NOW)` und die Stunde aus `TIME$` bei `Fest(n)` übereinstimmen
|
||||
- [ ] 2.4 `TIMER` zählt ab lokaler Mitternacht; verifiziert durch Test, dass sich `TIMER` bei `Fest(3600)` gegenüber `Unbekannt` um 3600 verschiebt (modulo Tageslänge)
|
||||
- [ ] 2.5 `DATE$ =` / `TIME$ =` wirken auf die Ortszeit; verifiziert durch Test, dass `TIME$` nach `TIME$ = "10:20:30"` genau diesen Wert nennt und `HOUR(NOW)` 10 liefert
|
||||
|
||||
## 3. Rückfallebene
|
||||
|
||||
- [ ] 3.1 Bei nicht ermittelbarer Zone auf UTC zurückfallen, ohne Fehler; verifiziert durch Test mit `Zeitzone::Unbekannt`, dass alle Zeitfunktionen Werte liefern
|
||||
- [ ] 3.2 Den Rückfall beim Programmstart einmalig auf die Fehlerausgabe melden (nicht je Abfrage); verifiziert dadurch, dass `tbc run` bei `Unbekannt` genau eine Meldung schreibt und die Standardausgabe unberührt bleibt
|
||||
- [ ] 3.3 Rückfallebene in docs/sprachreferenz.md beschreiben; verifiziert durch den Abschnittseintrag
|
||||
|
||||
## 4. Prüfbarkeit ohne Abhängigkeit von der Testmaschine
|
||||
|
||||
- [ ] 4.1 Kopfzeilen-Direktive `' tb-tz: ±HH:MM` im Korpus-Harness auswerten und als `Zeitzone::Fest` setzen; verifiziert dadurch, dass ein Korpusprogramm ohne die Direktive weiterhin läuft und eines mit ihr den gesetzten Versatz verwendet
|
||||
- [ ] 4.2 Korpusprogramm `zeitzone.bas` mit festem Versatz: `DATE$`/`TIME$`/`TIMER`/`NOW` gegen eine per `DATE$ =`/`TIME$ =` gesetzte Uhrzeit prüfen; verifiziert dadurch, dass die Sollausgabe unabhängig von der Zone des Testrechners stimmt
|
||||
- [ ] 4.3 Bestehendes `datumzeit.bas` auf die Direktive umstellen, damit es auch nach der Umstellung deterministisch bleibt; verifiziert dadurch, dass seine Sollausgabe unverändert bleibt
|
||||
|
||||
## 5. Abschluss
|
||||
|
||||
- [ ] 5.1 Abweichung „Uhr läuft in UTC" aus docs/sprachreferenz.md entfernen und durch die Beschreibung der Zonenermittlung ersetzen; verifiziert dadurch, dass der Abweichungspunkt nicht mehr auftaucht
|
||||
- [ ] 5.2 `docs/bibliothek.md`, Abschnitt Datum/Zeit, fortschreiben; verifiziert durch den geänderten Abschnitt
|
||||
- [ ] 5.3 PLAN.md Stufe 2, Eintrag „Runtime-Bibliotheken als BASIC-Bibliotheken", um die Zeitzonenbibliothek erweitern (Zonennamen, Versatz zu einem Zeitpunkt, Umrechnung zwischen Zonen) — neben crossterm; verifiziert durch den ergänzten Eintrag
|
||||
- [ ] 5.4 PLAN.md Phase 3: den Hinweis auf die UTC-Abweichung durch den Verweis auf diesen Change ersetzen; verifiziert durch den aktualisierten Abschnitt
|
||||
- [ ] 5.5 Inventar unverändert lassen und den Abgleichstest prüfen; verifiziert dadurch, dass `cargo test -p tb-frontend --test inventar` grün bleibt (die Elemente waren bereits implementiert, nur ihr Zeitbezug ändert sich)
|
||||
- [ ] 5.6 Vollständige Suite ausführen; verifiziert dadurch, dass `cargo test --workspace` grün ist
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-03
|
||||
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.
|
||||
186
openspec/changes/phase-3-runtime-bildschirm/proposal.md
Normal file
186
openspec/changes/phase-3-runtime-bildschirm/proposal.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# Phase 3 (Kern) — Laufzeitbibliothek, Bildschirm und Datei-E/A
|
||||
|
||||
## Why
|
||||
|
||||
Phase 2 hat eine lauffähige VM geliefert, aber nur eine dünne
|
||||
Bibliotheksscheibe: rund 50 dokumentierte Anweisungen und Funktionen sind
|
||||
im Frontend zwar typgeprüft, senken aber auf `HStmtKind::Unsupported`/
|
||||
`HExpr::Unsupported` ab und enden zur Laufzeit in Fehler 73. Betroffen
|
||||
sind alle Bildschirmanweisungen (`CLS`, `LOCATE`, `COLOR`, `VIEW PRINT`,
|
||||
`CSRLIN`, `POS`, `INKEY$`), die komplette Datei-E/A, `PRINT USING`,
|
||||
die Datums-/Zeitfunktionen und die System-Anweisungen. Klassische
|
||||
Konsolenprogramme des Vorbilds laufen damit nicht.
|
||||
|
||||
Zusätzlich fehlt das Messinstrument: die Leitplanke „Vollständigkeit ist
|
||||
das Soll" fordert ein Inventar aller Anweisungen/Funktionen mit
|
||||
Abdeckungsstatus als Abnahmekriterium für Phase 6 — bislang existiert nur
|
||||
die Implementierungsseite (die `Unsupported`-Marker), nicht die Sollseite
|
||||
aus der Original-Hilfe.
|
||||
|
||||
Dieser Change löst den Plan-Meilenstein „klassische Konsolenprogramme
|
||||
laufen unverändert" ein. ISAM ist als eigener Change `phase-3-isam`
|
||||
ausgegliedert und schließt Phase 3 ab.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Vollständigkeits-Inventar** (`docs/inventar.md`): Tabelle aller
|
||||
Anweisungen, Funktionen und Metabefehle des Vorbilds aus der
|
||||
Original-Hilfe mit Status `implementiert` / `offen` / `Non-Feature`
|
||||
und Fundstelle. Ein automatischer Test gleicht die Tabelle gegen
|
||||
`builtin_fn`/`builtin_stmt` und die `Unsupported`-Marker im Code ab;
|
||||
Divergenz schlägt fehl. Damit misst sich die Abdeckung selbst.
|
||||
- **Bildschirmpuffer wandert nach `tb-runtime`** (Entscheidung
|
||||
2026-09-03, Explore-Session): `Cell`/`TextScreen` mit der kompletten
|
||||
Bildschirmsemantik (Cursor, Farbattribute, Scrollen, `VIEW PRINT`,
|
||||
`resize`) ziehen ratatui-frei von `tb-ui::screen` nach
|
||||
`tb-runtime::screen`. In `tb-ui` bleiben nur die Ratatui-Anbindung
|
||||
(`basic_color`, `impl Widget`) und künftig der Terminal-Adapter.
|
||||
Begründung: `PRINT`, `LOCATE`, `CLS` und `SCREEN()` sind Elemente der
|
||||
Laufzeitbibliothek und müssen den Puffer erreichen; zugleich bleiben
|
||||
`tb-vm` und `tb-runtime` damit frei von einer Terminal-Abhängigkeit,
|
||||
sodass jedes Bildschirmverhalten ohne Terminal prüfbar ist. Die
|
||||
Forms-Engine bleibt laut PLAN.md in `tb-ui::forms` (Entscheidung
|
||||
2026-09-03) und zeichnet von dort in denselben Puffer — `tb-ui` hängt
|
||||
künftig an `tb-runtime`, nicht umgekehrt.
|
||||
- **BREAKING: `Host`-Trait neu geschnitten**: statt `write(&str)` /
|
||||
`read_line()` künftig Präsentation eines Zellenpuffers und
|
||||
Ereignisabfrage. `write`/`read_line` können den Bildschirmzustand
|
||||
nicht transportieren (`LOCATE`, `COLOR`, `CSRLIN`, `SCREEN(r,c)` liest
|
||||
vom Bildschirm zurück). Betrifft `ConsoleHost`, `CaptureHost` und
|
||||
`tbc run`.
|
||||
- **BREAKING: Korpusvergleich auf Bildschirm-Snapshot** (getrimmt):
|
||||
`.out`-Dateien halten künftig das Textbild des Puffers bis zur letzten
|
||||
belegten Zelle statt eines Byte-Streams; eine Attributebene kommt nur
|
||||
hinzu, wenn das Programm `COLOR` verwendet. Die acht bestehenden
|
||||
Golden Files werden neu erzeugt. Ein Byte-Stream kann `LOCATE`/`COLOR`
|
||||
nicht abbilden.
|
||||
- **Bildschirmanweisungen und -funktionen**: `CLS`, `COLOR`, `LOCATE`,
|
||||
`WIDTH`, `SCREEN` (Anweisung und Funktion), `VIEW PRINT`, `CSRLIN`,
|
||||
`POS`, `INKEY$`, `INPUT$`, `TAB`, `SPC`, `KEY` (inkl. Klärung des
|
||||
Umfangs der Funktionstasten-Makros `KEY LIST`/`ON`/`OFF`).
|
||||
- **80×25 ist nirgends eine feste Grenze** (Vorgabe des Projektinhabers
|
||||
2026-09-03, Priorität für das ganze Programm): 80×25 steht stets als
|
||||
Synonym für „voller Bildschirm". Kein beobachtbares Verhalten darf eine
|
||||
feste Spalten- oder Zeilenzahl voraussetzen — `CLS`, der
|
||||
voreingestellte Scrollbereich, Umbruch- und Scrollgrenzen sowie die
|
||||
Bereichsprüfungen von `LOCATE` und `SCREEN()` folgen den aktuellen
|
||||
Abmessungen. Größenänderungen während der Ausführung werden als
|
||||
Ereignis zugestellt und angepasst (Inhalt oben links erhalten, Cursor
|
||||
und `VIEW PRINT`-Bereich geklemmt). Das Mindestmaß 80×25 bleibt
|
||||
ausschließlich eine untere Schranke der Darstellung.
|
||||
- **Breite Unicode-Zeichen belegen zwei Zellen** (Entscheidung
|
||||
2026-09-02): Zellenmodell, Cursorfortschritt, `POS`/`LOCATE` und
|
||||
Umbruch am rechten Rand berücksichtigen die Darstellungsbreite
|
||||
(unicode-width). Ersetzt die bisherige Regel „1 Zeichen = 1 Zelle".
|
||||
- **Vollständige String- und Zahlenbibliothek**: die restlichen
|
||||
dokumentierten Stringfunktionen sowie `PRINT USING` und `FORMAT$`
|
||||
vollständig, inklusive Klärung des `%`-Präfixes bei Feldüberlauf.
|
||||
- **Datums- und Zeitfunktionen**: `NOW`, `DATESERIAL`, `TIMESERIAL`,
|
||||
`DATEVALUE`, `TIMEVALUE`, `DAY`, `MONTH`, `YEAR`, `WEEKDAY`, `HOUR`,
|
||||
`MINUTE`, `SECOND` sowie `DATE$`/`TIME$` als Anweisungsform.
|
||||
- **Finanzmathematik** (Befund aus Aufgabe 1.1, 2026-09-03 — im
|
||||
ursprünglichen Umfang übersehen): `FV#`, `PV#`, `Pmt#`, `IPmt#`,
|
||||
`PPmt#`, `NPer#`, `Rate#`, `NPV#`, `IRR#`, `MIRR#`, `SLN#`, `SYD#`,
|
||||
`DDB#`.
|
||||
- **Record-Konvertierungsfunktionen** (ebenfalls Befund 1.1):
|
||||
`MKI$`/`MKL$`/`MKS$`/`MKD$`/`MKC$` und `CVI`/`CVL`/`CVS`/`CVD`/`CVC`
|
||||
sowie die MBF-Varianten `MKSMBF$`/`MKDMBF$`/`CVSMBF`/`CVDMBF` — sie
|
||||
gehören zum `FIELD`-basierten wahlfreien Zugriff.
|
||||
- **Weitere Befunde aus 1.1**: `CURDIR$`, `DIR$`, `CHDRIVE`,
|
||||
`SHELL` als Funktion, `LPOS`, `STACK` (Anweisung und Funktion),
|
||||
`SYSTEM`, `ERDEV`/`ERDEV$`, `SetFormatCC`.
|
||||
- **Signaturen für alle Inventarelemente**: Die Semantik führt für jedes
|
||||
im Inventar geführte Element eine Signatur, auch wenn das
|
||||
Laufzeitverhalten noch aussteht — sonst enden dokumentierte Elemente
|
||||
in „unbekannter Bezeichner" statt in einer namentlichen Ablehnung.
|
||||
- **Mathematik vervollständigen**: kompatibler `RND`/`RANDOMIZE`-PRNG
|
||||
(gleiche Zahlenfolge wie das Vorbild bei gleichem Startwert),
|
||||
Rundungsverhalten, Integer-Überlauf → Fehler 6.
|
||||
- **Datei-E/A**: `OPEN` in beiden Syntaxen (sequenziell, random, binär),
|
||||
`CLOSE`, `RESET`, `PRINT #`, `WRITE #`, `INPUT #`, `LINE INPUT #`,
|
||||
`GET`, `PUT`, `FIELD`, `LSET`, `RSET`, `SEEK` (Anweisung und
|
||||
Funktion), `EOF`, `LOF`, `LOC`, `FREEFILE`, `FILEATTR`, `LOCK`/
|
||||
`UNLOCK`, `NAME … AS`, `LPRINT`. Feste Strings in Records als UTF-32
|
||||
(Entscheidung 2026-09-02, 4 Bytes/Zeichen, feste Recordlänge).
|
||||
- **System-Anweisungen**: `FILES`, `KILL`, `CHDIR`, `MKDIR`, `RMDIR`,
|
||||
`SHELL`, `ENVIRON`/`ENVIRON$`, `CLEAR`, `FRE`, `TRON`/`TROFF`,
|
||||
plattformübergreifende Pfadsemantik.
|
||||
- **Drei Altlasten aus Phase 2**:
|
||||
- `ON ERROR GOTO`-Scoping: ein Handler-Label auf Modulebene ist auch
|
||||
aus Prozeduren heraus ansprechbar; heute wird nicht-lokales
|
||||
`ON ERROR` in einer Prozedur fälschlich wie `ON LOCAL ERROR`
|
||||
behandelt (`sema.rs`, `lower_stmt`/`OnError`).
|
||||
- DATA-Rohtext: unquotierte `DATA`-Elemente verlieren die Groß-/
|
||||
Kleinschreibung, weil der Lexer Bezeichner normalisiert.
|
||||
- `TODO verify`-Zellen der Konvertierungsmatrix in
|
||||
`docs/tbvm-design.md` gegen die Original-Hilfe auflösen
|
||||
(CURRENCY-Promotion mit Gleitkomma, `^`-Ergebnistyp,
|
||||
Exponentialform-Schwellen, CURRENCY-Ausgabeformat).
|
||||
|
||||
**Non-Goals:** ISAM (eigener Change `phase-3-isam`, schließt Phase 3 ab);
|
||||
Forms-Engine, Ereignisschleife und Ereignissteuerung (`TIMER/KEY(n) ON|
|
||||
OFF|STOP`, `ON SIGNAL`/`SIGNAL`, `SETUEVENT`, `MSGBOX`, `INPUTBOX$` —
|
||||
Phase 4); IDE und
|
||||
Debugger (Phase 5); `tbc build --exe` (Phase 6). Die Forms-Engine bleibt
|
||||
laut PLAN.md in `tb-ui::forms` (Entscheidung 2026-09-03); dieser Change
|
||||
schafft ihr mit dem Puffer in `tb-runtime` nur die Zeichenfläche.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `datei-eio`: Dateiverwaltung und Datenübertragung — `OPEN`/`CLOSE`,
|
||||
die drei Zugriffsarten (sequenziell, random, binär), Record-Layout mit
|
||||
UTF-32-Festtextfeldern, `FIELD`/`LSET`/`RSET`, Positions- und
|
||||
Statusfunktionen (`EOF`/`LOF`/`LOC`/`SEEK`/`FREEFILE`/`FILEATTR`),
|
||||
Sperren, Dateisystem-Anweisungen und plattformübergreifende
|
||||
Pfadsemantik.
|
||||
- `sprachinventar`: das Vollständigkeits-Inventar als prüfbares
|
||||
Artefakt — Aufbau der Tabelle, Statusvokabular, Pflicht zur
|
||||
Vollständigkeit gegenüber der Original-Hilfe und der automatische
|
||||
Abgleich gegen den Code.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `textbildschirm`: Der Zellenpuffer SHALL in `tb-runtime` liegen und
|
||||
ratatui-frei sein; das Unicode-Zellenmodell wechselt von „1 Zeichen =
|
||||
1 Zelle" auf Darstellungsbreite (breite Zeichen belegen zwei Zellen);
|
||||
neue Anforderung für die Bildschirmanweisungen des Dialekts auf dem
|
||||
Puffer.
|
||||
- `konsolen-basisbibliothek`: Der `Host`-Trait wechselt vom Stream- auf
|
||||
ein Bildschirmmodell (Präsentation + Ereignisabfrage); die
|
||||
Funktionsliste wird von der Korpusscheibe auf die vollständige
|
||||
String-, Zahlen-, Formatierungs- (`PRINT USING`) und Datums-/
|
||||
Zeitbibliothek erweitert; `RND` erhält eine Kompatibilitätsanforderung.
|
||||
- `kompat-testkorpus`: Der Laufzeitvergleich wechselt vom Byte-Stream
|
||||
auf einen getrimmten Bildschirm-Snapshot; neue Abdeckungsanforderung
|
||||
für Bildschirm-, Formatierungs- und Datei-E/A-Semantik.
|
||||
- `sprach-frontend`: `ON ERROR GOTO` auf Modulebene SHALL aus
|
||||
Prozeduren heraus ansprechbar sein (bisher fälschlich lokal);
|
||||
unquotierte `DATA`-Elemente SHALL ihren Rohtext behalten.
|
||||
|
||||
## Impact
|
||||
|
||||
- `crates/tb-ui`: `screen.rs` verliert Puffer und Semantik an
|
||||
`tb-runtime`; zurück bleiben `basic_color` und `impl Widget` als
|
||||
Adapter auf den Runtime-Puffer. Neue Abhängigkeit `tb-runtime`
|
||||
(bisher waren beide Blätter im Abhängigkeitsgraphen); `forms.rs`
|
||||
bleibt als Platzhalter für Phase 4 unberührt.
|
||||
- `crates/tb-runtime`: neues `screen`-Modul; `host.rs` neu geschnitten
|
||||
(**BREAKING**); `strings`, `format`, `math`, `datetime`, `fileio`,
|
||||
`builtins` von Stub/Scheibe auf Vollimplementierung; neue
|
||||
Abhängigkeit `unicode-width`.
|
||||
- `crates/tb-frontend`: rund 50 `Unsupported`-Marker weichen echter
|
||||
Absenkung; rund 30 bislang signaturlose Elemente (Finanzmathematik,
|
||||
`MK…$`/`CV…`, `CURDIR$`, `DIR$`, `CHDRIVE`, `LPOS`, `STACK`, `SYSTEM`,
|
||||
`ERDEV`/`ERDEV$`, `SetFormatCC`) erhalten Signaturen; `sema.rs` (`ON ERROR`-Scoping), `lexer.rs` (DATA-Rohtext).
|
||||
- `crates/tb-vm`: neue Builtin-Indizes; `Instr::Unsupported` bleibt für
|
||||
die verbleibenden Phase-4-Marker bestehen.
|
||||
- `crates/tb-cli`: `tbc run` auf den neuen `Host`; Harness auf
|
||||
Snapshot-Vergleich (**BREAKING** für die acht bestehenden `.out`).
|
||||
- `tests/compat`: acht Golden Files neu erzeugt, neue Korpusdateien für
|
||||
Bildschirm, `PRINT USING`, Datei-E/A, Datum/Zeit, breite Zeichen.
|
||||
- `docs/`: neu `docs/inventar.md` und `docs/bibliothek.md`;
|
||||
fortgeschrieben `docs/sprachreferenz.md` (Doppelzellen, `KEY n`,
|
||||
`PRINT USING`-Überlauf) und `docs/tbvm-design.md`
|
||||
(Konvertierungsmatrix). PLAN.md-Haken folgen bei Abschluss.
|
||||
@@ -0,0 +1,295 @@
|
||||
# Rohliste — Themen der Original-Hilfe (Aufgabe 1.1)
|
||||
|
||||
**Quelle:** `bas7advr.hlp` (BASIC 7 Advisor, Sprachreferenz der
|
||||
Professional Edition), Themenliste unter
|
||||
https://dos-help.soulsphere.org/bas7advr.hlp/TOPIC_LIST.html
|
||||
(abgerufen 2026-09-03).
|
||||
|
||||
**Nicht verwendet:** `vbdpss.hlp` ist eine Knowledge-Base mit
|
||||
Artikelnummern und Support-Adressen, keine Sprachreferenz — geprüft und
|
||||
verworfen.
|
||||
|
||||
Die Liste ist wortgetreu übernommen. Sie enthält neben Sprachthemen auch
|
||||
Unterseiten (`SCREEN Mode …`, `DRAW Statement Macro Language`,
|
||||
`PLAY Statement (Music) — …`) und einen Anker (`@L8000`); diese sind
|
||||
keine eigenen Sprachelemente und gehen nicht als Inventareintrag ein.
|
||||
|
||||
## Themen (wortgetreu)
|
||||
|
||||
$INCLUDE Metacommand
|
||||
$STATIC and $DYNAMIC Metacommands
|
||||
@L8000
|
||||
ABS Function
|
||||
ASC Function
|
||||
ATN Function
|
||||
Absolute Routine
|
||||
BEGINTRANS Statement
|
||||
BLOAD Statement
|
||||
BOF Function
|
||||
BSAVE Statement
|
||||
CALL Statement (BASIC Procedures)
|
||||
CALL, CALLS Statements (Non-BASIC Procedures)
|
||||
CCUR Function
|
||||
CDBL Function
|
||||
CHAIN Statement
|
||||
CHDIR Statement
|
||||
CHDRIVE Statement
|
||||
CHR$ Function
|
||||
CINT Function
|
||||
CIRCLE Statement
|
||||
CLEAR Statement
|
||||
CLNG Function
|
||||
CLOSE Statement
|
||||
CLS Statement
|
||||
COLOR Statement
|
||||
COM Statements
|
||||
COMMAND$ Function
|
||||
COMMITTRANS Statement
|
||||
COMMON Statement
|
||||
CONST Statement
|
||||
COS Function
|
||||
CREATEINDEX Statement
|
||||
CSNG Function
|
||||
CURDIR$ Function
|
||||
CVI, CVL, CVS, CVD, and CVC Functions
|
||||
CVSMBF, CVDMBF Function
|
||||
DATA Statement
|
||||
DATE$ Statement
|
||||
DDB# Function
|
||||
DECLARE Statement (BASIC Procedures)
|
||||
DECLARE Statement (Non-BASIC Procedures)
|
||||
DEF FN Statement
|
||||
DEF SEG Statement
|
||||
DEFtype Statement
|
||||
DELETE Statement
|
||||
DELETEINDEX Statement
|
||||
DELETETABLE Statement
|
||||
DIM Statement
|
||||
DIR$ Function
|
||||
DO...LOOP Statement
|
||||
DRAW Statement - Differences from BASICA
|
||||
DRAW Statement Macro Language
|
||||
DateSerial# Function
|
||||
DateValue# Function
|
||||
Day& Function
|
||||
END Statement
|
||||
ENVIRON Statement
|
||||
ENVIRON$ Function
|
||||
EOF Function
|
||||
ERASE Statement
|
||||
ERDEV, ERDEV$ Function
|
||||
ERR Statement
|
||||
ERR, ERL Functions
|
||||
ERROR Statement
|
||||
EVENT Statements
|
||||
EXIT Statement
|
||||
EXP Function
|
||||
FIELD Statement
|
||||
FILEATTR Function
|
||||
FILES Statement
|
||||
FIX Function
|
||||
FOR...NEXT Statement
|
||||
FRE Function
|
||||
FREEFILE Function
|
||||
FUNCTION Statement
|
||||
FV# Function
|
||||
FormatX$ Functions
|
||||
GET Statement (File I/O)
|
||||
GET Statement (Graphics)
|
||||
GETINDEX$ Function
|
||||
GOSUB...RETURN Statement
|
||||
GOTO Statement
|
||||
HEX$ Function
|
||||
Hour& Function
|
||||
IF...THEN...ELSE Statement
|
||||
INKEY$ Function
|
||||
INP Function
|
||||
INPUT # Statement
|
||||
INPUT Statement
|
||||
INPUT$ Function
|
||||
INSERT Statement
|
||||
INSTR Function
|
||||
INT Function
|
||||
IOCTL Statement
|
||||
IOCTL$ Function
|
||||
IPmt# Function
|
||||
IRR# Function
|
||||
Interrupt, InterruptX Routines
|
||||
KEY Statements (Assignment)
|
||||
KEY Statements (Event Trapping)
|
||||
KILL Statement
|
||||
LBOUND Function
|
||||
LCASE$ Function
|
||||
LEFT$ Function
|
||||
LEN Function
|
||||
LET Statement
|
||||
LINE INPUT # Statement
|
||||
LINE INPUT Statement
|
||||
LINE Statement
|
||||
LOC Function
|
||||
LOCATE Statement
|
||||
LOCK...UNLOCK Statement
|
||||
LOF Function
|
||||
LOG Function
|
||||
LPOS Function
|
||||
LPRINT Statement
|
||||
LPRINT USING Statement
|
||||
LSET Statement
|
||||
LTRIM$ Function
|
||||
MID$ Function
|
||||
MID$ Statement
|
||||
MIRR# Function
|
||||
MKDIR Statement
|
||||
MKI$, MKL$, MKS$, MKD$, and MKC$ Functions
|
||||
MKSMBF$, MKDMBF$ Functions
|
||||
MOVEFIRST, MOVELAST, MOVENEXT, MOVEPREVIOUS Statements
|
||||
Minute& Function
|
||||
Month& Function
|
||||
NAME Statement
|
||||
NPV# Function
|
||||
NPer# Function
|
||||
Now# Function
|
||||
OCT$ Function
|
||||
ON COM Statement
|
||||
ON ERROR Statement
|
||||
ON KEY Statement
|
||||
ON PEN Statement
|
||||
ON PLAY Statement
|
||||
ON SIGNAL Statement
|
||||
ON STRIG Statement
|
||||
ON TIMER Statement
|
||||
ON UEVENT Statement
|
||||
ON...GOSUB and ON...GOTO Statement
|
||||
OPEN COM Statement
|
||||
OPEN Statement (File I/O)
|
||||
OPTION BASE Statement
|
||||
OUT Statement
|
||||
PAINT Statement
|
||||
PALETTE, PALETTE USING Statements
|
||||
PCOPY Statement
|
||||
PEEK Function
|
||||
PEN Function
|
||||
PEN Statements
|
||||
PLAY Statement (Music)
|
||||
PLAY Statements (Event Trapping)
|
||||
PMAP Function
|
||||
POINT Function
|
||||
POKE Statement
|
||||
POS Function
|
||||
PPmt# Function
|
||||
PRESET Statement
|
||||
PRINT # Statement
|
||||
PRINT Statement
|
||||
PRINT USING Statement
|
||||
PSET Statement
|
||||
PUT Statement (File I/O)
|
||||
PUT Statement (Graphics)
|
||||
PV# Function
|
||||
Pmt# Function
|
||||
RANDOMIZE Statement
|
||||
READ Statement
|
||||
REDIM Statement
|
||||
REM Statement
|
||||
RESTORE Statement
|
||||
RESUME Statement
|
||||
RETRIEVE Statement
|
||||
RETURN Statement
|
||||
RIGHT$ Function
|
||||
RMDIR Statement
|
||||
RND Function
|
||||
ROLLBACK, ROLLBACK ALL Statements
|
||||
RSET Statement
|
||||
RTRIM$ Function
|
||||
RUN Statement
|
||||
Rate# Function
|
||||
SADD Function
|
||||
SAVEPOINT Function
|
||||
SCREEN Function
|
||||
SCREEN Statement
|
||||
SEEK Function
|
||||
SEEK Statement
|
||||
SEEKGT, SEEKGE, SEEKEQ Statements
|
||||
SELECT CASE Statement
|
||||
SETINDEX Statement
|
||||
SETMEM Function
|
||||
SHARED Statement
|
||||
SHELL Function
|
||||
SHELL Statement
|
||||
SIGNAL Statements
|
||||
SIN Function
|
||||
SLEEP Statement
|
||||
SLN# Function
|
||||
SOUND Statement
|
||||
SPC Function
|
||||
SSEG Function
|
||||
SSEGADD Function
|
||||
STACK Function
|
||||
STACK Statement
|
||||
STATIC Statement
|
||||
STICK Function
|
||||
STOP Statement
|
||||
STR$ Function
|
||||
STRIG Function
|
||||
STRIG Statements
|
||||
SUB Statement
|
||||
SWAP Statement
|
||||
SYD# Function
|
||||
SYSTEM Statement
|
||||
Second& Function
|
||||
SetFormatCC Routine
|
||||
SetUEvent Routine
|
||||
StringAddress Routine
|
||||
StringAssign Routine
|
||||
StringLength Routine
|
||||
StringRelease Routine
|
||||
TAB Function
|
||||
TAN Function
|
||||
TIME$ Function
|
||||
TIME$ Statement
|
||||
TIMER Function
|
||||
TIMER Statements
|
||||
TRON/TROFF Statements
|
||||
TYPE Statement
|
||||
TimeSerial# Function
|
||||
TimeValue# Function
|
||||
UBOUND Function
|
||||
UCASE$ Function
|
||||
UEVENT Statements
|
||||
UNLOCK Statement
|
||||
UPDATE Statement
|
||||
VAL Function
|
||||
VARPTR$ Function
|
||||
VARPTR, VARSEG Functions
|
||||
VIEW PRINT Statement
|
||||
VIEW Statement (Graphics)
|
||||
WAIT Statement
|
||||
WHILE...WEND Statement
|
||||
WIDTH Statements
|
||||
WINDOW Statement
|
||||
WRITE # Statement
|
||||
WRITE Statement
|
||||
Weekday& Function
|
||||
Year& Function
|
||||
|
||||
## Gegenprobe (Aufgabe 1.1, Befund C)
|
||||
|
||||
`bas7advr.hlp/TOPIC_LIST.html` ist **nicht vollständig**. Die Gegenprobe
|
||||
gegen `qb45advr.hlp/TOPIC_LIST.html` (abgerufen 2026-09-03) belegt das:
|
||||
diese Liste führt interne Ankernamen statt Klarnamen, enthält aber
|
||||
Einträge, zu denen `bas7advr` kein Thema führt.
|
||||
|
||||
| Anker in `qb45advr` | Element | in `bas7advr`-Themenliste |
|
||||
|---|---|---|
|
||||
| `.sgnr` / `.sgnx` | `SGN` | fehlt |
|
||||
| `.sqrr` / `.sqrx` | `SQR` | fehlt |
|
||||
| `.sp$r` / `.sp$x` | `SPACE$` | fehlt |
|
||||
| `.str$$$r` / `.str$$$x` | `STRING$` | fehlt |
|
||||
| `.csrlinr` / `.csrlinx` | `CSRLIN` | fehlt |
|
||||
| `.beepr` / `.beepx` | `BEEP` | fehlt |
|
||||
| `.datef` | `DATE$` (Funktion) | nur `DATE$ Statement` |
|
||||
|
||||
**Folge für das Inventar:** Maßgeblich ist die Vereinigungsmenge aus
|
||||
beiden Themenlisten und `docs/sprachreferenz.md`. Jeder Inventareintrag
|
||||
führt seine Fundstelle; Einträge, die nur über die Gegenprobe belegt
|
||||
sind, nennen `qb45advr`. Die Zählung einer einzelnen Themenliste ist
|
||||
kein hinreichender Vollständigkeitsnachweis.
|
||||
@@ -0,0 +1,173 @@
|
||||
## Purpose
|
||||
Die Datei-E/A stellt die Dateiverwaltung des Dialekts bereit: sequenzieller,
|
||||
wahlfreier und binärer Zugriff über Dateinummern, Record-Puffer mit
|
||||
Festtextfeldern, Positions- und Statusfunktionen sowie die
|
||||
Dateisystem-Anweisungen — plattformübergreifend und mit den
|
||||
Laufzeitfehlern des Vorbilds.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Dateinummern und Öffnen in drei Zugriffsarten
|
||||
`OPEN` SHALL in beiden Syntaxen des Vorbilds (Klausel- und Kurzform) die
|
||||
Zugriffsarten sequenziell (`INPUT`, `OUTPUT`, `APPEND`), wahlfrei
|
||||
(`RANDOM`) und binär (`BINARY`) unterstützen und die Datei an eine
|
||||
Dateinummer binden. `FREEFILE` SHALL die kleinste freie Dateinummer
|
||||
liefern. `CLOSE` ohne Argument SHALL alle offenen Dateien schließen,
|
||||
`RESET` ebenso. Eine bereits belegte Dateinummer MUST Laufzeitfehler 55
|
||||
„File already open" auslösen, eine nicht geöffnete Nummer Fehler 52
|
||||
„Bad file name or number", eine fehlende Datei bei `INPUT` Fehler 53
|
||||
„File not found".
|
||||
|
||||
#### Scenario: Sequenzielles Schreiben und Lesen
|
||||
- **WHEN** eine Datei mit `OPEN "d.txt" FOR OUTPUT AS #1` beschrieben, geschlossen und mit `FOR INPUT` wieder geöffnet wird
|
||||
- **THEN** liefert das Lesen genau die geschriebenen Zeilen
|
||||
|
||||
#### Scenario: Doppeltes Öffnen derselben Nummer
|
||||
- **WHEN** `OPEN` eine bereits offene Dateinummer verwendet
|
||||
- **THEN** tritt Laufzeitfehler 55 auf
|
||||
|
||||
#### Scenario: Freie Dateinummer
|
||||
- **WHEN** `#1` offen ist und `FREEFILE` aufgerufen wird
|
||||
- **THEN** liefert `FREEFILE` eine Nummer, die noch nicht belegt ist
|
||||
|
||||
### Requirement: Sequenzielle Datenübertragung
|
||||
`PRINT #` SHALL wie `PRINT` formatieren (Druckzonen, Zahlendarstellung mit
|
||||
Vorzeichenspalte), `WRITE #` SHALL Elemente komma-getrennt mit Strings in
|
||||
Anführungszeichen schreiben. `INPUT #` SHALL komma-getrennte Felder lesen
|
||||
und dabei Anführungszeichen auswerten, `LINE INPUT #` die ganze Zeile ohne
|
||||
Zeilenende. `EOF` SHALL das Erreichen des Dateiendes melden; Lesen über das
|
||||
Dateiende hinaus MUST Laufzeitfehler 62 „Input past end of file" auslösen.
|
||||
|
||||
#### Scenario: WRITE # und INPUT # sind gegenläufig
|
||||
- **WHEN** `WRITE #1, "a,b", 5` geschrieben und die Zeile mit `INPUT #1, s$, n` gelesen wird
|
||||
- **THEN** enthält `s$` den Text `a,b` und `n` den Wert 5
|
||||
|
||||
#### Scenario: Lesen über das Dateiende
|
||||
- **WHEN** `INPUT #1, x` nach dem letzten Datensatz ausgeführt wird
|
||||
- **THEN** tritt Laufzeitfehler 62 auf
|
||||
|
||||
### Requirement: Wahlfreier Zugriff mit Record-Puffern
|
||||
`OPEN … FOR RANDOM … LEN = n` SHALL Datensätze fester Länge bereitstellen.
|
||||
`GET` und `PUT` SHALL einen Datensatz an einer 1-basierten Recordnummer
|
||||
lesen bzw. schreiben; ohne Nummer gilt der Satz nach der letzten Operation.
|
||||
`FIELD` SHALL den Recordpuffer in benannte Festtextfelder aufteilen,
|
||||
`LSET`/`RSET` SHALL links- bzw. rechtsbündig in ein Feld schreiben und
|
||||
überlange Werte abschneiden. Alternativ SHALL `GET`/`PUT` mit einer
|
||||
Variablen eines benutzerdefinierten Typs arbeiten.
|
||||
|
||||
#### Scenario: Datensatz schreiben und an gleicher Stelle lesen
|
||||
- **WHEN** ein Record an Position 3 geschrieben und anschließend von Position 3 gelesen wird
|
||||
- **THEN** stimmen alle Feldwerte mit den geschriebenen überein
|
||||
|
||||
#### Scenario: RSET schneidet ab
|
||||
- **WHEN** in ein Feld der Länge 5 mit `RSET` der Text `ABCDEFG` geschrieben wird
|
||||
- **THEN** enthält das Feld genau 5 Zeichen
|
||||
|
||||
### Requirement: Record-Konvertierungsfunktionen
|
||||
Die Bibliothek SHALL die Konvertierungsfunktionen für Recordpuffer
|
||||
bereitstellen: `MKI$`, `MKL$`, `MKS$`, `MKD$` und `MKC$` wandeln einen
|
||||
numerischen Wert in seine Byte-Darstellung als String, `CVI`, `CVL`,
|
||||
`CVS`, `CVD` und `CVC` wandeln zurück. Jedes Paar MUST gegenläufig sein.
|
||||
Ein Argument, dessen Länge nicht zur Zielbreite passt, MUST
|
||||
Laufzeitfehler 5 „Illegal function call" auslösen. `MKSMBF$`, `MKDMBF$`,
|
||||
`CVSMBF` und `CVDMBF` SHALL dieselbe Rolle für das
|
||||
Microsoft-Binärformat älterer BASIC-Versionen erfüllen; ist dieses
|
||||
Format ein deklariertes Non-Feature, MUST die Ablehnung namentlich
|
||||
erfolgen und in der Sprachreferenz unter „Abweichungen" stehen.
|
||||
|
||||
#### Scenario: MK und CV sind gegenläufig
|
||||
- **WHEN** `CVL(MKL$(123456))` ausgewertet wird
|
||||
- **THEN** ist das Ergebnis 123456
|
||||
|
||||
#### Scenario: Falsche Argumentlänge
|
||||
- **WHEN** `CVI("x")` mit einem ein Zeichen langen String ausgewertet wird
|
||||
- **THEN** tritt Laufzeitfehler 5 auf
|
||||
|
||||
### Requirement: Festtextfelder als UTF-32 mit fester Recordlänge
|
||||
Feste Strings (`STRING * n`) in Recordpuffern SHALL als UTF-32 gespeichert
|
||||
werden — 4 Bytes je Zeichen, damit `STRING * n` stets genau `4 * n` Bytes
|
||||
belegt und die Recordlänge unabhängig vom Zeicheninhalt konstant bleibt.
|
||||
Die daraus folgende Binär-Inkompatibilität zu Dateien des Vorbilds ist
|
||||
beschlossen (2026-09-02) und MUST in der Sprachreferenz unter
|
||||
„Abweichungen" dokumentiert sein.
|
||||
|
||||
#### Scenario: Recordlänge unabhängig vom Inhalt
|
||||
- **WHEN** in ein Feld `STRING * 10` einmal `abc` und einmal `äöüßÄÖÜ` geschrieben wird
|
||||
- **THEN** belegt der Datensatz in beiden Fällen dieselbe Byteanzahl und der Folgesatz beginnt an derselben Position
|
||||
|
||||
### Requirement: Binärzugriff auf Byteebene
|
||||
`OPEN … FOR BINARY` SHALL byteweise Positionierung erlauben. `GET`/`PUT`
|
||||
SHALL an einer 1-basierten Byteposition lesen bzw. schreiben, wobei die
|
||||
übertragene Länge der Größe der Zielvariablen entspricht. `SEEK` als
|
||||
Anweisung SHALL die Position setzen, `SEEK` als Funktion sie liefern.
|
||||
|
||||
#### Scenario: Positioniertes Byte überschreiben
|
||||
- **WHEN** in einer Binärdatei an Position 4 ein Byte geschrieben und anschließend gelesen wird
|
||||
- **THEN** liefert das Lesen den geschriebenen Wert und die übrigen Bytes bleiben unverändert
|
||||
|
||||
### Requirement: Positions- und Statusfunktionen
|
||||
`LOF` SHALL die Dateigröße in Bytes liefern, `LOC` die aktuelle Position
|
||||
in der Einheit der Zugriffsart (Datensatz bei `RANDOM`, Byte bei
|
||||
`BINARY`), `SEEK` die nächste Lese-/Schreibposition und `FILEATTR` den
|
||||
Modus bzw. das Betriebssystem-Handle. Alle Funktionen MUST bei einer nicht
|
||||
geöffneten Dateinummer Laufzeitfehler 52 auslösen.
|
||||
|
||||
#### Scenario: Größe nach dem Schreiben
|
||||
- **WHEN** 100 Bytes geschrieben wurden und `LOF(1)` aufgerufen wird
|
||||
- **THEN** liefert `LOF(1)` den Wert 100
|
||||
|
||||
#### Scenario: Statusfunktion auf geschlossener Datei
|
||||
- **WHEN** `LOF(9)` ohne offene Datei 9 aufgerufen wird
|
||||
- **THEN** tritt Laufzeitfehler 52 auf
|
||||
|
||||
### Requirement: Satzsperren
|
||||
`LOCK` und `UNLOCK` SHALL einen Datensatz-, Byte- oder den gesamten
|
||||
Dateibereich sperren und wieder freigeben. Auf Plattformen ohne
|
||||
entsprechende Sperrmechanik MUST das Verhalten in der Sprachreferenz unter
|
||||
„Abweichungen" dokumentiert sein statt still abzuweichen.
|
||||
|
||||
#### Scenario: Sperren und Freigeben
|
||||
- **WHEN** ein Datensatzbereich gesperrt und anschließend mit denselben Grenzen freigegeben wird
|
||||
- **THEN** läuft die Folgeoperation auf diesem Bereich ohne Fehler
|
||||
|
||||
### Requirement: Dateisystem-Anweisungen mit plattformübergreifender Pfadsemantik
|
||||
`KILL`, `NAME … AS`, `FILES`, `CHDIR`, `CHDRIVE`, `MKDIR`, `RMDIR`,
|
||||
`CURDIR$`, `DIR$` sowie `SHELL` als Anweisung **und** als Funktion SHALL
|
||||
verfügbar sein. `CURDIR$` SHALL das aktuelle Verzeichnis liefern, `DIR$`
|
||||
den ersten bzw. — bei leerem Argument — den nächsten Treffer eines
|
||||
Suchmusters und den leeren String, wenn keiner mehr folgt. Die
|
||||
`SHELL`-Funktion SHALL den Beendigungsstatus des aufgerufenen Prozesses
|
||||
liefern. `CHDRIVE` SHALL auf Plattformen ohne Laufwerksbuchstaben
|
||||
namentlich abgewiesen oder als Non-Feature dokumentiert werden. Pfade SHALL sowohl `\` als auch `/` als Trennzeichen
|
||||
akzeptieren und auf die Konvention der laufenden Plattform abgebildet
|
||||
werden; Laufwerksbuchstaben SHALL auf Nicht-Windows-Plattformen als
|
||||
gewöhnlicher Namensbestandteil behandelt oder mit benannter Ablehnung
|
||||
abgewiesen werden. Fehlende Dateien MUST Fehler 53 auslösen, fehlende
|
||||
Pfade Fehler 76 „Path not found".
|
||||
|
||||
#### Scenario: Rückwärts-Schrägstrich auf Unix
|
||||
- **WHEN** `OPEN "unter\d.txt" FOR OUTPUT AS #1` auf einer Unix-Plattform ausgeführt wird
|
||||
- **THEN** entsteht die Datei `d.txt` im Verzeichnis `unter`
|
||||
|
||||
#### Scenario: Verzeichnis durchlaufen
|
||||
- **WHEN** `DIR$("*.txt")` und danach `DIR$("")` wiederholt ausgewertet werden
|
||||
- **THEN** liefern sie nacheinander alle passenden Namen und zuletzt den leeren String
|
||||
|
||||
#### Scenario: Fehlender Pfad
|
||||
- **WHEN** `MKDIR` in einem nicht existierenden Elternverzeichnis aufgerufen wird
|
||||
- **THEN** tritt Laufzeitfehler 76 auf
|
||||
|
||||
### Requirement: LPRINT als Druckausgabe
|
||||
`LPRINT` und `LPRINT USING` SHALL wie `PRINT` formatieren und in einen
|
||||
Druckerkanal schreiben. `LPOS` SHALL die aktuelle Spalte im
|
||||
Druckerpuffer liefern. Existiert auf der Plattform kein Druckerkanal,
|
||||
MUST das Ziel (Datei, Standardausgabe oder Ablehnung) in der
|
||||
Sprachreferenz unter „Abweichungen" dokumentiert sein.
|
||||
|
||||
#### Scenario: LPRINT formatiert wie PRINT
|
||||
- **WHEN** `LPRINT 1; 2` ausgeführt wird
|
||||
- **THEN** entspricht der erzeugte Text der Ausgabe von `PRINT 1; 2`
|
||||
|
||||
#### Scenario: Druckerspalte
|
||||
- **WHEN** nach `LPRINT "abc";` die Funktion `LPOS(0)` ausgewertet wird
|
||||
- **THEN** liefert sie die Spalte hinter dem ausgegebenen Text
|
||||
@@ -0,0 +1,85 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Korpusabdeckung der Phase-3-Semantik
|
||||
Der Korpus SHALL um Referenzprogramme mit dokumentierter Sollausgabe für
|
||||
die Bildschirmsteuerung (`LOCATE`, `COLOR`, `CLS`, `VIEW PRINT`-Scrollen,
|
||||
`CSRLIN`/`POS`, Zurücklesen per `SCREEN`), die Formatierung
|
||||
(`PRINT USING` inklusive Feldüberlauf), breite Unicode-Zeichen
|
||||
(Doppelzellen, Umbruch am rechten Rand), Datum und Zeit sowie die
|
||||
Datei-E/A in allen drei Zugriffsarten erweitert werden. Dateiprogramme
|
||||
MUST in einem temporären Arbeitsverzeichnis laufen und dürfen keine
|
||||
Artefakte im Projektbaum hinterlassen.
|
||||
|
||||
#### Scenario: Bildschirmsteuerung als Korpustest
|
||||
- **WHEN** die Testsuite läuft
|
||||
- **THEN** existiert ein Korpusprogramm, das mit `LOCATE` und `COLOR` an definierten Positionen ausgibt, und sein Snapshot entspricht der Sollausgabe
|
||||
|
||||
#### Scenario: Datei-Korpustest hinterlässt nichts
|
||||
- **WHEN** ein Datei-E/A-Korpusprogramm gelaufen ist
|
||||
- **THEN** ist das Arbeitsverzeichnis wieder entfernt und der Projektbaum unverändert
|
||||
|
||||
### Requirement: Nachweis der Größenunabhängigkeit
|
||||
Die Bildschirmgröße des Test-Hosts SHALL je Korpusprogramm explizit
|
||||
festgelegt und in der Sollausgabe vermerkt sein; ein Vorgabewert MUST NOT
|
||||
stillschweigend gelten. Mindestens ein Korpusprogramm SHALL bei zwei
|
||||
verschiedenen Bildschirmgrößen laufen und je Größe eine eigene
|
||||
Sollausgabe besitzen, um nachzuweisen, dass Löschen, Umbruch, Scrollen
|
||||
und Cursorgrenzen der jeweiligen Größe folgen. Mindestens ein
|
||||
Korpusprogramm SHALL eine Größenänderung während der Ausführung
|
||||
durchlaufen und danach die neuen Grenzen ausnutzen.
|
||||
|
||||
#### Scenario: Gleiches Programm bei zwei Größen
|
||||
- **WHEN** dasselbe Bildschirm-Korpusprogramm bei 80×25 und bei 120×40 läuft
|
||||
- **THEN** stimmt jede Ausgabe mit der Sollausgabe ihrer Größe überein und die Sollausgaben unterscheiden sich in Umbruch- und Scrollverhalten
|
||||
|
||||
#### Scenario: Größenänderung mitten im Programm
|
||||
- **WHEN** der Test-Host während der Ausführung von 80×25 auf 120×40 wechselt
|
||||
- **THEN** bleibt der bisherige Inhalt oben links erhalten und die anschließende Ausgabe nutzt die neuen Grenzen
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Korpusdateien mit byte-genauer Sollausgabe
|
||||
Jedes Korpusprogramm `tests/compat/<name>.bas` SHALL eine `<name>.out`
|
||||
mit dem exakten Sollzustand des Bildschirms besitzen (UTF-8,
|
||||
LF-Zeilenenden). Die Sollausgabe SHALL den Zellenpuffer als Textbild
|
||||
abbilden, getrimmt bis zur letzten belegten Zeile und Spalte; nachgestellte
|
||||
Leerzeichen innerhalb einer Zeile sind signifikant — `PRINT` gibt Zahlen
|
||||
mit führendem Vorzeichen-/Leerzeichen und nachgestelltem Leerzeichen aus.
|
||||
Verwendet ein Programm `COLOR`, SHALL die Sollausgabe zusätzlich eine
|
||||
Attributebene gleicher Abmessung enthalten; ohne `COLOR` MUST sie
|
||||
entfallen. `.gitattributes` MUST die `.out`-Dateien vor
|
||||
Zeilenenden-Konvertierung schützen.
|
||||
|
||||
#### Scenario: Zahlformatierung in der Sollausgabe
|
||||
- **WHEN** ein Korpusprogramm `PRINT 1; 2; 3` enthält
|
||||
- **THEN** lautet die Sollzeile ` 1 2 3 ` (mit nachgestelltem Leerzeichen)
|
||||
|
||||
#### Scenario: Getrimmter Snapshot
|
||||
- **WHEN** ein Korpusprogramm nur zwei Zeilen ausgibt
|
||||
- **THEN** umfasst die Sollausgabe genau diese zwei Zeilen und keine leeren Folgezeilen
|
||||
|
||||
#### Scenario: Attributebene nur bei COLOR
|
||||
- **WHEN** ein Korpusprogramm ohne `COLOR` läuft
|
||||
- **THEN** enthält seine `.out` keine Attributebene
|
||||
|
||||
### Requirement: Laufzeitvergleich über den Korpus
|
||||
Ein automatischer Test SHALL jede Korpusdatei `tests/compat/*.bas`
|
||||
kompilieren, über die VM mit einem Host ohne Terminal ausführen und den
|
||||
resultierenden Bildschirmzustand gegen die zugehörige `.out`-Datei
|
||||
vergleichen — getrimmt, mit signifikanten Leerzeichen innerhalb der
|
||||
Zeilen und, sofern vorhanden, einschließlich der Attributebene. Bei
|
||||
Abweichung MUST der Test Datei, erste abweichende Zeile sowie Soll und
|
||||
Ist nennen; weicht nur die Attributebene ab, MUST er Zeile, Spalte, Soll-
|
||||
und Ist-Attribut nennen.
|
||||
|
||||
#### Scenario: Korpus läuft mit korrekter Ausgabe
|
||||
- **WHEN** die Laufzeit-Testsuite läuft
|
||||
- **THEN** stimmt der Bildschirmzustand jeder Korpusdatei 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
|
||||
|
||||
#### Scenario: Abweichendes Farbattribut
|
||||
- **WHEN** ein Korpusprogramm dasselbe Textbild, aber ein abweichendes Attribut erzeugt
|
||||
- **THEN** schlägt der Test fehl und nennt Zeile, Spalte, Soll- und Ist-Attribut
|
||||
@@ -0,0 +1,208 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Vollständige Stringbibliothek
|
||||
Die Bibliothek SHALL sämtliche in der Original-Hilfe dokumentierten
|
||||
Stringfunktionen und -anweisungen mit der Semantik des Vorbilds
|
||||
bereitstellen — darunter `LEN`, `LEFT$`, `RIGHT$`, `MID$` (Funktion und
|
||||
Anweisung), `INSTR`, `UCASE$`, `LCASE$`, `LTRIM$`, `RTRIM$`, `STRING$`,
|
||||
`SPACE$`, `CHR$`, `ASC`, `STR$`, `VAL`, `HEX$`, `OCT$` und `SWAP` für
|
||||
Strings. Zeichen sind Unicode-Codepoints (dokumentierte Abweichung).
|
||||
Randfälle folgen dem Vorbild: `VAL` liest das führende Zahlenpräfix und
|
||||
überspringt Leerraum, `STR$` stellt nicht-negativen Zahlen ein Leerzeichen
|
||||
voran, `ASC("")` löst Laufzeitfehler 5 aus, eine Startposition kleiner 1
|
||||
bei `MID$`/`INSTR` ebenso, `LEFT$`/`RIGHT$` mit Länge über der Stringlänge
|
||||
liefern den ganzen String. Welche Elemente die Liste umfasst, MUST das
|
||||
Sprachinventar bestimmen; ein dort als `implementiert` geführtes Element
|
||||
MUST verfügbar sein.
|
||||
|
||||
#### Scenario: VAL mit Restzeichen
|
||||
- **WHEN** `VAL(" 12.5abc")` ausgewertet wird
|
||||
- **THEN** ist das Ergebnis 12.5
|
||||
|
||||
#### Scenario: Länge über Stringende
|
||||
- **WHEN** `LEFT$("ab", 10)` ausgewertet wird
|
||||
- **THEN** ist das Ergebnis `ab`
|
||||
|
||||
#### Scenario: Ungültige Startposition
|
||||
- **WHEN** `MID$("abc", 0)` ausgewertet wird
|
||||
- **THEN** tritt Laufzeitfehler 5 auf
|
||||
|
||||
### Requirement: PRINT USING und FORMAT$
|
||||
`PRINT USING`, `LPRINT USING` und `FORMAT$` SHALL die Formatzeichen des
|
||||
Vorbilds unterstützen: numerische Platzhalter (`#`), Dezimalpunkt,
|
||||
Tausendertrennung (`,`), Vorzeichenführung (`+`, `-`), Währungs- und
|
||||
Füllzeichen (`$$`, `**`, `**$`), Exponentialform (`^^^^`) sowie die
|
||||
String-Platzhalter `&`, `!` und `\ \`. Literale Zeichen SHALL unverändert
|
||||
übernommen werden, `_` SHALL das Folgezeichen literal machen. Passt ein
|
||||
Wert nicht in das Feld, SHALL die Ausgabe mit einem `%`-Präfix erfolgen
|
||||
und der Wert vollständig dargestellt werden; dieses Überlaufverhalten MUST
|
||||
in der Sprachreferenz festgehalten sein. Eine Formatzeichenkette ohne
|
||||
Platzhalter bei vorhandenen Argumenten MUST Laufzeitfehler 5 auslösen.
|
||||
`SetFormatCC` SHALL das für `$$` verwendete Währungszeichen und die
|
||||
zugehörige Stellung setzen; ohne Aufruf gilt ein dokumentierter
|
||||
Vorgabewert.
|
||||
|
||||
#### Scenario: Numerisches Feld mit Nachkommastellen
|
||||
- **WHEN** `PRINT USING "###.##"; 3.14159` ausgeführt wird
|
||||
- **THEN** lautet die Ausgabe ` 3.14`
|
||||
|
||||
#### Scenario: Feldüberlauf
|
||||
- **WHEN** `PRINT USING "##"; 12345` ausgeführt wird
|
||||
- **THEN** beginnt die Ausgabe mit `%` und enthält den vollständigen Wert
|
||||
|
||||
#### Scenario: Formatzeichenkette wird wiederverwendet
|
||||
- **WHEN** `PRINT USING "## "; 1; 2; 3` ausgeführt wird
|
||||
- **THEN** wird die Formatzeichenkette für jeden Wert erneut angewandt
|
||||
|
||||
### Requirement: Datums- und Zeitbibliothek
|
||||
Die Bibliothek SHALL die Datums- und Zeitfunktionen des Vorbilds
|
||||
bereitstellen: `DATE$` und `TIME$` als Funktion und als Anweisung, `TIMER`,
|
||||
`NOW`, `DATESERIAL`, `TIMESERIAL`, `DATEVALUE`, `TIMEVALUE`, `DAY`,
|
||||
`MONTH`, `YEAR`, `WEEKDAY`, `HOUR`, `MINUTE`, `SECOND`. Serielle Datumswerte
|
||||
SHALL der Zählweise des Vorbilds folgen (ganzzahliger Anteil = Tag,
|
||||
Nachkommaanteil = Tageszeit). `WEEKDAY` SHALL 1 für Sonntag liefern.
|
||||
Ungültige Datums- oder Zeitangaben MUST Laufzeitfehler 5 auslösen.
|
||||
|
||||
#### Scenario: Serienwert und Rückumwandlung
|
||||
- **WHEN** `d = DATESERIAL(1992, 3, 15)` gebildet und `YEAR(d)`, `MONTH(d)`, `DAY(d)` ausgewertet werden
|
||||
- **THEN** liefern sie 1992, 3 und 15
|
||||
|
||||
#### Scenario: Wochentagszählung
|
||||
- **WHEN** `WEEKDAY` auf einen Sonntag angewandt wird
|
||||
- **THEN** ist das Ergebnis 1
|
||||
|
||||
#### Scenario: Ungültiges Datum
|
||||
- **WHEN** `DATESERIAL(1992, 13, 1)` ausgewertet wird
|
||||
- **THEN** tritt Laufzeitfehler 5 auf
|
||||
|
||||
### Requirement: Kompatibler Zufallszahlengenerator
|
||||
`RND` und `RANDOMIZE` SHALL die Zahlenfolge des Vorbilds reproduzieren:
|
||||
gleicher Startwert MUST dieselbe Folge liefern. `RND` ohne Argument und
|
||||
mit positivem Argument SHALL die nächste Zahl liefern, mit Argument 0 die
|
||||
zuletzt gelieferte wiederholen, mit negativem Argument die Folge auf einen
|
||||
aus dem Argument abgeleiteten Startwert setzen. `RANDOMIZE` ohne Argument
|
||||
SHALL einen Startwert erfragen, `RANDOMIZE TIMER` einen zeitabhängigen
|
||||
setzen. Der verwendete Algorithmus MUST in der Sprachreferenz dokumentiert
|
||||
sein.
|
||||
|
||||
#### Scenario: Reproduzierbare Folge
|
||||
- **WHEN** zweimal `RANDOMIZE 42` gefolgt von drei `RND`-Aufrufen ausgeführt wird
|
||||
- **THEN** sind beide Dreierfolgen identisch
|
||||
|
||||
#### Scenario: RND(0) wiederholt
|
||||
- **WHEN** nach einem `RND`-Aufruf `RND(0)` ausgewertet wird
|
||||
- **THEN** liefert es denselben Wert wie der vorangegangene Aufruf
|
||||
|
||||
### Requirement: Mathematikbibliothek mit Überlaufverhalten
|
||||
Die Bibliothek SHALL die numerischen Funktionen des Vorbilds
|
||||
bereitstellen (`ABS`, `SGN`, `INT`, `FIX`, `SQR`, `EXP`, `LOG`, `SIN`,
|
||||
`COS`, `TAN`, `ATN`, `CINT`, `CLNG`, `CSNG`, `CDBL`, `CCUR`). Rundung
|
||||
SHALL kaufmännisch-symmetrisch zur geraden Zahl erfolgen (Banker's
|
||||
Rounding). Überschreitet ein Ergebnis den Wertebereich des Zieltyps, MUST
|
||||
Laufzeitfehler 6 „Overflow" auftreten; `SQR` und `LOG` mit unzulässigem
|
||||
Argument MUST Laufzeitfehler 5 auslösen, Division durch null Fehler 11.
|
||||
|
||||
#### Scenario: Rundung zur geraden Zahl
|
||||
- **WHEN** `CINT(0.5)` und `CINT(1.5)` ausgewertet werden
|
||||
- **THEN** liefern sie 0 und 2
|
||||
|
||||
#### Scenario: Überlauf bei Konvertierung
|
||||
- **WHEN** `CINT(40000)` ausgewertet wird
|
||||
- **THEN** tritt Laufzeitfehler 6 auf
|
||||
|
||||
#### Scenario: Wurzel aus negativer Zahl
|
||||
- **WHEN** `SQR(-1)` ausgewertet wird
|
||||
- **THEN** tritt Laufzeitfehler 5 auf
|
||||
|
||||
### Requirement: Finanzmathematische Funktionen
|
||||
Die Bibliothek SHALL die finanzmathematischen Funktionen des Vorbilds
|
||||
bereitstellen: `FV#` (Endwert), `PV#` (Barwert), `Pmt#` (Zahlung),
|
||||
`IPmt#` (Zinsanteil), `PPmt#` (Tilgungsanteil), `NPer#` (Anzahl
|
||||
Zahlungsperioden), `Rate#` (Zinssatz), `NPV#` (Kapitalwert), `IRR#`
|
||||
(interner Zinsfuß), `MIRR#` (modifizierter interner Zinsfuß), `SLN#`
|
||||
(lineare Abschreibung), `SYD#` (arithmetisch-degressive Abschreibung) und
|
||||
`DDB#` (geometrisch-degressive Abschreibung). Alle SHALL in DOUBLE
|
||||
rechnen. Das Argument für die Fälligkeit SHALL zwischen nachschüssiger
|
||||
und vorschüssiger Zahlung unterscheiden. Ungültige Argumente — etwa ein
|
||||
Zinssatz von null bei `NPer#` oder eine Periode außerhalb der Laufzeit
|
||||
bei `IPmt#`/`PPmt#` — MUST Laufzeitfehler 5 auslösen. `Rate#` und `IRR#`
|
||||
nähern sich iterativ; findet die Näherung keine Lösung, MUST
|
||||
Laufzeitfehler 5 auftreten statt eines stillen Ersatzwerts. Die
|
||||
Abbruchbedingung der Näherung MUST in docs/bibliothek.md dokumentiert
|
||||
sein.
|
||||
|
||||
#### Scenario: Endwert einer Rentenzahlung
|
||||
- **WHEN** `FV#` für 10 Perioden, 5 % Zins und eine Zahlung von 100 nachschüssig ausgewertet wird
|
||||
- **THEN** entspricht das Ergebnis dem dokumentierten Sollwert der Bibliotheksreferenz
|
||||
|
||||
#### Scenario: Zins- und Tilgungsanteil ergänzen sich
|
||||
- **WHEN** `IPmt#` und `PPmt#` für dieselbe Periode desselben Darlehens ausgewertet werden
|
||||
- **THEN** ist ihre Summe gleich `Pmt#` für dieses Darlehen
|
||||
|
||||
#### Scenario: Näherung ohne Lösung
|
||||
- **WHEN** `IRR#` mit einer Zahlungsreihe ohne Vorzeichenwechsel ausgewertet wird
|
||||
- **THEN** tritt Laufzeitfehler 5 auf
|
||||
|
||||
### Requirement: System- und Umgebungsanweisungen
|
||||
`ENVIRON$` SHALL Umgebungsvariablen nach Namen oder Position liefern,
|
||||
`ENVIRON` sie setzen. `COMMAND$` SHALL die Aufrufargumente ohne
|
||||
Programmnamen liefern. `FRE` SHALL einen Speicherwert liefern, `CLEAR`
|
||||
Variablen zurücksetzen, `TRON`/`TROFF` die Ablaufverfolgung schalten.
|
||||
`STACK` SHALL als Anweisung die Stapelgröße setzen und als Funktion die
|
||||
verfügbare Stapelgröße liefern. `SYSTEM` SHALL das Programm beenden und
|
||||
die Kontrolle an das Betriebssystem zurückgeben. `ERDEV` und `ERDEV$`
|
||||
SHALL den Fehlercode bzw. den Namen des zuletzt meldenden Geräts
|
||||
liefern. Nicht abbildbare Größen (Speicher- und Stapelwerte, Gerätefehler)
|
||||
MUST einen definierten, dokumentierten Ersatzwert liefern statt still zu
|
||||
variieren; der Ersatzwert MUST in docs/sprachreferenz.md unter
|
||||
„Abweichungen" stehen.
|
||||
|
||||
#### Scenario: Umgebungsvariable lesen
|
||||
- **WHEN** eine Umgebungsvariable gesetzt ist und `ENVIRON$("NAME")` ausgewertet wird
|
||||
- **THEN** liefert es deren Wert
|
||||
|
||||
#### Scenario: SYSTEM beendet das Programm
|
||||
- **WHEN** ein Programm `SYSTEM` ausführt
|
||||
- **THEN** endet es mit demselben Beendigungsstatus wie bei `END`
|
||||
|
||||
#### Scenario: Unbekannte Umgebungsvariable
|
||||
- **WHEN** `ENVIRON$` für einen nicht gesetzten Namen ausgewertet wird
|
||||
- **THEN** ist das Ergebnis der leere String
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Host-Abstraktion für Konsolen-E/A
|
||||
Alle Konsolenwirkungen der Ausführungsschicht SHALL über eine
|
||||
austauschbare Host-Schnittstelle laufen, die einen Bildschirmzustand
|
||||
darstellt statt eines Zeichenstroms: Übergabe des Zellenpuffers zur
|
||||
Anzeige, Abholen von Tastatur- und Größenänderungsereignissen sowie die
|
||||
Abbruchprüfung (Strg+Untbr). Host-Aufrufe dürfen blockieren.
|
||||
`tbc run` SHALL einen Konsolen-Host verwenden, der den Puffer im Terminal
|
||||
darstellt und Tastendrücke liefert. Tests MUST einen Host verwenden
|
||||
können, der ohne Terminal arbeitet, den Bildschirmzustand nach jedem
|
||||
Schritt festhält und vorbereitete Tastendrücke einspeist. Eine
|
||||
Bildschirmwirkung, die nur über einen Zeichenstrom ausdrückbar wäre, MUST
|
||||
NOT vorkommen: `LOCATE`, `COLOR`, `CSRLIN`, `POS` und die Funktionsform
|
||||
von `SCREEN` MUST über diese Schnittstelle vollständig abgebildet sein.
|
||||
|
||||
#### Scenario: Capture-Host im Test
|
||||
- **WHEN** ein Korpusprogramm im Test-Harness läuft
|
||||
- **THEN** entsteht der vollständige Bildschirmzustand im Capture-Host und ist vergleichbar, ohne dass ein Terminal vorhanden ist
|
||||
|
||||
#### Scenario: Cursorbewegung über die Schnittstelle
|
||||
- **WHEN** ein Programm `LOCATE 5, 10 : PRINT "x"` ausführt
|
||||
- **THEN** meldet der Host-Bildschirmzustand das Zeichen `x` an Position (5, 10)
|
||||
|
||||
#### Scenario: Tastendruck aus dem Host
|
||||
- **WHEN** der Test-Host einen vorbereiteten Tastendruck liefert und das Programm `INKEY$` auswertet
|
||||
- **THEN** erhält das Programm diese Taste
|
||||
|
||||
## REMOVED Requirements
|
||||
|
||||
### Requirement: Korpus-Stringfunktionen
|
||||
**Reason**: Die auf den Phase-2-Korpus zugeschnittene Teilliste wird durch
|
||||
die Anforderung „Vollständige Stringbibliothek" ersetzt, die den
|
||||
vollständigen dokumentierten Funktionsumfang fordert und ihren Umfang aus
|
||||
dem Sprachinventar bezieht.
|
||||
**Migration**: Alle bisher geforderten Funktionen und Randfälle sind in
|
||||
„Vollständige Stringbibliothek" enthalten; kein Verhalten entfällt.
|
||||
@@ -0,0 +1,54 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Jedes dokumentierte Element besitzt eine Signatur
|
||||
Die Semantik SHALL für jedes im Sprachinventar geführte Element eine
|
||||
Signatur besitzen (Argumentanzahl und -typen) — auch für Elemente, deren
|
||||
Laufzeitverhalten noch aussteht. Ein im Inventar geführtes Element MUST
|
||||
NOT als unbekannter Bezeichner oder mit einem generischen Syntaxfehler
|
||||
abgewiesen werden; die Diagnose bzw. der Laufzeitfehler MUST es
|
||||
namentlich nennen (Guiding Principle). Das gilt insbesondere für die
|
||||
Gruppen, die bisher gar keine Signatur haben: die finanzmathematischen
|
||||
Funktionen, die Record-Konvertierungsfunktionen (`MK…$`/`CV…`), die
|
||||
Dateisystem-Funktionen `CURDIR$`, `DIR$`, `CHDRIVE`, sowie `LPOS`,
|
||||
`STACK`, `SYSTEM`, `ERDEV`/`ERDEV$` und `SetFormatCC`.
|
||||
|
||||
#### Scenario: Element ohne Laufzeitverhalten wird namentlich abgewiesen
|
||||
- **WHEN** ein Modul eine im Inventar als `offen` geführte Funktion verwendet
|
||||
- **THEN** nennt die Ablehnung deren Namen und nicht „unbekannter Bezeichner"
|
||||
|
||||
#### Scenario: Argumentprüfung ohne Laufzeitverhalten
|
||||
- **WHEN** ein Modul `MKL$()` ohne Argument enthält
|
||||
- **THEN** meldet die Semantik eine Argumentanzahl-Diagnose, die `MKL$` nennt
|
||||
|
||||
### Requirement: Modulweites ON ERROR ist aus Prozeduren ansprechbar
|
||||
`ON ERROR GOTO label` ohne `LOCAL` SHALL einen modulweiten Fehlerhandler
|
||||
setzen, dessen Sprungziel auch dann aufgelöst wird, wenn die Anweisung in
|
||||
einer Prozedur steht und das Label auf Modulebene liegt. Nur
|
||||
`ON LOCAL ERROR GOTO label` SHALL prozedurlokale Handler-Auflösung
|
||||
verlangen. Ein modulweites `ON ERROR` in einer Prozedur MUST NOT wie
|
||||
`ON LOCAL ERROR` behandelt werden, und ein auf Modulebene vorhandenes
|
||||
Label MUST NOT als „Label not defined" gemeldet werden.
|
||||
|
||||
#### Scenario: Handler auf Modulebene aus einer Prozedur
|
||||
- **WHEN** ein Modul das Label `Fehler:` auf Modulebene führt und eine `SUB` die Anweisung `ON ERROR GOTO Fehler` enthält
|
||||
- **THEN** wird das Label aufgelöst und es entsteht keine Diagnose
|
||||
|
||||
#### Scenario: ON LOCAL ERROR bleibt prozedurlokal
|
||||
- **WHEN** eine `SUB` die Anweisung `ON LOCAL ERROR GOTO Fehler` enthält und `Fehler:` nur auf Modulebene existiert
|
||||
- **THEN** wird „Label not defined" gemeldet
|
||||
|
||||
### Requirement: DATA-Elemente behalten ihren Rohtext
|
||||
Unquotierte Elemente einer `DATA`-Anweisung SHALL exakt so erhalten
|
||||
bleiben, wie sie im Quelltext stehen — insbesondere in ihrer Groß- und
|
||||
Kleinschreibung. Die Normalisierung von Bezeichnern MUST NOT auf
|
||||
`DATA`-Elemente wirken. Führender und nachgestellter Leerraum eines
|
||||
unquotierten Elements SHALL entfernt werden, innenliegender erhalten
|
||||
bleiben.
|
||||
|
||||
#### Scenario: Gemischte Schreibweise bleibt erhalten
|
||||
- **WHEN** ein Modul `DATA Hallo Welt` enthält und `READ s$` ausgeführt wird
|
||||
- **THEN** enthält `s$` den Text `Hallo Welt` mit unveränderter Groß- und Kleinschreibung
|
||||
|
||||
#### Scenario: Leerraum an den Rändern
|
||||
- **WHEN** ein Modul `DATA ab cd ` enthält und gelesen wird
|
||||
- **THEN** lautet der Wert `ab cd`
|
||||
@@ -0,0 +1,74 @@
|
||||
## Purpose
|
||||
Das Sprachinventar ist das Messinstrument für die Leitplanke
|
||||
„Vollständigkeit ist das Soll": eine gepflegte Liste aller in der
|
||||
Original-Hilfe des Vorbilds dokumentierten Anweisungen, Funktionen und
|
||||
Metabefehle mit Abdeckungsstatus, gegen die sich der Implementierungsstand
|
||||
jederzeit prüfen lässt.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Vollständiges Inventar der Original-Hilfe
|
||||
`docs/inventar.md` SHALL jede in der Original-Hilfe des Vorbilds
|
||||
dokumentierte Anweisung, Funktion, Metabefehl, Operator-Schlüsselwort und
|
||||
Klausel als eigenen Eintrag führen. Ein dokumentiertes Element MUST NOT
|
||||
fehlen; Vollständigkeit gegenüber der Quelle geht der Kürze vor. Jeder
|
||||
Eintrag SHALL Name, Art (Anweisung/Funktion/Metabefehl), Themengruppe,
|
||||
Status und Fundstelle tragen.
|
||||
|
||||
#### Scenario: Dokumentiertes Element ist erfasst
|
||||
- **WHEN** die Original-Hilfe die Anweisung `VIEW PRINT` dokumentiert
|
||||
- **THEN** enthält das Inventar einen Eintrag `VIEW PRINT` mit Art, Gruppe, Status und Fundstelle
|
||||
|
||||
#### Scenario: Non-Feature ist erfasst, nicht weggelassen
|
||||
- **WHEN** `PEEK` als Non-Feature deklariert ist
|
||||
- **THEN** steht `PEEK` mit Status `Non-Feature` im Inventar und die Fundstelle verweist auf den Abschnitt „Abweichungen" der Sprachreferenz
|
||||
|
||||
### Requirement: Festes Statusvokabular
|
||||
Der Status eines Eintrags SHALL genau einen von drei Werten tragen:
|
||||
`implementiert` (Frontend senkt ab und die Laufzeit führt aus),
|
||||
`offen` (dokumentiert, noch nicht verfügbar) oder `Non-Feature`
|
||||
(vom Projektinhaber abgelehnt und in der Sprachreferenz unter
|
||||
„Abweichungen" gelistet). Ein Eintrag mit Status `Non-Feature` MUST eine
|
||||
Fundstelle in der Sprachreferenz nennen.
|
||||
|
||||
#### Scenario: Unzulässiger Status
|
||||
- **WHEN** ein Eintrag den Status `teilweise` trägt
|
||||
- **THEN** schlägt die Inventarprüfung mit Nennung des Eintrags fehl
|
||||
|
||||
#### Scenario: Non-Feature ohne Fundstelle
|
||||
- **WHEN** ein Eintrag den Status `Non-Feature` trägt, aber keine Fundstelle nennt
|
||||
- **THEN** schlägt die Inventarprüfung mit Nennung des Eintrags fehl
|
||||
|
||||
### Requirement: Automatischer Abgleich Inventar gegen Implementierung
|
||||
Ein automatischer Test SHALL das Inventar gegen den tatsächlichen
|
||||
Implementierungsstand prüfen und bei jeder Divergenz fehlschlagen. Als
|
||||
`implementiert` markierte Elemente MUST im Frontend eine Signatur besitzen
|
||||
und MUST NOT auf einen „nicht verfügbar"-Marker absenken; als `offen`
|
||||
markierte Elemente MUST auf einen solchen Marker absenken oder ohne
|
||||
Signatur sein; als `Non-Feature` markierte Elemente MUST zur Compile-Zeit
|
||||
mit „Feature unavailable" abgewiesen werden. Der Test MUST das
|
||||
divergierende Element, seinen Inventarstatus und den vorgefundenen
|
||||
Zustand benennen.
|
||||
|
||||
#### Scenario: Inventar hinkt der Implementierung hinterher
|
||||
- **WHEN** `LOCATE` implementiert wurde, im Inventar aber noch `offen` steht
|
||||
- **THEN** schlägt der Abgleich fehl und nennt `LOCATE`, Status `offen` und den Befund „implementiert"
|
||||
|
||||
#### Scenario: Inventar behauptet zu viel
|
||||
- **WHEN** ein Eintrag `implementiert` trägt, das Element aber weiterhin auf den „nicht verfügbar"-Marker absenkt
|
||||
- **THEN** schlägt der Abgleich fehl und nennt das Element
|
||||
|
||||
#### Scenario: Abdeckungsstand ist abrufbar
|
||||
- **WHEN** der Abgleich läuft
|
||||
- **THEN** nennt er die Zahl der Einträge je Status als Abdeckungsstand
|
||||
|
||||
### Requirement: Keine stillen Lücken bei dokumentierten Elementen
|
||||
Ein Element mit Status `offen` MUST bei Verwendung eine benannte
|
||||
Ablehnung erzeugen — entweder eine Compile-Diagnose oder den Laufzeitfehler
|
||||
für nicht verfügbare Funktionen —, die das Element namentlich nennt. Ein
|
||||
generischer Syntaxfehler oder „unbekannter Bezeichner" für ein
|
||||
dokumentiertes Element MUST NOT auftreten (Guiding Principle).
|
||||
|
||||
#### Scenario: Offenes Element wird namentlich abgewiesen
|
||||
- **WHEN** ein Programm eine im Inventar als `offen` geführte Anweisung verwendet
|
||||
- **THEN** nennt die Ablehnung den Namen der Anweisung, nicht nur „Syntaxfehler"
|
||||
@@ -0,0 +1,173 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Zellenpuffer ohne Terminalabhängigkeit
|
||||
Der Zellenpuffer mit der vollständigen Bildschirmsemantik (Cursor,
|
||||
Farbattribute, Umbruch, Scrollen, Scrollbereich, Größenänderung) SHALL
|
||||
ohne Terminal instanziierbar, veränderbar und auslesbar sein. Die
|
||||
Anbindung an ein konkretes Terminal SHALL ausschließlich in der
|
||||
Darstellungsschicht liegen. Damit MUST jedes Bildschirmverhalten in
|
||||
automatischen Tests ohne Terminal prüfbar sein, und die Ausführungsschicht
|
||||
MUST NOT von einer Terminal-Bibliothek abhängen.
|
||||
|
||||
#### Scenario: Bildschirmverhalten im Test ohne Terminal
|
||||
- **WHEN** ein Testprogramm in einer Umgebung ohne Terminal `LOCATE 5, 10 : PRINT "x"` ausführt
|
||||
- **THEN** trägt die Zelle (5, 10) das Zeichen `x` und der Test benötigt kein Terminal
|
||||
|
||||
### Requirement: Bildschirmanweisungen des Dialekts
|
||||
`CLS`, `COLOR`, `LOCATE`, `WIDTH`, `VIEW PRINT` und die Anweisungsform von
|
||||
`SCREEN` SHALL auf dem Zellenpuffer wirken. `CLS` SHALL den Scrollbereich
|
||||
löschen und den Cursor an dessen Anfang setzen; `CLS 2` SHALL nur den
|
||||
Textbereich löschen. `COLOR` SHALL Vordergrund und Hintergrund für
|
||||
nachfolgende Ausgaben setzen, ausgelassene Argumente lassen den bisherigen
|
||||
Wert unverändert. `WIDTH` SHALL die Spalten- und Zeilenzahl setzen, soweit
|
||||
die Darstellungsfläche es zulässt. `VIEW PRINT oben TO unten` SHALL den
|
||||
Scrollbereich begrenzen, `VIEW PRINT` ohne Argumente ihn auf den ganzen
|
||||
Bildschirm zurücksetzen.
|
||||
|
||||
#### Scenario: CLS setzt Cursor zurück
|
||||
- **WHEN** nach Ausgaben in Zeile 10 `CLS` ausgeführt wird
|
||||
- **THEN** ist der Puffer leer und `CSRLIN` liefert 1, `POS(0)` liefert 1
|
||||
|
||||
#### Scenario: COLOR wirkt nur auf Folgeausgaben
|
||||
- **WHEN** `PRINT "a" : COLOR 14, 1 : PRINT "b"` ausgeführt wird
|
||||
- **THEN** trägt die Zelle mit `a` das vorherige Attribut und die Zelle mit `b` Vordergrund 14 auf Hintergrund 1
|
||||
|
||||
#### Scenario: Ausgelassenes COLOR-Argument
|
||||
- **WHEN** nach `COLOR 14, 1` die Anweisung `COLOR , 4` ausgeführt wird
|
||||
- **THEN** bleibt der Vordergrund 14 und der Hintergrund wird 4
|
||||
|
||||
### Requirement: Bildschirm-Abfragefunktionen
|
||||
`CSRLIN` SHALL die aktuelle Cursorzeile liefern, `POS(0)` die aktuelle
|
||||
Cursorspalte, beide 1-basiert. Die Funktionsform `SCREEN(zeile, spalte
|
||||
[, farbe])` SHALL das Zeichen an der genannten Position als Codepoint
|
||||
liefern, bei gesetztem dritten Argument stattdessen dessen Farbattribut.
|
||||
Positionen außerhalb des Bildschirms MUST Laufzeitfehler 5 „Illegal
|
||||
function call" auslösen.
|
||||
|
||||
#### Scenario: Zeichen zurücklesen
|
||||
- **WHEN** `LOCATE 3, 7 : PRINT "Q";` ausgeführt und danach `SCREEN(3, 7)` ausgewertet wird
|
||||
- **THEN** liefert `SCREEN(3, 7)` den Codepoint von `Q`
|
||||
|
||||
#### Scenario: Abfrage außerhalb des Bildschirms
|
||||
- **WHEN** bei 80×25 `SCREEN(30, 1)` ausgewertet wird
|
||||
- **THEN** tritt Laufzeitfehler 5 auf
|
||||
|
||||
### Requirement: Tastatureingabe ohne Zeilenmodell
|
||||
`INKEY$` SHALL ohne zu blockieren die nächste anstehende Taste liefern:
|
||||
den leeren String bei leerem Puffer, ein Zeichen bei einer
|
||||
Zeichentaste, eine zwei Zeichen lange Folge mit führendem Nullzeichen bei
|
||||
einer Sondertaste. `INPUT$(n [, #dateinummer])` SHALL genau `n` Zeichen
|
||||
lesen und dabei blockieren, ohne sie am Bildschirm zu wiederholen.
|
||||
|
||||
#### Scenario: INKEY$ bei leerem Tastaturpuffer
|
||||
- **WHEN** `INKEY$` ohne anstehende Taste ausgewertet wird
|
||||
- **THEN** liefert es den leeren String und blockiert nicht
|
||||
|
||||
#### Scenario: Sondertaste als zwei Zeichen
|
||||
- **WHEN** F1 gedrückt wurde und `INKEY$` ausgewertet wird
|
||||
- **THEN** hat das Ergebnis die Länge 2 und beginnt mit dem Nullzeichen
|
||||
|
||||
### Requirement: Keine feste Bildschirmgröße im Verhalten
|
||||
Kein beobachtbares Verhalten SHALL eine feste Spalten- oder Zeilenzahl
|
||||
voraussetzen. Wo die Referenz des Vorbilds von 80×25 spricht, ist stets
|
||||
der volle aktuelle Bildschirm gemeint. Insbesondere SHALL `CLS` den
|
||||
vollen aktuellen Bildschirm löschen, der Scrollbereich ohne eigene
|
||||
`VIEW PRINT`-Einstellung den vollen aktuellen Bildschirm umfassen, der
|
||||
Zeilenumbruch an der aktuell letzten Spalte erfolgen, das Scrollen an
|
||||
der aktuell letzten Zeile des Bereichs auslösen und die Grenzprüfung von
|
||||
`LOCATE` und der Funktionsform von `SCREEN` gegen die aktuellen
|
||||
Abmessungen prüfen. Die Werte 80 und 25 MUST NOT als Grenze in
|
||||
beobachtbarem Verhalten auftreten.
|
||||
|
||||
#### Scenario: Löschen und Scrollen auf großem Bildschirm
|
||||
- **WHEN** der Bildschirm 120×40 misst, in Zeile 40 ein Umbruch erfolgt und danach `CLS` ausgeführt wird
|
||||
- **THEN** scrollt der Bildschirm erst an Zeile 40 und `CLS` löscht alle 40 Zeilen
|
||||
|
||||
#### Scenario: Adressierbarkeit jenseits von 80×25
|
||||
- **WHEN** der Bildschirm 120×40 misst und `LOCATE 40, 120` ausgeführt wird
|
||||
- **THEN** entsteht kein Fehler und `CSRLIN` liefert 40, `POS(0)` liefert 120
|
||||
|
||||
### Requirement: Größenänderung zur Laufzeit
|
||||
Ändert sich die Größe der Darstellungsfläche während ein Programm läuft,
|
||||
SHALL die Größenänderung als Ereignis bis zum Bildschirmzustand
|
||||
durchgereicht und der Zellenpuffer angepasst werden. Der Inhalt SHALL
|
||||
oben links erhalten bleiben; der Cursor SHALL in die neuen Grenzen
|
||||
geklemmt werden; ein `VIEW PRINT`-Bereich, der nicht mehr vollständig in
|
||||
den Bildschirm passt, SHALL auf die neuen Grenzen geklemmt und, falls er
|
||||
dadurch leer würde, auf den vollen Bildschirm zurückgesetzt werden. Alle
|
||||
programmseitig sichtbaren Größen — `CSRLIN`, `POS`, die Grenzen von
|
||||
`LOCATE` und der Funktionsform von `SCREEN` — MUST unmittelbar nach der
|
||||
Änderung die neuen Abmessungen widerspiegeln.
|
||||
|
||||
#### Scenario: Vergrößerung während der Ausführung
|
||||
- **WHEN** ein laufendes Programm bei 80×25 ausgibt und die Fläche auf 120×40 wächst
|
||||
- **THEN** bleibt der bisherige Inhalt oben links stehen und `LOCATE 40, 120` ist danach zulässig
|
||||
|
||||
#### Scenario: Verkleinerung klemmt den Cursor
|
||||
- **WHEN** der Cursor auf Zeile 40 steht und die Fläche auf 80×25 schrumpft
|
||||
- **THEN** liegt der Cursor danach innerhalb der neuen Grenzen
|
||||
|
||||
#### Scenario: Scrollbereich überlebt die Verkleinerung
|
||||
- **WHEN** `VIEW PRINT 30 TO 38` gesetzt ist und die Fläche auf 25 Zeilen schrumpft
|
||||
- **THEN** ist der Scrollbereich danach gültig und liegt vollständig innerhalb des Bildschirms
|
||||
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Dynamische Terminalgröße mit Mindestmaß
|
||||
Der Bildschirm SHALL der Terminalgröße folgen (Puffer per `resize`
|
||||
anpassbar; Inhalt bleibt oben links erhalten, Cursor wird geklemmt).
|
||||
Die Größe SHALL zu jedem Zeitpunkt aus der Darstellungsfläche abgeleitet
|
||||
und nicht fest vorgegeben werden. Die Mindestgröße ist 80×25: kleinere
|
||||
Werte werden auf 80×25 geklemmt, und ist die Render-Fläche kleiner als
|
||||
80×25, SHALL nur ein Hinweis „Terminal zu klein" mit Ist- und
|
||||
Mindestgröße gerendert werden. Dieses Mindestmaß ist ausschließlich eine
|
||||
untere Schranke der Darstellung; es MUST NOT als Bildschirmgröße im
|
||||
Verhalten des Programms auftreten, solange die Fläche größer ist.
|
||||
|
||||
#### Scenario: Vergrößertes Terminal
|
||||
- **WHEN** der Bildschirm auf 120×40 gesetzt wird
|
||||
- **THEN** sind alle 120 Spalten und 40 Zeilen adressierbar (`LOCATE 40, 120`)
|
||||
|
||||
#### Scenario: Zu kleines Terminal
|
||||
- **WHEN** die Render-Fläche 60×20 misst
|
||||
- **THEN** erscheint statt des Puffers der Hinweis mit Minimum 80×25
|
||||
|
||||
### Requirement: Unicode-Zellenmodell
|
||||
Der Puffer SHALL Unicode-Zeichen speichern (keine CP437-Emulation).
|
||||
Zeichen mit Darstellungsbreite 2 (u. a. Emoji, CJK) SHALL zwei
|
||||
nebeneinanderliegende Zellen belegen (Entscheidung 2026-09-02): die erste
|
||||
trägt das Zeichen, die zweite ist als Fortsetzung markiert und MUST NOT
|
||||
eigenständig beschrieben werden. Der Cursor SHALL nach der Ausgabe eines
|
||||
breiten Zeichens um zwei Spalten vorrücken; `POS` SHALL die Spalte des
|
||||
Zeichenanfangs zählen. Passt ein breites Zeichen nicht mehr in die letzte
|
||||
Spalte, SHALL es vollständig in die nächste Zeile umgebrochen werden und
|
||||
die letzte Spalte leer bleiben. `LOCATE` auf die Fortsetzungszelle SHALL
|
||||
auf den Zeichenanfang wirken.
|
||||
|
||||
#### Scenario: Umlaute und Symbole
|
||||
- **WHEN** `Ä☃` ausgegeben wird
|
||||
- **THEN** belegen `Ä` und `☃` je genau eine Zelle
|
||||
|
||||
#### Scenario: Breites Zeichen belegt zwei Zellen
|
||||
- **WHEN** an Spalte 1 ein CJK-Zeichen ausgegeben wird
|
||||
- **THEN** ist Spalte 2 als Fortsetzung belegt und der Cursor steht auf Spalte 3
|
||||
|
||||
#### Scenario: Breites Zeichen am rechten Rand
|
||||
- **WHEN** bei 80 Spalten der Cursor auf Spalte 80 steht und ein breites Zeichen ausgegeben wird
|
||||
- **THEN** bleibt Spalte 80 leer und das Zeichen steht in Spalte 1 der Folgezeile
|
||||
|
||||
### Requirement: 1-basierte Cursor-API mit Bereichsprüfung
|
||||
`LOCATE`, `CSRLIN` und `POS` SHALL 1-basiert arbeiten. `LOCATE`
|
||||
außerhalb der aktuellen Bildschirmgrenzen SHALL Laufzeitfehler 5
|
||||
„Illegal function call" auslösen. Ausgelassene Argumente von `LOCATE`
|
||||
SHALL den jeweiligen Wert unverändert lassen; die Argumente für
|
||||
Cursorsichtbarkeit und Cursorform SHALL entgegengenommen und, soweit das
|
||||
Terminal sie nicht abbilden kann, folgenlos bleiben.
|
||||
|
||||
#### Scenario: Grenzprüfung
|
||||
- **WHEN** bei 80×25 `LOCATE 26, 1` aufgerufen wird
|
||||
- **THEN** tritt Laufzeitfehler 5 auf
|
||||
|
||||
#### Scenario: Ausgelassenes LOCATE-Argument
|
||||
- **WHEN** der Cursor auf (5, 9) steht und `LOCATE , 3` ausgeführt wird
|
||||
- **THEN** steht der Cursor auf (5, 3)
|
||||
106
openspec/changes/phase-3-runtime-bildschirm/tasks.md
Normal file
106
openspec/changes/phase-3-runtime-bildschirm/tasks.md
Normal file
@@ -0,0 +1,106 @@
|
||||
## 1. Vollständigkeits-Inventar
|
||||
|
||||
- [x] 1.1 Anweisungs-, Funktions- und Metabefehlsliste aus der Original-Hilfe erheben (dos-help.soulsphere.org, Topic-Listen, README der Professional Edition) und als Rohliste ablegen; verifiziert dadurch, dass die Zahl der erfassten Themen gegen die Topic-Übersicht der Quelle abgeglichen und im Change notiert ist
|
||||
- [x] 1.2 `docs/inventar.md` als Pipe-Tabelle mit den Spalten Name, Art, Gruppe, Status, Fundstelle anlegen und alle Einträge aus 1.1 mit Erststatus füllen; verifiziert dadurch, dass jede Zeile sechs Spalten hat (Name, Art, Gruppe, Status, Fundstelle, Quelle) und das Statusvokabular nur `implementiert`/`offen`/`Non-Feature` enthält
|
||||
- [x] 1.3 Abgleichstest schreiben, der die Tabelle parst und gegen `builtin_fn`, `builtin_stmt`, `banned_feature` sowie die `Unsupported`-Marker hält; verifiziert dadurch, dass der Test auf dem Ist-Stand grün ist und nach einer künstlich falsch gesetzten Statuszeile mit Nennung des Elements fehlschlägt
|
||||
- [x] 1.4 Prüfung ergänzen, dass `Non-Feature`-Einträge eine Fundstelle in docs/sprachreferenz.md nennen und dokumentierte `offen`-Elemente namentlich abgewiesen werden (kein generischer Syntaxfehler); verifiziert durch je einen Testfall pro Regel
|
||||
- [x] 1.5 Abdeckungsstand (Anzahl je Status) im Testausgabetext ausweisen; verifiziert dadurch, dass `cargo test` die drei Zahlen nennt
|
||||
- [x] 1.6 Vom Inventar aufgedeckte Abweichung von der Phase-1-Spec schließen: Non-Features, die der Compiler nicht abwies, ergänzen — `CALLS`, `SSEG`, `SSEGADD`, `StringAddress`/`Assign`/`Length`/`Release`, `POINT`, `VIEW`, `COM`, `PEN`, `STRIG`, `STICK` in `banned_feature`; `LINE` ohne `INPUT` und `OPEN "COMn:"` als „Feature unavailable"; verifiziert durch `dokumentierte_elemente_werden_namentlich_abgewiesen`
|
||||
- [x] 1.7 Signaturen in `builtin_fn`/`builtin_stmt` für alle bislang signaturlosen Inventarelemente ergänzen (Finanzmathematik, `MK…$`/`CV…`, `CURDIR$`, `DIR$`, `CHDRIVE`, `LPOS`, `STACK`, `SYSTEM`, `ERDEV`/`ERDEV$`, `SetFormatCC`); verifiziert durch Frontend-Test über ein Programm, das jedes davon einmal verwendet, ohne Diagnose „unbekannter Bezeichner"
|
||||
- [x] 1.8 Sicherstellen, dass jedes als `offen` geführte Element namentlich abgewiesen wird (Diagnose oder Laufzeitfehler nennt den Namen); verifiziert durch Test, dass `MKL$()` ohne Argument eine Diagnose mit dem Namen `MKL$` erzeugt
|
||||
|
||||
## 2. Bruchschritt: Puffer, Host, Snapshot-Harness
|
||||
|
||||
- [x] 2.1 `Cell`/`TextScreen` samt Tests von `tb-ui::screen` nach `tb-runtime::screen` verschieben, ratatui-Importe entfernen; verifiziert dadurch, dass `cargo test -p tb-runtime` grün ist und `tb-runtime/Cargo.toml` weiterhin weder ratatui noch crossterm führt
|
||||
- [x] 2.2 `tb-runtime` als Abhängigkeit in `tb-ui` aufnehmen; `basic_color()` und `impl Widget` in `tb-ui` belassen und auf den Runtime-Typ umstellen; verifiziert dadurch, dass `cargo run -p tb-ui --example spike` unverändert rendert
|
||||
- [x] 2.3 `Host`-Trait neu schneiden: Übergabe des Zellenpuffers zur Anzeige, Ereignisabholung (Taste, Größenänderung), Abbruchprüfung; verifiziert dadurch, dass `tb-runtime` kompiliert und der Trait keine Stream-Methoden mehr trägt
|
||||
- [x] 2.4 `ConsoleHost` auf Darstellung des Puffers im Terminal und Tastaturereignisse umstellen; verifiziert dadurch, dass `tbc run tests/compat/hello.bas` im Terminal die erwartete Ausgabe zeigt
|
||||
- [x] 2.5 `CaptureHost` auf Bildschirmzustand plus vorbereitete Tastendrücke umstellen; verifiziert durch einen Unit-Test, der Zeichen und Attribut einer Zelle nach `LOCATE`/`COLOR`/`PRINT` prüft
|
||||
- [x] 2.6 Darstellungsaufrufe an die bestehenden Tick-Grenzen und vor blockierende Eingaben hängen; verifiziert dadurch, dass ein Programm mit Ausgabe vor `INPUT` den Text vor der Eingabeaufforderung zeigt
|
||||
- [x] 2.7 Snapshot-Serialisierung schreiben: Textbild getrimmt bis zur letzten belegten Zeile/Spalte, Attributebene nur bei verwendetem `COLOR`; verifiziert durch Unit-Tests für getrimmte Ausgabe, signifikante Zeilen-Innenleerzeichen und Weglassen der Attributebene
|
||||
- [x] 2.8 Harness `crates/tb-cli/tests/compat.rs` auf Snapshot-Vergleich umstellen, inklusive Fehlermeldung mit Datei, erster abweichender Zeile sowie Zeile/Spalte/Soll/Ist bei Attributabweichung; verifiziert durch je einen absichtlich abweichenden Testfall
|
||||
- [x] 2.9 Die acht bestehenden `.out`-Dateien neu erzeugen und jeden Diff einzeln prüfen und begründen; verifiziert dadurch, dass `PRINT`-only-Programme unverändert bleiben und jede Abweichung im Change begründet notiert ist
|
||||
- [x] 2.10 `.gitattributes` auf die neuen Snapshot-Dateien prüfen; verifiziert dadurch, dass `.out`-Dateien weiterhin vor Zeilenenden-Konvertierung geschützt sind
|
||||
|
||||
## 3. Größenunabhängigkeit und Laufzeit-Größenänderung
|
||||
|
||||
- [x] 3.1 Alle Bildschirmpfade auf die Abmessungen des Puffers umstellen (`CLS`, voreingestellter Scrollbereich, Umbruchspalte, Scrollzeile, Grenzprüfungen von `LOCATE` und `SCREEN()`); verifiziert dadurch, dass eine Suche über `tb-runtime` und `tb-ui` keine Vergleiche gegen die Literale 80 oder 25 außerhalb der Mindestmaß-Prüfung der Darstellungsschicht mehr findet
|
||||
- [x] 3.2 Größenänderungsereignis vom Host bis zum Puffer durchreichen; verifiziert durch Test, dass ein vom Test-Host ausgelöster Wechsel die Abmessungen des Puffers ändert
|
||||
- [x] 3.3 Anpassung bei Größenänderung: Inhalt oben links erhalten, Cursor klemmen, `VIEW PRINT`-Bereich klemmen und bei leerem Ergebnis auf den vollen Bildschirm zurücksetzen; verifiziert durch je einen Unit-Test für Vergrößerung, Verkleinerung und ungültig gewordenen Scrollbereich
|
||||
- [x] 3.4 Test-Host mit explizit gesetzter Bildschirmgröße je Korpusprogramm, ohne stillschweigenden Vorgabewert; verifiziert dadurch, dass ein Korpusprogramm ohne Größenangabe vom Harness abgewiesen wird
|
||||
- [x] 3.5 Bildschirm-Korpusprogramm bei 80×25 und 120×40 mit je eigener Sollausgabe; verifiziert dadurch, dass sich die Sollausgaben in Umbruch- und Scrollverhalten unterscheiden und beide Läufe grün sind
|
||||
- [x] 3.6 Korpusprogramm mit Größenänderung während der Ausführung; verifiziert dadurch, dass der Snapshot den erhaltenen Inhalt und die Ausgabe in den neuen Grenzen zeigt
|
||||
|
||||
## 4. Unicode-Doppelzellen
|
||||
|
||||
- [x] 4.1 `unicode-width` als Abhängigkeit aufnehmen und Zellenmodell um Fortsetzungszellen erweitern; verifiziert durch Unit-Test, dass ein CJK-Zeichen an Spalte 1 die Spalte 2 als Fortsetzung belegt
|
||||
- [x] 4.2 Cursorfortschritt, `POS` und Umbruch am rechten Rand auf Darstellungsbreite umstellen; verifiziert durch Unit-Tests für Vorrücken um zwei Spalten und vollständigen Umbruch bei belegter letzter Spalte
|
||||
- [x] 4.3 Überschreiben einer Zellenhälfte leert die andere; verifiziert durch Unit-Test, dass keine Geisterzeichen zurückbleiben
|
||||
- [x] 4.4 `LOCATE` auf eine Fortsetzungszelle auf den Zeichenanfang abbilden; verifiziert durch Unit-Test
|
||||
- [x] 4.5 Korpusprogramm mit CJK und Emoji inklusive Randumbruch ergänzen; verifiziert dadurch, dass der Snapshot der dokumentierten Sollausgabe entspricht
|
||||
- [x] 4.6 Abweichung in docs/sprachreferenz.md festhalten; verifiziert durch den Abschnittseintrag
|
||||
|
||||
## 5. Bildschirmanweisungen und -funktionen
|
||||
|
||||
- [x] 5.1 `CLS` (inkl. `CLS 2`), `COLOR` mit ausgelassenen Argumenten, `LOCATE` mit ausgelassenen Argumenten und Fehler 5 bei Grenzüberschreitung; verifiziert durch Korpustest mit Snapshot und je einem Fehlerfall
|
||||
- [x] 5.2 `WIDTH`, `VIEW PRINT` (setzen und zurücksetzen), Scrollen im begrenzten Bereich; verifiziert durch Korpustest, dass Zeilen außerhalb des Bereichs unverändert bleiben
|
||||
- [x] 5.3 `CSRLIN`, `POS(0)` und die Funktionsform `SCREEN(zeile, spalte [, farbe])` inklusive Fehler 5 außerhalb des Bildschirms; verifiziert durch Korpustest mit Zurücklesen eines geschriebenen Zeichens
|
||||
- [x] 5.4 Anweisungsform `SCREEN` entgegennehmen und dokumentiertes Verhalten festlegen; verifiziert dadurch, dass keine `Unsupported`-Absenkung mehr erfolgt und das Verhalten in der Sprachreferenz steht
|
||||
- [x] 5.5 `INKEY$` nicht blockierend (leerer String, Zeichen, Sondertaste als zwei Zeichen mit führendem Nullzeichen); verifiziert durch Test mit vorbereiteten Tastendrücken im Capture-Host
|
||||
- [x] 5.6 `INPUT$(n)` blockierend ohne Echo; verifiziert durch Test, dass genau `n` Zeichen gelesen werden und der Bildschirm unverändert bleibt
|
||||
- [x] 5.7 `TAB` und `SPC` in der `PRINT`-Familie; verifiziert durch Korpustest mit Spaltenpositionen
|
||||
- [x] 5.8 `KEY`-Umfang (`KEY LIST`/`ON`/`OFF`, Funktionstasten-Makros) aus der Original-Hilfe klären, umsetzen und in docs/sprachreferenz.md festhalten; verifiziert durch Korpustest und Dokumentationsabschnitt
|
||||
|
||||
## 6. String-, Zahlen- und Formatbibliothek
|
||||
|
||||
- [x] 6.1 Alle im Inventar geführten Stringfunktionen vervollständigen inklusive Randfälle (`VAL`-Präfix, `STR$`-Leerzeichen, Fehler 5 bei Startposition < 1, Länge über Stringende); verifiziert durch Korpustest je Randfall
|
||||
- [x] 6.2 `PRINT USING`/`LPRINT USING`/`FORMAT$` mit numerischen Platzhaltern, Tausendertrennung, Vorzeichenführung, `$$`/`**`/`**$`, Exponentialform und String-Platzhaltern; verifiziert durch Korpustest je Formatzeichenklasse
|
||||
- [x] 6.3 Feldüberlauf mit `%`-Präfix umsetzen und in docs/sprachreferenz.md festhalten; verifiziert durch Korpustest und Dokumentationsabschnitt
|
||||
- [x] 6.4 Wiederverwendung der Formatzeichenkette bei mehreren Werten; verifiziert durch Korpustest
|
||||
- [x] 6.5 Mathematikfunktionen vervollständigen inklusive Banker's Rounding, Fehler 6 bei Überlauf, Fehler 5 bei `SQR`/`LOG`, Fehler 11 bei Division durch null; verifiziert durch Korpustest je Fehlerfall
|
||||
- [x] 6.6 Kompatiblen `RND`/`RANDOMIZE`-PRNG umsetzen (`RND(0)` wiederholt, negatives Argument setzt Startwert) und den Algorithmus dokumentieren; verifiziert durch Korpustest mit reproduzierbarer Folge nach `RANDOMIZE 42`
|
||||
- [x] 6.7 Datums- und Zeitfunktionen inklusive Serienwert-Zählweise, `WEEKDAY` = 1 für Sonntag und Fehler 5 bei ungültigen Angaben; verifiziert durch Korpustest mit festem Datum
|
||||
- [x] 6.8 `DATE$`/`TIME$` als Anweisung; verifiziert durch Test, dass die Funktionsform danach den gesetzten Wert liefert
|
||||
- [x] 6.9 Finanzmathematische Funktionen `FV#`, `PV#`, `Pmt#`, `IPmt#`, `PPmt#`, `NPer#`, `Rate#`, `NPV#`, `IRR#`, `MIRR#`, `SLN#`, `SYD#`, `DDB#` in DOUBLE mit Fälligkeitsargument; verifiziert durch Korpustest je Funktion gegen dokumentierte Sollwerte
|
||||
- [x] 6.10 Fehler 5 bei ungültigen Argumenten der Finanzfunktionen und bei erfolgloser Näherung von `Rate#`/`IRR#`; verifiziert durch Korpustest je Fall
|
||||
- [x] 6.11 Abbruchbedingung der Näherung in docs/bibliothek.md dokumentieren; verifiziert durch den Abschnittseintrag
|
||||
- [x] 6.12 `SetFormatCC` für Währungszeichen und -stellung von `$$`; verifiziert durch Korpustest, dass `PRINT USING "$$#.##"` das gesetzte Zeichen verwendet
|
||||
- [x] 6.13 `docs/bibliothek.md` anlegen und mit dem umgesetzten Stand füllen; verifiziert dadurch, dass jede in Gruppe 6 umgesetzte Funktion dort einen Eintrag hat
|
||||
|
||||
## 7. Datei-E/A
|
||||
|
||||
- [x] 7.1 Dateitabelle und `OPEN` in beiden Syntaxen für sequenziell, `RANDOM` und `BINARY`, `CLOSE` (mit und ohne Argument), `RESET`, `FREEFILE`; verifiziert durch Korpustest inklusive Fehler 55, 52 und 53
|
||||
- [x] 7.2 Sequenzielle Übertragung: `PRINT #`, `WRITE #`, `INPUT #`, `LINE INPUT #`, `EOF`, Fehler 62 beim Lesen über das Dateiende; verifiziert durch Korpustest, dass `WRITE #` und `INPUT #` gegenläufig sind
|
||||
- [x] 7.3 Recordpuffer mit `FIELD`, `LSET`, `RSET` inklusive Abschneiden überlanger Werte; verifiziert durch Korpustest
|
||||
- [x] 7.4 Festtextfelder als UTF-32 mit konstanter Recordlänge; verifiziert durch Korpustest, dass ein Record mit ASCII und einer mit Nicht-ASCII dieselbe Byteanzahl belegen
|
||||
- [x] 7.5 `GET`/`PUT` für `RANDOM` mit Recordnummer und mit UDT-Variablen; verifiziert durch Korpustest mit Schreiben und Lesen an Position 3
|
||||
- [x] 7.6 Binärzugriff mit byteweiser Positionierung, `SEEK` als Anweisung und Funktion; verifiziert durch Korpustest, dass ein positioniert überschriebenes Byte zurückgelesen wird und die Nachbarbytes unverändert bleiben
|
||||
- [x] 7.7 `LOF`, `LOC`, `FILEATTR` inklusive Fehler 52 auf geschlossener Datei; verifiziert durch Korpustest
|
||||
- [x] 7.8 `LOCK`/`UNLOCK` umsetzen; verifiziert durch Korpustest für Sperren und Freigeben, plattformbedingte Abweichungen in docs/sprachreferenz.md notiert
|
||||
- [x] 7.9 Dateisystem-Anweisungen `KILL`, `NAME … AS`, `FILES`, `CHDIR`, `MKDIR`, `RMDIR`, `SHELL` mit plattformübergreifender Pfadsemantik (`\` und `/`, Laufwerksbuchstaben) inklusive Fehler 53 und 76; verifiziert durch Korpustest mit Rückwärts-Schrägstrich
|
||||
- [x] 7.10 `LPRINT`/`LPRINT USING` mit dokumentiertem Ausgabeziel sowie `LPOS`; verifiziert durch Test, dass die Formatierung `PRINT` entspricht, `LPOS` die Druckerspalte liefert, und durch den Abschnitt in der Sprachreferenz
|
||||
- [x] 7.11 Record-Konvertierungsfunktionen `MKI$`/`MKL$`/`MKS$`/`MKD$`/`MKC$` und `CVI`/`CVL`/`CVS`/`CVD`/`CVC`; verifiziert durch Korpustest, dass jedes Paar gegenläufig ist und falsche Argumentlänge Fehler 5 auslöst
|
||||
- [x] 7.12 MBF-Varianten `MKSMBF$`/`MKDMBF$`/`CVSMBF`/`CVDMBF` umsetzen oder als Non-Feature namentlich abweisen und in docs/sprachreferenz.md listen; verifiziert durch Korpustest bzw. den Abschnittseintrag
|
||||
- [x] 7.13 `CURDIR$`, `DIR$` (erster und Folgetreffer, leerer String am Ende), `CHDRIVE` und `SHELL` als Funktion; verifiziert durch Korpustest, der ein Verzeichnis mit `DIR$` vollständig durchläuft
|
||||
- [x] 7.14 Korpus-Dateiprogramme in einem temporären Arbeitsverzeichnis ausführen; verifiziert dadurch, dass nach `cargo test` `git status` sauber ist
|
||||
|
||||
## 8. System- und Umgebungsanweisungen
|
||||
|
||||
- [x] 8.1 `ENVIRON$` (nach Name und Position), `ENVIRON`, `COMMAND$`; verifiziert durch Korpustest inklusive leerem String für unbekannte Namen
|
||||
- [x] 8.2 `FRE`, `CLEAR`, `TRON`, `TROFF` mit definiertem, dokumentiertem Verhalten; verifiziert dadurch, dass keine `Unsupported`-Absenkung mehr erfolgt und der Ersatzwert für `FRE` in der Sprachreferenz steht
|
||||
- [x] 8.3 `STACK` als Anweisung und Funktion, `SYSTEM`, `ERDEV`/`ERDEV$` mit definierten Ersatzwerten; verifiziert durch Korpustest, dass `SYSTEM` mit demselben Status endet wie `END`, und durch die Ersatzwert-Einträge in docs/sprachreferenz.md
|
||||
|
||||
## 9. Altlasten aus Phase 2
|
||||
|
||||
- [x] 9.1 `ON ERROR GOTO` auf Modulebene aus Prozeduren auflösbar machen (`sema.rs`, `lower_stmt`/`OnError`), `ON LOCAL ERROR` bleibt prozedurlokal; verifiziert durch je einen Frontend-Test für beide Fälle
|
||||
- [x] 9.2 DATA-Rohtext im Lexer erhalten (keine Bezeichner-Normalisierung, Randleerraum entfernt, Innenleerraum erhalten); verifiziert durch Korpustest mit gemischter Schreibweise
|
||||
- [x] 9.3 `TODO verify`-Zellen der Konvertierungsmatrix in docs/tbvm-design.md gegen die Original-Hilfe auflösen (CURRENCY-Promotion mit Gleitkomma, `^`-Ergebnistyp, Exponentialform-Schwellen, CURRENCY-Ausgabeformat); verifiziert dadurch, dass keine `TODO verify`-Zelle mehr im Dokument steht
|
||||
- [x] 9.4 Korpustest `konvertierung.bas` um die in 9.3 geklärten Randfälle schärfen; verifiziert durch die erweiterte Sollausgabe
|
||||
|
||||
## 10. Abschluss
|
||||
|
||||
- [x] 10.1 Inventarstatus für alle in diesem Change umgesetzten Elemente auf `implementiert` setzen; verifiziert dadurch, dass der Abgleichstest aus 1.3 grün bleibt
|
||||
- [x] 10.2 docs/sprachreferenz.md fortschreiben (Doppelzellen, `KEY n`, `PRINT USING`-Überlauf, UTF-32-Records, plattformbedingte Abweichungen); verifiziert durch die vorhandenen Abschnitte
|
||||
- [x] 10.3 Vollständige Suite ausführen; verifiziert dadurch, dass `cargo test --workspace` grün ist. **Nur macOS geprüft** — Windows und Linux stehen aus und gehören zu den Plattformtests in Phase 6 (PLAN.md); die Laufzeit enthält keinen plattformspezifischen Code außer der Pfadnormierung und `SHELL`.
|
||||
- [x] 10.4 Meilenstein prüfen: klassische Konsolenprogramme des Korpus laufen unverändert; verifiziert dadurch, dass jedes Korpusprogramm ohne Laufzeitfehler 73 durchläuft
|
||||
- [x] 10.5 PLAN.md Phase 3 abhaken, soweit dieser Change reicht, und den offenen ISAM-Teil auf `phase-3-isam` verweisen; verifiziert durch den aktualisierten Planabschnitt
|
||||
Reference in New Issue
Block a user