Files
TerminalBasic/openspec/specs/vm-ausfuehrung/spec.md

158 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 \ 2` ausgefü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%: NEXT` ausgeführt wird
- **THEN** wird der Schleifenkörper nicht betreten
#### Scenario: Korpus Kontrollfluss
- **WHEN** `tests/compat/kontrollfluss.bas` ausgefü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 `RETURN` ohne vorangehendes `GOSUB` ausfü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 SUB` mit `Inc 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 `DATA` bereitstellt
- **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.
#### 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
### 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 run` gestartetes Programm `STOP` in 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.
#### Scenario: Trap aus einer Prozedur heraus
- **WHEN** ein Ereignis zugestellt wird, während eine `SUB` läuft, und das Trap-Ziel im Modulrumpf liegt
- **THEN** läuft der Handler und nach `RETURN` setzt die `SUB` an 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
### 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 10000` unmittelbar von `NEXT` gefolgt 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`- oder `DO`/`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 `DOEVENTS` aufruft und ein Trap fällig wird
- **THEN** läuft der Handler innerhalb dieser Schleife