Files
TerminalBasic/openspec/changes/phase-3-runtime-bildschirm/specs/datei-eio/spec.md
Chili Palmer 52ccbb5848 Phase 3 (Kern): Laufzeitbibliothek, Bildschirm und Datei-E/A
Setzt den OpenSpec-Change phase-3-runtime-bildschirm um (77/77 Aufgaben).
Abdeckung laut Inventar: 195 implementiert, 37 offen, 53 Non-Feature.

Vollstaendigkeits-Inventar
- docs/inventar.md mit 285 Eintraegen aus den Themenlisten von
  bas7advr.hlp und qb45advr.hlp, je mit Status und Fundstelle
- crates/tb-frontend/tests/inventar.rs haelt die Tabelle in beide
  Richtungen gegen den Code; der Abdeckungsstand kann nicht veralten

Bruchschritt (Puffer, Host, Korpus)
- Cell/TextScreen ziehen ratatui-frei nach tb-runtime::screen; tb-ui
  behaelt Farbabbildung, Widget und den neuen Terminal-Host
- Host wechselt vom Zeichenstrom auf Anzeige des Zellenpuffers plus
  Ereignisse (Taste, Groessenaenderung, Abbruch)
- Korpusvergleich auf getrimmten Bildschirm-Snapshot; die acht
  bestehenden Sollausgaben blieben dabei unveraendert

Groessenunabhaengigkeit
- 80x25 ist nirgends mehr eine feste Grenze; Groessenaenderungen waehrend
  der Ausfuehrung werden zugestellt (Inhalt oben links erhalten, Cursor
  und VIEW PRINT geklemmt)
- Korpusnachweis: dasselbe Programm bei 80x25 und 120x40 mit je eigener
  Sollausgabe, plus ein Programm mit Groessenwechsel mitten im Lauf

Bibliothek
- Breite Unicode-Zeichen belegen zwei Zellen (Cursor, POS, Randumbruch)
- Bildschirm: CLS, COLOR, LOCATE, WIDTH, VIEW PRINT, SCREEN, CSRLIN, POS
- Tastatur: INKEY$, INPUT$, Funktionstasten-Makros (KEY n / LIST / ON)
- PRINT USING, LPRINT USING, FORMAT$, SetFormatCC
- Mathematik mit kompatiblem PRNG (gleiche Saat, gleiche Folge)
- Datum und Zeit mit Serienwerten ab 1899
- Finanzmathematik: FV#, PV#, Pmt#, IPmt#, PPmt#, NPer#, Rate#, NPV#,
  IRR#, MIRR#, SLN#, SYD#, DDB#
- Datei-E/A: sequenziell, RANDOM (Recordpuffer und UDT-Variablen),
  BINARY, FIELD/LSET/RSET, Statusfunktionen, Dateisystem, MK$/CV
- System: ENVIRON, FRE, CLEAR, TRON/TROFF, STACK, ERDEV, ERR-Anweisung

Altlasten aus Phase 2
- ON ERROR GOTO auf Modulebene ist aus Prozeduren ansprechbar
  (prozeduruebergreifender Fixup im Codegenerator)
- DATA behaelt seinen Rohtext (Gross-/Kleinschreibung, innerer Leerraum)
- Die vier TODO-verify-Zellen der Konvertierungsmatrix sind aufgeloest

Vom Inventar aufgedeckte Fehler
- Zwoelf Non-Features wies der Compiler entgegen der Phase-1-Spec nicht
  ab (CALLS, SSEG, POINT, VIEW, COM, PEN, STRIG, STICK, die
  String*-Routinen, LINE und OPEN "COMn:") -- geschlossen
- Drei Gruppen fehlten im urspruenglichen Umfang: Finanzmathematik,
  Record-Konvertierung, Rest der Dateisystemfunktionen
- bas7advr.hlp allein ist keine vollstaendige Quelle; das Inventar
  bildet die Vereinigung mit qb45advr.hlp

Neue Changes
- phase-3-isam: schliesst Phase 3 ab (Speicherschicht redb)
- phase-3-ortszeit: zieht die UTC-Abweichung zurueck

Neue Abhaengigkeit: unicode-width.
Dokumentiert: sprachreferenz.md und tbvm-design.md sind TODO-frei,
docs/bibliothek.md neu, PLAN.md fortgeschrieben.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 06:54:19 +02:00

174 lines
8.9 KiB
Markdown

## 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