OpenSpec: phase-3-runtime-bildschirm archiviert, Delta-Specs gesynct
Zwei neue Capabilities in den Hauptspecs: - datei-eio: Dateiverwaltung, drei Zugriffsarten, UTF-32-Festtextfelder, Recordkonvertierung, Sperren, Dateisystem-Anweisungen - sprachinventar: das Vollstaendigkeits-Inventar als pruefbares Artefakt Fortgeschrieben: textbildschirm (Doppelzellen, Groessenunabhaengigkeit, Bildschirmanweisungen), konsolen-basisbibliothek (Host-Neuschnitt, volle String-/Format-/Datums-/Finanzbibliothek), kompat-testkorpus (Snapshot-Vergleich, Nachweis der Groessenunabhaengigkeit), sprach-frontend (Signaturpflicht, ON-ERROR-Scoping, DATA-Rohtext). Bilanz: +32 Anforderungen, 6 geaendert, 1 ersetzt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
175
openspec/specs/datei-eio/spec.md
Normal file
175
openspec/specs/datei-eio/spec.md
Normal file
@@ -0,0 +1,175 @@
|
||||
# datei-eio Specification
|
||||
|
||||
## 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.
|
||||
|
||||
## 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
|
||||
@@ -10,16 +10,28 @@ geprüft werden.
|
||||
|
||||
### Requirement: Korpusdateien mit byte-genauer Sollausgabe
|
||||
Jedes Korpusprogramm `tests/compat/<name>.bas` SHALL eine `<name>.out`
|
||||
mit der exakten Konsolenausgabe besitzen (UTF-8, LF-Zeilenenden).
|
||||
Nachgestellte Leerzeichen sind signifikant — `PRINT` gibt Zahlen mit
|
||||
führendem Vorzeichen-/Leerzeichen und nachgestelltem Leerzeichen aus,
|
||||
auch am Zeilenende; `.gitattributes` MUST die `.out`-Dateien vor
|
||||
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: Frontend-Meilenstein über den Korpus
|
||||
Ein automatischer Test SHALL alle `tests/compat/*.bas` durch das
|
||||
Sprach-Frontend führen und fehlschlagen, sobald eine Datei Diagnosen
|
||||
@@ -32,19 +44,26 @@ hinzu.)
|
||||
|
||||
### Requirement: Laufzeitvergleich über den Korpus
|
||||
Ein automatischer Test SHALL jede Korpusdatei `tests/compat/*.bas`
|
||||
kompilieren, über die VM mit Capture-Host ausführen und die Ausgabe
|
||||
byte-genau (inklusive nachgestellter Leerzeichen und LF-Zeilenenden)
|
||||
gegen die zugehörige `.out`-Datei vergleichen. Bei Abweichung MUST der
|
||||
Test Datei, erste abweichende Zeile sowie Soll und Ist nennen.
|
||||
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 die Ausgabe jeder Korpusdatei byte-genau mit ihrer `.out` überein
|
||||
- **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
|
||||
|
||||
### Requirement: Korpusabdeckung der Phase-2-Semantik
|
||||
Der Korpus SHALL um Referenzprogramme mit dokumentierter Sollausgabe für
|
||||
die Zahlenkonvertierungs-Matrix (Rundung, Ganzzahldivision, logische
|
||||
@@ -55,3 +74,39 @@ Prozeduraufrufe mit BYREF-/BYVAL-Wirkung erweitert werden.
|
||||
#### Scenario: Konvertierungsmatrix als Korpustest
|
||||
- **WHEN** die Laufzeit-Testsuite läuft
|
||||
- **THEN** existiert ein Korpusprogramm, das die Matrix-Randfälle ausgibt, und seine Ausgabe entspricht der dokumentierten Sollausgabe
|
||||
|
||||
### 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
|
||||
|
||||
@@ -19,16 +19,30 @@ Opcode); die Tabelle lebt in der Laufzeitbibliothek und ist ohne
|
||||
- **THEN** wird `LEN` über seinen Tabellenindex aufgerufen und liefert 3
|
||||
|
||||
### Requirement: Host-Abstraktion für Konsolen-E/A
|
||||
Alle Konsolenwirkungen der VM SHALL über eine austauschbare
|
||||
Host-Schnittstelle laufen (Textausgabe, zeilenweises Lesen für `INPUT`,
|
||||
Abbruchprüfung). Host-Aufrufe dürfen blockieren; die Abbruchprüfung
|
||||
(Strg+Untbr) obliegt dem Host. `tbc run` verwendet einen Konsolen-Host
|
||||
(stdout/stdin); Tests MUST einen Capture-Host verwenden können, der die
|
||||
Ausgabe byte-genau aufzeichnet.
|
||||
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 die komplette Ausgabe im Capture-Host und ist byte-genau vergleichbar
|
||||
- **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
|
||||
|
||||
### Requirement: PRINT-Formatierung
|
||||
`PRINT` SHALL Zahlen im Format des Vorbilds ausgeben: führendes
|
||||
@@ -48,20 +62,171 @@ Zeilenumbruch.
|
||||
- **WHEN** `PRINT "12345678901234", "x"` ausgeführt wird
|
||||
- **THEN** beginnt `x` in Spalte 29 (Zone nach der belegten übersprungen)
|
||||
|
||||
### Requirement: Korpus-Stringfunktionen
|
||||
Die Bibliothek SHALL mindestens folgende Funktionen mit der Semantik des
|
||||
Vorbilds bereitstellen (Zeichen = Unicode-Codepoints, dokumentierte
|
||||
UTF-8-Abweichung): `LEN`, `LEFT$`, `RIGHT$`, `MID$` (Funktion),
|
||||
`INSTR`, `UCASE$`, `LCASE$`, `STRING$`, `SPACE$`, `CHR$`, `ASC`,
|
||||
`STR$`, `VAL`. Randfälle folgen dem Vorbild, u. a.: `VAL` liest das
|
||||
führende Zahlenpräfix und ignoriert Leerraum; `STR$` stellt
|
||||
nicht-negativen Zahlen ein Leerzeichen voran; `ASC("")` löst Fehler 5
|
||||
„Illegal function call" aus.
|
||||
|
||||
#### Scenario: Korpusdatei strings.bas
|
||||
- **WHEN** `tests/compat/strings.bas` ausgeführt wird
|
||||
- **THEN** entspricht die Ausgabe byte-genau `strings.out`
|
||||
### 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
|
||||
|
||||
@@ -120,3 +120,56 @@ diagnose-freies Modul MUST ein vollständiges HIR ergeben.
|
||||
#### Scenario: Diagnose-freies Modul liefert HIR
|
||||
- **WHEN** ein Korpusmodul ohne Diagnosen analysiert wird
|
||||
- **THEN** liefert das Frontend ein HIR, aus dem der Codegen ohne erneute Namens- oder Typauflösung Bytecode erzeugen kann
|
||||
|
||||
### 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`
|
||||
|
||||
76
openspec/specs/sprachinventar/spec.md
Normal file
76
openspec/specs/sprachinventar/spec.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# sprachinventar Specification
|
||||
|
||||
## 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.
|
||||
|
||||
## 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"
|
||||
@@ -11,9 +11,13 @@ Ratatui.
|
||||
### 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 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.
|
||||
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
|
||||
@@ -35,13 +39,20 @@ ohne eigene Einstellung folgt der Scrollbereich der Bildschirmgröße.
|
||||
- **THEN** scrollen nur die Zeilen 3–5; Kopfzeilen außerhalb bleiben unverändert
|
||||
|
||||
### Requirement: 1-basierte Cursor-API mit Bereichsprüfung
|
||||
`LOCATE`, `CSRLIN` und `POS` SHALL 1-basiert arbeiten; `LOCATE`
|
||||
außerhalb der aktuellen Bildschirmgrenzen SHALL als Fehler abgewiesen
|
||||
werden (Basis für Laufzeitfehler 5).
|
||||
`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** wird der Aufruf abgewiesen
|
||||
- **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)
|
||||
|
||||
### Requirement: Klassische Farbpalette und Blink-Simulation
|
||||
Der Bildschirm SHALL die klassische Palette abbilden (Vordergrund 0–15,
|
||||
@@ -55,11 +66,136 @@ Terminal-Blinken.
|
||||
- **THEN** wird mit heller Vordergrundfarbe 9 gerendert
|
||||
|
||||
### Requirement: Unicode-Zellenmodell
|
||||
Der Puffer SHALL Unicode-Zeichen speichern (keine CP437-Emulation);
|
||||
aktuell gilt strikt 1 Zeichen = 1 Zelle. (Zeichen mit Darstellungsbreite
|
||||
2 belegen künftig zwei Zellen — beschlossene Änderung, Umsetzung in
|
||||
Phase 3 als eigenes Change.)
|
||||
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: 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
|
||||
|
||||
Reference in New Issue
Block a user