320 lines
17 KiB
Markdown
320 lines
17 KiB
Markdown
# 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").
|
||
|
||
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`.
|
||
|
||
Die TerminalBasic-Erweiterung TIMEZONEKNOWN SHALL als parameterlose INTEGER-Funktion eine Signatur besitzen und im Inventar mit ihrer tatsächlichen Umsetzung geführt sein.
|
||
|
||
#### 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
|
||
|
||
#### Scenario: Signatur der Zonenzustandsabfrage
|
||
- **WHEN** PRINT TIMEZONEKNOWN übersetzt wird
|
||
- **THEN** entsteht eine INTEGER-Funktionsabfrage; TIMEZONEKNOWN(1) wird mit benannter Argumentanzahl-Diagnose abgewiesen
|
||
|
||
### 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`
|
||
|
||
### 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 Steuerelement `Text9` enthält
|
||
- **THEN** nennt die Diagnose `Text9` als unbekanntes Objekt statt „Type mismatch" zu melden
|
||
|
||
#### Scenario: Unbekannte Eigenschaft wird benannt
|
||
- **WHEN** ein Modul `Text1.Farbe = 3` enthält
|
||
- **THEN** nennt die Diagnose `Farbe` als unbekannte Eigenschaft von `TextBox`
|
||
|
||
#### Scenario: Eigenschaftstyp wird geprüft
|
||
- **WHEN** ein Modul `Text1.Text = 5` enthält
|
||
- **THEN** meldet die Semantik einen Typkonflikt mit Nennung der Eigenschaft
|
||
|
||
#### Scenario: TYPEOF
|
||
- **WHEN** ein Modul `IF TYPEOF Ziel IS CommandButton THEN` enthä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.5` und `Form1!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 Objekt `Zins` enthält
|
||
- **THEN** wird sie als gewöhnliche Prozedur übersetzt
|