Ereigniszustellung und Hostgrenzen korrigieren und Change archivieren
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-05
|
||||
@@ -0,0 +1,41 @@
|
||||
## Context
|
||||
|
||||
RtState hält getrennte Tasten-/Mauspuffer; forms_zustellen setzt weitere Handlerframes auf, auch vor der ersten Anweisung eines gerade begonnenen Handlers. Der Stmt-Pfad dupliziert die Trap-Zustellung. SLEEP benutzt eine zweite reale Uhr. tb-vm zieht tb-ui samt unbedingten Terminaldependencies ein. Siehe proposal.md und die Delta-Specs.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:** Eine gemeinsame Zustellentscheidung nutzen und Ereignisordnung vom Host bis zum sichtbaren Handlerablauf bewahren.
|
||||
|
||||
**Non-Goals:** Keine präemptiven Threads, kein neuer Scheduler und keine Änderung der festgelegten Priorität gleichzeitig fälliger klassischer Trap-Quellen.
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1 — Reihenfolge einmal festhalten
|
||||
|
||||
Geordnete Hostereignisse bis zur Annahme durch Trap, Control oder Konsoleneingabe erhalten. KEY STOP merkt höchstens ein Ereignis, lässt aber die Taste normal lesbar. Keine spätere Rekonstruktion der Reihenfolge aus zwei bereits getrennten Queues.
|
||||
|
||||
### D2 — Zustellung und Handlerbeginn koordinieren
|
||||
|
||||
Die vorhandenen Zustellpunkte auf eine gemeinsame Entscheidung führen. Der erste Statement-Eintritt eines zugestellten Handlers darf nicht durch das nächste bereits wartende Ereignis überholt werden; rekursive Zustellung derselben Forms-Ereignisprozedur verhindern und Rückkehr geordnet abarbeiten. Explizites DOEVENTS/Modalität bleiben kooperative Zustellpunkte. Menüsperre gilt überall und verändert keine expliziten ON/OFF/STOP-Zustände.
|
||||
|
||||
### D3 — Host besitzt Zeit und Warten
|
||||
|
||||
Instant-Fallback aus der VM entfernen. Für blockierende Wartephasen eine Host-Wartemöglichkeit mit Deadline vorsehen, die Terminal/Pipe in Realzeit und Testhosts durch virtuelle Zeit umsetzen; SLEEP darf weder mit stehender virtueller Zeit scheinbar erfolgreich enden noch busy-spinnen. Ohne Trap, Timer oder zeitabhängige Mausauswertung keine Zeitabfrage.
|
||||
|
||||
### D4 — Backend als vorhandene Paketgrenze
|
||||
|
||||
Terminalmodule und deren Dependencies in tb-ui durch ein terminal-Feature abtrennen; tb-vm nutzt tb-ui ohne dieses Feature, der CLI-Runner aktiviert es ausdrücklich. Modelle/FRM/Dialogs im terminalfreien Teil verwenden nur Host und Zellenpuffer. Eine zusätzliche Forms-Crate ist dafür nicht erforderlich.
|
||||
|
||||
### D5 — BackTab normalisieren
|
||||
|
||||
KeyCode::BackTab auf denselben Tab-Eingabepfad mit gesetztem Shiftbit abbilden. Kein eigener Fokusmechanismus.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- Neue Zustellung kann Referenzen/Fehlerzustand eines wartenden Frames beschädigen → bestehende Trap-, RESUME-, BYREF- und Modalitätstests mit ausführen.
|
||||
- Cargo-Feature-Unifikation kann Terminalabhängigkeiten verdecken → terminalfreien VM-Build über einen isolierten Einbetter und dessen aufgelösten Dependency-Graph prüfen.
|
||||
- Virtuelle Hosts können bisher SLEEP ohne Zeitfortschritt verwenden → diese Hosts ausdrücklich auf die neue Wartevereinbarung umstellen, keine reale Rückfallebene beibehalten.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Vor forms-zustand-und-bedienung umsetzen. F06 bleibt Eigentum dieses Changes; Forms-State-Change verändert diese Requirement-Blöcke nicht. Bestehende Spec-/Codeverträge erst nach erfolgreicher Umsetzung synchronisieren und archivieren. Bis dahin bleiben alle Tasks offen. Änderungen als zusammenhängenden Commit je Change integrieren; bei Fehlschlag auf den vorherigen Code zurückgehen und neue Datenformatversionen nicht mit alten Lesern öffnen.
|
||||
@@ -0,0 +1,30 @@
|
||||
## Why
|
||||
|
||||
Ereignisse gehen verloren, laufen in falscher Reihenfolge oder umgehen Menüsperren. F04–F08, F18 und F20 zeigen außerdem Zeitabfragen ohne Bedarf und eine unerwünschte Terminalabhängigkeit der VM. Siehe [Review vom 05.09.2026](../../../../docs/reviews/2026-09-05-openspec-code-review.md).
|
||||
|
||||
## What Changes
|
||||
|
||||
- Signal 1/2, KEY STOP und die FIFO-Reihenfolge von Tasten, Maus und Forms-Handlern durchgängig erhalten.
|
||||
- Menüsperre und Zustellpunkte für Statements, DOEVENTS, SLEEP und blockierende Eingaben vereinheitlichen.
|
||||
- Zeit ausschließlich bedarfsgerecht über den Host verwenden und Forms-Ereignisse während SLEEP zustellen.
|
||||
- BackTab in den vorhandenen Rückwärtstab-Pfad übersetzen.
|
||||
- VM und Forms ohne Terminalbackend separat baubar machen.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
Keine neuen Capability-Pfade.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `ereignissteuerung`: Reihenfolge, KEY-STOP-Verhalten, Signalzustellung und Hostzeit durch ergänzende Grenzfallszenarien absichern.
|
||||
- `vm-ausfuehrung`: Den Pfad ohne Zeitbedarf und die Fortsetzung nach Ereignissen präzisieren.
|
||||
- `forms-steuerelemente`: Fokusreihenfolge, BackTab und Menüsperre an allen Zustellpunkten festhalten.
|
||||
- `textbildschirm`: Terminalfreie Einbettung der vollständigen VM einschließlich Forms präzisieren.
|
||||
|
||||
## Impact
|
||||
|
||||
RtState/Traps, Vm::exec und Zustell-/Wartepfade, tb-ui TerminalHost und Cargo-Features; Unit-, VM- und CLI-Tests. Keine Änderung des BASIC-Sprachumfangs.
|
||||
|
||||
Vor forms-zustand-und-bedienung umsetzen. F06 bleibt Eigentum dieses Changes; Forms-State-Change verändert diese Requirement-Blöcke nicht.
|
||||
@@ -0,0 +1,167 @@
|
||||
//! Ursprüngliche Reproduktionen: fünf behobene Befunde und eine Kontrollprobe.
|
||||
//! Separater Cargo-Treiber; siehe review.md. Alle sechs Prüfungen müssen bestehen.
|
||||
use tb_frontend::forms::{FormCatalog, ObjectClass};
|
||||
use tb_runtime::host::{CaptureHost, Ereignis, Host, MausArt, MausEreignis};
|
||||
use tb_runtime::screen::TextScreen;
|
||||
use tb_runtime::value::Value;
|
||||
use tb_vm::interp::{RunEvent, Vm};
|
||||
|
||||
fn vm(src: &str) -> Vm {
|
||||
let mut catalog = FormCatalog::default();
|
||||
catalog.add("Form1", ObjectClass::Form, None, false);
|
||||
catalog.add("Text1", ObjectClass::TextBox, Some("Form1"), false);
|
||||
catalog.add("Text2", ObjectClass::TextBox, Some("Form1"), false);
|
||||
catalog.add("Timer1", ObjectClass::Timer, Some("Form1"), false);
|
||||
catalog.add("Menu1", ObjectClass::Menu, Some("Form1"), false);
|
||||
catalog.add("Item1", ObjectClass::Menu, Some("Menu1"), false);
|
||||
Vm::new(
|
||||
tb_vm::compile_source_with_forms("FORM1", src, &catalog)
|
||||
.unwrap_or_else(|d| panic!("{d:?}")),
|
||||
)
|
||||
}
|
||||
|
||||
fn stop(vm: &mut Vm, host: &mut dyn Host) {
|
||||
assert!(matches!(vm.run(host), RunEvent::Stopped { .. }));
|
||||
}
|
||||
|
||||
fn string(vm: &Vm, name: &str) -> String {
|
||||
match vm.inspect(name) {
|
||||
Some(Value::Str(s)) => s.to_string(),
|
||||
other => panic!("{other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn key(s: &str) -> Ereignis {
|
||||
Ereignis::Taste(s.into(), 0)
|
||||
}
|
||||
fn mouse() -> Ereignis {
|
||||
Ereignis::Maus(MausEreignis {
|
||||
art: MausArt::Druck,
|
||||
taste: 1,
|
||||
shift: 0,
|
||||
zeile: 1,
|
||||
spalte: 1,
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn review_signal_waehrend_blockierender_eingabe() {
|
||||
struct Input {
|
||||
reads: usize,
|
||||
}
|
||||
impl Host for Input {
|
||||
fn present(&mut self, _: &TextScreen) {}
|
||||
fn next_event(&mut self, block: bool) -> Option<Ereignis> {
|
||||
if !block {
|
||||
return None;
|
||||
}
|
||||
self.reads += 1;
|
||||
Some(if self.reads == 1 {
|
||||
Ereignis::Signal(2)
|
||||
} else {
|
||||
Ereignis::Ende
|
||||
})
|
||||
}
|
||||
fn jetzt_ms(&mut self) -> u64 {
|
||||
0
|
||||
}
|
||||
fn warten(&mut self, _: Option<u64>) -> Option<Ereignis> {
|
||||
self.next_event(true)
|
||||
}
|
||||
}
|
||||
let mut results = vec![];
|
||||
for statement in ["INPUT s$", "LINE INPUT s$", "s$ = INPUT$(1)"] {
|
||||
let src =
|
||||
format!("ON SIGNAL(2) GOSUB H\nSIGNAL(2) ON\n{statement}\nEND\nH:\nPRINT \"H\"\nEND");
|
||||
let mut vm = Vm::new(tb_vm::compile_source("TEST", &src).unwrap());
|
||||
let result = vm.run(&mut Input { reads: 0 });
|
||||
println!(
|
||||
"{statement}: {result:?}; Ausgabe={:?}",
|
||||
tb_runtime::snapshot::text(&vm.rt.screen)
|
||||
);
|
||||
results.push(result);
|
||||
}
|
||||
assert!(results.iter().all(|result| *result == RunEvent::Ended));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn review_objektargument_darf_gotfocus_nicht_vorziehen() {
|
||||
let mut vm = vm("DIM SHARED s$\nText1.TabIndex = 0\nText2.TabIndex = 1\nForm1.Show\nText1.SetFocus\nSTOP\nDOEVENTS\nEND\nSUB Text1_LostFocus()\nSHARED s$\nCALL Mark(Text1)\ns$ = s$ + \"l\"\nEND SUB\nSUB Mark(c AS CONTROL)\nSHARED s$\ns$ = s$ + \"L\"\nEND SUB\nSUB Text2_GotFocus()\nSHARED s$\ns$ = s$ + \"G\"\ns$ = s$ + \"g\"\nEND SUB");
|
||||
let mut host = CaptureHost::default();
|
||||
stop(&mut vm, &mut host);
|
||||
host.ereignis(key("\t"));
|
||||
assert_eq!(vm.run(&mut host), RunEvent::Ended);
|
||||
assert_eq!(string(&vm, "s"), "LlGg");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn review_gleichzeitig_key_und_uevent_behalten_prioritaet() {
|
||||
let mut vm = vm("ON KEY(15) GOSUB K\nKEY 15, CHR$(0) + CHR$(20)\nKEY(15) ON\nON UEVENT GOSUB U\nUEVENT ON\nSTOP\nDOEVENTS\nEND\nK:\ns$ = s$ + \"K\"\nRETURN\nU:\ns$ = s$ + \"U\"\nRETURN");
|
||||
let mut host = CaptureHost::default();
|
||||
stop(&mut vm, &mut host);
|
||||
host.ereignis(key("t"));
|
||||
vm.rt.pump(&mut host, false);
|
||||
vm.rt.traps.melden(tb_runtime::traps::Quelle::UEvent);
|
||||
assert_eq!(vm.run(&mut host), RunEvent::Ended);
|
||||
assert_eq!(string(&vm, "s"), "KU");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn review_modale_eingabe_vor_eof_wird_zugestellt() {
|
||||
let mut vm = vm("DIM SHARED s$\nForm1.Show 1\nEND\nSUB Form_MouseDown(Button AS INTEGER, Shift AS INTEGER, X AS SINGLE, Y AS SINGLE)\nSHARED s$\ns$ = \"M\"\nUNLOAD Form1\nEND SUB");
|
||||
let mut host = CaptureHost::default();
|
||||
host.ereignis_nach(2, mouse());
|
||||
host.ereignis_nach(2, Ereignis::Ende);
|
||||
assert_eq!(vm.run(&mut host), RunEvent::Ended);
|
||||
assert_eq!(string(&vm, "s"), "M");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn review_menu_schliessen_stellt_pending_trap_vor_erneutem_warten_zu() {
|
||||
struct MenuHost {
|
||||
time: u64,
|
||||
waits: usize,
|
||||
}
|
||||
impl Host for MenuHost {
|
||||
fn present(&mut self, _: &TextScreen) {}
|
||||
fn next_event(&mut self, _: bool) -> Option<Ereignis> {
|
||||
None
|
||||
}
|
||||
fn jetzt_ms(&mut self) -> u64 {
|
||||
self.time
|
||||
}
|
||||
fn warten(&mut self, deadline: Option<u64>) -> Option<Ereignis> {
|
||||
self.waits += 1;
|
||||
if self.waits == 1 {
|
||||
return Some(key("\u{1b}"));
|
||||
}
|
||||
self.time = deadline.unwrap();
|
||||
None
|
||||
}
|
||||
}
|
||||
let mut vm = vm("ON UEVENT GOSUB H\nUEVENT ON\nMenu1.Caption = \"&Datei\"\nItem1.Caption = \"&Ende\"\nForm1.Show\nSTOP\nSLEEP 60\nEND\nH:\ns$ = \"U\"\nRETURN");
|
||||
let mut host = CaptureHost::default();
|
||||
stop(&mut vm, &mut host);
|
||||
vm.forms.handle_key("d", 4);
|
||||
vm.rt.traps.melden(tb_runtime::traps::Quelle::UEvent);
|
||||
let mut host = MenuHost { time: 0, waits: 0 };
|
||||
assert_eq!(vm.run(&mut host), RunEvent::Ended);
|
||||
println!(
|
||||
"Menü/SLEEP: Zeit={}, Warteaufrufe={}, Handler={}",
|
||||
host.time,
|
||||
host.waits,
|
||||
string(&vm, "s")
|
||||
);
|
||||
assert_eq!(host.waits, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn review_maus_vor_getrappter_taste() {
|
||||
let mut vm = vm("DIM SHARED s$\nON KEY(15) GOSUB H\nKEY 15, CHR$(0) + CHR$(20)\nKEY(15) ON\nForm1.Show\nSTOP\nDOEVENTS\nDOEVENTS\nEND\nH:\ns$ = s$ + \"K\"\nRETURN\nSUB Form_MouseDown(Button AS INTEGER, Shift AS INTEGER, X AS SINGLE, Y AS SINGLE)\nSHARED s$\ns$ = s$ + \"M\"\nEND SUB");
|
||||
let mut host = CaptureHost::default();
|
||||
stop(&mut vm, &mut host);
|
||||
host.ereignis(mouse());
|
||||
host.ereignis(key("t"));
|
||||
assert_eq!(vm.run(&mut host), RunEvent::Ended);
|
||||
assert_eq!(string(&vm, "s"), "MK");
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
# Verifikation: ereigniszustellung-und-hostgrenzen
|
||||
|
||||
Stand nach Behebung: 05.09.2026, aktueller Arbeitsbaum auf `57d6386`.
|
||||
**Alle fünf Verifikationsbefunde sind behoben.** Die ursprünglichen sechs
|
||||
Review-Proben bestehen unverändert in ihren Erwartungen; zusätzlich wurden
|
||||
sie in die Workspace-Regressionen übernommen und um verwandte Pfade ergänzt.
|
||||
|
||||
| Dimension | Ergebnis |
|
||||
| --- | --- |
|
||||
| Vollständigkeit | 12/12 Tasks erledigt; ursprüngliche Zuordnung der 9 Delta-Requirements und 33 Szenarien um Korrektur-Nachweise ergänzt. |
|
||||
| Korrektheit | 419 Workspace-Tests und alle sechs separaten Review-Proben bestanden; keine offenen V1–V5-Befunde. |
|
||||
| Kohärenz | Gemeinsame Zustellung auch während Eingabe und nach Menüschluss; synchrone LOAD-Wirkungen getrennt, klassische Quellpriorität erhalten. |
|
||||
|
||||
## Korrekturen und Regressionen
|
||||
|
||||
| Befund | Behebung | Nachweis in `crates/tb-vm/tests/events.rs` |
|
||||
| --- | --- | --- |
|
||||
| V1 | Fortsetzbarer Konsolenleser für INPUT, LINE INPUT, INPUT$ und RANDOMIZE ohne Argument. Frame hält Teiltext/Restlänge, Stack hält Operanden und Referenzen. Trap-Unwinding erhält gemeinsam genutzte Locals; Fehler und RETURN label verwerfen abgebrochene Eingaben. | Ursprüngliche Signalprobe; Teiltext/BYREF/Ausdruck, verschachtelte Eingaben mit STOP/CONT, Timer, Redo, lokaler Fehlerhandler, RANDOMIZE und RETURN label. |
|
||||
| V2 | Implizite Objektaufrufe stellen ausschließlich notwendige LOAD-Handler zu. Deren Rückkehr zieht keine unabhängigen Forms-Ereignisse vor. | LlGg mit Control-Argument, Property-Lesen/-Schreiben, Refresh und LOAD; bestehende Load-vor-Show- und Unload-Tests. |
|
||||
| V3 | Nach dem Schließen des Menüs wird die gemeinsame Auswahl vor jedem erneuten Warten wiederholt. | Esc bei Zeit 0, UEVENT zugestellt nach genau einem Warteaufruf; bestehende Menüsperrtests einschließlich Forms-Timer. |
|
||||
| V4 | Modale und modellose Schleifen beenden bei EOF erst, wenn kein Handler mehr zugestellt wurde. | Ursprüngliche MouseDown/Unload-Probe; jeweils zwei Mausereignisse vor EOF ergeben MM in beiden Schleifen. |
|
||||
| V5 | Getrappte Tasten am Queue-Anfang werden vor der klassischen Quellenauswahl gemeldet. | Gleichzeitiges KEY/UEVENT ergibt KU; Maus vor getrappter Taste bleibt MK. |
|
||||
|
||||
## Ursprüngliche Befunde — historischer Stand vor der Behebung
|
||||
|
||||
Die folgenden Ist-Beobachtungen und Ursachen dokumentieren den fehlgeschlagenen
|
||||
Review vor diesen Korrekturen. Alle fünf sind durch die Nachweise oben geschlossen.
|
||||
|
||||
### V1 — Während blockierender Eingabe werden eintreffende Traps nicht zugestellt (hoch)
|
||||
|
||||
**Reproduktion:** `ON SIGNAL(2) GOSUB H`, `SIGNAL(2) ON`, danach INPUT.
|
||||
Der Host liefert erst im blockierenden `next_event(true)` Signal(2), beim
|
||||
nächsten Aufruf Eingabeende. Der Handler H würde `PRINT "H"` und END ausführen.
|
||||
|
||||
**Ist:** INPUT, LINE INPUT und INPUT$(1) enden jeweils mit Fehler 62, H läuft
|
||||
nicht. Ohne nachfolgende Eingabe kann die Laufzeit stattdessen erneut
|
||||
blockieren, obwohl der Signal-Trap schon ansteht.
|
||||
|
||||
**Soll:** Vor dem erneuten Blockieren muss der erlaubte Zustellpunkt genutzt
|
||||
werden; H beendet das Programm, Ausgabe H, kein Fehler 62.
|
||||
|
||||
**Ursache:** Der einmalige VM-Check vor der Eingabeinstruktion genügt nicht.
|
||||
[RtState::naechste_taste](../../../../crates/tb-runtime/src/builtins.rs#L209)
|
||||
pumpt innerhalb seiner Schleife weitere Ereignisse und blockiert erneut,
|
||||
ohne zur VM-Zustellung zurückzukehren. Dasselbe betrifft die drei genannten
|
||||
Eingabefunktionen über ihren gemeinsamen Leser.
|
||||
|
||||
**Vertrag:** `ereignissteuerung` / Abschließende Liste der Zustellpunkte,
|
||||
Signal-Trap auf Betriebssystemsignalen; Design D2; Task 2.1.
|
||||
**Korrektur:** Blockierende Eingabe so fortsetzbar machen, dass nach einem
|
||||
Nicht-Tasten-Ereignis und vor dem nächsten Host-Warten die gemeinsame
|
||||
VM-Zustellung laufen kann. Eingabepuffer, Referenzen und Operanden erhalten.
|
||||
|
||||
### V2 — Control-Argumente stellen wartende Forms-Handler mitten in einer Anweisung zu (hoch)
|
||||
|
||||
**Reproduktion:** Fokuswechsel Text1 → Text2. Text1_LostFocus ruft als erste
|
||||
wirksame Anweisung `CALL Mark(Text1)` auf; Mark hängt L an eine gemeinsame
|
||||
Variable, LostFocus danach l. Text2_GotFocus hängt G und g an.
|
||||
|
||||
**Ist:** `GgLl`. **Soll:** `LlGg`.
|
||||
|
||||
**Ursache:** [Instr::PushObject](../../../../crates/tb-vm/src/interp.rs#L1344)
|
||||
ruft nach dem Ablegen eines bereits geladenen Control-Objekts bedingungslos
|
||||
`dispatch_next_form_event` auf. Dadurch läuft GotFocus noch vor dem eigentlichen
|
||||
CALL, obwohl weder DOEVENTS noch modales Warten vorliegt. Der Schutz am ersten
|
||||
Stmt-Eintritt und `forms_frei` werden damit umgangen.
|
||||
|
||||
**Vertrag:** Fokus, Tabreihenfolge und Access-Keys / Handlerwirkungen in
|
||||
Fokusreihenfolge; Abschließende Liste der Zustellpunkte; Design D1/D2;
|
||||
Tasks 1.2 und 2.1.
|
||||
**Korrektur:** Unabhängig wartende Ereignisse aus Objekt-Opcodes heraus nicht
|
||||
zustellen. Notwendige synchrone Load-/Unload-Wirkungen gezielt behandeln;
|
||||
alle Aufrufer von `dispatch_next_form_event` gegen dieselben Zustellregeln prüfen.
|
||||
|
||||
### V3 — Menüschließen während SLEEP führt zu erneutem Warten trotz bereitem Trap (hoch)
|
||||
|
||||
**Reproduktion:** Menü offen, UEVENT anstehend, `SLEEP 60`. Der erste
|
||||
Host-Warteaufruf liefert Esc bei unveränderter virtueller Zeit 0.
|
||||
|
||||
**Ist:** Esc schließt das Menü; die VM wartet ein zweites Mal bis Hostzeit
|
||||
60.000 ms. Erst dann läuft der UEVENT-Handler.
|
||||
**Soll:** Nach Esc läuft der freigegebene Handler ohne zweiten Warteaufruf
|
||||
und beendet SLEEP sofort. Bei einer Wartephase ohne eigene Deadline droht
|
||||
entsprechend Warten auf ein weiteres externes Ereignis.
|
||||
|
||||
**Ursache:** Die klassische Trap-Auswahl in
|
||||
[Vm::zustellen](../../../../crates/tb-vm/src/interp.rs#L625) erfolgt vor der
|
||||
Eingabeverarbeitung. Nachdem Esc die Menüsperre aufgehoben hat, kehrt die
|
||||
Methode ohne erneute Auswahl zurück;
|
||||
[SLEEP](../../../../crates/tb-vm/src/interp.rs#L707) ruft unmittelbar wieder
|
||||
`warten` auf.
|
||||
|
||||
**Vertrag:** Menüsystem / Traps ruhen im geöffneten Menü und werden danach
|
||||
fortgesetzt; Abschließende Liste der Zustellpunkte / SLEEP endet durch
|
||||
Ereignis; Design D2/D3; Tasks 2.1/2.2.
|
||||
**Korrektur:** Nach einer Eingabe, die die Zustellbarkeit verändert, bereite
|
||||
Ereignisse vor jedem weiteren blockierenden Warten erneut auswählen.
|
||||
|
||||
### V4 — Modale Schleife verliert bereits angenommenes Ereignis unmittelbar vor EOF (hoch)
|
||||
|
||||
**Reproduktion:** `Form1.Show 1`; nach der zweiten Anzeige liefert der Host
|
||||
MouseDown und unmittelbar danach Eingabeende. Der MouseDown-Handler hängt M
|
||||
an und entlädt das Formular.
|
||||
|
||||
**Ist:** VM meldet Ended, die gemeinsame Variable bleibt leer; der Handler
|
||||
wurde nicht ausgeführt. **Soll:** Das vor EOF eingetroffene Mausereignis wird
|
||||
noch wirksam, Ergebnis M, Formular entladen.
|
||||
|
||||
**Ursache:** In der [modalen Run-Schleife](../../../../crates/tb-vm/src/interp.rs#L890)
|
||||
setzt `zustellen` einen Handlerframe auf. Direkt danach wird ungeachtet von
|
||||
`dispatched` wegen `rt.ende` zurückgekehrt. Das Ereignis ist bereits aus der
|
||||
Queue entfernt und wird stillschweigend verloren.
|
||||
|
||||
**Vertrag:** Mausereignisse in Zellenkoordinaten / Reihenfolge bis zur
|
||||
beobachtbaren Handlerausführung; Design D1/D2; Tasks 1.2/2.1.
|
||||
**Korrektur:** EOF als Ende der Eingabequelle behandeln und bereits angenommene
|
||||
Ereignisse/Handler geordnet abarbeiten, bevor die modale Ausführung endet.
|
||||
|
||||
## Ursprüngliche weitere Designabweichung
|
||||
|
||||
### V5 — Gleichzeitig wartende klassische Quellen erhalten eine andere Priorität (mittel)
|
||||
|
||||
**Reproduktion:** KEY(15) ON und UEVENT ON. Vor demselben Zustellpunkt liegen
|
||||
eine passende Taste in der Eingabequeue und ein gemeldeter UEVENT vor.
|
||||
Die Handler hängen K bzw. U an.
|
||||
|
||||
**Ist:** `UK`. **Soll:** `KU` gemäß der bestehenden klassischen Quellpriorität.
|
||||
|
||||
**Ursache:** [Vm::zustellen](../../../../crates/tb-vm/src/interp.rs#L631) wählt
|
||||
bereits gemeldete Traps aus, bevor die wartende Taste durch `taste_trappen`
|
||||
gemeldet wird. Die Prioritätsauswahl in Traps sieht die Taste deshalb noch
|
||||
nicht. Früher erfolgte die Tastenprüfung vor der gemeinsamen Trap-Auswahl.
|
||||
|
||||
**Vertrag:** Design / Non-Goals: keine Änderung der festgelegten Priorität
|
||||
gleichzeitig fälliger klassischer Quellen; D1/D2; Task 2.1.
|
||||
**Korrektur:** Fälligkeitserfassung und Trap-Auswahl so koordinieren, dass die
|
||||
bestehende Quellpriorität erhalten bleibt, ohne die Reihenfolge zwischen
|
||||
Maus- und Tasteneingaben wieder nach Quellarten umzusortieren.
|
||||
|
||||
## Abdeckung und positive Nachweise
|
||||
|
||||
| Delta-Requirement | Nachweis / Einschränkung |
|
||||
| --- | --- |
|
||||
| Ereignisquellen mit Zeit vom Host | Host-Warteschnittstelle, eingefrorene virtuelle Zeit, klassischer Timer und Forms-Timer-SLEEP getestet. Keine reale Rückfalluhr in der VM; Zustellung nach Menüschluss durch V3-Regression abgesichert. |
|
||||
| Mausereignisse in Zellenkoordinaten | Mapper-/Bounds-Tests, leere Verbraucher, KM/MK-Korpus und gemischte Eingaben. V4-Regressionen sichern EOF in modalen und modellosen Schleifen ab. |
|
||||
| Abschließende Liste der Zustellpunkte | DOEVENTS-Wert, SLEEP, vorbereitete und erst beim Warten eintreffende INPUT-Ereignisse getestet; V1–V3 geschlossen. |
|
||||
| Getrappte Tasten verlassen den Eingabestrom | ON/OFF/STOP × INKEY$/INPUT/LINE INPUT/INPUT$, mehrfaches STOP-Merken und Dialogrestpuffer getestet. |
|
||||
| Signal-Trap auf Betriebssystemsignalen | Beide Kennungen, Maskierung und ungültige Werte getestet. V1-Regression sichert Signalzustellung während blockierender Eingabe ab. |
|
||||
| Fokus, Tabreihenfolge und Access-Keys | Tab/Shift-Tab, BackTab-Mapper, Access-Keys und einfacher LostFocus→GotFocus-Lauf belegt. V2-Regressionen sichern Control-Argumente und verwandte Objektaufrufe ab. |
|
||||
| Menüsystem | Menüeigenschaften/-auswahl und Sperre an normalen Grenzen getestet. V3-Regression sichert Freigabe innerhalb von SLEEP ab. |
|
||||
| Zellenpuffer ohne Terminalabhängigkeit | Separater Workspace kompiliert FRM/VM und liefert KM; 16 Pakete im normalen Dependency-Graph, keines davon crossterm/ratatui/signal-hook. CLI schaltet das Feature ausdrücklich ein. |
|
||||
| Ereigniszustellung an Anweisungsgrenzen | Bestehende SUB-/GOSUB-/BYREF-/RESUME-Tests und Host ohne erlaubte Uhrabfragen grün. V2/V5-Regressionen sichern zulässige Handlerbeginn-Reihenfolge und klassische Priorität ab. |
|
||||
|
||||
Erneut ausgeführt:
|
||||
|
||||
- `cargo test --workspace`: **419 bestanden, 0 fehlgeschlagen, 1 ignoriert**.
|
||||
- `openspec validate ereigniszustellung-und-hostgrenzen --strict`: gültig.
|
||||
- Isolierter Einbetter mit CaptureHost: **KM**, Dependency-Prüfung erfolgreich.
|
||||
- Separate [Review-Proben](review-probes.rs): **6 bestanden, 0 fehlgeschlagen**.
|
||||
Die Kontrollprobe prüft Maus unmittelbar vor getrappter Taste.
|
||||
- Workspace-Ereignistests: **25 bestanden**, einschließlich Frist-Watchdog.
|
||||
- Keine bestehenden Sollausgaben neu aufgezeichnet oder geändert.
|
||||
|
||||
Warnings-denied Clippy, Formatprüfungen und Compile-/VM-Benchmarks wurden
|
||||
nach der Behebung erneut erfolgreich ausgeführt; Zahlen siehe
|
||||
[verification.md](verification.md). Kein interaktiver Terminaltest und keine
|
||||
Windows-/Linux-Matrix. Die Proben speisen Ereignisse
|
||||
gezielt über Host ein und benötigen weder echte Signale noch reale Wartefristen.
|
||||
|
||||
## Review-Proben reproduzieren
|
||||
|
||||
Im Repository-Wurzelverzeichnis ausführen. Das erzeugt ausschließlich einen
|
||||
separaten temporären Cargo-Treiber und referenziert den aktuellen Produktcode:
|
||||
|
||||
```sh
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
import json, subprocess, tempfile
|
||||
root = Path.cwd()
|
||||
p = Path(tempfile.mkdtemp(prefix='tb-review-'))
|
||||
manifest = '[package]\nname="tb-review"\nversion="0.0.0"\nedition="2021"\n[workspace]\n'
|
||||
manifest += '[[test]]\nname="befunde"\npath=' + json.dumps(str(root / 'openspec/changes/archive/2026-09-05-ereigniszustellung-und-hostgrenzen/review-probes.rs')) + '\n[dependencies]\n'
|
||||
for name in ['tb-vm', 'tb-runtime', 'tb-ui', 'tb-frontend']:
|
||||
manifest += name + ' = { path = ' + json.dumps(str(root / 'crates' / name)) + ' }\n'
|
||||
(p / 'Cargo.toml').write_text(manifest)
|
||||
subprocess.run(['cargo', 'test', '--manifest-path', str(p / 'Cargo.toml'), '--test', 'befunde', '--', '--nocapture'], check=True, timeout=120)
|
||||
PY
|
||||
```
|
||||
|
||||
Alle sechs Proben müssen auf dem korrigierten Stand bestehen. Ihre Erwartungen
|
||||
wurden auch in den regulären Workspace-Testlauf übernommen.
|
||||
@@ -0,0 +1,139 @@
|
||||
## MODIFIED 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.
|
||||
|
||||
Auch Wartefristen der VM SHALL ausschließlich von der Hostzeit abhängen. Ein Host mit stehender virtueller Zeit MUST NOT durch reale verstrichene Zeit eine Frist scheinbar erreichen.
|
||||
|
||||
#### 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
|
||||
|
||||
#### Scenario: Stehende virtuelle Zeit
|
||||
- **WHEN** ein Host während SLEEP seine Zeit unverändert hält
|
||||
- **THEN** endet SLEEP nicht wegen realer Zeit; eine vom Host gemeldete Frist oder ein zugestelltes Ereignis beendet das Warten
|
||||
|
||||
### 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.
|
||||
|
||||
Die Reihenfolge SHALL bis zur beobachtbaren Ausführung der zugehörigen Handler erhalten bleiben; getrennte Quellarten MUST NOT nachträglich nach ihrer Art umgeordnet werden.
|
||||
|
||||
#### 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
|
||||
|
||||
#### Scenario: Taste vor Maus bis zum Handler
|
||||
- **WHEN** eine Taste vor einem Mausklick eintrifft und beide Handler eine Kennung anhängen
|
||||
- **THEN** steht die Tastenkennung vor der Mauskennung
|
||||
|
||||
#### Scenario: Maus vor Taste bis zum Handler
|
||||
- **WHEN** ein Mausklick vor einer Taste eintrifft
|
||||
- **THEN** wird zuerst der Maushandler wirksam
|
||||
|
||||
### 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.
|
||||
|
||||
Forms-Ereignisse SHALL auch während SLEEP an dessen bestehenden Zustellpunkten verarbeitet werden. Eine für das Menü geltende Zustellsperre SHALL an jedem Zustellpunkt gleichermaßen gelten.
|
||||
|
||||
#### 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
|
||||
|
||||
#### Scenario: Forms-Timer beendet SLEEP
|
||||
- **WHEN** während SLEEP ein aktiver Forms-Timer fällig wird
|
||||
- **THEN** läuft dessen Handler und SLEEP kehrt danach zurück
|
||||
|
||||
### 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.
|
||||
|
||||
Unter STOP SHALL die Taste normal lesbar bleiben und zugleich höchstens ein Ereignis für das spätere ON gemerkt werden.
|
||||
|
||||
#### 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
|
||||
|
||||
#### Scenario: STOP merkt und erhält Eingabe
|
||||
- **WHEN** unter KEY(1) STOP F1 gedrückt, per INKEY$ gelesen und danach KEY(1) ON ausgeführt wird
|
||||
- **THEN** liefert INKEY$ die Sondertastenfolge und der Handler läuft nach ON genau einmal
|
||||
|
||||
### 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
|
||||
|
||||
#### Scenario: SIGTERM wird zugestellt
|
||||
- **WHEN** SIGNAL(2) ON gilt und ein SIGTERM über den Host eintrifft
|
||||
- **THEN** wird das Ziel von ON SIGNAL(2) GOSUB am nächsten erlaubten Zustellpunkt ausgeführt
|
||||
@@ -0,0 +1,53 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Fokus, Tabreihenfolge und Access-Keys
|
||||
Der Fokus SHALL mit Tab in aufsteigender `TabIndex`-Folge und mit
|
||||
Umschalt-Tab rückwärts wechseln; Elemente mit `TabStop = 0` oder
|
||||
`Enabled = 0` MUST übersprungen werden. Ein `&` im Text SHALL den
|
||||
folgenden Buchstaben zum Access-Key machen, der mit Alt das Element
|
||||
auslöst oder ihm den Fokus gibt. Enter SHALL die `Default`-Schaltfläche
|
||||
auslösen, Esc die `Cancel`-Schaltfläche. Fokuswechsel MUST `LostFocus`
|
||||
am alten und `GotFocus` am neuen Element auslösen, in dieser Reihenfolge.
|
||||
|
||||
Die über das Terminal gelieferte Rückwärtstab-Taste SHALL denselben Fokuswechsel wie Tab mit Umschalt auslösen. Die Reihenfolge LostFocus vor GotFocus SHALL auch an den Wirkungen der BASIC-Handler sichtbar bleiben.
|
||||
|
||||
#### Scenario: Tab überspringt
|
||||
- **WHEN** das mittlere von drei Elementen `TabStop = 0` trägt und Tab gedrückt wird
|
||||
- **THEN** erhält das dritte Element den Fokus
|
||||
|
||||
#### Scenario: Access-Key
|
||||
- **WHEN** eine Schaltfläche `Caption = "&OK"` trägt und Alt+O gedrückt wird
|
||||
- **THEN** wird ihr `Click`-Ereignis ausgelöst
|
||||
|
||||
#### Scenario: Reihenfolge der Fokusereignisse
|
||||
- **WHEN** der Fokus von `Text1` auf `Text2` wechselt
|
||||
- **THEN** läuft erst `Text1_LostFocus`, danach `Text2_GotFocus`
|
||||
|
||||
#### Scenario: Handlerwirkungen in Fokusreihenfolge
|
||||
- **WHEN** LostFocus den Text L und GotFocus den Text G an dieselbe Variable anhängen
|
||||
- **THEN** lautet das Ergebnis LG
|
||||
|
||||
#### Scenario: Rückwärtstab vom Terminal
|
||||
- **WHEN** das Terminal eine Rückwärtstab-Taste liefert
|
||||
- **THEN** wechselt der Fokus zum vorherigen zulässigen TabIndex
|
||||
|
||||
### Requirement: Menüsystem
|
||||
Ein Formular SHALL eine Menüleiste mit bis zu sechs Ebenen tragen.
|
||||
Menüeinträge SHALL Access-Keys (`&`), Shortcuts, `Checked`, `Enabled`,
|
||||
`Visible` und Separatoren (`-`) unterstützen; ein Separator MUST NOT
|
||||
`Checked`, deaktiviert oder mit Shortcut versehen sein, ein Menütitel
|
||||
MUST NOT einen Shortcut tragen. Solange ein Menü geöffnet ist, MUST die
|
||||
Zustellung von Zeitereignissen und klassischen Traps ruhen und danach
|
||||
fortgesetzt werden.
|
||||
|
||||
#### Scenario: Menüauswahl löst Click aus
|
||||
- **WHEN** ein Menüeintrag über seinen Access-Key gewählt wird
|
||||
- **THEN** läuft seine `Click`-Prozedur
|
||||
|
||||
#### Scenario: Traps ruhen im geöffneten Menü
|
||||
- **WHEN** ein Menü geöffnet ist und ein Zeit-Trap fällig wird
|
||||
- **THEN** läuft sein Handler erst, nachdem das Menü geschlossen wurde
|
||||
|
||||
#### Scenario: UEVENT an gewöhnlicher Anweisungsgrenze
|
||||
- **WHEN** ein Menü offen ist und UEVENT vor einer Zuweisung ansteht
|
||||
- **THEN** läuft dessen Handler erst nach dem Schließen des Menüs
|
||||
@@ -0,0 +1,20 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Zellenpuffer ohne Terminalabhängigkeit
|
||||
Der Zellenpuffer mit der vollständigen Bildschirmsemantik (Cursor,
|
||||
Farbattribute, Umbruch, Scrollen, Scrollbereich, Größenänderung) SHALL
|
||||
ohne Terminal instanziierbar, veränderbar und auslesbar sein. Die
|
||||
Anbindung an ein konkretes Terminal SHALL ausschließlich in der
|
||||
Darstellungsschicht liegen. Damit MUST jedes Bildschirmverhalten in
|
||||
automatischen Tests ohne Terminal prüfbar sein, und die Ausführungsschicht
|
||||
MUST NOT von einer Terminal-Bibliothek abhängen.
|
||||
|
||||
Ein separater Einbetter SHALL die VM einschließlich Forms ohne Terminalbackend bauen und ausführen können. Der CLI-Runner SHALL das Backend ausdrücklich zuschalten.
|
||||
|
||||
#### Scenario: Bildschirmverhalten im Test ohne Terminal
|
||||
- **WHEN** ein Testprogramm in einer Umgebung ohne Terminal `LOCATE 5, 10 : PRINT "x"` ausführt
|
||||
- **THEN** trägt die Zelle (5, 10) das Zeichen `x` und der Test benötigt kein Terminal
|
||||
|
||||
#### Scenario: Terminalfreier Einbetter
|
||||
- **WHEN** ein separates Programm ausschließlich die VM mit einem Capture-Host einbindet
|
||||
- **THEN** enthält sein aufgelöster Abhängigkeitsbaum keine Terminalbibliothek und die Forms-Tests können darin laufen
|
||||
@@ -0,0 +1,24 @@
|
||||
## MODIFIED 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.
|
||||
|
||||
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 `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
|
||||
|
||||
#### 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
|
||||
@@ -0,0 +1,25 @@
|
||||
## 1. Hosteingabe und Maskierung
|
||||
|
||||
- [x] 1.1 SIGNAL(2), KEY STOP und BackTab am gemeinsamen Eingang berichtigen; verifizieren durch Tests für beide Signale, alle drei KEY-Zustände mit INKEY$/INPUT und den Terminal-Key-Mapper.
|
||||
- [x] 1.2 Geordnete Tasten-/Mausannahme implementieren; verifizieren mit K→M, M→K und gemischten Folgen einschließlich ungetrappter und getrappter Tasten.
|
||||
|
||||
## 2. Dispatch und Warten
|
||||
|
||||
- [x] 2.1 Handlerbeginn/Rückkehr und Menüsperre an allen Zustellpunkten vereinheitlichen; verifizieren mit LostFocus→GotFocus, gleichzeitig wartenden Handlern, UEVENT bei offenem Menü, DOEVENTS und den bestehenden Fehler-/Modalitätstests.
|
||||
- [x] 2.2 Hostgesteuertes Warten und bedarfsgerechte Zeitabfrage implementieren; verifizieren mit einem Host, der unerwartete Uhrabfragen zählt/abweist, eingefrorener und vorrückender Zeit sowie einem Forms-Timer, der SLEEP beendet.
|
||||
- [x] 2.3 Terminal-Feature in tb-ui abgrenzen und CLI ausdrücklich anschließen; verifizieren durch isolierten terminalfreien VM-Einbetter, Dependency-Graph ohne crossterm/ratatui/signal-hook und normalen CLI-Build.
|
||||
|
||||
## 3. Abnahme
|
||||
|
||||
- [x] 3.1 Gezielte geordnete Ereignisprogramme zum Korpus ergänzen; verifizieren mit cargo test --workspace und Vergleich der bestehenden Snapshots ohne automatisches Neuaufzeichnen.
|
||||
- [x] 3.2 Zeit-/Zustellverträge und Backend-Feature dokumentieren; verifizieren mit Compile-/VM-Benchmarks und openspec validate ereigniszustellung-und-hostgrenzen --strict.
|
||||
|
||||
Abnahmenachweise: [verification.md](verification.md).
|
||||
|
||||
## 4. Verifikationsbefunde
|
||||
|
||||
- [x] 4.1 V1: Blockierende Konsoleneingabe fortsetzbar machen; Signale, Timer, Teiltexte und Handler-Rückkehr absichern.
|
||||
- [x] 4.2 V2: Synchrone Load-Wirkungen von wartenden Forms-Ereignissen trennen; Control-Argumente und Geschwisterpfade prüfen.
|
||||
- [x] 4.3 V3: Nach Menüschluss vor erneutem Warten bereit gewordene Ereignisse zustellen.
|
||||
- [x] 4.4 V4: Vor EOF angenommene Ereignisse im modalen Lauf vollständig abarbeiten.
|
||||
- [x] 4.5 V5: Klassische Quellpriorität bei gleichzeitig wartenden Tasten und UEVENT erhalten.
|
||||
@@ -0,0 +1,82 @@
|
||||
# Implementierungsnachweis — 05.09.2026
|
||||
|
||||
Change: `ereigniszustellung-und-hostgrenzen`, Ausgangscommit `57d6386`.
|
||||
Alle zwölf Implementierungs-/Abnahmetasks einschließlich der fünf
|
||||
[Verifikationsbefunde](review.md) sind abgeschlossen. Die vier betroffenen
|
||||
Hauptspezifikationen sind synchronisiert; der Change wurde am 05.09.2026 archiviert.
|
||||
|
||||
## Befunde und Umsetzung
|
||||
|
||||
| Befund / Tasks | Umsetzung | Ausführbarer Nachweis |
|
||||
| --- | --- | --- |
|
||||
| F04 / 1.1 | SIGNAL(1) und SIGNAL(2) werden im gemeinsamen RtState-Eingang gemeldet; nur ungetrapptes SIGINT wird Abbruch. Zusätzlich erkennt der Parser SIGNAL(n) ON/OFF/STOP. | VM-Test `beide_signale_werden_zugestellt_und_beginnen_in_quellreihenfolge` einschließlich ungültiger konstanter/dynamischer Kennungen; Runtime-Tests für STOP/OFF beider Quellen und SIGINT ohne Trap. |
|
||||
| F05 / 1.1 | ON verbraucht die Taste; STOP merkt den Trap einmal und belässt die Eingabe auch nach erneutem ON lesbar. Die Markierung bleibt beim Dialogaufruf erhalten. | VM-Test `key_on_off_stop_gelten_fuer_alle_konsoleneingaben`: drei Zustände × INKEY$, INPUT, LINE INPUT, INPUT$. Runtime-Test mit drei STOP-Tasten; VM-Regression für verbleibende Dialogeingaben. |
|
||||
| F06 / 1.2, 2.1 | Eine Eingabequeue für Tasten/Maus bis zur Annahme. Forms-Eingaben und ihre Handler werden geordnet verarbeitet; erster Handler-Statement-Eintritt geschützt, gleiche Forms-Prozedur nicht rekursiv zugestellt. | VM-Tests für aM, Ma, aMbTM einschließlich KEY-Trap; Fokusfolge LlGg; explizites DOEVENTS ergibt arbr; UNLOAD im eigenen Handler ergibt ur. |
|
||||
| F07 / 2.1 | Gemeinsame Zustellung; separate Menüsperre in Traps ohne Änderung der ON/OFF/STOP-Zustände. Bereits wartende Forms-Timer bleiben gesperrt, Menübedienung kann fortfahren. | VM-Menütest über gewöhnliche Zuweisung, DOEVENTS, SLEEP und LINE INPUT: Zähler bleibt 1, nach Esc werden UEVENT und Forms-Timer nachgeholt, Ergebnis 111. |
|
||||
| F08 / 2.2 | Host besitzt Zeit und Deadline-Warten; keine Instant-/Sleep-/Yield-Rückfallebene in der VM. Auch Forms-SLEEP, modales Warten und sichtbare Forms nach Programmende nutzen den Host. Zeitabfragen nur bei tatsächlichem Zeitbedarf. | Host, der jede unerwartete Uhrabfrage/Warteoperation ablehnt; eingefrorene Uhr über reale 10 ms mit zwei identischen Hostdeadlines; Forms-Timer beendet SLEEP 60 bei Hostzeit 100 ms. Bestehender klassischer SLEEP-/TIMER-Test. |
|
||||
| F18 / 1.1 | BackTab wird als TAB mit SHIFT normalisiert, auch wenn crossterm kein separates SHIFT-Flag liefert. | Terminal-Mapper-Test `backtab_ist_tab_mit_shift_auch_ohne_modifier`; bestehende Forms-Tests für Rückwärtstab, übersprungene Controls und Access-Keys. |
|
||||
| F20 / 2.3 | tb-ui ist standardmäßig terminalfrei; `terminal` aktiviert Backendmodule und optionale Dependencies. CLI aktiviert dieses Feature ausdrücklich. | Separater Workspace `tests/terminalfrei`: FRM laden/kompilieren, Forms ausführen, CaptureHost-Ausgabe KM prüfen. Normaler Dependency-Graph: 16 Pakete, keines davon crossterm, ratatui oder signal-hook. CLI-Kompilation und CLI-Prozesstests erfolgreich. |
|
||||
| 3.1 | Zwei kleine geordnete Ereignisprogramme mit ausdrücklich festgelegten Ausgaben ergänzt. | `eingabe-taste-maus.frm` → KM; `eingabe-maus-taste.frm` → MK. Beide laufen zweimal im bestehenden Korpusharness. Keine bestehende .out-Datei verändert. |
|
||||
| 3.2 | Zeit-, Zustell-, Eingabequeue- und Backend-Verträge einschließlich Einbetter-Migration dokumentiert. | [VM-Design](../../../../docs/tbvm-design.md#ausführungsmodell--unterbrechbarkeit), Checks und Benchmarks unten. |
|
||||
|
||||
Die neuen VM-Regressionen liegen in
|
||||
[crates/tb-vm/tests/events.rs](../../../../crates/tb-vm/tests/events.rs).
|
||||
Bestehende Trap-Rückkehr-, GOSUB-, Fehler-/RESUME-, BYREF- und Modalitätstests
|
||||
wurden im vollständigen Workspace-Testlauf mit ausgeführt.
|
||||
|
||||
## Behebung der Verifikationsbefunde
|
||||
|
||||
V1–V5 sind geschlossen. Der [aktualisierte Report](review.md) ordnet jede
|
||||
Korrektur den ursprünglichen Beobachtungen und ihren Regressionen zu.
|
||||
Die 25 Tests in `events.rs` decken nun auch fortsetzbare Eingabe samt Teiltext,
|
||||
Ausdrucksoperanden, BYREF, verschachtelten Handlern, STOP/CONT, Timer,
|
||||
Fehler-Unwinding und RETURN label ab. Objektaufrufe bewahren die Fokusfolge,
|
||||
Menüschluss gibt Traps sofort frei, beide Formularschleifen arbeiten Eingaben
|
||||
vor EOF ab und die klassische KEY-/UEVENT-Priorität bleibt erhalten.
|
||||
|
||||
## Ausgeführte Prüfungen
|
||||
|
||||
- `cargo test --workspace`: **419 bestanden, 0 fehlgeschlagen, 1 ignoriert**.
|
||||
Ignoriert bleibt ausschließlich `erzeuge_sollausgaben`; keine automatische
|
||||
Neuaufzeichnung von Golden Files.
|
||||
- Separater Treiber der ursprünglichen `review-probes.rs`: **6 bestanden, 0 fehlgeschlagen**.
|
||||
- `cargo clippy -p tb-runtime -p tb-ui -p tb-vm -p tb-cli --all-targets --all-features -- -D warnings`: erfolgreich, ohne Warnungen.
|
||||
- `cargo fmt --all -- --check` und Formatprüfung des separaten Einbetters: erfolgreich.
|
||||
- `cargo run --manifest-path tests/terminalfrei/Cargo.toml --offline`: erfolgreich,
|
||||
`Terminalfreier FRM-/VM-Lauf: KM`.
|
||||
- `cargo tree --manifest-path tests/terminalfrei/Cargo.toml --offline --edges normal --prefix none --format '{p}'`:
|
||||
mit expliziter Prüfung auf ausgeschlossene Terminalpakete, erfolgreich.
|
||||
- `openspec validate ereigniszustellung-und-hostgrenzen --strict`: erfolgreich.
|
||||
- `git diff --check`: erfolgreich.
|
||||
|
||||
## Release-Benchmarks
|
||||
|
||||
`cargo bench -p tb-vm --bench compile --bench vm`, auf diesem Rechner:
|
||||
|
||||
| Last | Ergebnis |
|
||||
| --- | --- |
|
||||
| Compile: 508 Zeilen | 0,83 ms, Budget 50 ms eingehalten |
|
||||
| Compile: 49.760 Zeilen in 20 Modulen | 58 ms, Budget 1.000 ms eingehalten |
|
||||
| INTEGER: 10 Mio. Iterationen | 1.048 ms, 9,5 Mio. Iterationen/s |
|
||||
| DOUBLE: 5 Mio. Iterationen | 504 ms, 9,9 Mio. Iterationen/s |
|
||||
| SUB/BYREF: 1 Mio. Aufrufe | 123 ms, 8,2 Mio. Aufrufe/s |
|
||||
| String-Funktionen: 200.000 Runden | 99 ms, 2,0 Mio. Runden/s |
|
||||
|
||||
Der Compile-Projektbenchmark übersetzt 20 Module einzeln; er prüft keinen
|
||||
Mehrmodul-Linker. Die VM-Zahlen sind lokale Durchsatzmessungen, keine
|
||||
zugesicherte Performance auf anderer Hardware.
|
||||
|
||||
## Synchronisation und Archivierung
|
||||
|
||||
Neun bestehende Requirements in vier Hauptspezifikationen wurden um elf
|
||||
Szenarien ergänzt; alle bisherigen Szenarien bleiben erhalten. Vollständiger
|
||||
Abgleich aller Delta-Requirements erfolgreich; `openspec validate --specs
|
||||
--strict`: 15 bestanden, 0 fehlgeschlagen.
|
||||
|
||||
## Grenzen
|
||||
|
||||
Terminaleingaben wurden über den Mapper und vorbereitete Hostereignisse
|
||||
geprüft. Kein interaktiver Terminal-/Fremdprogrammvergleich und keine
|
||||
Windows-/Linux-Ausführung. Der neue Vertrag verlangt von Einbettern eine
|
||||
Implementierung von Host::warten; CaptureHost modelliert eine endliche
|
||||
Ereignisquelle und explizites virtuelles Fortschreiten bis zur Deadline.
|
||||
Reference in New Issue
Block a user