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

8.9 KiB

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