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>
11 KiB
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,1D3fü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 TYPEmit 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 a–z(buchstabenbereichsweise, wirkt pro Modul/Prozedur ab Deklaration). OPTION EXPLICITgibt 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, perOPTION BASE 1änderbar.$STATIC/$DYNAMICbzw. Kontext bestimmen statisch/dynamisch;REDIM(dynamisch, löscht Inhalt),ERASE(reinitialisiert statisch / gibt dynamisch frei). Max. 8 Dimensionen (TODO: prüfen).LBOUND/UBOUNDliefern Grenzen. - Konvertierung: implizit zwischen numerischen Typen mit Rundung
(Banker's Rounding bei
CINT/CLNGund 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 anSUB/FUNCTIONmacht 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)
^(Potenz)- unäres
- *,/(Fließkommadivision)\(Ganzzahldivision; Operanden werden gerundet auf INTEGER/LONG)MOD(Ganzzahlrest, Vorzeichen wie Dividend)+,-(+auch String-Verkettung)- Vergleiche
= <> < > <= >=(Zahlen und Strings; Strings codepoint-weise — Abweichung, s. u.) NOTANDORXOREQVIMP
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 IFSELECT CASE ausdruckmitCASE wert,CASE a TO b,CASE IS >= x,CASE ELSEFOR i = a TO b [STEP s] … NEXT [i](Grenzen werden einmal ausgewertet; Schleifenvariable numerisch)DO [WHILE|UNTIL b] … LOOP [WHILE|UNTIL b],WHILE … WENDEXIT FOR / EXIT DO / EXIT SUB / EXIT FUNCTION / EXIT DEFGOTO 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; AufrufCALL name(args)odername args(ohne Klammern).FUNCTION name (…) [STATIC] … name = wert … END FUNCTION; Typ über Suffix oderDEF…-Regel.- Parameterübergabe standardmäßig BYREF. Klammern um ein Argument
(
CALL f((x))) erzwingen Wertübergabe.BYVALnur inDECLAREfür externe Routinen (entfällt bei uns; TODO:BYVALallgemein erlauben?). - Arrays werden mit
name()übergeben, TYPEs BYREF. - Rekursion erlaubt (außer bei
STATIC-Semantik-Konflikten). DEF FNname (args) = ausdruckund Block-DEF FN … END DEF; AufrufFNname(…). 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-SequenzCHR$(0)+codeim Vorbild — Abweichung s. u.),INPUT$(n [,#f]).LOCATE [zeile][,spalte][,cursor an/aus][,start,ende],CSRLIN,POS(0).COLOR [vg][,hg](vg 0–31: 16–31 = blinkend; hg 0–7),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) undGET/PUT #n [,satznr] [,var]mit TYPE-Variablen;LSET/RSETfü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)
- UTF-8/Unicode statt CP437 (2026-09-01). Konsequenzen:
- STRING ist eine Folge von Unicode-Zeichen (Codepoints);
LENzä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).
- STRING ist eine Folge von Unicode-Zeichen (Codepoints);
- Keine Hardware-Nähe:
PEEK/POKE/INP/OUT/CALL ABSOLUTE/ Interrupts lösen Fehler 73 (Feature unavailable) aus. INKEY$für erweiterte Tasten liefert weiterhinCHR$(0) + code-Sequenzen mit den klassischen Scancodes (F1 =CHR$(0)+";"usw.), damit bestehender Code funktioniert. Zusätzliche moderne Tasten: TODO.- Kein
CHAIN/Overlay-Mechanismus;SHELLstartet die System-Shell. - Grafik-Anweisungen (
SCREEN n>0,PSET,LINE,CIRCLE,PAINT,DRAW) sind Nicht-Ziel (Fehler 73) — das Vorbild war im Forms-Modus ebenfalls textonly. PLAY/SOUND:BEEP= Terminal-Bell; Rest Nicht-Ziel (Fehler 73).WIDTH 40wird nicht unterstützt (nur 80×25).