8.3 KiB
vm-ausfuehrung Specification
Purpose
Die TBVM führt das Bytecode-Kompilat mit der exakten Semantik des
Dialekts aus: Ausdrücke mit der Zahlenkonvertierungs-Matrix des Vorbilds,
Kontrollfluss, Prozeduraufrufe mit BYREF, GOSUB, unterbrechbare
step()-Ausführung als Grundlage für Debugger und Ereignisschleife.
Requirements
Requirement: Zahlenkonvertierungs-Matrix
Die VM SHALL implizite und explizite Zahlenkonvertierungen nach der in
docs/tbvm-design.md dokumentierten Matrix ausführen, insbesondere:
Gleitkomma→Ganzzahl rundet kaufmännisch zur nächsten geraden Zahl
(Banker's Rounding); Bereichsüberschreitung löst Fehler 6 „Overflow"
aus; / liefert stets Gleitkomma, \ und MOD konvertieren ihre
Operanden vorab zu Ganzzahlen; logische Operatoren (AND, OR, XOR,
EQV, IMP) arbeiten bitweise auf zu Ganzzahlen konvertierten
Operanden; CURRENCY rechnet als Festkomma ×10 000. Die Matrix SHALL
durch ein Korpusprogramm mit dokumentierter Sollausgabe abgedeckt sein.
Scenario: Banker's Rounding
- WHEN
PRINT CINT(0.5); CINT(1.5); CINT(2.5)ausgeführt wird - THEN lautet die Ausgabe
0 2 2
Scenario: Überlauf bei Konvertierung
- WHEN einer INTEGER-Variablen der Wert 40000 zugewiesen wird
- THEN tritt Laufzeitfehler 6 „Overflow" auf
Scenario: Ganzzahldivision rundet Operanden vor
- WHEN
PRINT 7.5 \ 2ausgeführt wird - THEN wird 7.5 zuerst zu 8 gerundet und die Ausgabe ist
4
Requirement: Kontrollfluss
Die VM SHALL IF/ELSEIF/ELSE, SELECT CASE (Werte, Bereiche,
IS-Vergleiche, CASE ELSE), FOR/NEXT (inkl. STEP, null
Durchläufen bei unpassender Grenze), DO/LOOP (alle vier
Bedingungsformen), WHILE/WEND, GOTO, ON expr GOTO/GOSUB und
EXIT FOR/DO mit der Semantik des Vorbilds ausführen.
Scenario: FOR ohne Durchlauf
- WHEN
FOR i% = 3 TO 1: PRINT i%: NEXTausgeführt wird - THEN wird der Schleifenkörper nicht betreten
Scenario: Korpus Kontrollfluss
- WHEN
tests/compat/kontrollfluss.basausgeführt wird - THEN entspricht die Ausgabe byte-genau
kontrollfluss.out
Requirement: GOSUB/RETURN neben Prozeduraufrufen
GOSUB SHALL die Rücksprungadresse auf einem GOSUB-Stack im aktuellen
Frame ablegen (kein eigener Frame); RETURN kehrt dorthin zurück,
RETURN label an das benannte Ziel. Ein RETURN ohne anhängigen
GOSUB MUST Fehler 3 „RETURN without GOSUB" auslösen.
Scenario: RETURN ohne GOSUB
- WHEN ein Programm
RETURNohne vorangehendesGOSUBausführt - THEN tritt Laufzeitfehler 3 auf
Requirement: Prozeduraufrufe mit BYREF-Semantik
SUB-/FUNCTION-Aufrufe SHALL eigene Frames mit Locals-Slots verwenden.
Variablen, Arrayelemente und UDT-Felder werden BYREF übergeben
(Änderungen im Rumpf wirken auf das Argument zurück); ein geklammertes
Argument oder ein Ausdruck wird als temporärer Wert übergeben (BYVAL-
Wirkung). STATIC-Prozeduren behalten ihre Locals zwischen Aufrufen;
rekursive Aufrufe nicht-statischer Prozeduren erhalten frische Locals.
DEF FN-Funktionen werten im Modulkontext aus (Zugriff auf
Modulvariablen), ihre Parameter wirken lokal.
Scenario: BYREF wirkt zurück
- WHEN
SUB Inc(x%) : x% = x% + 1 : END SUBmitInc n%aufgerufen wird - THEN ist
n%danach um 1 erhöht
Scenario: Klammern erzwingen Wertübergabe
- WHEN dieselbe SUB mit
Inc (n%)aufgerufen wird - THEN bleibt
n%unverändert
Requirement: DATA/READ/RESTORE
READ SHALL die DATA-Konstanten des Moduls in Deklarationsreihenfolge
konsumieren und in den Zieltyp konvertieren; RESTORE [label] setzt den
Lesezeiger auf den Anfang bzw. auf die erste DATA-Anweisung ab dem
Label. Lesen hinter dem letzten Element MUST Fehler 4 „Out of DATA"
auslösen; eine nicht konvertierbare Konstante MUST Fehler 13 „Type
mismatch" auslösen.
Scenario: Out of DATA
- WHEN ein Programm mehr Werte liest als
DATAbereitstellt - THEN tritt Laufzeitfehler 4 auf
Requirement: Unterbrechbare step()-Ausführung
Die VM SHALL als zustandsbehaftete, vom Einbetter getriebene Struktur ausführbar sein: An Anweisungsgrenzen prüft sie ein Flag-Wort und kann anhalten (Breakpoint auf (Modul, Zeile), Einzelschritt, Abbruchwunsch). Im angehaltenen Zustand MUST der Einbetter Variableninhalte (Skalare, Arrayelemente, UDT-Felder) inspizieren und die Ausführung fortsetzen können. Die Tick-Prüfung erfolgt nur an Anweisungsgrenzen, nicht pro Opcode.
Ein Breakpoint SHALL durch Modulidentität und physische Quellzeile bestimmt sein. Gleiche Zeilennummern anderer Module MUST NOT denselben Breakpoint auslösen.
Scenario: Breakpoint hält an
- WHEN ein Breakpoint auf Zeile 10 gesetzt ist und die Ausführung Zeile 10 erreicht
- THEN hält die VM vor der Anweisung an und meldet Modul und Zeile
Scenario: Inspektion im Halt
- WHEN die VM an einem Breakpoint steht
- THEN liefert die Inspektions-API den aktuellen Wert einer benannten Variablen
Scenario: Gleiche Zeile in zwei Modulen
- WHEN ein Breakpoint auf Modul A, Zeile 10 gesetzt ist und Modul B Zeile 10 ausführt
- THEN läuft B weiter; erst A Zeile 10 hält an
Requirement: Programmende im Runner
Außerhalb der IDE SHALL END (und das Erreichen des Programmendes) den
Prozess mit Exit-Code 0 beenden; STOP SHALL mit der Meldung
STOP in line n (bzw. Modul/Zeile) und einem Exit-Code ungleich 0
terminieren (Projektentscheidung 2026-09-02). SYSTEM verhält sich wie
END. CONT existiert nur als IDE-Konzept (Phase 5) und ist kein
Programmbestandteil.
Scenario: STOP im Runner
- WHEN ein per
tbc rungestartetes ProgrammSTOPin Zeile 7 ausführt - THEN endet der Prozess mit Exit-Code ungleich 0 und die Meldung nennt Zeile 7
Requirement: Ereigniszustellung an Anweisungsgrenzen
Die VM SHALL an Anweisungsgrenzen prüfen, ob ein Ereignis zuzustellen
ist, und in diesem Fall das GOSUB-Ziel des zugehörigen Traps
ausführen. Der unterbrochene Zustand — Aufrufkette, Schleifen, Locals,
GOSUB-Stapel und Fehlerzustand — MUST dabei erhalten bleiben; mit
RETURN SHALL die Ausführung an der unterbrochenen Stelle fortfahren.
Die Prüfung MUST NOT pro Opcode erfolgen; solange kein Trap definiert ist,
MUST sie auf einen einzelnen Test hinauslaufen und MUST NOT die Uhr lesen.
Ohne klassische Traps, aktive Forms-Timer oder zeitabhängige Mausauswertung SHALL auch die Forms-Integration keine Hostzeit abfragen.
Scenario: Trap aus einer Prozedur heraus
- WHEN ein Ereignis zugestellt wird, während eine
SUBläuft, und das Trap-Ziel im Modulrumpf liegt - THEN läuft der Handler und nach
RETURNsetzt dieSUBan der unterbrochenen Anweisung fort
Scenario: Ohne aktiven Trap wird die Uhr nicht gelesen
- WHEN ein Programm ohne jede
ON …-Trap-Definition ausgeführt wird - THEN fragt die Ausführung an keiner Anweisungsgrenze die Zeit ab, und der Durchsatz bleibt im Rahmen der Messgenauigkeit unverändert
Scenario: Einfaches Programm liest keine Ereigniszeit
- WHEN ein Programm nur Zuweisungen und END ausführt und ein Testhost Zeitabfragen zählt
- THEN ist der Zähler am Ende 0
Requirement: Jede Schleife trägt eine Anweisungsgrenze im Kreis
Der zurückspringende Zweig jeder Schleife (FOR/NEXT, WHILE/WEND,
DO/LOOP in allen Bedingungsformen) MUST über mindestens eine
Anweisungsgrenze führen, auch wenn der Schleifenrumpf leer ist. Andernfalls
gäbe es in einer Warteschleife keinen Zustellpunkt: kein Ereignis, kein
Breakpoint und kein Abbruch würden dort wirken.
Scenario: Leere Warteschleife
- WHEN
FOR i% = 1 TO 10000unmittelbar vonNEXTgefolgt wird - THEN führt der Rücksprung über eine Anweisungsgrenze und ein aktiver Zeit-Trap wird darin zugestellt
Scenario: Leere Schleife in jeder Form
- WHEN eine
WHILE/WEND-,DO WHILE/LOOP- oderDO/LOOP UNTIL-Schleife ohne Rumpf übersetzt wird - THEN liegt zwischen Rücksprungziel und Rücksprung eine Anweisungsgrenze
Requirement: DOEVENTS gibt die Kontrolle ab
DOEVENTS SHALL anstehende Ereignisse zustellen und danach einen Wert
liefern. Solange keine Formulare geladen werden können, SHALL der Wert 0
sein.
Scenario: DOEVENTS in einer Warteschleife
- WHEN ein Programm in einer Schleife
DOEVENTSaufruft und ein Trap fällig wird - THEN läuft der Handler innerhalb dieser Schleife