16 KiB
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$undSTRINGliest - THEN wird
STRING$als Bezeichner (Builtin) undSTRINGals 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.5und3.14159265gelext werden - THEN ist
1.5SINGLE und3.14159265DOUBLE
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 CASEmitCASE 1: PRINT "eins"undCASE ELSEgeparst 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$ = 1geprüft wird - THEN wird „Type mismatch" mit Quelltextposition gemeldet
Scenario: UDT-Feldtyp
- WHEN ein
TYPEmit FeldName AS STRING * 30deklariert ist undk.Name = 5geprü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, 1oderx = 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").
Bei Projekten und Includes SHALL jede Diagnose den ursprünglichen Dateinamen mit dessen physischer Zeile und Spalte tragen.
Scenario: Position in der Diagnose
- WHEN in Zeile 3 ein Typkonflikt auftritt
- THEN nennt die Diagnose Zeile 3 und die Spalte des Ausdrucks
Scenario: Include-Diagnose
- WHEN ein eingebundenes Include einen Typfehler enthält
- THEN nennt die Diagnose die Include-Datei und ihre eigene Quellposition
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
offengefü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 eineSUBdie AnweisungON ERROR GOTO Fehlerenthält - THEN wird das Label aufgelöst und es entsteht keine Diagnose
Scenario: ON LOCAL ERROR bleibt prozedurlokal
- WHEN eine
SUBdie AnweisungON LOCAL ERROR GOTO Fehlerenthält undFehler: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 Weltenthält undREAD s$ausgeführt wird - THEN enthält
s$den TextHallo Weltmit unveränderter Groß- und Kleinschreibung
Scenario: Leerraum an den Rändern
- WHEN ein Modul
DATA ab cdenthä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 #1ohne Schlüsselwert enthält - THEN nennt die Diagnose
SEEKGTund 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 Markeenthält - THEN wird die Anweisung als Zeit-Trap erfasst und nicht als
ON ausdruck GOSUBmit einer einelementigen Zielliste
Scenario: Ungültige Kennung wird namentlich gemeldet
- WHEN ein Modul
ON KEY(99) GOSUB Markeenthält - THEN nennt die Diagnose
ON KEYund den zulässigen Wertebereich
Scenario: EVENT kennt kein STOP
- WHEN ein Modul
EVENT STOPenthält - THEN nennt die Diagnose
EVENTund die zulässigen FormenONundOFF
Scenario: Zeitintervall außerhalb des Bereichs
- WHEN ein Modul
ON TIMER(90000) GOSUB Markeenthält - THEN nennt die Diagnose
ON TIMERund den Bereich 1 bis 86400
Scenario: Non-Feature-Quelle wird abgewiesen
- WHEN ein Modul
ON PEN GOSUB Markeenthä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 SetUEvententhä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 Fehltohne LabelFehltenthält - THEN meldet die Übersetzung ein undefiniertes Sprungziel und nennt
Fehlt
Requirement: Objektzugriffe in Grammatik und Semantik
Der Parser SHALL Punktpfade auf Formulare und Steuerelemente
(objekt.eigenschaft), den Container-Operator
form!steuerelement[.eigenschaft], IF TYPEOF ausdruck IS klasse, den
Metabefehl $FORM und die Datentypen FORM und CONTROL als
Parametertypen erkennen. Die Semantik SHALL Eigenschaften gegen die
Klasse des Objekts prüfen und ihren Typ bestimmen. Ein Punktpfad, dessen
Basis weder ein benutzerdefinierter Typ noch ein bekanntes Objekt ist,
MUST eine Diagnose erzeugen, die Basis und Eigenschaft namentlich nennt;
er MUST NOT zu einer impliziten Variablen mit Punkt im Namen werden.
Gleichnamige Steuerelemente unterschiedlicher Formulare SHALL im jeweiligen Formularnamensraum aufgelöst werden; die explizite Containerangabe SHALL vor der lokalen Namenssuche wirken.
Scenario: Unbekanntes Objekt wird benannt
- WHEN ein Modul
Text9.Text = "a"ohne SteuerelementText9enthält - THEN nennt die Diagnose
Text9als unbekanntes Objekt statt „Type mismatch" zu melden
Scenario: Unbekannte Eigenschaft wird benannt
- WHEN ein Modul
Text1.Farbe = 3enthält - THEN nennt die Diagnose
Farbeals unbekannte Eigenschaft vonTextBox
Scenario: Eigenschaftstyp wird geprüft
- WHEN ein Modul
Text1.Text = 5enthält - THEN meldet die Semantik einen Typkonflikt mit Nennung der Eigenschaft
Scenario: TYPEOF
- WHEN ein Modul
IF TYPEOF Ziel IS CommandButton THENenthält - THEN wird es übersetzt und die Klasse gegen die bekannten Steuerelementklassen geprüft
Scenario: Gleichnamige Controls verschiedener Formulare
- WHEN Form1 und Form2 jeweils Text1 enthalten und Form2!Text1.Text gelesen wird
- THEN wird Text1 in Form2 aufgelöst und das Programm übersetzt diagnosefrei
Requirement: Bang-Operator neben dem SINGLE-Suffix
Der Lexer SHALL ! als Container-Operator erkennen, wenn ihm ein
Bezeichner folgt, und andernfalls als Typsuffix für SINGLE. Beide Formen
MUST im selben Modul nebeneinander verwendbar sein.
Scenario: Beide Bedeutungen im selben Modul
- WHEN ein Modul
Wert! = 1.5undForm1!Text1.Text = "a"enthält - THEN wird die erste Zeile als SINGLE-Variable und die zweite als Containerzugriff übersetzt
Requirement: Ereignisprozeduren mit festen Signaturen
Die Semantik SHALL eine SUB, deren Name der Form Objekt_Ereignis
entspricht und deren Objekt bekannt ist, als Ereignisprozedur erkennen
und ihre Parameterliste gegen die Signatur der Forms-Referenz prüfen.
Eine abweichende Parameterliste MUST namentlich gemeldet werden. Eine
SUB mit Unterstrich im Namen ohne bekanntes Objekt MUST weiterhin eine
gewöhnliche Prozedur sein.
Scenario: Falsche Signatur
- WHEN ein Modul
SUB Form_MouseDown (X AS SINGLE)enthält - THEN nennt die Diagnose die erwartete Parameterliste der Ereignisprozedur
Scenario: Gewöhnliche SUB mit Unterstrich
- WHEN ein Modul
SUB Zins_Berechnen (n AS INTEGER)ohne ObjektZinsenthält - THEN wird sie als gewöhnliche Prozedur übersetzt