Files
TerminalBasic/docs/sprachreferenz.md
Chili Palmer 333e794540 Phase 1: Sprach-Frontend (Lexer, AST, Parser, Semantik) + Entscheidungen
Frontend:
- Lexer komplett: Typ-Suffixe, Literal-Typisierung (Entscheidung: > 7
  signifikante Stellen -> DOUBLE), Hex/Oktal, Zeilenfortsetzung mit _,
  Strings mit ""-Escape, case-insensitive Keywords (Bibliotheksnamen
  bleiben Bezeichner)
- AST fuer Module/Prozeduren/Anweisungen/Ausdruecke
- Parser: fehlertolerant, zeilenorientiert; Kern-Anweisungssatz inkl.
  Bloecke, ON [LOCAL] ERROR, DEF FN (einzeilig); Datei-E/A als
  Phase-3-Platzhalter
- Semantik: Symboltabellen, implizite Deklaration, DEFtype, OPTION
  EXPLICIT, Arrays, Builtin-Signaturen, Labelpruefung; Hardware-Features
  (PEEK/POKE/...) werden zur Compile-Zeit abgewiesen
- Meilenstein: Testkorpus parst und wird typgeprueft (corpus.rs); 27 Tests

Entscheidungen eingearbeitet:
- Binaries heissen tb (IDE) und tbc (Compiler)
- Dynamische Terminalgroesse statt 80x25 (Minimum 80x25, btop-artiger
  Hinweis darunter); tb-ui::screen mit resize(), Spike angepasst
- Vollstaendigkeits-Leitplanke: 100% Sprache/Stdlib minus deklarierte
  Non-Features; Original-Doku als Guiding Principle; Inventar-Aufgabe
- CURRENCY als i64-Festkomma; ISAM wird implementiert; breite Zeichen
  belegen 2 Zellen; GET/PUT-Strings als UTF-32; Blink als hell simuliert
- LICENSE: MIT

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

308 lines
16 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.
**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 az` (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).
## 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` (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 176, ISAM-Codes 8089, Forms-Codes
260480 — 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 031: 1631 = blinkend — bei uns als „hell"
simuliert, Entscheidung 2026-09-02; hg 07), `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 013, 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).