# 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. **Primärquellen:** Original-Hilfedateien des Vorbilds als HTML unter https://dos-help.soulsphere.org/ (`qck.hlp` Quick Reference, `advr.hlp` Details, `ener.hlp` Fehlermeldungen; Vergleich PDS 7.1: `bas7qck.hlp`) sowie das Original-README der Professional Edition. Aussagen ohne TODO-Markierung sind daraus belegt. **Guiding Principle:** Die Original-Dokumentation führt. Jedes dort dokumentierte Sprachelement wird unterstützt oder — nur nach expliziter Non-Feature-Entscheidung des Projektinhabers — mit einer klaren Fehlermeldung abgewiesen. **Bewusste Abweichungen und Non-Features** stehen am Ende dieses Dokuments; nur diese Liste rechtfertigt Abweisungen. --- ## 1. Lexik - **Zeilenorientiert.** Eine logische Zeile enthält eine oder mehrere Anweisungen, getrennt durch `:`. **Zeilenfortsetzung:** `_` als letztes Zeichen (mit vorangehendem Leerzeichen/Tab) setzt die Zeile fort — im Vorbild für extern erstellten Code gedacht (der IDE-Editor fügt fortgesetzte Zeilen beim Laden zusammen); Terminal Basic akzeptiert es überall. - **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. (`_` in Bezeichnern ist eine dokumentierte Neuerung des Vorbilds.) - **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 erzwingen den Typ (`10%`, `10&`, `1.5!`, `1.5#`, `2.5@`). Ohne Suffix gilt: Ganzzahl im INTEGER-Bereich → INTEGER; Hex/Oktal ohne `&`-Suffix → 16 Bit (INTEGER), mit `&` → LONG; `E`-Exponent → SINGLE, `D`-Exponent → DOUBLE; Dezimalpunkt-Wert: **Entscheidung (2026-09-02):** bis 7 signifikante Stellen → SINGLE, darüber → DOUBLE. (Die Original-Hilfe nennt widersprüchlich eine 15-Stellen-Schwelle, die SINGLE-Präzision verlöre; wir legen die Schwelle auf die SINGLE-Genauigkeit — präzisionserhaltende Auslegung.) Ganzzahl außerhalb des LONG-Bereichs wird Gleitkommawert (kein Fehler). - **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 a–z` (buchstabenbereichsweise, wirkt pro Modul/Prozedur ab Deklaration). - `OPTION EXPLICIT` **existiert** (Neuerung des Vorbilds gegenüber der QB/PDS-Familie): erzwingt Deklaration aller Variablen (über COMMON, CONST, DEF FN, DIM, FUNCTION, REDIM, SHARED, STATIC oder SUB); gilt auch für das Direktfenster. - **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. **60 Dimensionen**, Indizes −32 767 … 32 767 (mit `TO`). `LBOUND`/`UBOUND` liefern Grenzen. (DOS-Speichergrenzen der Arrays — 64K/128K-Regeln — übernehmen wir nicht.) - **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). Die vollständige Konvertierungsmatrix (Ergebnistypen der Operatoren, Rundungs- und Überlaufregeln, Textdarstellung) steht in docs/tbvm-design.md und ist über `tests/compat/konvertierung.bas` ausführbar verankert (Phase 2, 2026-09-02). ## 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"). Runner-Verhalten (Entscheidung 2026-09-02): außerhalb der IDE beendet `STOP` das Programm mit der Meldung `STOP in line n` und Exit-Code ≠ 0; `END`/`SYSTEM`/Programmende liefern Exit-Code 0. `CONT` existiert nur als Direktmodus-Befehl der IDE (Phase 5) und ist nicht kompilierbar. - `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` (mit `EXIT DEF`); Aufruf `FNname(…)`. Nur auf Modulebene definier- und nutzbar, **nicht rekursiv**, Argumente by value, keine Arrays/Records/ Fixed-Strings als Argumente. ## 7. Fehlerbehandlung - Vollständige Syntax: `ON [LOCAL] ERROR {GOTO zeile/label | RESUME NEXT | GOTO 0}`. `LOCAL` installiert einen **prozedurlokalen** Handler; ohne `LOCAL` gilt der Handler modulweit. `GOTO 0` deaktiviert (in einem aktiven Handler: Fehler weiterreichen → Programmabbruch). - Im Handler: `RESUME` (fehlerauslösende Anweisung wiederholen), `RESUME NEXT`, `RESUME zeile/label`. - `ERR` (Code), `ERL` (Zeilennummer, nur numerische Zeilennummern!), `ERROR n` (Fehler auslösen). Fehlernummer 105 ist im Vorbild intern reserviert und sollte nicht via `ERROR n` verwendet werden. - Fehler im aktiven Handler → sofortiger Abbruch. Fehler ohne Handler → Abbruch mit Meldung „Fehlertext in Zeile n" bzw. Debugger in der IDE. - Fehlerkatalog: klassische Codes 1–76, ISAM-Codes 80–89, Forms-Codes 260–480 — vollständig implementiert in `tb-runtime/src/errors.rs`. ## 8. Ereignis-Traps (klassisch, ohne Forms) Vorhanden im Vorbild: `ON COM/KEY/PEN/PLAY/STRIG/TIMER/UEVENT(…) GOSUB label` mit Steueranweisungen `… ON/OFF/STOP`, dazu das `EVENT`-Statement (auch für Forms-Ereignisse) und `SetUEvent`. Timer-Events und Trapping sind ausgesetzt, solange ein Menü-Control den Fokus hat. Für Terminal Basic relevant: `TIMER`, `KEY`, `UEVENT`; `COM/PEN/PLAY/STRIG`: Nicht-Ziel (Fehler 73). ## 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 (offizielle Liste): numerisch `#` Ziffer, `.` Dezimalpunkt, `,` Tausendertrennung, `+` Vorzeichen- position, `-` nachgestelltes Minus, `$$` führendes $, `**` Füllsterne, `**$` kombiniert, `^^^^` Exponent; String `&` ganz, `!` erstes Zeichen, `\ \` n Zeichen (Leerzeichen + 2); `_` Literal-Escape; alle anderen Zeichen literal. TODO: `%`-Präfix bei Feldüberlauf (QB-Verhalten) in der Vorbild-Hilfe nicht gelistet — per Test klären, wir übernehmen es vorerst. - `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)` — arbeiten auf der tatsächlichen Bildschirmgröße (dynamisch, s. Abweichungen). - `COLOR [vg][,hg]` (vg 0–31: 16–31 = blinkend — bei uns als „hell" simuliert, Entscheidung 2026-09-02; hg 0–7), `CLS`, `WIDTH` (im Vorbild 80/40 — bei uns wirkungslos, s. Abweichungen), `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`. - **ISAM** (Professional-Edition des Vorbilds): wird unterstützt (Entscheidung 2026-09-02) — `OPEN … FOR ISAM`, Tabellen/Indizes, `SEEKGT`-Familie usw.; Umfang wird mit dem Inventar (Phase 3) erfasst. - 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); dazu die nativen Schlüsselwörter des Vorbilds (Neuerung ggü. QB/PDS): `NOW`, `DATESERIAL`, `DATEVALUE`, `TIMESERIAL`, `TIMEVALUE`, `DAY`, `MONTH`, `YEAR`, `WEEKDAY`, `HOUR`, `MINUTE`, `SECOND` (serielle Datumswerte als DOUBLE) sowie `FORMAT$` (Zahlen-, Datums-/Zeitformatierung). **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 [style%]` (0 = modeless, Standard; 1 = modal), `form.HIDE`, `DOEVENTS()`, `SCREEN`-Objekt, Datentypen `FORM`/`CONTROL`, `IF TYPEOF`, `$FORM`-Metabefehl, Include-Dateien (Konstanten-`.BI`). Dialoge: - `MSGBOX msg$ [,type% [,title$]]` (Anweisung) bzw. Funktion → INTEGER. `type%` = Buttons (0 OK · 1 OK/Cancel · 2 Abort/Retry/Ignore · 3 Yes/No/Cancel · 4 Yes/No · 5 Retry/Cancel) + Default-Button (0/256/512). Rückgabe 1 OK · 2 Cancel/Esc · 3 Abort · 4 Retry · 5 Ignore · 6 Yes · 7 No. Keine Icon-Flags (Textmodus). msg$ max. 1024 Zeichen, modal. - `INPUTBOX$(prompt$ [,title$ [,default$ [,xpos%, ypos%]]])` → STRING; Cancel liefert `""`; Position in **Zeichen**, sonst zentriert; Dialog fest 46×16 Zeichen; prompt$ max. 255 Zeichen. --- ## 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 2 (Emoji, CJK) **belegen zwei Zellen** (Entscheidung 2026-09-02; Umsetzung Phase 3). - `GET`/`PUT`-Records: feste Strings (`STRING * n`) werden als **UTF-32** gespeichert (4 Bytes/Zeichen, feste Record-Länge) — Binärdateien sind dadurch bewusst inkompatibel zum Vorbild (Entscheidung 2026-09-02). 2. **Keine Hardware-Nähe** (bestätigt 2026-09-02): `PEEK`/`POKE`/`INP`/ `OUT`/`WAIT`/`BLOAD`/`BSAVE`/`VARPTR`/`VARSEG`/`SADD`/`CALL ABSOLUTE`/ `CALL INTERRUPT`/`IOCTL` werden bereits **zur Compile-Zeit** mit der Meldung „Feature unavailable" abgewiesen (kein Laufzeitfehler). 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`, Grafik-`GET`/`PUT`, `PALETTE`, `WINDOW`/`VIEW`, `PMAP`, `PCOPY`) sind Nicht-Ziel (Fehler 73). Hinweis: das Vorbild **hatte** die Grafikmodi 0–13, aber nicht gleichzeitig mit Forms (dort Fehler 272 „Invalid when forms are showing") — wir übernehmen nur den Forms-/Textmodus-Zweig. 6. **`PLAY`/`SOUND`**: `BEEP` = Terminal-Bell; Rest Nicht-Ziel (Fehler 73). 7. **Dynamische Bildschirmgröße** (2026-09-02): kein festes 80×25 — der Bildschirm folgt der Terminalgröße (Mindestgröße 80×25, darunter nur ein Hinweis). `CSRLIN`/`POS`/`LOCATE`/`VIEW PRINT` und das `SCREEN`-Objekt arbeiten auf der tatsächlichen Größe. `WIDTH 40/80` wird akzeptiert, ändert aber nichts (die Größe bestimmt das Terminal).