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>
9.3 KiB
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::errorsbereits 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 Statusoffenund 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-eiohinaus. - 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-bildschirmist 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
(
EOFnachMOVENEXTam Ende, Cursor nachDELETE, nachSETINDEX, nachROLLBACK) → 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;
COMMITTRANSbeendet 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.