Phase 4: Ereignisschleife und klassische Traps
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-04
|
||||
@@ -0,0 +1,228 @@
|
||||
# Design — Ereignisschleife und klassische Ereignis-Traps
|
||||
|
||||
## Context
|
||||
|
||||
Siehe proposal.md — Why. Technisch maßgeblich ist der Bestand aus Phase 3:
|
||||
|
||||
```
|
||||
crossterm TerminalHost RtState Vm
|
||||
--------- ------------ ------- --
|
||||
Key --> next_event() --> Ereignis::Taste --> tasten[] <-- INKEY$
|
||||
Resize --> next_event() --> Ereignis::Groesse -> screen.resize()
|
||||
Ctrl+C --> next_event() --> Ereignis::Abbruch -> abbruch
|
||||
Maus --> x verworfen (`Ok(_) => {}` im TerminalHost) <-- fehlt
|
||||
Zeit --> x keine Quelle <-- fehlt
|
||||
|
||||
Zustellpunkte heute: Instr::Stmt (screen dirty oder alle 1024 Ticks),
|
||||
vor blockierender Eingabe.
|
||||
```
|
||||
|
||||
Zwei Randbedingungen schneiden den Entwurf zu:
|
||||
|
||||
1. **Builtins sehen die VM nicht.** Ihre Signatur ist
|
||||
`(&mut RtState, &mut dyn Host, &mut [Value])`. `DOEVENTS` ist heute
|
||||
ein Builtin und kann deshalb prinzipiell keine Prozedur des laufenden
|
||||
Programms ausführen.
|
||||
2. **`Vm::run()` ist nicht wiedereintrittsfähig.** Es gibt keinen
|
||||
Einstiegspunkt „führe Ziel X aus und komm zurück".
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Eine Zustellung, die den unterbrochenen Zustand nicht verliert und
|
||||
ohne aktiven Trap nichts kostet.
|
||||
- Zeitgesteuerte Programme im Korpus ohne echte Wartezeit und ohne
|
||||
Flackern zwischen zwei Läufen.
|
||||
- Ein Ereignismodell, das die Forms-Ereignisse des nächsten Changes
|
||||
trägt, ohne dann umgebaut zu werden.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Nebenläufigkeit. Es gibt weiterhin genau einen Ausführungsstrang; die
|
||||
Zeitquelle ist kein Thread, sondern eine Abfrage am Zustellpunkt.
|
||||
- Priorisierbare oder verschachtelte Handler.
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1 — Zustellung ist ein Vorgang der VM, kein Builtin
|
||||
|
||||
Die Prüfung „ist etwas fällig?" sitzt in der bestehenden Tick-Prüfung an
|
||||
`Instr::Stmt`. Ist etwas fällig, springt die VM das Trap-Ziel an, als
|
||||
hätte das Programm dort ein `GOSUB` ausgeführt. `DOEVENTS` und `SLEEP`
|
||||
werden zu Anweisungen, die denselben Weg auslösen, statt zu Builtins,
|
||||
die ihn nicht auslösen können.
|
||||
|
||||
| Alternative | Warum nicht |
|
||||
|---|---|
|
||||
| `&mut Vm` an alle Builtins durchreichen | Signaturänderung an ~150 Builtins; echte Rekursion in `run()` mit unbegrenzter Stapeltiefe |
|
||||
| Trampolin: Builtin liefert „bitte Ziel X rufen" zurück | Trägt die klassischen Traps, aber modales `SHOW` des nächsten Changes wird zur Zustandsmaschine — der Umbau käme dann doch |
|
||||
|
||||
### D2 — Der Handler läuft in einem eigenen Frame, es wird nicht abgewickelt
|
||||
|
||||
`ON ERROR` wickelt die Frame-Kette bis zum Handler-Frame ab. Für einen
|
||||
Trap ist das falsch: das Programm muss danach weiterlaufen. Die
|
||||
Zustellung **legt** deshalb einen Frame auf den Modulrumpf **auf**, mit
|
||||
einer Rückmarke auf die unterbrochene Stelle; `RETURN` nimmt ihn wieder
|
||||
herunter.
|
||||
|
||||
```
|
||||
vor der Zustellung waehrend des Handlers nach RETURN
|
||||
------------------ --------------------- -----------
|
||||
[ SUB Rechne ] <- top [ Trap-Frame @Marke ] [ SUB Rechne ]
|
||||
[ Modulrumpf ] [ SUB Rechne ] [ Modulrumpf ]
|
||||
[ Modulrumpf ]
|
||||
```
|
||||
|
||||
Der Trap-Frame teilt die Modulvariablen (sie sind Globals), hat aber
|
||||
einen eigenen `GOSUB`-Stapel — damit kann ein Handler selbst `GOSUB`
|
||||
verwenden, ohne den unterbrochenen Stapel zu berühren.
|
||||
|
||||
### D3 — Die Zeit kommt vom Host
|
||||
|
||||
Der `Host`-Trait bekommt eine monotone Zeitabfrage (**BREAKING**). Die
|
||||
Laufzeit rechnet daraus die Fälligkeit der Zeit-Traps; der Host weiß
|
||||
nichts von Traps.
|
||||
|
||||
Grund ist die Prüfbarkeit: der `CaptureHost` stellt die Zeit selbst und
|
||||
ein Korpusprogramm mit `ON TIMER(5)` läuft in Mikrosekunden und immer
|
||||
gleich. Ein direkter Uhrzugriff in der Laufzeit hätte beides verhindert
|
||||
und wäre zudem die zweite Uhr neben der von `TIMER`/`TIME$` — die
|
||||
bleibt, wo sie ist, denn sie liefert Ortszeit, keine monotone Dauer.
|
||||
|
||||
### D4 — Maskierung als Tabelle, Prüfung über das vorhandene Flagwort
|
||||
|
||||
Je Quelle ein Zustand (`ON`/`OFF`/`STOP`) und höchstens ein anstehendes
|
||||
Ereignis. Belegt aus der Original-Hilfe (`umfang-und-form.md`): der
|
||||
Eintritt in den Handler führt selbsttätig `STOP` aus, das `RETURN`
|
||||
selbsttätig `ON` — außer im Handler stand ein ausdrückliches `OFF`; und
|
||||
`OFF` verwirft auch ein unter `STOP` gemerktes Ereignis. `EVENT` ist
|
||||
keine dritte Zustandsebene, sondern eine Klammer mit nur `ON` und `OFF`. Die Tick-Prüfung bekommt ein weiteres Bit im vorhandenen
|
||||
Flagwort neben `F_STEP`/`F_BREAK`/`F_POLL`. Ohne definierten Trap ist
|
||||
das Bit aus, und die heiße Schleife prüft weiterhin genau ein Wort —
|
||||
das trägt die Zusicherung „ohne aktiven Trap kein messbarer Aufwand"
|
||||
aus der Spec.
|
||||
|
||||
### D5 — Getrappte Tasten werden erst am Zustellpunkt aussortiert
|
||||
|
||||
Die Tastenschlange bleibt, wie sie ist. Am Zustellpunkt vergleicht die
|
||||
Laufzeit den Kopf der Schlange mit den aktiven `KEY`-Traps und nimmt die
|
||||
Taste nur dann heraus. Damit wirkt ein `KEY(1) OFF` sofort und auch auf
|
||||
Tasten, die schon in der Schlange liegen — Filtern beim Einsortieren
|
||||
hätte den Zustand zum Zeitpunkt des Tastendrucks eingefroren.
|
||||
|
||||
### D6 — Virtuelle Zeit im Korpus über eine Kopfzeilen-Direktive
|
||||
|
||||
Der Harness kennt bereits `' tb-screen:` und `' tb-tz:`. Dazu kommt
|
||||
`' tb-clock: <n>ms/Zustellpunkt`: die virtuelle Uhr des `CaptureHost`
|
||||
rückt an jedem Zustellpunkt um `n` Millisekunden vor. Ein Programm mit
|
||||
`ON TIMER(1)` und `' tb-clock: 100ms/Zustellpunkt` feuert damit
|
||||
reproduzierbar nach zehn Zustellpunkten. Fehlt die Direktive und nutzt
|
||||
das Programm Zeit-Traps, bricht der Harness ab, statt auf die Systemuhr
|
||||
auszuweichen.
|
||||
|
||||
### D7 — Maus: Quelle jetzt, Auswertung mit den Steuerelementen
|
||||
|
||||
Die Maus kommt als Quelle in diesem Change herein, weil die
|
||||
Eingangsaufgabe der Phase die Terminal-Events in die Warteschlange
|
||||
führt. Zwei Festlegungen fallen dabei an:
|
||||
|
||||
- **Koordinaten in Zellen, 1-basiert.** `crossterm` zählt ab 0, der
|
||||
Bildschirm des Dialekts ab 1 (`LOCATE`, `CSRLIN`, `SCREEN(z, s)`).
|
||||
Umgerechnet wird an der Stelle, an der die Ereignisse entstehen — in
|
||||
der Darstellungsschicht —, damit es in der Laufzeit nur eine Zählweise
|
||||
gibt. Die Forms-Referenz nennt Mauskoordinaten SINGLE, aber in Zellen;
|
||||
die Umrechnung in Formularkoordinaten macht später das Objektmodell.
|
||||
- **Ohne Verbraucher verworfen.** Bis die Steuerelemente da sind, nimmt
|
||||
niemand ein Mausereignis an. Es am Zustellpunkt zu verwerfen ist die
|
||||
einzige Variante, die keine unbegrenzt wachsende Warteschlange
|
||||
hinterlässt; Bewegungsereignisse kommen bei gedrückter Taste in hoher
|
||||
Frequenz.
|
||||
|
||||
### D8 — `SIGNAL` liegt auf Betriebssystemsignalen
|
||||
|
||||
Entscheidung des Projektinhabers vom 2026-09-04, Beleg und Begründung in
|
||||
`umfang-und-form.md`. Abgebildet wird nur, was auf **allen**
|
||||
Zielplattformen besteht:
|
||||
|
||||
| `n%` | Signal | Bemerkung |
|
||||
|---|---|---|
|
||||
| 1 | `SIGINT` | überall zustellbar |
|
||||
| 2 | `SIGTERM` | unter Windows von außen nicht zustellbar (Abweichung) |
|
||||
|
||||
Der Signalhandler tut nichts weiter, als eine Marke zu setzen; der Host
|
||||
liest sie beim nächsten Abholen und macht ein Ereignis daraus. Damit
|
||||
läuft kein Programmcode im Signalkontext, und die Zustellung folgt
|
||||
denselben Punkten wie jede andere Quelle. Liegen beide Marken an, hat
|
||||
`SIGINT` Vorrang.
|
||||
|
||||
Ein Codepfad für alle drei Plattformen: `signal-hook` gilt sein
|
||||
`flag`-Modul nicht nach Plattform ab und bildet unter Windows auf das
|
||||
`signal` der C-Laufzeit ab. Die Abhängigkeit liegt ohnehin im Baum —
|
||||
`crossterm` nutzt sie unter Unix für `SIGWINCH` —, sie wird für `tb-ui`
|
||||
nur ausdrücklich deklariert, nicht neu hereingeholt. Damit entfällt der
|
||||
sonst nötige zweite Pfad über den Konsolen-Steuerhandler von Windows.
|
||||
|
||||
### D9 — Umfang und Form aus den Einzelseiten der Original-Hilfe
|
||||
|
||||
Befund aus `phase-3-isam`: die Themenliste klärt den *Umfang*, die
|
||||
Einzelseiten klären die *Form*. Für diesen Change heißt das: die
|
||||
Wertebereiche von `ON KEY(n)`, die Reihenfolge gleichzeitig fälliger
|
||||
Ereignisse und die Frage, ob Trapping in Prozeduren ruht, werden vor der
|
||||
Implementierung aus den Einzelseiten belegt (erste Aufgabe in tasks.md),
|
||||
nicht aus der Erinnerung nachgebaut. Ergibt die Hilfe nichts, gilt eine
|
||||
dokumentierte Festlegung, die in der Sprachreferenz als Abweichung
|
||||
steht.
|
||||
|
||||
### D10 — Schleifen bekommen eine Anweisungsgrenze im Rücksprung
|
||||
|
||||
Befund bei der Umsetzung von D1: der Codegenerator übersetzte eine
|
||||
Schleife **ohne Rumpf** in reine Sprünge — in `FOR i = 1 TO 10000: NEXT`
|
||||
gab es keine Anweisungsgrenze und damit keinen Zustellpunkt. Das trifft
|
||||
alle vier Schleifenformen und betraf auch Breakpoints und den Abbruch,
|
||||
war also schon vor diesem Change eine Lücke.
|
||||
|
||||
Der Rücksprung führt jetzt in jedem Fall über eine Grenze. Bei
|
||||
`DO`/`WHILE` zeigt er auf die ohnehin vorhandene Grenze des
|
||||
Schleifenkopfs — das kostet keine Instruktion. Nur `FOR` braucht eine
|
||||
eigene, weil vor dem Schleifentest die Initialisierung steht.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Der Trap-Frame sieht einen halbfertigen Zustand** → Zustellung nur
|
||||
an Anweisungsgrenzen; die Liste der Zustellpunkte ist in der Spec
|
||||
abschließend und wird nicht „für den Komfort" erweitert.
|
||||
- **Ereignisse feuern schneller, als der Handler zurückkehrt** →
|
||||
höchstens ein anstehendes Ereignis je Quelle, und die eigene Quelle
|
||||
ist während des Handlers gestoppt. Ein Zeit-Trap kann damit Fristen
|
||||
überspringen; das entspricht dem Vorbild.
|
||||
- **Tick-Kosten** → `benches/vm.rs` misst vorher und nachher; die
|
||||
Zusicherung aus der Spec ist ein Messwert, keine Behauptung. Die
|
||||
zusätzliche Grenze je `FOR`-Durchlauf (D10) geht in dieselbe Messung
|
||||
ein.
|
||||
- **`SIGNAL(2)` ist unter Windows wirkungslos** — es gibt dort kein
|
||||
`kill` → als Abweichung dokumentiert, statt ein Ersatzereignis zu
|
||||
erfinden, das es im Vorbild nicht gab.
|
||||
- **`SIGINT` kollidiert mit dem Abbruch** → im Rohmodus liefert das
|
||||
Terminal Strg+C als Taste, nicht als Signal; ein von außen gesendetes
|
||||
`SIGINT` geht an den Trap, wenn einer aktiv ist, sonst bleibt es beim
|
||||
Abbruch. Dieselbe Regel wie bei getrappten Tasten.
|
||||
- **`Host`-Trait ist BREAKING** → betrifft `TerminalHost`, `CaptureHost`
|
||||
und den Test-Host in `tb-cli`; alle drei liegen im Repo, kein externer
|
||||
Verbraucher.
|
||||
- **Mausereignisse ohne Verbraucher könnten stumm falsch sein** — ein
|
||||
Fehler im Koordinatenmodell fiele erst im nächsten Change auf →
|
||||
Unit-Tests der Umrechnung an den Rändern (Zelle 1,1 und die letzte
|
||||
Zelle) und nach einer Größenänderung, nicht erst über Steuerelemente.
|
||||
|
||||
## Open Questions
|
||||
|
||||
- Ruht das Trapping in Prozeduren eines *anderen* Moduls? Mehrere Module
|
||||
gibt es erst mit der Projektverwaltung in Phase 5; die Antwort ändert
|
||||
hier weder Spec noch Aufgaben.
|
||||
- Ob die Zeitquelle später auch Forms-`Timer`-Controls speist oder diese
|
||||
eine eigene Fälligkeitsliste bekommen, entscheidet der Change zum
|
||||
Objektmodell.
|
||||
- Ob Mausbewegungen ohne gedrückte Taste zusammengefasst werden müssen
|
||||
(`MouseMove` kommt sehr häufig), zeigt sich erst mit dem ersten
|
||||
Verbraucher; das Ereignismodell bleibt davon unberührt.
|
||||
@@ -0,0 +1,29 @@
|
||||
# Messung: Kosten der Ereigniszustellung (Aufgabe 8.1)
|
||||
|
||||
`cargo bench -p tb-vm --bench vm`, Release, dieselbe Maschine. „Vorher" ist
|
||||
der Stand von `060674b` in einem eigenen Worktree, „nachher" der Median aus
|
||||
drei Läufen dieses Changes.
|
||||
|
||||
| Last | vorher | nachher | Abweichung |
|
||||
|---|---|---|---|
|
||||
| INTEGER-Schleife (10 Mio) | 898 ms | 901 ms | +0,3 % |
|
||||
| DOUBLE-Schleife (5 Mio) | 419 ms | 429 ms | +2,4 % |
|
||||
| SUB-Aufrufe (1 Mio, BYREF) | 94 ms | 96 ms | +2,1 % |
|
||||
| String-Funktionen (200k) | 86 ms | 83 ms | −3,5 % |
|
||||
|
||||
**Deutung.** Zwei Änderungen wirken hier zusammen, und nur die zweite kostet:
|
||||
|
||||
1. **Die Zustellprüfung** an der Anweisungsgrenze ist ein einzelner
|
||||
Bool-Test (`traps.aktiv()`, ein gepflegtes Feld). Ohne definierten Trap
|
||||
fällt sie sofort durch; die Uhr wird gar nicht erst gelesen. Das ist im
|
||||
Rauschen nicht auffindbar.
|
||||
2. **Die zusätzliche Anweisungsgrenze je `FOR`-Durchlauf** (design.md, D10)
|
||||
ist eine Instruktion mehr pro Iteration. Sie erklärt die ~2 % auf den
|
||||
schleifenlastigen Lasten und ist bewusst bezahlt: ohne sie ist
|
||||
`FOR i = 1 TO n: NEXT` für Ereignisse, Breakpoints und Abbruch taub.
|
||||
`DO`/`WHILE` zahlen nichts, dort zeigt der Rücksprung auf eine ohnehin
|
||||
vorhandene Grenze.
|
||||
|
||||
Die Streuung zwischen den Läufen liegt bei ±1,5 % (String-Last sogar
|
||||
schneller als vorher), die 2 % sind also am Rand der Auflösung dieses
|
||||
Harness — als obere Schranke aber belastbar.
|
||||
@@ -0,0 +1,125 @@
|
||||
# Phase 4 (Eingang) — Ereignisschleife und klassische Ereignis-Traps
|
||||
|
||||
## Why
|
||||
|
||||
Phase 4 beginnt laut PLAN mit der Ereignisschleifen-Architektur. Diese
|
||||
Architektur lässt sich an den **klassischen Ereignis-Traps** vollständig
|
||||
bauen und beweisen, ohne eine Zeile Forms-Objektmodell: Sie brauchen
|
||||
dieselbe Warteschlange, dieselben Zustellpunkte, dieselbe Maskierung und
|
||||
denselben Rückruf in die VM, aber keine Formulare, keine Steuerelemente
|
||||
und kein `.FRM`. Umgekehrt müsste der Entwurf der Schleife mitten im
|
||||
Objektmodell blind bezahlt werden.
|
||||
|
||||
Phase 3 hat die halbe Maschinerie bereits nebenbei gebaut: die
|
||||
`Ereignis`-Warteschlange, das Einsortieren in `RtState::pump`, die
|
||||
Zustellpunkte an Anweisungsgrenzen und vor blockierender Eingabe sowie
|
||||
den `CaptureHost` für Tests ohne Terminal. Es fehlen drei Dinge: eine
|
||||
**Zeitquelle**, die **Maskierung** einzelner Ereignisquellen und vor
|
||||
allem der **Rückruf in die VM** — heute gibt es keinen Weg, aus der
|
||||
Laufzeit heraus eine Prozedur oder ein `GOSUB`-Ziel des laufenden
|
||||
Programms auszuführen. `DOEVENTS()` liefert deshalb konstant 0.
|
||||
|
||||
Zugleich sind die zehn Ereignis-Elemente die letzte Lücke des Inventars
|
||||
außerhalb von Forms: von den 15 offenen Einträgen entfallen zehn auf
|
||||
diese Gruppe, drei auf die Forms-Dialoge und zwei auf Phase 5.
|
||||
|
||||
## What Changes
|
||||
|
||||
- **Ereignisquellen und Warteschlange**: Das Ereignismodell der Laufzeit
|
||||
wird um eine **Zeitquelle** erweitert. Die Zeit kommt vom Host, nicht
|
||||
aus einem direkten Uhrzugriff der Laufzeit — nur so kann der
|
||||
`CaptureHost` virtuelle Zeit stellen und ein Korpusprogramm mit
|
||||
`ON TIMER` deterministisch prüfen. **BREAKING** für den `Host`-Trait.
|
||||
- **Mausereignisse als vierte Quelle**: Der `TerminalHost` verwirft die
|
||||
Mausereignisse von `crossterm` heute. Sie werden aufgenommen und in
|
||||
Zellenkoordinaten geführt — Tastendruck, Loslassen und Bewegung mit
|
||||
Taste und Umschaltzustand. Die Eingangsaufgabe der Phase lautet
|
||||
„Terminal-Events → Event-Queue → VM-Ticks"; die Maus ist ein
|
||||
Terminal-Event und damit Teil der Schleife, auch wenn sie erst die
|
||||
Steuerelemente auswerten.
|
||||
- **Rückruf in die VM**: Ereigniszustellung wird ein Vorgang der VM, kein
|
||||
Builtin. Ein zugestelltes Ereignis führt das `GOSUB`-Ziel seines Traps
|
||||
aus und kehrt danach an die unterbrochene Stelle zurück. Damit
|
||||
bekommt auch `DOEVENTS()` seine Wirkung.
|
||||
- **Zustellpunkte**: Die vorhandenen Punkte (Anweisungsgrenze,
|
||||
blockierende Eingabe) werden zu einer geschlossenen, dokumentierten
|
||||
Liste ausgebaut: Anweisungsgrenze, `DOEVENTS`, `SLEEP` (wird durch ein
|
||||
zugestelltes Ereignis beendet) und blockierende Eingabe. Zwischen
|
||||
Zustellpunkten unterbricht nichts das Programm.
|
||||
- **Klassische Ereignis-Traps** (Sprachreferenz §8, **10
|
||||
Inventareinträge**): `ON TIMER(n&)`, `ON KEY(n%)`, `ON UEVENT` und
|
||||
`ON SIGNAL(n%)` je mit `GOSUB label`; die Steueranweisungen `TIMER`,
|
||||
`KEY(n)`, `UEVENT` und `SIGNAL(n)` je mit `ON`/`OFF`/`STOP` sowie die
|
||||
Klammer `EVENT` mit `ON`/`OFF`; dazu `SetUEvent`. Wertebereiche nach
|
||||
Beleg: `KEY` 0, 1–25, 30–31; `TIMER` 1–86 400 Sekunden; `SIGNAL` 1–2.
|
||||
Ihr Status im Inventar wechselt
|
||||
auf `implementiert`.
|
||||
- **Maskierung mit drei Zuständen**: `ON` stellt zu, `OFF` verwirft —
|
||||
auch ein unter `STOP` bereits gemerktes Ereignis —, `STOP` merkt eines
|
||||
und stellt es beim nächsten `ON` zu. Der Eintritt in einen Handler
|
||||
führt selbsttätig `STOP` seiner Quelle aus, das `RETURN` selbsttätig
|
||||
`ON`, außer im Handler stand ein ausdrückliches `OFF`.
|
||||
- **`SIGNAL` auf Betriebssystemsignale** (Entscheidung 2026-09-04): Die
|
||||
Quelle des Vorbilds war OS/2-gebunden (Prozessflags über
|
||||
`DOSFLAGPROCESS`) und existiert nicht. Abgebildet wird die minimale
|
||||
Menge, die auf allen Zielplattformen besteht: `SIGNAL(1)` auf `SIGINT`,
|
||||
`SIGNAL(2)` auf `SIGTERM`. Signale erreichen die Laufzeit über den
|
||||
Host, nicht im Signalkontext.
|
||||
- **Getrappte Tasten verlassen den Eingabestrom**: Eine Taste, für die
|
||||
ein aktiver `ON KEY`-Trap besteht, wird zugestellt und MUST NOT
|
||||
zusätzlich bei `INKEY$`, `INPUT` oder `INPUT$` erscheinen.
|
||||
- **Namentliche Abweisung im Frontend**: Die zehn Elemente erhalten
|
||||
Grammatik und Signaturen. Heute rutscht `ON TIMER(5) GOSUB marke`
|
||||
stillschweigend in das berechnete `ON ausdruck GOSUB` — genau der
|
||||
Fall, den der Guiding Principle als Bug definiert.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Die **Auswertung** von Mausereignissen durch Steuerelemente
|
||||
(Trefferprüfung, Fokuswechsel, Ziehen) — sie setzt das Objektmodell
|
||||
voraus und gehört zum Change der Steuerelemente. Quelle,
|
||||
Warteschlange und Koordinatenmodell entstehen hier.
|
||||
- Das Forms-Objektmodell, Steuerelemente, `.FRM` und die Dialoge
|
||||
(`MSGBOX`, `INPUTBOX$`) — eigene Changes derselben Phase.
|
||||
- `COM`, `PEN`, `PLAY`, `STRIG` bleiben Non-Feature (Fehler 73).
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `ereignissteuerung`: Ereignisquellen (Tastatur, Zeit,
|
||||
benutzerdefiniert), Warteschlange und Reihenfolge, Zustellpunkte,
|
||||
Maskierung mit `ON`/`OFF`/`STOP` je Quelle und global, Zustellung als
|
||||
`GOSUB` in das laufende Programm, Zusammenspiel mit Fehlerbehandlung
|
||||
und blockierender Eingabe.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `sprach-frontend`: Grammatik und Signaturprüfung SHALL die
|
||||
Trap-Anweisungen und `SetUEvent` umfassen; `ON <quelle>(n) GOSUB`
|
||||
MUST NOT als berechnetes `ON ausdruck GOSUB` behandelt werden.
|
||||
- `vm-ausfuehrung`: Die VM SHALL an Anweisungsgrenzen anstehende
|
||||
Ereignisse zustellen und dafür das `GOSUB`-Ziel des Traps ausführen,
|
||||
ohne den unterbrochenen Zustand zu verlieren.
|
||||
- `kompat-testkorpus`: Korpusabdeckung der Ereignis-Traps mit
|
||||
deterministischer Zeit.
|
||||
|
||||
## Impact
|
||||
|
||||
- `crates/tb-runtime`: `Ereignis` und der `Host`-Trait um die Zeitquelle
|
||||
erweitert (**BREAKING**); Trap-Zustände und Warteschlange in `RtState`;
|
||||
`INKEY$`-Puffer filtert getrappte Tasten; `SLEEP` endet bei Zustellung.
|
||||
- `crates/tb-vm`: Zustellung als Vorgang der Ausführung (neue
|
||||
Instruktion und Frame-Behandlung); `DOEVENTS` erhält Wirkung.
|
||||
- `crates/tb-frontend`: Grammatik und Signaturen der zehn Elemente;
|
||||
`Unsupported`-Absenkung von `SETUEVENT` entfällt.
|
||||
- `crates/tb-ui`: `TerminalHost` liefert die Zeit, reicht die
|
||||
Mausereignisse von `crossterm` durch, statt sie zu verwerfen, und
|
||||
meldet Betriebssystemsignale als Ereignis.
|
||||
- `tests/compat`: neue Korpusprogramme für Zeit-, Tasten- und
|
||||
UEvent-Traps sowie für `ON`/`OFF`/`STOP`.
|
||||
- `docs/`: `sprachreferenz.md` §8 ausformuliert; `inventar.md` setzt die
|
||||
zehn Einträge auf `implementiert`; `bibliothek.md` um die Elemente
|
||||
erweitert.
|
||||
- PLAN.md: die Eingangsaufgabe von Phase 4 und der Punkt „Klassische
|
||||
Ereignis-Traps" sind damit erledigt.
|
||||
@@ -0,0 +1,208 @@
|
||||
## Purpose
|
||||
|
||||
Die Ereignissteuerung bestimmt, woher Ereignisse kommen, wann ein
|
||||
laufendes Programm sie zu sehen bekommt und wie es darauf reagiert:
|
||||
Ereignisquellen (Tastatur, Maus, Zeit, benutzerdefiniert) und ihre
|
||||
Warteschlange, die abschließende Liste der
|
||||
Zustellpunkte, die Maskierung je Quelle und global, sowie die Zustellung
|
||||
als `GOSUB` in das laufende Programm. Sie ist die gemeinsame Grundlage
|
||||
der klassischen Ereignis-Traps und späterer Forms-Ereignisse.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Ereignisquellen mit Zeit vom Host
|
||||
Die Laufzeit SHALL Ereignisse aus vier Quellen führen: Tastatur, Maus,
|
||||
Zeit und benutzerdefinierte Ereignisse. Die Zeit SHALL vom Host bezogen
|
||||
werden;
|
||||
die Laufzeit MUST NOT für die Ereigniszustellung selbst auf die
|
||||
Systemuhr zugreifen. Ein Host ohne Terminal MUST die Zeit frei stellen
|
||||
können, sodass zeitgesteuerte Programme ohne Wartezeit und mit
|
||||
reproduzierbarem Ergebnis prüfbar sind.
|
||||
|
||||
#### Scenario: Zeitgesteuerter Trap ohne echte Wartezeit
|
||||
- **WHEN** ein Testhost die Zeit um 5 Sekunden vorstellt und ein Programm `ON TIMER(5) GOSUB Marke` mit `TIMER ON` aktiv hat
|
||||
- **THEN** wird der Trap zugestellt, ohne dass der Test tatsächlich wartet
|
||||
|
||||
#### Scenario: Wiederholter Lauf liefert dasselbe Ergebnis
|
||||
- **WHEN** dasselbe zeitgesteuerte Korpusprogramm zweimal mit demselben Zeitverlauf ausgeführt wird
|
||||
- **THEN** ist die Ausgabe beide Male identisch
|
||||
|
||||
### Requirement: Mausereignisse in Zellenkoordinaten
|
||||
Die Darstellungsschicht SHALL Mausereignisse der Ausführungsumgebung an
|
||||
die Laufzeit weitergeben: Drücken, Loslassen und Bewegung, jeweils mit
|
||||
gedrückter Taste, Umschaltzustand und Position. Die Position SHALL in
|
||||
Zellen des Textbildschirms angegeben werden, gezählt wie bei `LOCATE`,
|
||||
also 1-basiert. Ereignisse außerhalb der aktuellen Bildschirmfläche MUST
|
||||
verworfen werden. Die Reihenfolge zwischen Maus- und Tastenereignissen
|
||||
SHALL der Reihenfolge ihres Eintreffens entsprechen. Solange kein
|
||||
Verbraucher ein Mausereignis annimmt, MUST es am Zustellpunkt verworfen
|
||||
werden, damit die Warteschlange nicht unbegrenzt wächst.
|
||||
|
||||
#### Scenario: Position als Zelle
|
||||
- **WHEN** in der linken oberen Ecke der Darstellungsfläche die linke Maustaste gedrückt wird
|
||||
- **THEN** trägt das Ereignis Zeile 1 und Spalte 1
|
||||
|
||||
#### Scenario: Reihenfolge bleibt erhalten
|
||||
- **WHEN** eine Taste gedrückt und danach die Maus geklickt wird
|
||||
- **THEN** liefert die Warteschlange erst das Tasten-, dann das Mausereignis
|
||||
|
||||
#### Scenario: Ereignis außerhalb der Fläche
|
||||
- **WHEN** ein Mausereignis eine Position außerhalb der aktuellen Bildschirmgröße nennt
|
||||
- **THEN** wird es verworfen und erreicht die Warteschlange nicht
|
||||
|
||||
#### Scenario: Kein Verbraucher, keine Anhäufung
|
||||
- **WHEN** ein Programm ohne Verbraucher für Mausereignisse läuft und die Maus dauerhaft bewegt wird
|
||||
- **THEN** wächst die Warteschlange nicht über ihre Grenze und das Programm läuft unverändert weiter
|
||||
|
||||
### Requirement: Abschließende Liste der Zustellpunkte
|
||||
Ereignisse SHALL ausschließlich an folgenden Punkten zugestellt werden
|
||||
— das Vorbild prüft die Ereignismarke „before executing the next
|
||||
statement":
|
||||
an einer Anweisungsgrenze, bei `DOEVENTS`, während `SLEEP` und vor einer
|
||||
blockierenden Eingabe. Zwischen zwei Zustellpunkten MUST NOT ein
|
||||
Trap-Handler beginnen; insbesondere MUST NOT eine Anweisung in ihrer
|
||||
Mitte unterbrochen werden. Ein zugestelltes Ereignis SHALL ein laufendes
|
||||
`SLEEP` beenden.
|
||||
|
||||
#### Scenario: Keine Zustellung innerhalb einer Anweisung
|
||||
- **WHEN** ein Ereignis fällig wird, während eine mehrteilige Anweisung ausgewertet wird
|
||||
- **THEN** läuft die Anweisung zu Ende und der Handler beginnt erst an der folgenden Anweisungsgrenze
|
||||
|
||||
#### Scenario: SLEEP endet durch Ereignis
|
||||
- **WHEN** ein Programm `SLEEP 60` ausführt und nach 2 Sekunden ein aktiver Trap fällig wird
|
||||
- **THEN** wird der Handler ausgeführt und `SLEEP` kehrt danach zurück, ohne die vollen 60 Sekunden abzuwarten
|
||||
|
||||
#### Scenario: DOEVENTS stellt zu
|
||||
- **WHEN** ein anstehendes Ereignis vorliegt und `DOEVENTS` ausgewertet wird
|
||||
- **THEN** läuft der zugehörige Handler, bevor `DOEVENTS` einen Wert liefert
|
||||
|
||||
### Requirement: Maskierung je Quelle mit drei Zuständen
|
||||
Jede Ereignisquelle SHALL drei Zustände kennen. `ON` stellt Ereignisse
|
||||
zu. `OFF` verwirft sie; ein während `OFF` eingetretenes Ereignis MUST
|
||||
NOT nachträglich zugestellt werden, und ein zuvor unter `STOP` gemerktes
|
||||
Ereignis MUST von `OFF` verworfen werden. `STOP` merkt höchstens ein
|
||||
anstehendes Ereignis und stellt es beim nächsten `ON` zu. Ohne
|
||||
vorangehende `ON …`-Trap-Definition MUST eine Steueranweisung wirkungslos
|
||||
bleiben, aber keinen Fehler auslösen.
|
||||
|
||||
#### Scenario: OFF verwirft
|
||||
- **WHEN** `TIMER OFF` gilt, die Frist verstreicht und danach `TIMER ON` ausgeführt wird
|
||||
- **THEN** wird kein Handler ausgeführt
|
||||
|
||||
#### Scenario: STOP merkt genau eines
|
||||
- **WHEN** `KEY(1) STOP` gilt, F1 dreimal gedrückt wird und danach `KEY(1) ON` ausgeführt wird
|
||||
- **THEN** läuft der Handler genau einmal
|
||||
|
||||
#### Scenario: OFF verwirft das unter STOP Gemerkte
|
||||
- **WHEN** `TIMER STOP` gilt, die Frist verstreicht, danach `TIMER OFF` und dann `TIMER ON` ausgeführt werden
|
||||
- **THEN** läuft kein Handler
|
||||
|
||||
### Requirement: EVENT klammert Abschnitte ohne Ereignisprüfung
|
||||
`EVENT OFF` SHALL die Ereignisprüfung für den folgenden Abschnitt
|
||||
abschalten, `EVENT ON` sie wieder einschalten; die beiden klammern einen
|
||||
Abschnitt, in dem keine Ereignisse erkannt werden müssen. Ein dritter
|
||||
Zustand MUST NOT bestehen: `EVENT STOP` ist im Vorbild nicht
|
||||
dokumentiert und MUST namentlich abgewiesen werden. Der Schalter SHALL
|
||||
die Zustände der einzelnen Quellen überlagern, ohne sie zu verändern:
|
||||
nach `EVENT ON` gilt für jede Quelle wieder ihr eigener Zustand.
|
||||
Ereignisse, die während `EVENT OFF` eintreten, MUST NOT nachträglich
|
||||
zugestellt werden.
|
||||
|
||||
#### Scenario: Klammer überlagert Einzelzustand
|
||||
- **WHEN** `TIMER ON` gilt und danach `EVENT OFF` ausgeführt wird
|
||||
- **THEN** wird kein Zeit-Trap zugestellt, und nach `EVENT ON` wird wieder zugestellt, ohne dass `TIMER ON` erneut nötig ist
|
||||
|
||||
#### Scenario: EVENT STOP gibt es nicht
|
||||
- **WHEN** ein Modul `EVENT STOP` enthält
|
||||
- **THEN** wird es namentlich abgewiesen
|
||||
|
||||
### Requirement: Zustellung als GOSUB ohne Wiedereintritt
|
||||
Ein zugestelltes Ereignis SHALL das `GOSUB`-Ziel seines Traps ausführen
|
||||
und danach an die unterbrochene Stelle zurückkehren; der unterbrochene
|
||||
Zustand (aufrufende Prozedur, Schleifen, Variablen) MUST erhalten
|
||||
bleiben. Beim Eintritt in den Handler SHALL für seine eigene Quelle
|
||||
selbsttätig `STOP` wirken, sodass keine rekursiven Traps entstehen; das
|
||||
`RETURN` SHALL für sie selbsttätig `ON` ausführen — es sei denn, im
|
||||
Handler wurde ausdrücklich `OFF` für diese Quelle ausgeführt, dann bleibt
|
||||
sie aus. Ein `RETURN label` aus einem Handler MUST erlaubt sein.
|
||||
|
||||
#### Scenario: Rückkehr in die unterbrochene Schleife
|
||||
- **WHEN** ein Trap während einer `FOR`-Schleife zugestellt wird
|
||||
- **THEN** läuft die Schleife nach `RETURN` mit unverändertem Zählerstand weiter
|
||||
|
||||
#### Scenario: Kein Wiedereintritt
|
||||
- **WHEN** ein Zeit-Trap läuft und die nächste Frist während des Handlers verstreicht
|
||||
- **THEN** wird der Handler nicht erneut betreten, sondern das Ereignis nach `RETURN` einmal zugestellt
|
||||
|
||||
#### Scenario: OFF im Handler überlebt das RETURN
|
||||
- **WHEN** ein Handler `TIMER OFF` ausführt und danach `RETURN` erreicht
|
||||
- **THEN** bleibt die Quelle aus und kein weiterer Zeit-Trap wird zugestellt
|
||||
|
||||
### Requirement: Getrappte Tasten verlassen den Eingabestrom
|
||||
Eine Taste, für die ein aktiver `ON KEY`-Trap besteht, SHALL dem Trap
|
||||
zugestellt werden und MUST NOT zusätzlich über `INKEY$`, `INPUT`,
|
||||
`LINE INPUT` oder `INPUT$` sichtbar werden. Bei `OFF` oder `STOP` der
|
||||
Quelle SHALL die Taste dem normalen Eingabestrom erhalten bleiben.
|
||||
|
||||
#### Scenario: Getrappte Taste erscheint nicht bei INKEY$
|
||||
- **WHEN** `ON KEY(1) GOSUB Marke` mit `KEY(1) ON` gilt und F1 gedrückt wird
|
||||
- **THEN** läuft der Handler und ein anschließendes `INKEY$` liefert den leeren String
|
||||
|
||||
#### Scenario: Nicht getrappte Taste bleibt im Strom
|
||||
- **WHEN** derselbe Trap mit `KEY(1) OFF` gilt und F1 gedrückt wird
|
||||
- **THEN** liefert `INKEY$` die Sondertastenfolge für F1
|
||||
|
||||
### Requirement: Benutzerdefiniertes Ereignis
|
||||
`SetUEvent` SHALL ein benutzerdefiniertes Ereignis auslösen, das über
|
||||
`ON UEVENT GOSUB` und die Steueranweisung `UEVENT` denselben Regeln für
|
||||
Maskierung und Zustellung unterliegt wie die übrigen Quellen.
|
||||
|
||||
#### Scenario: SetUEvent löst den Handler aus
|
||||
- **WHEN** `ON UEVENT GOSUB Marke` mit `UEVENT ON` gilt und `SetUEvent` aufgerufen wird
|
||||
- **THEN** läuft der Handler am nächsten Zustellpunkt genau einmal
|
||||
|
||||
### Requirement: Reihenfolge bei mehreren fälligen Ereignissen
|
||||
Sind an einem Zustellpunkt mehrere Ereignisse fällig, SHALL genau eines
|
||||
zugestellt werden; die übrigen bleiben anstehend und werden an
|
||||
folgenden Zustellpunkten zugestellt. Die Reihenfolge SHALL festgelegt
|
||||
und dokumentiert sein, sodass sie über Läufe hinweg gleich bleibt.
|
||||
|
||||
#### Scenario: Zwei gleichzeitig fällige Traps
|
||||
- **WHEN** an einem Zustellpunkt ein Zeit- und ein Tasten-Trap zugleich fällig sind
|
||||
- **THEN** läuft zuerst genau ein Handler und der andere am nächsten Zustellpunkt
|
||||
|
||||
### Requirement: Fehler in einem Handler
|
||||
Ein Laufzeitfehler in einem Trap-Handler SHALL der geltenden
|
||||
Fehlerbehandlung unterliegen. `RESUME` und `RESUME NEXT` SHALL sich auf
|
||||
die Anweisung im Handler beziehen, nicht auf die unterbrochene
|
||||
Anweisung.
|
||||
|
||||
#### Scenario: Fehler im Handler erreicht den Handler des Moduls
|
||||
- **WHEN** ein Trap-Handler eine Anweisung mit Laufzeitfehler ausführt und ein modulweiter `ON ERROR GOTO`-Handler gesetzt ist
|
||||
- **THEN** wird dieser Handler betreten und `ERL` nennt die Zeile im Trap-Handler
|
||||
|
||||
### Requirement: Signal-Trap auf Betriebssystemsignalen
|
||||
`ON SIGNAL(n%) GOSUB` und die Steueranweisung `SIGNAL(n%)` SHALL
|
||||
denselben Regeln für Maskierung, Zustellung und Wiedereintritt
|
||||
unterliegen wie die übrigen Quellen. `n%` SHALL auf
|
||||
Betriebssystemsignale abgebildet werden, und zwar ausschließlich auf die
|
||||
Menge, die auf allen Zielplattformen besteht: 1 auf den
|
||||
Unterbrechungswunsch (`SIGINT`), 2 auf den Beendigungswunsch
|
||||
(`SIGTERM`). Ein anderer Wert MUST namentlich abgewiesen werden — zur
|
||||
Übersetzungszeit, wenn er konstant ist, sonst mit Laufzeitfehler 5. Ein
|
||||
Signal MUST NOT im Signalkontext verarbeitet werden; es SHALL über den
|
||||
Host als Ereignis in die Warteschlange gelangen. Die Abweichung zur
|
||||
OS/2-gebundenen Quelle des Vorbilds SHALL in der Sprachreferenz
|
||||
ausgewiesen sein.
|
||||
|
||||
#### Scenario: Signal-Trap folgt der Maskierung
|
||||
- **WHEN** `ON SIGNAL(1) GOSUB Marke` mit `SIGNAL(1) STOP` gilt und ein `SIGINT` eintrifft
|
||||
- **THEN** läuft der Handler erst nach `SIGNAL(1) ON`, und zwar genau einmal
|
||||
|
||||
#### Scenario: Unzulässige Signalnummer
|
||||
- **WHEN** ein Modul `ON SIGNAL(7) GOSUB Marke` enthält
|
||||
- **THEN** wird es namentlich abgewiesen und die Meldung nennt den zulässigen Bereich
|
||||
|
||||
#### Scenario: Signal ohne Trap bleibt Abbruch
|
||||
- **WHEN** ein `SIGINT` eintrifft, ohne dass ein `SIGNAL(1)`-Trap aktiv ist
|
||||
- **THEN** bleibt es beim bisherigen Abbruchverhalten
|
||||
@@ -0,0 +1,19 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Korpusabdeckung der Ereignis-Traps
|
||||
Der Testkorpus SHALL die Ereignissteuerung mit Programmen abdecken, die
|
||||
ohne echte Wartezeit und mit reproduzierbarer Ausgabe laufen: Zeit-Trap,
|
||||
Tasten-Trap einschließlich der Frage, ob die Taste im Eingabestrom
|
||||
verbleibt, benutzerdefiniertes Ereignis, die drei Maskierungszustände je
|
||||
Quelle, der Hauptschalter `EVENT` sowie Rückkehr und Nicht-Wiedereintritt
|
||||
eines Handlers. Ein Korpusprogramm mit Zeitbezug SHALL seinen
|
||||
Zeitverlauf im Programmkopf deklarieren; ohne Deklaration MUST der
|
||||
Harness den Lauf abweisen statt auf die Systemuhr auszuweichen.
|
||||
|
||||
#### Scenario: Zeit-Trap im Korpus
|
||||
- **WHEN** ein Korpusprogramm mit deklariertem Zeitverlauf und `ON TIMER` ausgeführt wird
|
||||
- **THEN** entspricht der Bildschirminhalt byte-genau der Sollausgabe und der Lauf benötigt keine messbare Wartezeit
|
||||
|
||||
#### Scenario: Fehlende Zeitdeklaration
|
||||
- **WHEN** ein Korpusprogramm zeitgesteuerte Traps nutzt, aber keinen Zeitverlauf deklariert
|
||||
- **THEN** schlägt der Harness mit einer Meldung fehl, die die fehlende Deklaration nennt
|
||||
@@ -0,0 +1,49 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### 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`
|
||||
@@ -0,0 +1,42 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### 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
|
||||
@@ -0,0 +1,60 @@
|
||||
## 1. Belege aus der Original-Hilfe
|
||||
|
||||
- [x] 1.1 Einzelseiten zu `ON TIMER`, `ON KEY`, `ON UEVENT`, `ON SIGNAL`, `TIMER`, `KEY(n)`, `UEVENT`, `SIGNAL(n)`, `EVENT` und `SetUEvent` erfassen und in `umfang-und-form.md` dieses Changes wortgetreu festhalten (Syntaxzeilen, Wertebereiche von `n`, Reihenfolge gleichzeitig fälliger Ereignisse, Verhalten in Prozeduren); verifiziert durch die vorhandene Datei mit Quellenangabe je Element
|
||||
- [x] 1.2 Aus 1.1 die Festlegungen ableiten, die die Hilfe **nicht** hergibt, und sie als Abweichung in `docs/sprachreferenz.md` §8 eintragen — insbesondere die Quellenzuordnung von `SIGNAL(n)`; verifiziert durch den Abschnitt „Abweichungen" mit je einem Satz Begründung
|
||||
- [x] 1.3 Delta-Specs an die Belege aus 1.1 angleichen (Leitplanke „Referenzverhalten schlägt Eleganz"): `EVENT` ohne `STOP`, automatischer `STOP`/`ON` um den Handler, `OFF` verwirft Gemerktes, Wertebereiche von `KEY`/`TIMER`/`SIGNAL`; verifiziert durch `openspec validate --strict` und die geänderten Requirements
|
||||
|
||||
## 2. Zeit- und Mausquelle im Host (BREAKING)
|
||||
|
||||
- [x] 2.1 `Host`-Trait um eine monotone Zeitabfrage erweitern und `TerminalHost` daran anschließen; verifiziert durch `cargo build`, das alle drei Host-Implementierungen ohne Warnung übersetzt
|
||||
- [x] 2.2 `CaptureHost` um eine stellbare virtuelle Uhr erweitern (setzen und vorrücken); verifiziert durch einen Unit-Test, der die Uhr vorrückt und den gelesenen Wert prüft
|
||||
- [x] 2.3 Signalquelle: Marke im Signalhandler setzen, Übersetzung in ein Ereignis beim Abholen (D8), `SIGINT` und `SIGTERM` plattformübergreifend über `signal-hook`; verifiziert durch einen Unit-Test, der eine gesetzte Marke als Ereignis wiederfindet
|
||||
- [x] 2.4 Mausereignis in das Ereignismodell aufnehmen (Drücken, Loslassen, Bewegung, Taste, Umschaltzustand, Position) und `TerminalHost` die `crossterm`-Mausereignisse durchreichen lassen, statt sie zu verwerfen; verifiziert durch Unit-Tests der Abbildung je Ereignisart
|
||||
- [x] 2.5 Umrechnung auf 1-basierte Zellenkoordinaten in der Darstellungsschicht, Ereignisse außerhalb der Fläche verwerfen (D7); verifiziert durch Unit-Tests an den Rändern — Zelle 1,1, letzte Zelle, Position außerhalb, und dieselben Fälle nach einer Größenänderung
|
||||
- [x] 2.6 `CaptureHost` kann Mausereignisse einspeisen und die Reihenfolge gegenüber Tastenereignissen bleibt erhalten; verifiziert durch einen Unit-Test mit gemischter Folge
|
||||
|
||||
## 3. Trap-Zustände in der Laufzeit
|
||||
|
||||
- [x] 3.1 Trap-Tabelle in `RtState`: je Quelle Zustand `ON`/`OFF`/`STOP`, höchstens ein anstehendes Ereignis, Zielkennung; verifiziert durch Unit-Tests für die drei Zustandsübergänge inklusive „STOP merkt genau eines"
|
||||
- [x] 3.2 Hauptschalter `EVENT` als Überlagerung der Einzelzustände; verifiziert durch einen Unit-Test, der nach `EVENT ON` den vorherigen Einzelzustand wiederfindet
|
||||
- [x] 3.3 Fälligkeit der Zeit-Traps aus der Host-Zeit berechnen; verifiziert durch einen Unit-Test mit virtueller Uhr über mehrere Fristen inklusive übersprungener Frist
|
||||
- [x] 3.4 `SetUEvent` als Auslöser des benutzerdefinierten Ereignisses; verifiziert durch einen Unit-Test, der genau eine Zustellung sieht
|
||||
- [x] 3.5 Getrappte Tasten am Zustellpunkt aus der Tastenschlange nehmen (D5); verifiziert durch Unit-Tests für beide Richtungen — getrappte Taste erscheint nicht bei `INKEY$`, nicht getrappte bleibt erhalten
|
||||
- [x] 3.7 Umschaltzustand im Tastenereignis mitführen und `KEY n, CHR$(flag)+CHR$(scan)` für n = 15–25 als Erklärung einer Trap-Taste (statt Makrozuweisung); verifiziert durch Unit-Tests für Strg+A mit und ohne Strg sowie durch die Scancode-Tabelle aus der Original-Hilfe
|
||||
- [x] 3.6 Mausereignisse ohne Verbraucher am Zustellpunkt verwerfen und die Warteschlange begrenzen (D7); verifiziert durch einen Unit-Test, der viele Bewegungsereignisse einspeist und die Warteschlangenlänge prüft
|
||||
|
||||
## 4. Zustellung in der VM
|
||||
|
||||
- [x] 4.1 Zustellbit im Flagwort und Fälligkeitsprüfung an `Instr::Stmt`; verifiziert durch einen VM-Test, der ohne aktiven Trap keine Zustellung auslöst
|
||||
- [x] 4.2 Trap-Frame auf den Modulrumpf legen und mit `RETURN` abräumen (D2), inklusive eigenem `GOSUB`-Stapel und `RETURN label`; verifiziert durch VM-Tests für Zustellung aus dem Modulrumpf und aus einer `SUB` heraus, jeweils mit unverändertem Zustand danach
|
||||
- [x] 4.3 Eigene Quelle während des Handlers stoppen und mit `RETURN` freigeben; verifiziert durch einen VM-Test, der den Handler nicht erneut betritt
|
||||
- [x] 4.7 Anweisungsgrenze in den Rücksprung jeder Schleife legen — ohne sie ist `FOR i = 1 TO n: NEXT` für Ereignisse, Breakpoints und Abbruch taub (Befund bei 4.2); verifiziert durch einen Test, der für alle vier Schleifenformen eine Grenze im Kreis nachweist, und dadurch, dass die Trap-Tests mit leerem Schleifenrumpf laufen
|
||||
- [x] 4.4 Reihenfolge bei mehreren fälligen Ereignissen nach Beleg aus 1.1 festlegen; verifiziert durch einen VM-Test mit zwei gleichzeitig fälligen Quellen
|
||||
- [x] 4.5 `DOEVENTS` und `SLEEP` von Builtins auf Anweisungen mit Zustellung umstellen; verifiziert durch VM-Tests — Handler läuft innerhalb einer `DOEVENTS`-Schleife, und `SLEEP` endet vorzeitig durch eine Zustellung
|
||||
- [x] 4.8 Befund bei 4.6: `ERL` meldete die Zeile des Fehlerhandlers statt der Fehlerzeile, sobald der Handler selbst nummeriert war — laufende Zeilennummer und `ERL` getrennt; verifiziert durch einen Regressionstest mit nummeriertem Handler
|
||||
- [x] 4.6 Fehler in einem Handler an die geltende Fehlerbehandlung geben, `RESUME` bezieht sich auf den Handler; verifiziert durch einen VM-Test, dessen `ERL` die Zeile im Handler nennt
|
||||
|
||||
## 5. Frontend
|
||||
|
||||
- [x] 5.1 Grammatik für `ON TIMER(n)/KEY(n)/UEVENT/SIGNAL(n) GOSUB ziel` als eigene Konstrukte; verifiziert durch einen Parser-Test, der zeigt, dass daraus kein berechnetes `ON ausdruck GOSUB` entsteht
|
||||
- [x] 5.2 Signaturen und Wertebereichsprüfung der Steueranweisungen und von `SetUEvent`; verifiziert durch Sema-Tests, deren Diagnosen das Element namentlich nennen (`ON KEY(99)`, `ON PEN`, `CALL SetUEvent`)
|
||||
- [x] 5.3 Trap-Ziele wie `GOSUB`-Ziele auflösen; verifiziert durch einen Sema-Test, der ein fehlendes Label namentlich meldet
|
||||
- [x] 5.4 Absenkung ins HIR/Bytecode, `Unsupported`-Absenkung von `SETUEVENT` entfernen; verifiziert durch `cargo test -p tb-frontend`, inklusive des Inventartests
|
||||
|
||||
## 6. Korpus und Harness
|
||||
|
||||
- [x] 6.1 Kopfzeilen-Direktive `' tb-clock: <n>ms/Zustellpunkt` im Harness auswerten und ohne Direktive den Lauf mit zeitgesteuerten Traps abweisen (D6); verifiziert durch einen Harness-Test für beide Fälle
|
||||
- [x] 6.2 Korpusprogramme mit Sollausgabe anlegen: Zeit-Trap, Tasten-Trap mit Eingabestrom-Nachweis, `SetUEvent`, die drei Maskierungszustände, `EVENT`-Hauptschalter, Rückkehr in eine unterbrochene Schleife; verifiziert durch `cargo test -p tb-cli`, byte-genau gegen die `.out`
|
||||
- [x] 6.3 Denselben Korpuslauf zweimal ausführen und auf identische Ausgabe prüfen; verifiziert durch einen Test, der die Reproduzierbarkeit sicherstellt
|
||||
|
||||
## 7. Dokumentation und Inventar
|
||||
|
||||
- [x] 7.1 `docs/sprachreferenz.md` §8 ausformulieren: Zustellpunkte, Maskierung, Wiedereintritt, Wertebereiche; verifiziert durch den Abschnitt, der jede Spec-Zusicherung wiederfindet
|
||||
- [x] 7.2 `docs/bibliothek.md` um die zehn Elemente erweitern; verifiziert durch je einen Eintrag mit Syntax und Wirkung
|
||||
- [x] 7.3 `docs/inventar.md`: die zehn Einträge auf `implementiert` mit Fundstelle setzen; verifiziert durch `inventar_stimmt_mit_code_ueberein`
|
||||
- [x] 7.4 PLAN.md: Eingangsaufgabe und Punkt „Klassische Ereignis-Traps" der Phase 4 abhaken, Befunde festhalten; verifiziert durch die aktualisierte Phase-4-Liste
|
||||
|
||||
## 8. Abnahme
|
||||
|
||||
- [x] 8.1 `benches/vm.rs` vor und nach der Änderung messen und das Ergebnis in der Änderungsnotiz festhalten; verifiziert durch die Messwerte zur Zusicherung „ohne aktiven Trap kein messbarer Aufwand"
|
||||
- [x] 8.2 `cargo test` über alle Crates grün und `openspec validate phase-4-ereignisschleife --strict` ohne Befund; verifiziert durch beide Kommandos
|
||||
@@ -0,0 +1,261 @@
|
||||
# Umfang und Form der Ereignis-Elemente (Aufgaben 1.1, 1.2)
|
||||
|
||||
**Quelle.** `bas7advr.hlp` (BASIC 7 Advisor), Einzelseiten unter
|
||||
https://dos-help.soulsphere.org/bas7advr.hlp/ (abgerufen 2026-09-04);
|
||||
Gegenprobe `qb45advr.hlp` unter derselben Domain. Zitate wörtlich aus den
|
||||
Einzelseiten, Übersetzung nur in den Kommentarzeilen.
|
||||
|
||||
## 1.1 Einzelseiten
|
||||
|
||||
| Element | Seite |
|
||||
|---|---|
|
||||
| `ON TIMER` | `x_dot_ontimerrr.html` |
|
||||
| `ON KEY` | `x_dot_onkeyr.html` |
|
||||
| `ON UEVENT` | `x_dot_onur.html` |
|
||||
| `ON SIGNAL` | `x_dot_onsigbar.html` |
|
||||
| `TIMER` (Anweisung) | `x_dot_timersr.html` |
|
||||
| `KEY(n)` (Ereignis) | `x_dot_ketvr.html` |
|
||||
| `UEVENT` | `x_dot_uevvvr.html` |
|
||||
| `SIGNAL` | `x_dot_signalr.html` |
|
||||
| `EVENT` | `x_dot_evntonr.html` |
|
||||
| `SetUEvent` | `x_dot_SETUEVr.html` |
|
||||
|
||||
### ON TIMER
|
||||
|
||||
> `ON TIMER(n&) GOSUB {linenumber | linelabel}`
|
||||
>
|
||||
> „The maximum number of seconds that can be specified for the time
|
||||
> interval, n&, is the number of seconds in a 24-hour day (86,400)."
|
||||
>
|
||||
> „A linenumber value of 0 disables event trapping and does not specify
|
||||
> line 0 as the start of the routine."
|
||||
|
||||
Der Zeitwert ist **LONG** (`n&`), Bereich 1–86 400 Sekunden.
|
||||
`GOSUB 0` schaltet den Trap ab.
|
||||
|
||||
### TIMER ON/OFF/STOP
|
||||
|
||||
> „TIMER ON enables timer-event trapping."
|
||||
>
|
||||
> „TIMER OFF disables timer-event trapping. … Events occurring while
|
||||
> trapping is off are ignored."
|
||||
>
|
||||
> „TIMER STOP suspends timer-event trapping. … Events occurring while
|
||||
> trapping is off are remembered and processed when the next TIMER ON
|
||||
> statement is executed."
|
||||
|
||||
**Wiedereintritt — wörtlich belegt:**
|
||||
|
||||
> „an automatic TIMER STOP is executed so that recursive traps cannot
|
||||
> take place."
|
||||
>
|
||||
> „The RETURN from the trapping routine automatically performs a TIMER ON
|
||||
> statement unless an explicit TIMER OFF was performed inside the
|
||||
> routine."
|
||||
|
||||
Das ist genauer als „die Quelle gilt während des Handlers als gestoppt":
|
||||
`RETURN` schaltet die Quelle **auf ON**, außer im Handler stand ein
|
||||
ausdrückliches `OFF`.
|
||||
|
||||
### KEY(n) — Wertebereich
|
||||
|
||||
> ```
|
||||
> KEY(n%) ON
|
||||
> KEY(n%) OFF
|
||||
> KEY(n%) STOP
|
||||
> ```
|
||||
|
||||
| n% | Taste |
|
||||
|---|---|
|
||||
| 0 | alle Tasten dieser Tabelle |
|
||||
| 1–10 | F1–F10 |
|
||||
| 11 | Pfeil hoch |
|
||||
| 12 | Pfeil links |
|
||||
| 13 | Pfeil rechts |
|
||||
| 14 | Pfeil runter |
|
||||
| 15–25 | benutzerdefinierte Tasten |
|
||||
| 30–31 | F11–F12 (101-Tasten-Tastatur) |
|
||||
|
||||
`ON KEY` selbst: `ON KEY(n%) GOSUB {linenumber | linelabel}`. Zur Frage,
|
||||
ob eine getrappte Taste im Eingabestrom bleibt, sagt die Einzelseite nur:
|
||||
|
||||
> „After a key is trapped, the information on which key was trapped is no
|
||||
> longer available."
|
||||
|
||||
Die Gegenprobe in `qb45advr.hlp` (`x_dot_onkeyba.html`, `x_dot_ketv.html`)
|
||||
enthält dazu **nichts**. → Festlegung nötig, siehe 1.2.
|
||||
|
||||
### Benutzerdefinierte Tasten 15–25
|
||||
|
||||
Die Einzelseite zu `KEY n, zeichenkette$` (`x_dot_setfkr.html`) behandelt
|
||||
**nur** die Makrozuweisung für 1–10 und 30–31; die Erklärungsform für
|
||||
15–25 steht in keiner der beiden Hilfen, ebenso wenig die Bedeutung der
|
||||
Bits des Tastaturflagbytes. Belegt ist dagegen die **Scancode-Tabelle**
|
||||
(`qb45advr.hlp`, `x_dot_kbsct.html`), aus der die Zuordnung Zeichen →
|
||||
Scancode stammt:
|
||||
|
||||
> ESC 01 · „! or 1" 02 … „) or 0" 0B · „_ or -" 0C · „+ or =" 0D ·
|
||||
> TAB 0F · Q 10 … P 19 · ENTER 1C · A 1E … L 26 · Z 2C … M 32 ·
|
||||
> SPACEBAR 39 · F1 3B … F10 44 · HOME 47 · UP 48 · LEFT 4B · RIGHT 4D ·
|
||||
> END 4F · DOWN 50 · INS 52 · DEL 53
|
||||
|
||||
→ Festlegung nötig für das Flagbyte, siehe 1.2.
|
||||
|
||||
### UEVENT und SetUEvent
|
||||
|
||||
> `ON UEVENT GOSUB {linenumber | linelabel}`
|
||||
>
|
||||
> „This event typically originates from a hardware interrupt but can also
|
||||
> be triggered by software."
|
||||
>
|
||||
> „SetUEvent … sets a flag that BASIC checks before executing the next
|
||||
> statement."
|
||||
|
||||
Bestätigt die Zustellung **an der Anweisungsgrenze**.
|
||||
|
||||
> ```
|
||||
> UEVENT ON
|
||||
> UEVENT OFF
|
||||
> UEVENT STOP
|
||||
> ```
|
||||
>
|
||||
> UEVENT STOP „preserves events in memory for processing once UEVENT ON
|
||||
> resumes, though executing UEVENT OFF afterward will discard these queued
|
||||
> events."
|
||||
|
||||
`OFF` verwirft also auch ein unter `STOP` gemerktes Ereignis.
|
||||
|
||||
`SetUEvent` ist eine **Routine** ohne dokumentierte Argumentliste und ohne
|
||||
dokumentierten Rückgabewert:
|
||||
|
||||
> „It sets the BASIC entry point that causes a user-defined event."
|
||||
|
||||
### SIGNAL
|
||||
|
||||
> ```
|
||||
> SIGNAL(n%) ON
|
||||
> SIGNAL(n%) OFF
|
||||
> SIGNAL(n%) STOP
|
||||
> ```
|
||||
>
|
||||
> `ON SIGNAL(n%) GOSUB {linenumber | linelabel}`
|
||||
>
|
||||
> „A linenumber value of 0 disables event trapping."
|
||||
|
||||
Quelle der Signale — wörtlich:
|
||||
|
||||
> „OS/2 protected-mode signal"
|
||||
>
|
||||
> „Process flags A, B, and C are used for communicating between
|
||||
> processes."
|
||||
>
|
||||
> aktiviert durch die OS/2-Funktion `DOSFLAGPROCESS` aus einem anderen
|
||||
> Prozess.
|
||||
|
||||
Ein **Wertebereich für `n%` ist nicht dokumentiert**; die Einzelseite
|
||||
verweist auf „Chapter 9, ‚Event Handling'" des Programmer's Guide, der
|
||||
nicht vorliegt.
|
||||
|
||||
### EVENT
|
||||
|
||||
> ```
|
||||
> EVENT ON
|
||||
> EVENT OFF
|
||||
> ```
|
||||
>
|
||||
> „EVENT OFF … equivalent to compiling a program from the BC command line
|
||||
> without using /V or /W."
|
||||
>
|
||||
> „EVENT OFF and EVENT ON can be used to bracket sections of code where
|
||||
> events do not need to be detected and trapped, and fast performance is
|
||||
> required."
|
||||
|
||||
**`EVENT STOP` ist nicht dokumentiert.** `EVENT` ist im Vorbild kein
|
||||
Laufzeit-Hauptschalter mit drei Zuständen, sondern eine Klammer, die die
|
||||
Erzeugung der Ereignisprüfungen im Kompilat ein- und ausschaltet.
|
||||
|
||||
### Reihenfolge und Trapping in Prozeduren
|
||||
|
||||
Zu beidem geben die Einzelseiten **nichts** her; sie verweisen auf
|
||||
Kapitel 9 des Programmer's Guide. → Festlegung nötig, siehe 1.2.
|
||||
|
||||
## 1.2 Abweichungen der Delta-Spec vom Beleg
|
||||
|
||||
Nach der Leitplanke „Referenzverhalten schlägt Eleganz … das gilt auch
|
||||
gegenüber den eigenen Spezifikationen" ist bei jeder Abweichung die
|
||||
**Spec** anzugleichen.
|
||||
|
||||
| Nr. | Delta-Spec sagt | Beleg sagt | Folge |
|
||||
|---|---|---|---|
|
||||
| A | `EVENT` hat `ON`/`OFF`/`STOP` und überlagert die Einzelzustände | nur `ON`/`OFF`; Klammer um die Erzeugung der Prüfungen | Spec angleichen; `EVENT STOP` wird namentlich abgewiesen |
|
||||
| B | „während des Handlers gilt die Quelle als gestoppt; mit `RETURN` gilt wieder ihr Zustand" | Eintritt = automatisches `STOP`; `RETURN` = automatisches `ON`, außer im Handler stand ein ausdrückliches `OFF` | Spec präzisieren |
|
||||
| C | `OFF` verwirft künftige Ereignisse | `OFF` verwirft **auch** ein unter `STOP` gemerktes | Spec ergänzen |
|
||||
| D | `ON KEY(n)` mit Wertebereich | 0, 1–25, 30–31 mit fester Tastenzuordnung | Spec unverändert, Bereich jetzt belegt |
|
||||
| E | `ON TIMER(n)` | `n&` ist LONG, 1–86 400; `GOSUB 0` schaltet ab | Spec unverändert, Form jetzt belegt |
|
||||
|
||||
**Ohne Beleg — unsere Festlegungen (gehören nach `sprachreferenz.md`
|
||||
unter „Abweichungen"):**
|
||||
|
||||
1. **Getrappte Tasten verlassen den Eingabestrom.** Von keiner der beiden
|
||||
Hilfen belegt. Wir legen fest: eine Taste mit aktivem Trap erscheint
|
||||
nicht zusätzlich bei `INKEY$`/`INPUT`. Begründung: andernfalls sähe ein
|
||||
Programm dieselbe Taste zweimal, und `KEY(n) OFF` wäre die einzige Art,
|
||||
eine Taste überhaupt normal zu lesen.
|
||||
2. **Reihenfolge gleichzeitig fälliger Ereignisse.** Unbelegt. Wir legen
|
||||
fest: `KEY` (aufsteigend nach `n`), dann `TIMER`, dann `UEVENT`, dann
|
||||
`SIGNAL` — Tasten zuerst, weil sie an einer Benutzereingabe hängen.
|
||||
3. **Trapping in Prozeduren.** Unbelegt. Wir legen fest: die Zustellung
|
||||
erfolgt an jeder Anweisungsgrenze, auch innerhalb von `SUB`/`FUNCTION`;
|
||||
das Ziel liegt im Modulrumpf. Das entspricht dem bereits umgesetzten
|
||||
Verhalten des modulweiten `ON ERROR`.
|
||||
4. **Bits des Tastaturflagbytes** in `KEY n, CHR$(flag)+CHR$(scan)`.
|
||||
Unbelegt. Wir legen die Belegung des BIOS-Flagbytes zugrunde:
|
||||
1 rechte Umschalttaste, 2 linke Umschalttaste, 4 Strg, 8 Alt. Im
|
||||
Terminal sind linke und rechte Umschalttaste nicht unterscheidbar,
|
||||
beide Bits gelten deshalb gleich; NumLock und CapsLock spielen für
|
||||
einen Trap keine Rolle und werden ignoriert.
|
||||
5. **`SIGNAL`-Quelle.** Die belegte Quelle ist OS/2-gebunden
|
||||
(`DOSFLAGPROCESS`, Prozessflags A/B/C) und existiert auf keiner
|
||||
Zielplattform; ein Wertebereich ist nicht dokumentiert. → **Entscheidung
|
||||
des Projektinhabers ausstehend**, siehe unten.
|
||||
|
||||
## Entscheidung zu `SIGNAL` (Projektinhaber, 2026-09-04)
|
||||
|
||||
`SIGNAL` wird auf **Betriebssystemsignale** abgebildet, und zwar nur auf
|
||||
die minimale Menge, die auf allen Zielplattformen existiert.
|
||||
|
||||
Der C-Standard garantiert überall — Linux, macOS **und** Windows —
|
||||
`SIGABRT`, `SIGFPE`, `SIGILL`, `SIGINT`, `SIGSEGV`, `SIGTERM`. Von diesen
|
||||
sind nur zwei asynchron, von außen ausgelöst und fortsetzbar; die vier
|
||||
Fault-Signale entstehen aus dem eigenen Programmzustand, und ein
|
||||
`RETURN` aus ihrem Handler führte in denselben Fehler zurück. Abgebildet
|
||||
werden deshalb:
|
||||
|
||||
| `n%` | Signal | Bedeutung |
|
||||
|---|---|---|
|
||||
| 1 | `SIGINT` | Unterbrechungswunsch von außen |
|
||||
| 2 | `SIGTERM` | Beendigungswunsch von außen |
|
||||
|
||||
Ein anderer Wert als 1 oder 2 wird namentlich abgewiesen — zur
|
||||
Übersetzungszeit, wenn er konstant ist, sonst mit Laufzeitfehler 5.
|
||||
|
||||
**Zustellung.** Signale erreichen die Laufzeit wie die Zeit über den
|
||||
Host; nur er berührt das Betriebssystem. Ein Signal wird nicht im
|
||||
Signalkontext verarbeitet, sondern setzt eine Marke, die der Host beim
|
||||
nächsten Abholen in ein Ereignis übersetzt.
|
||||
|
||||
**Verhältnis zum Abbruch.** Im Rohmodus liefert das Terminal Strg+C als
|
||||
Taste, nicht als Signal; daran ändert sich nichts. Trifft `SIGINT`
|
||||
dagegen von außen ein (`kill -INT`), gilt: mit aktivem Trap läuft der
|
||||
Handler, ohne aktiven Trap bleibt es beim bisherigen Abbruch. Dieselbe
|
||||
Regel wie bei getrappten Tasten.
|
||||
|
||||
**Abweichungen, die zu dokumentieren sind:**
|
||||
|
||||
1. Die belegte Quelle des Vorbilds (OS/2-Prozessflags A/B/C über
|
||||
`DOSFLAGPROCESS`) existiert nicht; `n%` bedeutet bei uns etwas
|
||||
anderes als im Vorbild. Ein Wertebereich war im Vorbild ohnehin nicht
|
||||
dokumentiert.
|
||||
2. `SIGTERM` ist unter Windows von außen nicht zustellbar (es gibt kein
|
||||
`kill`); `SIGNAL(2)` bleibt dort wirkungslos. `SIGNAL(1)` wird dort
|
||||
über den Konsolen-Steuerhandler (`CTRL_C_EVENT`) bedient.
|
||||
Reference in New Issue
Block a user