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

11 KiB
Raw Blame History

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).