296 lines
15 KiB
Markdown
296 lines
15 KiB
Markdown
# konsolen-basisbibliothek Specification
|
|
|
|
## Purpose
|
|
Die Konsolen-Basisbibliothek ist die in Phase 2 vorgezogene Scheibe der
|
|
Laufzeitbibliothek: die Builtin-Aufrufschnittstelle der VM, ein
|
|
`Host`-Abstraktion für Konsolen-E/A sowie PRINT-Formatierung und die vom
|
|
Testkorpus benötigten Stringfunktionen.
|
|
|
|
## Requirements
|
|
|
|
### Requirement: Builtin-Aufrufschnittstelle
|
|
Die VM SHALL Bibliotheksfunktionen und -anweisungen über eine indizierte
|
|
Dispatch-Tabelle aufrufen (Argumente auf dem Operandenstack, Index im
|
|
Opcode); die Tabelle lebt in der Laufzeitbibliothek und ist ohne
|
|
Änderung am Opcode-Satz erweiterbar (Phase 3 füllt sie auf).
|
|
|
|
#### Scenario: Builtin über Tabelle
|
|
- **WHEN** `PRINT LEN("abc")` ausgeführt wird
|
|
- **THEN** wird `LEN` über seinen Tabellenindex aufgerufen und liefert 3
|
|
|
|
### 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
|
|
|
|
### Requirement: PRINT-Formatierung
|
|
`PRINT` SHALL Zahlen im Format des Vorbilds ausgeben: führendes
|
|
Leerzeichen für nicht-negative bzw. `-` für negative Werte,
|
|
nachgestelltes Leerzeichen (auch am Zeilenende); Zahlendarstellung je
|
|
Typ wie beim Vorbild (u. a. kein Dezimalpunkt bei ganzzahligen Werten,
|
|
signifikante Stellen je nach SINGLE/DOUBLE). `;` schließt direkt an,
|
|
`,` springt zur nächsten 14-Zeichen-Druckzone (belegt ein Element die
|
|
Zone ganz, zur übernächsten); ohne abschließendes `;`/`,` folgt ein
|
|
Zeilenumbruch.
|
|
|
|
#### Scenario: Zahlen mit Vorzeichenspalte
|
|
- **WHEN** `PRINT 1; -2; 3.5` ausgeführt wird
|
|
- **THEN** lautet die Ausgabe ` 1 -2 3.5 ` mit Zeilenumbruch
|
|
|
|
#### Scenario: Druckzonen
|
|
- **WHEN** `PRINT "12345678901234", "x"` ausgeführt wird
|
|
- **THEN** beginnt `x` in Spalte 29 (Zone nach der belegten übersprungen)
|
|
|
|
### 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
|
|
|
|
### 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.
|
|
|
|
Die parameterlose INTEGER-Funktion TIMEZONEKNOWN SHALL -1 für eine ermittelte Systemzone und 0 für den UTC-Rückfall liefern. Auch eine nach Programmstart scheiternde Zonenermittlung SHALL diesen Zustand sichtbar ändern; die Abfrage SHALL keine Zeitzone oder Systemuhr verstellen.
|
|
|
|
#### 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
|
|
|
|
#### Scenario: Rückfall nach Programmstart
|
|
- **WHEN** zunächst eine Zone ermittelt wurde, eine spätere Ermittlung aber scheitert
|
|
- **THEN** verwenden die Zeitfunktionen UTC und TIMEZONEKNOWN liefert 0
|
|
|
|
### 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
|