Files
TerminalBasic/docs/sprachreferenz.md
Chili Palmer 7e6fa6ea88 Phase 0: Referenzdokumente, Fehlerkatalog, Testkorpus, Ratatui-Spike
Entscheidungen festgehalten: TBVM bestaetigt und eingebettet in die
Executables; durchgaengig UTF-8 statt CP437 (dokumentierte Abweichung).

- docs/: Sprachreferenz, Forms-Referenz, Dateiformate, TBVM-Design
- tb-runtime::errors: klassischer Laufzeitfehler-Katalog (implementiert)
- tb-ui::screen: 80x25-Unicode-Zellenpuffer mit 16-Farben-Abbildung,
  Scrollbereich (VIEW PRINT), Letterboxing; Ratatui-Widget + Tests
- Spike: cargo run -p tb-ui --example spike (Farben, Unicode, Tasten, Maus)
- tests/compat/: erste Referenzprogramme mit byte-genauer Sollausgabe

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 16:36:45 +02:00

241 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Sprachreferenz Terminal Basic
Rekonstruierte Referenz des Dialekts (DOS-BASIC, Stand 1992: prozedurale
BASIC-Familie mit Forms-Erweiterung). Dieses Dokument ist die verbindliche
Grundlage für Frontend und Runtime. Unklare Detailfragen sind mit `TODO`
markiert und werden per Testkorpus geklärt bzw. entschieden.
**Bewusste Abweichungen vom Vorbild** stehen am Ende des Dokuments.
---
## 1. Lexik
- **Zeilenorientiert.** Eine logische Zeile enthält eine oder mehrere
Anweisungen, getrennt durch `:`. Keine Zeilenfortsetzung im Vorbild
(`_` ist eine spätere Erfindung) — TODO: als Erweiterung erlauben?
- **Zeilennummern** sind optional und wirken als Labels. Alphanumerische
**Labels** enden mit `:` am Zeilenanfang (`Fehler:`).
- **Kommentare:** `REM` (ganze Anweisung) und `'` (bis Zeilenende).
`REM`/`'` am Zeilenanfang mit `$STATIC`/`$DYNAMIC`/`$INCLUDE: 'datei'`
sind **Metabefehle**.
- **Bezeichner:** Buchstabe, dann Buchstaben/Ziffern/`.`, max. 40 Zeichen,
case-insensitiv. Optionales Typ-Suffix als letztes Zeichen.
- **Typ-Suffixe:** `%` INTEGER · `&` LONG · `!` SINGLE · `#` DOUBLE ·
`$` STRING · `@` CURRENCY. `name`, `name%`, `name$` sind
**verschiedene Variablen**.
- **Keywords** sind reserviert und case-insensitiv; die IDE normalisiert
auf Großschreibung.
- **Numerische Literale:** dezimal (`123`, `1.5`, `1.5E3`, `1D3` für DOUBLE),
hexadezimal `&HFF`, oktal `&O777`; Suffixe wie bei Variablen (`10%`, `10&`,
`1.5#`, `2.5@`). Ohne Suffix: kleinster passender Typ (Ganzzahl → INTEGER,
sonst SINGLE/DOUBLE je nach Präzision; TODO: exakte Regel testen).
- **String-Literale:** `"…"`; doppeltes `""` ergibt ein Anführungszeichen.
## 2. Typsystem
| Typ | Suffix | Repräsentation | Wertebereich |
|---|---|---|---|
| INTEGER | `%` | i16 | 32 768 … 32 767 |
| LONG | `&` | i32 | 2 147 483 648 … 2 147 483 647 |
| SINGLE | `!` | f32 | ±3.4E38 |
| DOUBLE | `#` | f64 | ±1.8E308 |
| STRING | `$` | dynamischer Unicode-String | Länge 0 … 32 767 Zeichen |
| STRING * n | — | fester String, n Zeichen | nur in `TYPE`/`DIM` |
| CURRENCY | `@` | i64, Festkomma ×10 000 | ±922 337 203 685 477.5807 |
- **Benutzerdefinierte Typen:** `TYPE name … END TYPE` mit Elementen fester
Größe (numerische Typen, `STRING * n`, verschachtelte TYPEs). Kein
dynamischer STRING in TYPE.
- **Implizite Deklaration:** Erstverwendung deklariert die Variable. Ohne
Suffix gilt der Standardtyp SINGLE, änderbar per
`DEFINT/DEFLNG/DEFSNG/DEFDBL/DEFSTR/DEFCUR az` (buchstabenbereichsweise,
wirkt pro Modul/Prozedur ab Deklaration).
- `OPTION EXPLICIT` gibt es im Vorbild **nicht** — TODO: als opt-in
Erweiterung anbieten?
- **Arrays:** `DIM a(10)`, `DIM a(1 TO 10, 0 TO 5)`. Untergrenze standardmäßig
0, per `OPTION BASE 1` änderbar. `$STATIC`/`$DYNAMIC` bzw. Kontext bestimmen
statisch/dynamisch; `REDIM` (dynamisch, löscht Inhalt), `ERASE`
(reinitialisiert statisch / gibt dynamisch frei). Max. 8 Dimensionen
(TODO: prüfen). `LBOUND`/`UBOUND` liefern Grenzen.
- **Konvertierung:** implizit zwischen numerischen Typen mit Rundung
(Banker's Rounding bei `CINT`/`CLNG` und Zuweisung an Ganzzahl); Überlauf
→ Fehler 6. Keine implizite Konvertierung Zahl ↔ String (Fehler 13,
Type mismatch).
## 3. Deklarationen und Sichtbarkeit
- `DIM [SHARED] var[(dims)] [AS typ]``AS`-Klausel: INTEGER, LONG, SINGLE,
DOUBLE, STRING, STRING * n, CURRENCY, benutzerdefinierter Typ.
- `COMMON [SHARED] [/blockname/] liste` — modulübergreifend (Kette
CHAIN-kompatibel im Vorbild; TODO: Relevanz ohne CHAIN klären).
- `SHARED` (in Prozedur): Zugriff auf Modulebene-Variablen.
- `STATIC` (in Prozedur): Variablen behalten Werte zwischen Aufrufen;
`STATIC`-Attribut an `SUB`/`FUNCTION` macht alle lokalen Variablen statisch.
- `CONST name = ausdruck` — Konstanten (konstante Ausdrücke zur Compilezeit).
- `DECLARE SUB/FUNCTION name (parameter)` — Prototyp; die IDE erzeugt sie
automatisch beim Speichern.
## 4. Operatoren (nach Priorität, hoch → niedrig)
1. `^` (Potenz)
2. unäres `-`
3. `*`, `/` (Fließkommadivision)
4. `\` (Ganzzahldivision; Operanden werden gerundet auf INTEGER/LONG)
5. `MOD` (Ganzzahlrest, Vorzeichen wie Dividend)
6. `+`, `-` (`+` auch String-Verkettung)
7. Vergleiche `= <> < > <= >=` (Zahlen und Strings; Strings
codepoint-weise — Abweichung, s. u.)
8. `NOT`
9. `AND`
10. `OR`
11. `XOR`
12. `EQV`
13. `IMP`
Logische Operatoren sind **bitweise** auf Ganzzahlen; Vergleichsergebnis ist
INTEGER 1 (wahr) / 0 (falsch).
## 5. Kontrollfluss
- `IF b THEN … [ELSE …]` (einzeilig) und Block-`IF … THEN / ELSEIF / ELSE /
END IF`
- `SELECT CASE ausdruck` mit `CASE wert`, `CASE a TO b`, `CASE IS >= x`,
`CASE ELSE`
- `FOR i = a TO b [STEP s] … NEXT [i]` (Grenzen werden einmal ausgewertet;
Schleifenvariable numerisch)
- `DO [WHILE|UNTIL b] … LOOP [WHILE|UNTIL b]`, `WHILE … WEND`
- `EXIT FOR / EXIT DO / EXIT SUB / EXIT FUNCTION / EXIT DEF`
- `GOTO ziel`, `GOSUB ziel` / `RETURN [ziel]`
- `ON n GOTO liste`, `ON n GOSUB liste` (berechneter Sprung, 1-basiert;
0 oder > Anzahl: kein Sprung; negativ/>255: Fehler 5)
- `END` (Programmende), `STOP` (Unterbrechung → im IDE-Kontext Debugger),
`SYSTEM` (Programmende, im Vorbild „zurück zu DOS")
- `SLEEP [sekunden]`, `DO EVENTS`/`DOEVENTS` (Ereignisse verarbeiten —
zentral für Forms)
## 6. Prozeduren
- `SUB name (p1 AS t, p2(), …) [STATIC] … END SUB`; Aufruf `CALL name(args)`
oder `name args` (ohne Klammern).
- `FUNCTION name (…) [STATIC] … name = wert … END FUNCTION`; Typ über Suffix
oder `DEF…`-Regel.
- **Parameterübergabe standardmäßig BYREF.** Klammern um ein Argument
(`CALL f((x))`) erzwingen Wertübergabe. `BYVAL` nur in `DECLARE` für
externe Routinen (entfällt bei uns; TODO: `BYVAL` allgemein erlauben?).
- Arrays werden mit `name()` übergeben, TYPEs BYREF.
- Rekursion erlaubt (außer bei `STATIC`-Semantik-Konflikten).
- `DEF FNname (args) = ausdruck` und Block-`DEF FN … END DEF`; Aufruf
`FNname(…)`. Modulweit, kein eigener Namensraum.
## 7. Fehlerbehandlung
- `ON ERROR GOTO label` (aktiviert Handler), `ON ERROR GOTO 0` (deaktiviert;
in einem aktiven Handler: Fehler weiterreichen → Programmabbruch),
`ON ERROR RESUME NEXT` — TODO: prüfen, ob das Vorbild das kennt
(QB-Familie: nein; Forms-Dialekt: ja?).
- Im Handler: `RESUME` (fehlerauslösende Anweisung wiederholen),
`RESUME NEXT`, `RESUME label`.
- `ERR` (Code), `ERL` (Zeilennummer, nur numerische Zeilennummern!),
`ERROR n` (Fehler auslösen).
- Fehler im aktiven Handler → sofortiger Abbruch. Fehler ohne Handler →
Abbruch mit Meldung „Fehlertext in Zeile n" bzw. Debugger in der IDE.
- Fehlerkatalog: siehe `tb-runtime/src/errors.rs` (implementiert).
## 8. Ereignis-Traps (klassisch, ohne Forms)
`ON TIMER(n) GOSUB label` + `TIMER ON/OFF/STOP`; analog `ON KEY(n)`,
`ON PLAY`, `ON COM(n)`, `ON PEN`, `ON STRIG(n)`. Für Terminal Basic relevant:
`TIMER` und `KEY`; Rest: Nicht-Ziel (siehe Abweichungen).
## 9. Konsolen-E/A
- `PRINT [#n,] liste` — Trennzeichen `;` (direkt anschließend) und `,`
(nächste 14-Zeichen-Druckzone). Zahlen: führendes Leerzeichen bzw. `-`,
nachgestelltes Leerzeichen. Abschluss ohne `;`/`,` → Zeilenumbruch.
- `PRINT USING "format"; liste` — Formatzeichen: `#` Ziffer, `.` Dezimalpunkt,
`,` Tausendertrennung, `+`/`-` Vorzeichen, `$$` Währung, `**` Füllsterne,
`^^^^` Exponent, `&` String ganz, `!` erstes Zeichen, `\ \` n Zeichen,
`_` Literal-Escape.
- `INPUT ["prompt"{;|,}] var, …` (mit `;` vor Prompt: kein „? ");
`LINE INPUT` (ganze Zeile in String).
- `INKEY$` (nicht blockierend; "" wenn leer; erweiterte Tasten:
2-Zeichen-Sequenz `CHR$(0)+code` im Vorbild — Abweichung s. u.),
`INPUT$(n [,#f])`.
- `LOCATE [zeile][,spalte][,cursor an/aus][,start,ende]`, `CSRLIN`, `POS(0)`.
- `COLOR [vg][,hg]` (vg 031: 1631 = blinkend; hg 07), `CLS`,
`WIDTH` (80/40 — wir: nur 80), `VIEW PRINT oben TO unten` (Scrollbereich).
- `TAB(n)`, `SPC(n)` in PRINT-Listen.
- `BEEP`, `SOUND freq, dauer` (Terminal-Bell / Nicht-Ziel, s. Abweichungen),
`PLAY` (Nicht-Ziel).
- `KEY n, text$` / `KEY LIST` / `KEY ON/OFF` (Funktionstasten-Makros +
Statuszeile) — TODO: Umfang klären.
## 10. Datei-E/A
- `OPEN datei$ [FOR modus] [ACCESS zugriff] [lock] AS [#]n [LEN=reclen]`
Modi: `INPUT`, `OUTPUT`, `APPEND` (sequenziell), `RANDOM` (Standard),
`BINARY`.
- `CLOSE [#n, …]`, `RESET` (alle schließen).
- Sequenziell: `PRINT #`, `PRINT # USING`, `WRITE #` (CSV-artig, Strings in
`"…"`), `INPUT #`, `LINE INPUT #`, `EOF(n)`.
- Random: `FIELD #n, breite AS var$…` (klassisch) **und** `GET/PUT #n
[,satznr] [,var]` mit TYPE-Variablen; `LSET`/`RSET` für Feldpuffer.
- Binary: `GET/PUT #n, [pos], var`, `SEEK #n, pos` / `SEEK(n)`,
`LOC(n)`, `LOF(n)`.
- Verwaltung: `NAME alt$ AS neu$`, `KILL datei$`, `FILES [muster$]`,
`CHDIR`, `MKDIR`, `RMDIR`, `FILEATTR`, `FREEFILE`.
- Pfade: plattformneutral; `/` und `\` werden akzeptiert.
## 11. Eingebaute Funktionen (Katalog)
**Strings:** `LEN`, `LEFT$`, `RIGHT$`, `MID$` (Funktion **und** Anweisung),
`INSTR([start,] s$, such$)`, `UCASE$`, `LCASE$`, `LTRIM$`, `RTRIM$`,
`SPACE$`, `STRING$(n, zeichen|code)`, `ASC`, `CHR$`, `STR$`, `VAL`,
`HEX$`, `OCT$`, `LSET`/`RSET` (Anweisungen).
**Mathematik:** `ABS`, `SGN`, `INT` (abrunden), `FIX` (Richtung 0), `SQR`,
`EXP`, `LOG`, `SIN`, `COS`, `TAN`, `ATN`, `RND[(n)]`, `RANDOMIZE [saat]`
(kompatibler PRNG! → Testkorpus), `CINT`, `CLNG`, `CSNG`, `CDBL`, `CCUR`.
**Datum/Zeit:** `DATE$` (Funktion und Anweisung — Setzen: Nicht-Ziel),
`TIME$`, `TIMER` (Sekunden seit Mitternacht, SINGLE).
**Sonstiges:** `LBOUND`, `UBOUND`, `FRE(…)` (freier Speicher — liefert bei
uns Pseudowerte), `VARPTR`/`VARSEG`/`SADD`/`PEEK`/`POKE` → Nicht-Ziel
(Fehler 73), `ENVIRON$`, `COMMAND$`, `SHELL [cmd$]`.
**DATA:** `DATA konstanten`, `READ var, …`, `RESTORE [label]`.
## 12. Forms-Anbindung (Details in forms-referenz.md)
`form.eigenschaft = wert`, `form!steuerelement.eigenschaft`,
Ereignisprozeduren `SUB name_Ereignis (…)`, `LOAD`/`UNLOAD form`,
`form.SHOW [modal]`, `form.HIDE`, `MSGBOX`/`INPUTBOX$`-Funktionen,
`DOEVENTS`, `SCREEN`-Objekt (aktives Formular/Steuerelement).
---
## Abweichungen vom Vorbild (beschlossen)
1. **UTF-8/Unicode statt CP437** (2026-09-01). Konsequenzen:
- STRING ist eine Folge von Unicode-Zeichen (Codepoints); `LEN` zählt
Zeichen, nicht Bytes. `ASC`/`CHR$` arbeiten auf Codepoints
(`CHR$(9731)` = „☃"). `ASC("")` bleibt Fehler 5.
- String-Vergleich codepoint-weise (keine CP437-Sortierung).
- Der Bildschirmpuffer speichert Unicode-Zeichen; Rahmen werden mit
Unicode-Box-Drawing gezeichnet.
- Zeichen mit Darstellungsbreite ≠ 1: offen (siehe PLAN.md).
2. **Keine Hardware-Nähe:** `PEEK`/`POKE`/`INP`/`OUT`/`CALL ABSOLUTE`/
Interrupts lösen Fehler 73 (Feature unavailable) aus.
3. **`INKEY$` für erweiterte Tasten** liefert weiterhin
`CHR$(0) + code`-Sequenzen mit den klassischen Scancodes (F1 = `CHR$(0)+";"`
usw.), damit bestehender Code funktioniert. Zusätzliche moderne Tasten:
TODO.
4. **Kein `CHAIN`/Overlay-Mechanismus**; `SHELL` startet die System-Shell.
5. **Grafik-Anweisungen** (`SCREEN n>0`, `PSET`, `LINE`, `CIRCLE`, `PAINT`,
`DRAW`) sind Nicht-Ziel (Fehler 73) — das Vorbild war im Forms-Modus
ebenfalls textonly.
6. **`PLAY`/`SOUND`**: `BEEP` = Terminal-Bell; Rest Nicht-Ziel (Fehler 73).
7. **`WIDTH 40`** wird nicht unterstützt (nur 80×25).