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>
12 KiB
PLAN — Terminal Basic
Arbeitsplan für die Re-Implementierung des DOS-BASIC-Dialekts (1992) in Rust. Dieses Dokument wird fortgeschrieben: erledigte Punkte werden abgehakt, Entscheidungen mit Datum und Begründung festgehalten.
Konvention: [ ] offen · [x] erledigt · [~] in Arbeit
Leitplanken
- Referenzverhalten schlägt Eleganz. Bei Zweifeln zählt das beobachtbare Verhalten des Vorbilds (inkl. Fehlercodes, Rundung, Formatierung), nicht das, was „richtiger" wäre. Abweichungen werden dokumentiert.
- Markenrecht: Der Name des Vorbilds wird in Code, Doku und Artefakten nicht verwendet. Wir sprechen vom „Vorbild" bzw. „dem Dialekt".
- Jede Phase endet mit lauffähigen Tests gegen eine wachsende
Kompatibilitäts-Testsuite (Verzeichnis
tests/compat, geplant).
Phase 0 — Exploration und Grundsatzentscheidungen
0.1 Sprach- und Bibliotheksreferenz rekonstruieren
- [~] Sprachreferenz zusammentragen: Anweisungen, Funktionen, Operatoren,
Typsystem (INTEGER, LONG, SINGLE, DOUBLE, STRING, CURRENCY,
feste Strings,
TYPE…END TYPE), Typ-Suffixe (% & ! # $ @),DEFINT-Familie, Sichtbarkeitsregeln (SHARED,COMMON,STATIC) → Grundgerüst in docs/sprachreferenz.md; Detailfragen (exaktes Verhalten) werden dort als TODO markiert - [~] Forms-/Steuerelemente-Referenz: Steuerelementtypen, Eigenschaften, Methoden, Ereignisse, Standardwerte, Fokus- und Z-Reihenfolge → Grundgerüst in docs/forms-referenz.md
- [~] Dateiformate dokumentieren: Quelltext (
.BAS), Formulare (.FRM, Textformat mitVERSION/Begin…End-Blöcken), Projekt (.MAK) → docs/dateiformate.md - Laufzeitfehler-Katalog (Codes + Meldungstexte) erfasst und als
tb_runtime::errorsimplementiert (inkl. Tests) - [~] Testkorpus aufbauen: kleine Referenzprogramme, deren erwartete Ausgabe
dokumentiert ist → erste Programme in
tests/compat/, Harness folgt mit Phase 2 (tbdosc run+ Ausgabevergleich)
0.2 VM-/Runtime-Entscheidung (Aufstellung der Optionen)
Anforderungen an die Ausführungsschicht: BASIC-Fehlersemantik
(ON ERROR GOTO/RESUME mit ERR/ERL), GOSUB/RETURN,
unterbrechbare Ausführung (Debugger-Einzelschritt, Strg+Untbr,
ereignisgesteuerte Forms-Hauptschleife), dynamische Strings/Arrays mit
BASIC-Semantik, serialisierbares Kompilat, gute Fehlerortung (Zeile/Spalte).
| Option | Vorteile | Nachteile | Eignung |
|---|---|---|---|
| 1. Eigene Bytecode-VM in Rust („TBVM") | Volle Kontrolle über Semantik (Fehlerbehandlung, GOSUB, Events, Suspend/Resume); Bytecode als .tbc serialisierbar; Debugger-Integration trivial (Zeileninfo im Bytecode); keine Fremdabhängigkeit |
Eigenaufwand für VM, GC/Refcounting für Strings/Arrays; „nur" Interpreter-Geschwindigkeit | Empfohlen — Interpreter-Tempo genügt für 80×25-Programme bei Weitem |
| 2. Tree-Walking-Interpreter | Schnellster Weg zu ersten laufenden Programmen; ideal zum Validieren der Semantik | Langsam; Debugger/Resume-Semantik unsauber; wäre später Wegwerfcode | Optional als Bootstrap in Phase 1, dann ersetzen |
| 3. WebAssembly-Ziel (wasmtime/wasmer) | Ausgereifte VMs mit JIT (Cranelift); portables, standardisiertes Kompilat; Sandbox; theoretisch Browser-Ausführung | BASIC-Semantik passt schlecht: ON ERROR/RESUME, GOSUB und unterbrechbare Ausführung müssen aufwendig transformiert werden (Relooper/State-Machine); Strings/Arrays komplett selbst verwalten; Debugging über zwei Ebenen; schwere Abhängigkeit |
Später als zweites Backend denkbar (gleiche IR), nicht als Start |
| 4. Cranelift direkt (JIT auf nativen Code) | Native Geschwindigkeit; Rust-eigenes Projekt, gut eingebettet | Gleiche Semantik-Transformationsprobleme wie WASM; kein portables Kompilat; JIT auf allen drei Plattformen pflegen | Nur falls Performance je Thema wird — unwahrscheinlich |
| 5. LLVM-AOT (inkwell) | Maximale Performance, echte Executables | Sehr schwere Toolchain-Abhängigkeit (LLVM-Build je Plattform); lange Kompilierzeiten; Debugger/Edit-Run-Zyklus der IDE leidet massiv | Ungeeignet für dieses Projekt |
| 6. Transpilation auf fremde Skript-VM (Lua via mlua/piccolo) | Ausgereifte VM mit GC geschenkt | Semantik-Mismatch (Zahlentypen, Fehler-/Eventmodell, 1-basierte vs. BASIC-Arrays mit OPTION BASE); Fehlerortung und Debugger bilden schlecht ab |
Ungeeignet |
| 7. Transpilation nach Rust (AOT) | Native Binaries, keine VM | Kein Interpretermodus → IDE-Kernfeatures (Direktfenster, Start ohne Build-Wartezeit, Debugger) praktisch unmöglich; Rust-Toolchain als Laufzeitvoraussetzung | Ungeeignet als Primärziel |
Entscheidung (2026-09-01, bestätigt): Eigene Stack-basierte Bytecode-VM
(Option 1, TBVM), sowohl in der Entwicklungsphase als auch eingebettet in
die Executables (tbdos-IDE und von tbdosc erzeugte Programme), damit der
schnelle Edit-Run-Turnaround überall identisch ist.
Die IR/Bytecode-Schicht wird sauber vom Interpreter getrennt, sodass später ein
zweites Backend (WASM via Cranelift, Option 3) ergänzt werden kann, ohne das
Frontend anzufassen. Für die Ausführungsgeschwindigkeit typischer
Terminal-Programme ist ein Interpreter mehr als ausreichend; entscheidend sind
Debugger-Fähigkeit, exakte Fehlersemantik und der schnelle Edit-Run-Zyklus der IDE.
- Optionen aufstellen und bewerten (siehe Tabelle)
- [~] Bytecode-Grobdesign: Stack-Maschine, Konstantenpool, Zeilentabelle für Diagnostik/Debugger, Aufrufkonventionen (SUB/FUNCTION vs. GOSUB) → Entwurf in docs/tbvm-design.md
- [~] Speichermodell für Werte: Tagged Enum (kein NaN-Boxing); Refcounting für Strings/Arrays (kein Zyklenrisiko im Dialekt → Rc genügt) → Entwurf in docs/tbvm-design.md
0.3 Ratatui-/Terminal-Spike
Entscheidung (2026-09-01): Keine CP437-Emulation — der Textbildschirm ist
durchgängig UTF-8/Unicode. Strings sind Unicode-Text, CHR$/ASC
arbeiten auf Codepoints. Das ist eine bewusste Abweichung vom Vorbild und wird
in der Sprachreferenz unter „Abweichungen" dokumentiert.
- 80×25-Zellenpuffer (Zeichen + Farbattribut) als eigenes Widget rendern;
kleinere Terminals: Hinweis „Terminal zu klein", größere: zentriert
(Letterboxing) →
tb-ui::screen, Demo:cargo run -p tb-ui --example spike - 16-Farben-Palette (Vordergrund 0–15, Hintergrund 0–7) auf ANSI-Indexfarben abgebildet; Blink-Attribut vorerst ignoriert (offen)
- [~] Maus-Ereignisse (crossterm) und Sondertasten (F1–F12, Alt-Kombis) im Spike sichtbar gemacht; systematischer Test unter Windows Terminal, gnome-terminal, iTerm2 steht aus
- Ereignisschleifen-Architektur: Terminal-Events → Event-Queue → VM-Ticks
Phase 1 — Sprach-Frontend (tb-frontend)
- Lexer inkl. Typ-Suffixe, Zeilennummern/Labels,
REM/'-Kommentare, case-insensitive Keywords - AST für Module, Prozeduren, Anweisungen, Ausdrücke, Deklarationen
- Parser (zeilenorientiert, fehlertolerant — die IDE prüft pro Zeile)
- Semantik: Symboltabellen, implizite Deklaration,
DEFINT-Regeln, Typprüfung und -konvertierung,OPTION EXPLICIT/OPTION BASE, Array-Dimensionierung (statisch/dynamisch,REDIM) - Diagnostik mit exakten Positionen und den Meldungstexten des Vorbilds
- Meilenstein: kompletter Testkorpus parst und wird typgeprüft
Phase 2 — Bytecode und VM (tb-vm)
- Bytecode-Format und Serialisierung (
.tbc) - Codegenerator AST → Bytecode
- Interpreter: Ausdrücke, Kontrollfluss (
IF,SELECT CASE, Schleifen,GOTO/GOSUB), Prozeduraufrufe,BYREF-Semantik - Fehlerbehandlung:
ON ERROR GOTO/RESUME,ERR/ERL, Fehlerkaskaden - Unterbrechbarkeit: Tick-Grenzen, Breakpoints, Einzelschritt, Variableninspektion (Grundlage für IDE-Debugger)
- Meilenstein: Konsolen-Testkorpus läuft mit korrekter Ausgabe (
tbdosc run)
Phase 3 — Laufzeitbibliothek (tb-runtime) und Bildschirm (tb-ui::screen)
- Strings:
LEFT$,MID$(auch als Anweisung),INSTR,STR$/VAL,SPACE$,STRING$,LTRIM$/RTRIM$,UCASE$/LCASE$… - Zahlenformatierung:
PRINT-Zonen,PRINT USING(vollständig), Banker's Rounding,CINT/CLNG/CSNG/CDBL/CCUR - Mathematik:
RND/RANDOMIZE(kompatibler PRNG!), trigonometrische Funktionen, Integer-Überlaufverhalten (Fehler 6) - Datei-E/A:
OPEN(sequenziell/random/binär),INPUT#/LINE INPUT#,PRINT#/WRITE#,GET/PUTmit Record-Typen,EOF/LOF/SEEK, Pfadsemantik plattformübergreifend - Bildschirm:
PRINT,LOCATE,COLOR,CLS,INPUT,INKEY$,CSRLIN/POS,WIDTH,VIEW PRINTauf dem Zellenpuffer - Meilenstein: klassische Konsolenprogramme laufen unverändert
Phase 4 — Forms-Engine (tb-ui::forms)
- Formular-Modell: Eigenschaften, Lade-/Entladezyklus,
SHOW/HIDE(modal/nicht-modal) - Steuerelemente: CommandButton, TextBox, ListBox, ComboBox, CheckBox, OptionButton, Frame, Label, HScrollBar/VScrollBar, PictureBox (Text), Timer — mit allen Eigenschaften/Methoden/Ereignissen des Vorbilds
- Menüsystem (Menüleiste, Shortcuts, Access Keys)
- Fokus-/Tab-Reihenfolge, Access-Keys, Maussteuerung
.FRM-Dateiformat lesen/schreiben (Kompatibilität zu Originaldateien)- Ereignisdispatch: Event-Queue ↔ VM (Ereignisprozeduren
Name_Ereignis) - Meilenstein: Beispiel-Formularprogramme aus dem Testkorpus laufen
Phase 5 — IDE (tb-ide)
- IDE-Rahmen: Menüleiste, MDI-artige Fensterverwaltung, Statuszeile, klassisches Farbschema
- Editor: Syntaxprüfung/-normalisierung pro Zeile (Keywords groß, Leerzeichen), Suchen/Ersetzen, Hilfe-Verweise
- Projektverwaltung (
.MAK): mehrere Module/Formulare - Formular-Designer: Steuerelemente platzieren/verschieben/skalieren, Eigenschaftenfenster
- Ausführen aus der IDE: Start/Unterbrechen/Fortsetzen/Neustart
- Debugger: Breakpoints, Einzelschritt/Prozedurschritt, Direktfenster, Überwachungsausdrücke
- Meilenstein: Programm komplett in der IDE schreiben, gestalten, debuggen und ausführen
Phase 6 — Kompatibilität, Politur, Distribution
- Kompatibilitäts-Testsuite ausbauen (Snapshot-Tests der Bildschirmausgabe)
- Plattformtests: Windows Terminal, Linux (mind. 2 Emulatoren), macOS
- Performance-Pass über die VM (nur falls nötig)
tbdosc build→ binäres Ergebnis:.tbc-Bytecode bzw. eigenständig ausführbares Programm (Bytecode + eingebetteter Runner)- Dokumentation: Sprachreferenz, Migrationshinweise, Beispielprogramme
- CI (GitHub Actions: Build + Tests auf allen drei Plattformen), Releases
Offene Fragen
- Umgang mit
CURRENCY(@): als i64-Festkomma (×10 000) — klären, ob das Vorbild ihn überhaupt in allen Kontexten unterstützt - ISAM-Dateiunterstützung des Vorbilds: nachbilden oder als Nicht-Ziel erklären?
PEEK/POKE/CALL INTERRUPTu. ä. Hardware-Nähe: sinnvolle Teilmenge emulieren (z. B. Bildschirmspeicher B800) oder Laufzeitfehler?- Unicode-Zeichen mit Darstellungsbreite ≠ 1 (Emoji, CJK): das Zellenmodell ist strikt 1 Zeichen = 1 Zelle. Breite Zeichen ablehnen (Fehler 5), ersetzen oder 2 Zellen belegen?
GET/PUTmit Record-Typen unter UTF-8: feste Strings (STRING * n) waren im Vorbild n Bytes — bei uns n Zeichen. Binärlayout der Records festlegen (Vorschlag: UTF-8 mit Padding auf feste Bytelänge? Oder UTF-32?)- Blink-Attribut (
COLORmit Vordergrund 16–31): ignorieren, als „hell" darstellen oder echtes Terminal-Blink nutzen (nicht überall unterstützt)? - Lizenzwahl (MIT im Rahmen angenommen — bestätigen)