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>
16 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.
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,1D3fü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 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 EXPLICITexistiert (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, perOPTION BASE 1änderbar.$STATIC/$DYNAMICbzw. Kontext bestimmen statisch/dynamisch;REDIM(dynamisch, löscht Inhalt),ERASE(reinitialisiert statisch / gibt dynamisch frei). Max. 60 Dimensionen, Indizes −32 767 … 32 767 (mitTO).LBOUND/UBOUNDliefern Grenzen. (DOS-Speichergrenzen der Arrays — 64K/128K-Regeln — übernehmen wir nicht.) - 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(mitEXIT DEF); AufrufFNname(…). 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}.LOCALinstalliert einen prozedurlokalen Handler; ohneLOCALgilt der Handler modulweit.GOTO 0deaktiviert (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 viaERROR nverwendet 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-SequenzCHR$(0)+codeim 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) 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. - 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)
- 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 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).
- STRING ist eine Folge von Unicode-Zeichen (Codepoints);
- Keine Hardware-Nähe (bestätigt 2026-09-02):
PEEK/POKE/INP/OUT/WAIT/BLOAD/BSAVE/VARPTR/VARSEG/SADD/CALL ABSOLUTE/CALL INTERRUPT/IOCTLwerden bereits zur Compile-Zeit mit der Meldung „Feature unavailable" abgewiesen (kein Laufzeitfehler). 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, 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. PLAY/SOUND:BEEP= Terminal-Bell; Rest Nicht-Ziel (Fehler 73).- 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 PRINTund dasSCREEN-Objekt arbeiten auf der tatsächlichen Größe.WIDTH 40/80wird akzeptiert, ändert aber nichts (die Größe bestimmt das Terminal).