# sprach-frontend Specification ## Purpose Das Sprach-Frontend übersetzt Terminal-Basic-Quelltext (VBDOS-kompatibler Dialekt) in einen typgeprüften AST: Lexer, fehlertoleranter Parser und semantische Analyse mit Diagnostik nach den Meldungstexten des Vorbilds. ## Requirements ### Requirement: Lexikalische Analyse nach Sprachreferenz Der Lexer SHALL Quelltext gemäß docs/sprachreferenz.md §1 zerlegen: Typ-Suffixe (`% & ! # $ @`) an Bezeichnern und Literalen, case-insensitive Keywords (Bibliotheksnamen bleiben Bezeichner), `'`/`REM`-Kommentare, `:`-Anweisungstrenner, Zeilenfortsetzung mit `_`, Hex- (`&H`) und Oktal-Literale (`&O`), `""`-Escape in Strings sowie die Metabefehle `'$INCLUDE`, `'$STATIC`, `'$DYNAMIC`. #### Scenario: Suffix trennt Bibliotheksfunktion von Keyword - **WHEN** der Lexer `STRING$` und `STRING` liest - **THEN** wird `STRING$` als Bezeichner (Builtin) und `STRING` als Typ-Keyword erkannt #### Scenario: Zeilenfortsetzung - **WHEN** eine Zeile nach Leerraum mit `_` endet - **THEN** wird die Folgezeile ohne Zeilenende-Token angeschlossen ### Requirement: Literal-Typisierung Der Lexer SHALL numerische Literale typisieren: Ganzzahlen INTEGER → LONG → Gleitkomma nach Wertebereich; `E`-Exponent → SINGLE, `D`-Exponent → DOUBLE; Suffixe erzwingen den Typ (Überlauf → Diagnose „Overflow"); Dezimalpunkt-Literale bis 7 signifikante Stellen → SINGLE, darüber → DOUBLE (Projektentscheidung 2026-09-02); `@`-Literale als Currency-Festkomma ×10 000. #### Scenario: Schwelle SINGLE/DOUBLE - **WHEN** die Literale `1.5` und `3.14159265` gelext werden - **THEN** ist `1.5` SINGLE und `3.14159265` DOUBLE ### Requirement: Vollständige Anweisungs-Grammatik Der Parser SHALL den Anweisungssatz des Dialekts in einen AST überführen, darunter: Zuweisung inkl. `MID$`-Anweisung, `PRINT`/`LPRINT` (mit `USING`/`#`-Datei, `?`-Kurzform), `INPUT`/`LINE INPUT`, `IF` (Block und einzeilig, `THEN zeilennummer`), `SELECT CASE` (Werte, `TO`-Bereiche, `IS`-Vergleiche, `CASE ELSE`), `FOR`/`DO`/`WHILE`-Schleifen mit `EXIT`, `GOTO`/`GOSUB`/`RETURN`/`ON … GOTO|GOSUB`, `ON [LOCAL] ERROR`/`RESUME`, `DIM`/`REDIM`/`ERASE`/`CONST`/`DEFtype`/`OPTION`/`TYPE…END TYPE`/ `DECLARE`/`SUB`/`FUNCTION`/`CALL`, `COMMON`/`SHARED`/`STATIC`, `DATA`/`READ`/`RESTORE`, `DEF FN` (einzeilig und Blockform), `VIEW PRINT`, `NAME … AS`, Ereignissteuerung (`TIMER/KEY(n)/UEVENT ON|OFF|STOP`) sowie die komplette Datei-E/A-Grammatik (`OPEN` in beiden Syntaxen inkl. `ISAM`-/`ACCESS`-/`LOCK`-Klauseln, `CLOSE`, `FIELD`, `GET`/`PUT`, `LSET`/`RSET`, `WRITE`, `SEEK`, `LOCK`/`UNLOCK`). Zeilennummern und Labels am Zeilenanfang SHALL als Sprungziele erfasst werden. #### Scenario: Blockstrukturen über mehrere Zeilen - **WHEN** ein `SELECT CASE` mit `CASE 1: PRINT "eins"` und `CASE ELSE` geparst wird - **THEN** entsteht ein Select-Knoten mit zwei Armen und Inline-Anweisungen im ersten Arm #### Scenario: Fehlertoleranz je Anweisung - **WHEN** eine Anweisung einen Syntaxfehler enthält - **THEN** wird eine Diagnose mit Position gesammelt, bis zum Anweisungsende synchronisiert und die Folgeanweisung normal geparst ### Requirement: Semantische Prüfung Die Semantik SHALL prüfen: Typkompatibilität (numerisch ↔ numerisch, String ↔ String; sonst „Type mismatch"), implizite Deklaration bei Erstverwendung (Suffix-getrennte Variablen), `DEFtype`-Buchstabenbereiche, `OPTION EXPLICIT` („Variable not defined"), Arrays (implizit, `DIM`, `REDIM`; doppelte explizite Deklaration → „Duplicate definition"), UDT-Feldzugriffe (`kunde.name` mit Feldtyp; unbekanntes Feld → „Element not defined"), `SHARED`-Import von Modulvariablen in Prozeduren, Konstantenfaltung für `CONST` (nicht konstant → „Invalid constant"), Sprungziele („Label not defined") sowie Prozedur- und Builtin-Signaturen (Anzahl → „Argument-count mismatch", Typen). #### Scenario: Typkonflikt - **WHEN** `a$ = 1` geprüft wird - **THEN** wird „Type mismatch" mit Quelltextposition gemeldet #### Scenario: UDT-Feldtyp - **WHEN** ein `TYPE` mit Feld `Name AS STRING * 30` deklariert ist und `k.Name = 5` geprüft wird - **THEN** wird „Type mismatch" gemeldet ### Requirement: Compile-Zeit-Abweisung deklarierter Non-Features Die Semantik MUST jede Verwendung der in docs/sprachreferenz.md („Abweichungen") deklarierten Non-Features — Hardware-Nähe (`PEEK`, `POKE`, `INP`, `OUT`, `WAIT`, `BLOAD`/`BSAVE`, `VARPTR`-Familie, `CALL ABSOLUTE`/`INTERRUPT`, `IOCTL`), `CHAIN`, Grafik-Anweisungen, `SOUND`/ `PLAY` und die Event-Geräte `COM`/`PEN`/`PLAY`/`STRIG` — zur Compile-Zeit mit der Meldung „Feature unavailable" abweisen. Dokumentierte Features außerhalb dieser Liste MUST NOT abgewiesen werden (Guiding Principle). #### Scenario: Hardware-Zugriff - **WHEN** `POKE 100, 1` oder `x = PEEK(100)` kompiliert wird - **THEN** erscheint die Compile-Diagnose „Feature unavailable" ### Requirement: Diagnostik mit Vorbild-Meldungen Diagnosen SHALL exakte 1-basierte Quelltextpositionen (Zeile, Spalte) tragen; die Meldungstexte SHALL dem Katalog des Vorbilds folgen, wo eine Entsprechung existiert (z. B. „Type mismatch", „Duplicate definition", „Label not defined", „Subprogram not defined", „Overflow"). #### Scenario: Position in der Diagnose - **WHEN** in Zeile 3 ein Typkonflikt auftritt - **THEN** nennt die Diagnose Zeile 3 und die Spalte des Ausdrucks ### Requirement: Typisiertes HIR als Sema-Ausgabe Die semantische Analyse SHALL neben den Diagnosen einen typisierten, abgesenkten Zwischenbaum (HIR) liefern, der die Codegen-Eingabe bildet: Namen sind zu Slot-/Tabellenindizes aufgelöst, jeder Ausdrucksknoten trägt seinen Ergebnistyp, `CONST`-Werte sind gefaltet, `DEFtype`-Regeln angewandt und implizite Konvertierungen als explizite Konvertierungsknoten materialisiert. Bei Diagnosen mit Fehlercharakter darf kein HIR für das betroffene Konstrukt garantiert werden; ein diagnose-freies Modul MUST ein vollständiges HIR ergeben. #### Scenario: Konvertierung wird materialisiert - **WHEN** `d# = i% + 1.5#` semantisch analysiert wird - **THEN** enthält das HIR einen expliziten Konvertierungsknoten INTEGER→DOUBLE unter dem Additionsknoten und die Zuweisung trägt den Zieltyp DOUBLE #### Scenario: Diagnose-freies Modul liefert HIR - **WHEN** ein Korpusmodul ohne Diagnosen analysiert wird - **THEN** liefert das Frontend ein HIR, aus dem der Codegen ohne erneute Namens- oder Typauflösung Bytecode erzeugen kann ### Requirement: Jedes dokumentierte Element besitzt eine Signatur Die Semantik SHALL für jedes im Sprachinventar geführte Element eine Signatur besitzen (Argumentanzahl und -typen) — auch für Elemente, deren Laufzeitverhalten noch aussteht. Ein im Inventar geführtes Element MUST NOT als unbekannter Bezeichner oder mit einem generischen Syntaxfehler abgewiesen werden; die Diagnose bzw. der Laufzeitfehler MUST es namentlich nennen (Guiding Principle). Das gilt insbesondere für die Gruppen, die bisher gar keine Signatur haben: die finanzmathematischen Funktionen, die Record-Konvertierungsfunktionen (`MK…$`/`CV…`), die Dateisystem-Funktionen `CURDIR$`, `DIR$`, `CHDRIVE`, sowie `LPOS`, `STACK`, `SYSTEM`, `ERDEV`/`ERDEV$` und `SetFormatCC`. #### Scenario: Element ohne Laufzeitverhalten wird namentlich abgewiesen - **WHEN** ein Modul eine im Inventar als `offen` geführte Funktion verwendet - **THEN** nennt die Ablehnung deren Namen und nicht „unbekannter Bezeichner" #### Scenario: Argumentprüfung ohne Laufzeitverhalten - **WHEN** ein Modul `MKL$()` ohne Argument enthält - **THEN** meldet die Semantik eine Argumentanzahl-Diagnose, die `MKL$` nennt ### Requirement: Modulweites ON ERROR ist aus Prozeduren ansprechbar `ON ERROR GOTO label` ohne `LOCAL` SHALL einen modulweiten Fehlerhandler setzen, dessen Sprungziel auch dann aufgelöst wird, wenn die Anweisung in einer Prozedur steht und das Label auf Modulebene liegt. Nur `ON LOCAL ERROR GOTO label` SHALL prozedurlokale Handler-Auflösung verlangen. Ein modulweites `ON ERROR` in einer Prozedur MUST NOT wie `ON LOCAL ERROR` behandelt werden, und ein auf Modulebene vorhandenes Label MUST NOT als „Label not defined" gemeldet werden. #### Scenario: Handler auf Modulebene aus einer Prozedur - **WHEN** ein Modul das Label `Fehler:` auf Modulebene führt und eine `SUB` die Anweisung `ON ERROR GOTO Fehler` enthält - **THEN** wird das Label aufgelöst und es entsteht keine Diagnose #### Scenario: ON LOCAL ERROR bleibt prozedurlokal - **WHEN** eine `SUB` die Anweisung `ON LOCAL ERROR GOTO Fehler` enthält und `Fehler:` nur auf Modulebene existiert - **THEN** wird „Label not defined" gemeldet ### Requirement: DATA-Elemente behalten ihren Rohtext Unquotierte Elemente einer `DATA`-Anweisung SHALL exakt so erhalten bleiben, wie sie im Quelltext stehen — insbesondere in ihrer Groß- und Kleinschreibung. Die Normalisierung von Bezeichnern MUST NOT auf `DATA`-Elemente wirken. Führender und nachgestellter Leerraum eines unquotierten Elements SHALL entfernt werden, innenliegender erhalten bleiben. #### Scenario: Gemischte Schreibweise bleibt erhalten - **WHEN** ein Modul `DATA Hallo Welt` enthält und `READ s$` ausgeführt wird - **THEN** enthält `s$` den Text `Hallo Welt` mit unveränderter Groß- und Kleinschreibung #### Scenario: Leerraum an den Rändern - **WHEN** ein Modul `DATA ab cd ` enthält und gelesen wird - **THEN** lautet der Wert `ab cd` ### Requirement: ISAM-Anweisungen und -Funktionen in Grammatik und Signaturprüfung Der Parser SHALL die ISAM-Anweisungen und -Funktionen des Vorbilds als eigene Konstrukte erkennen, und die Semantik SHALL für jede eine Signatur führen (Argumentanzahl und -typen). Der maßgebliche Umfang ergibt sich aus dem Sprachinventar; er umfasst mindestens `CREATEINDEX`, `DELETEINDEX`, `SETINDEX`, `GETINDEX$`, `INSERT`, `RETRIEVE`, `UPDATE`, `DELETE`, `DELETETABLE`, `MOVEFIRST`, `MOVELAST`, `MOVENEXT`, `MOVEPREVIOUS`, `SEEKEQ`, `SEEKGT`, `SEEKGE`, `BEGINTRANS`, `COMMITTRANS`, `ROLLBACK`, `ROLLBACK ALL`, `SAVEPOINT`, `SETMEM` und `BOF`. Eine fehlerhafte Verwendung MUST eine Diagnose erzeugen, die das Element namentlich nennt; ein ISAM-Element MUST NOT als unbekannter Bezeichner oder mit einem generischen Syntaxfehler abgewiesen werden (Guiding Principle). #### Scenario: ISAM-Anweisung wird als solche erkannt - **WHEN** ein Modul `SETINDEX #1, "NachName"` enthält - **THEN** entsteht keine Diagnose „unbekannter Bezeichner" und die Anweisung wird als ISAM-Anweisung erfasst #### Scenario: Falsche Argumentanzahl wird namentlich gemeldet - **WHEN** ein Modul `SEEKGT #1` ohne Schlüsselwert enthält - **THEN** nennt die Diagnose `SEEKGT` und die erwartete Argumentanzahl #### Scenario: Typprüfung des Satzarguments - **WHEN** `RETRIEVE #1, x$` mit einem String statt einer Variablen des Tabellentyps geprüft wird - **THEN** wird „Type mismatch" mit Quelltextposition gemeldet ### Requirement: Ereignis-Traps in Grammatik und Signaturprüfung Der Parser SHALL die Trap-Anweisungen des Vorbilds als eigene Konstrukte erkennen: `ON TIMER(n) GOSUB ziel`, `ON KEY(n) GOSUB ziel`, `ON UEVENT GOSUB ziel` und `ON SIGNAL(n) GOSUB ziel`. Diese Formen MUST NOT als berechnetes `ON ausdruck GOSUB liste` behandelt werden. Die Steueranweisungen `TIMER`, `KEY(n)`, `UEVENT` und `SIGNAL(n)` mit `ON`, `OFF` oder `STOP` SHALL erkannt und nur für diese Quellen zugelassen werden; `EVENT` SHALL nur `ON` und `OFF` zulassen, weil das Vorbild keinen dritten Zustand kennt. `COM`, `PEN`, `PLAY` und `STRIG` MUST als Non-Feature namentlich abgewiesen werden. `SetUEvent` SHALL eine Signatur besitzen. Die Wertebereiche SHALL geprüft werden: `ON KEY(n%)` mit `n%` aus 0, 1–25 oder 30–31, `ON TIMER(n&)` mit `n&` aus 1–86 400 und `ON SIGNAL(n%)` mit `n%` aus 1–2. Eine fehlerhafte Verwendung MUST eine Diagnose erzeugen, die das Element namentlich nennt. #### Scenario: ON TIMER ist kein berechnetes GOSUB - **WHEN** ein Modul `ON TIMER(5) GOSUB Marke` enthält - **THEN** wird die Anweisung als Zeit-Trap erfasst und nicht als `ON ausdruck GOSUB` mit einer einelementigen Zielliste #### Scenario: Ungültige Kennung wird namentlich gemeldet - **WHEN** ein Modul `ON KEY(99) GOSUB Marke` enthält - **THEN** nennt die Diagnose `ON KEY` und den zulässigen Wertebereich #### Scenario: EVENT kennt kein STOP - **WHEN** ein Modul `EVENT STOP` enthält - **THEN** nennt die Diagnose `EVENT` und die zulässigen Formen `ON` und `OFF` #### Scenario: Zeitintervall außerhalb des Bereichs - **WHEN** ein Modul `ON TIMER(90000) GOSUB Marke` enthält - **THEN** nennt die Diagnose `ON TIMER` und den Bereich 1 bis 86400 #### Scenario: Non-Feature-Quelle wird abgewiesen - **WHEN** ein Modul `ON PEN GOSUB Marke` enthält - **THEN** meldet die Übersetzung „Feature unavailable" an der Fundstelle der Anweisung — wie bei jedem anderen Non-Feature — und nicht einen generischen Syntaxfehler #### Scenario: SetUEvent besitzt eine Signatur - **WHEN** ein Modul `CALL SetUEvent` enthält - **THEN** entsteht keine Diagnose „Subprogram not defined" ### Requirement: Trap-Ziele sind Sprungziele des Moduls Das Ziel eines Ereignis-Traps SHALL wie ein `GOSUB`-Ziel aufgelöst werden. Ein unbekanntes Ziel MUST zur Übersetzungszeit gemeldet werden und nennt dabei das Label. #### Scenario: Unbekanntes Trap-Ziel - **WHEN** ein Modul `ON TIMER(1) GOSUB Fehlt` ohne Label `Fehlt` enthält - **THEN** meldet die Übersetzung ein undefiniertes Sprungziel und nennt `Fehlt`