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

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

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