Projektmodule und vollständiges TBC-Kompilat umsetzen und Change archivieren

This commit is contained in:
2026-09-05 23:06:56 +02:00
parent 58b1f620ea
commit 815825dde7
40 changed files with 4177 additions and 555 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-05

View File

@@ -0,0 +1,37 @@
## Context
input_sources/append_project_source erzeugen einen einzigen String, doppelte Definitionen werden teilweise entfernt. FormCatalog.find benutzt den ersten globalen Namen. PROC enthält Code/Zeilen inline, OBJS nur Objektstammdaten. Das CLI-build-Ergebnis verwirft die separaten FormFile-Werte. Siehe proposal.md und die Delta-Specs.
## Goals / Non-Goals
**Goals:** Den vorhandenen Compilerpipeline-Vertrag auf ein vollständiges Projekt erweitern und daraus ein allein ausführbares Artefakt liefern.
**Non-Goals:** Keine IDE-Oberfläche, keine native Executable-Verpackung, keine Binärkompatibilität zu alten unvollständigen TBC-Versionen.
## Decisions
### D1 — Quellidentität vor Expansion sichern
Projektmitglieder und Include-Segmente als Quellen mit stabiler ID führen; physische Datei/Zeile/Spalte getrennt von BASICs numerischem ERL-Label bewahren. Kein nachträgliches Zurückrechnen aus der verketteten Zeilenzahl. Einmodul-APIs können den Projektpfad mit einem Mitglied aufrufen.
### D2 — Namen innerhalb ihres Containers auflösen
Form- und Control-IDs bleiben numerisch; Lookup sucht zuerst im expliziten Formular/aktuellen Modul. Modulprozeduren erhalten qualifizierte IDs, Form_Load pro Formular eine eigene Bindung. Doppelte lokale Namen dürfen nicht durch globales Deduplizieren verschwinden; echte Duplikate im selben Scope melden.
### D3 — Selbstständiges Kompilat
Vor dem Serialisieren das vollständige Forms-Anfangsmodell mit Werten, Indizes und Eltern-IDs aufnehmen. Lesen rekonstruiert dieselben Anfangswerte; CLI run akzeptiert das neue TBC direkt. Normale Projektdateien müssen nach build zum Ausführen nicht mehr verfügbar sein.
### D4 — Formatrevision statt zusätzlicher Scheinabschnitte
Inline-Code und Inline-Quellortinstruktionen beibehalten, aber vollständige Quell-/Modultabelle und Prozedursignaturen hinzufügen. Version 4 reservieren, vorhandene Kennungen dokumentieren und OBJS um vollständige Anfangsdaten erweitern. Kein separater CODE/LINES-Abschnitt allein zur Erfüllung einer veralteten Strukturvorgabe. Unbekannte/ältere inkompatible Versionen klar ablehnen.
## Risks / Trade-offs
- Scope-Änderungen können bisher zufällig verbundene globale Namen verändern → Mehrmodulprogramme mit SHARED/COMMON, wiederholten Includes und gleichnamigen privaten Prozeduren prüfen.
- Verlust von ERL-Semantik durch neue Quellorte → numerische Labels und physische Dateizeilen ausdrücklich getrennt testen.
- Containeränderung kann fremde Dateien fehlinterpretieren → Längen/IDs/Versionen beim Laden prüfen und Roundtrip-Ausführung mit entfernten Quellen testen.
## Migration Plan
Eigenständiger Compiler-/Container-Change. Nach Ereigniszustellung integrieren, falls beide interp.rs ändern. F24 und alle Bytecode-Requirement-Blöcke gehören ausschließlich hierher. 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.

View File

@@ -0,0 +1,30 @@
## Why
Mehrere Module/Formulare werden zu einem Quelltext und globalen Objektraum zusammengelegt. Dadurch scheitern formularlokale Namen, Fehlerorte stimmen nicht und FRM-Anfangswerte fehlen nach dem Kompilat-Rundlauf (F09F11); Containerdokumentation und Spec sind außerdem überholt (F24). Siehe [Review vom 05.09.2026](../../../../docs/reviews/2026-09-05-openspec-code-review.md).
## What Changes
- Projekt- und Include-Quellen mit ursprünglichem Modul/Datei/Zeile erhalten und Prozeduren/Controls im jeweiligen Namensraum auflösen.
- Quellorte und Breakpoints mit Modulidentität durch Frontend, Kompilat und VM führen.
- Formularbeschreibungen und Anfangswerte in das eigenständig ladbare Kompilat aufnehmen.
- **BREAKING**: Kompilatversion für das vollständige Projektmodell erhöhen; ältere Versionen weiterhin klar mit Versionsnennung ablehnen.
- Container-/Opcode-Spezifikation und Dokumentation an das tatsächlich vereinbarte Format angleichen.
## Capabilities
### New Capabilities
Keine neuen Capability-Pfade.
### Modified Capabilities
- `bytecode-kompilat`: Eigenständiges Projektkompilat, vollständige Quellorte und dokumentiertes Containerformat.
- `sprach-frontend`: Projektübergreifende Diagnostik und formularlokale Objektauflösung.
- `forms-objektmodell`: Gleichnamige Controls unterschiedlicher Formulare separat adressieren.
- `vm-ausfuehrung`: Breakpoints und Inspektion über Modulidentität ermöglichen.
## Impact
CLI input_sources/expand_includes, Frontend SourcePos/HIR/FormCatalog, Codegen/bytecode/interp, FRM-Anwendung, Tests und docs/tbvm-design.md/dateiformate.md. Kein neuer Linker als separater Prozess.
Eigenständiger Compiler-/Container-Change. Nach Ereigniszustellung integrieren, falls beide interp.rs ändern. F24 und alle Bytecode-Requirement-Blöcke gehören ausschließlich hierher.

View File

@@ -0,0 +1,223 @@
# Verifikation: projektmodule-und-kompilat
Geprüft am 05.09.2026: Arbeitsbaum auf Basis von `58b1f62`, alle sieben
Delta-Requirements mit 21 Szenarien, Tasks, D1D4, Implementierung und
Abnahmenachweis. **Sechs offene Befunde; der Change ist nicht abnahmebereit.**
Die vorherige Aussage vollständiger Umsetzung wird durch diese Verifikation
korrigiert. Der Produktcode wurde bei diesem Review nicht verändert.
| Dimension | Ergebnis |
| --- | --- |
| Vollständigkeit | 7/7 Tasks sind angehakt und alle sieben Requirements haben Implementierungspfade. Die Befunde widerlegen jedoch die vollständige Erfüllung insbesondere von 1.1, 1.2 und 2.2; Checkboxen allein sind kein Abnahmenachweis. |
| Korrektheit | 437 bestehende Workspace-Tests bestanden; zusätzliche unabhängige Review-Proben: 1 bestanden, 6 fehlgeschlagen. Vier Kernfunktionslücken (CRITICAL), zwei weitere Vertragsabweichungen (WARNING). |
| Kohärenz | D4 und die FRM-Grundpfade von D3 sind umgesetzt. Quellorte und Modulkontexte gehen in weiteren Absenkungs-/Importpfaden verloren; der gemeinsame Objektraum berücksichtigt `$FORM` nicht vollständig. |
## CRITICAL — vor Archivierung beheben
### V1 — Quellorte und Breakpoints fehlen an Zweig- und Schleifengrenzen
**Priorität: hoch.** Delta „Zeilentabelle für Fehlerortung“ und
„Unterbrechbare step()-Ausführung“, D1, Tasks 1.1/2.2.
Die Fehlerortung funktioniert bei einfachen Anweisungen, nicht jedoch bei
weiteren ausführbaren Teilen zusammengesetzter Anweisungen:
| Probe | Erwartet | Beobachtet |
| --- | --- | --- |
| `IF 0 THEN` / `PRINT 1` / `ELSEIF 1/0 THEN` | Fehler 11 in Zeile 3 | Zeile 1 |
| `SELECT CASE 1` / `CASE 1/0` | Fehler 11 in Zeile 2 | Zeile 1 |
| `FOR i%=32767 TO 32767` / `PRINT 1` / `NEXT` | Überlauf beim NEXT in Zeile 3 | Zeile 2 |
| `DO` / `PRINT 1` / `LOOP UNTIL 1/0` | Fehler 11 in Zeile 3 | Zeile 2 |
| ELSEIF-Bedingung aus `cond.bi:1` | Include-Datei `cond.bi` | `main.bas:1:1` |
| Breakpoint auf ELSEIF in Zeile 3 | Halt vor Bedingung | Programm endet ohne Halt |
**Ursache:** [ELSEIF-Absenkung](../../../../crates/tb-frontend/src/sema.rs#L1937)
und [CASE-Absenkung](../../../../crates/tb-frontend/src/sema.rs#L2914) verwenden
den Quellort des äußeren Statements. NEXT-/LOOP-Positionen werden nicht
vollständig durch AST/HIR bis zur auszuführenden Grenze getragen. Der
[FOR-Codegenerator](../../../../crates/tb-vm/src/codegen.rs#L737) dokumentiert
selbst die bisherige Verschiebung einer genauen NEXT-Zuordnung auf Phase 5;
das widerspricht der hier übernommenen vollständigen Quellortanforderung.
**Korrektur:** Physische Positionen aller ausführbaren Zweig-/Schleifenteile
bereits beim Parsen erhalten und passende Source-/Stmt-Grenzen ausgeben.
Nicht nur Fehlermeldungen umbenennen: Breakpoints, Einzelschritt und RESUME
müssen dieselbe richtige Zuordnung verwenden. Numerische ERL-Labels separat
lassen. Probe `v1_zweige_und_schleifen_behalten_eigene_quellorte`.
### V2 — Deklarationsimport verändert Typkontext und Abhängigkeitsreihenfolge
**Priorität: hoch.** Projektpipeline und vollständige Prozedursignaturen,
D2, Tasks 1.2/2.1.
Drei zulässige Mehrmodulkonstellationen werden abgewiesen:
- MAIN ruft `Doppelt(3)` auf; LIB enthält `DEFINT A-Z` und
`FUNCTION Doppelt(x)`. Erwartete Ausgabe 6, tatsächlich
`Parameter type mismatch: MAIN!DOPPELT`.
- MAIN enthält `CONST A=3` und verwendet B; LIB enthält `CONST A=3` und
`CONST B=A+1`. Erwartet B=4, tatsächlich `LIB.bas:2:1: Invalid constant`.
- Beide Module enthalten denselben TYPE Inner. LIB definiert anschließend
TYPE Outer mit einem Feld `i AS Inner`; MAIN verwendet Outer.
Erwartet korrektes Layout und Feldausgabe 3, tatsächlich `Type not defined`
an der gültigen Outer-Definition sowie Folgefehler für Feldzugriffe.
**Ursache:** [import_declarations](../../../../crates/tb-vm/src/project.rs#L103)
kopiert rohe Signatur-ASTs ohne den DEFtype-Kontext des definierenden Moduls.
Fremde CONST-/TYPE-Knoten werden [vor sämtliche lokalen Deklarationen
gehängt](../../../../crates/tb-vm/src/project.rs#L158). Dadurch fehlen ihnen zu
diesem Zeitpunkt lokal vorhandene Abhängigkeiten. Die Fehlerposition in LIB
ist hier irreführend: Die dortige Originalreihenfolge ist gültig.
**Korrektur:** Exportierte Signaturen und Konstanten im Ursprungskontext
auflösen; abhängige Typdefinitionen mit erhaltenen Identitäten und gültiger
Abhängigkeitsreihenfolge verbinden. Lokale Definitionen und echte Duplikate
nicht wieder durch Text-Deduplizierung verstecken. Probe
`v2_importierte_deklarationen_behalten_kontext_und_abhaengigkeiten`.
### V3 — `$FORM`-Module teilen versehentlich dasselbe Formularobjekt
**Priorität: hoch.** Delta „Objektzugriffe in Grammatik und Semantik“
einschließlich `$FORM`, getrennte Formulare, D2/D3, Task 1.2.
A.bas und B.bas enthalten jeweils `'$FORM`. A setzt `Caption="a"`, ruft eine
SUB aus B auf, die dort `Caption="b"` setzt, und liest anschließend die
eigene Caption. **Erwartet: a. Beobachtet: b.** Das verknüpfte Objektverzeichnis
enthält nur SCREEN und A. Auch `B.Caption` aus A wird als unbekanntes Objekt
abgewiesen. Der Fehler besteht nach TBC-Rundlauf fort.
**Ursache:** [lower_with_forms](../../../../crates/tb-frontend/src/sema.rs#L404)
erweitert pro Modul eine lokale Katalogkopie um das jeweilige `$FORM`-Objekt.
Der Linker übernimmt [nur die Objekte des ersten Moduls](../../../../crates/tb-vm/src/project.rs#L188)
und versetzt die Objektoperanden weiterer Module nicht. Deren Form-ID zeigt
anschließend auf A.
**Korrektur:** Sämtliche FRM- und `$FORM`-Formulare vor der modulweisen
Semantik in einen gemeinsamen Katalog mit stabilen IDs aufnehmen oder die
Objekt-/Ereignisreferenzen beim Linken vollständig versetzen. Zugriffe,
Form_Load-Bindungen und gegenseitige Formreferenzen für beide Quellarten
prüfen. Probe `v3_form_metabefehle_erzeugen_getrennte_formularobjekte`.
### V4 — COMMON verliert Variablenidentität und initialisiert Arrays mehrfach
**Priorität: hoch.** Designrisiko SHARED/COMMON, D2, Task 1.2 und Erhalt der
bestehenden COMMON-Sprachsemantik.
- Bereits ein Modul mit `COMMON SHARED c%, c$` wird mit
`COMMON type mismatch: C` abgewiesen. Die beiden durch Typ-Suffixe
verschiedenen Variablen dürfen nebeneinander existieren.
- MAIN und LIB deklarieren dasselbe `COMMON SHARED a%(2)`. MAIN soll
`a%(1)=7` setzen und LIB den gemeinsamen Wert 7 ausgeben. Stattdessen
entsteht vor dem Nutzcode `LIB.bas:1:1: Runtime error 10: Duplicate definition`.
**Ursache:** Die [COMMON-Zuordnung](../../../../crates/tb-vm/src/project.rs#L270)
verwendet nur Block und untypisierten Basisnamen. Arrays haben zusätzlich
nur `TypeInit::Empty` als Slotvorbelegung; Elementtyp und Form gehen in der
Vergleichsprüfung verloren. Der Slot wird zwar zusammengeführt, aber
[beide DIM-Initialisierungen](../../../../crates/tb-vm/src/project.rs#L381)
werden ausgeführt.
**Korrektur:** Vollständige Variablenidentität und Arrayvertrag beim Binden
berücksichtigen. Kompatible gemeinsame Arrays einmal initialisieren;
inkompatible Deklarationen vor der Ausführung am ursprünglichen Quellort
ablehnen. Probe `v4_common_behaelt_typidentitaet_und_initialisiert_arrays_einmal`.
## WARNING — weitere Vertragsabweichungen
### V5 — LOAD/UNLOAD umgehen die explizite Containerauflösung
**Priorität: mittel.** Formularnamensraum und expliziter Containerzugriff,
D2, Task 1.2.
Bei zwei Formularen mit jeweils Text1 als Control-Array werden
`LOAD Form2!Text1(3)` und `UNLOAD Form2!Text1(3)` mit
`Unknown object 'FORM2!TEXT1'` abgewiesen. Property-Zugriffe und die Übergabe
desselben qualifizierten Controls an einen CONTROL-Parameter funktionieren.
**Ursache:** Der [LOAD-/UNLOAD-Pfad](../../../../crates/tb-frontend/src/sema.rs#L3370)
übergibt den gesamten Bang-Pfad an `find_object`; die bereits vorhandene
containerbezogene Auflösung wird hier nicht verwendet.
**Korrektur:** LOAD/UNLOAD über dieselbe Objekt-/Containerauflösung wie
Property- und Referenzzugriffe führen; gleichnamige Arrays beider Formulare
und dynamische Instanzen gemeinsam prüfen. Probe
`v5_load_und_unload_loesen_den_expliziten_container_auf`.
### V6 — Linkerdiagnosen verlieren Datei, Zeile und Spalte
**Priorität: mittel.** Delta „Diagnostik mit Vorbild-Meldungen“, D1, Task 1.1.
MAIN deklariert `SUB F(x%)`, LIB implementiert `SUB F(x$)`. Die Ablehnung ist
richtig, die Meldung jedoch `0:0: Parameter type mismatch: MAIN!F` ohne Datei.
Das ist eine quellbezogene Diagnose, kein Fehler ohne zuordenbare Quelle.
**Ursache:** [diagnostic](../../../../crates/tb-vm/src/project.rs#L17) erzeugt
`file=None` und `SourcePos::default()`. [Linkfehler](../../../../crates/tb-vm/src/project.rs#L66)
werden erst nach `locate_diagnostics` in dieses Format umgewandelt. Dasselbe
Problem betrifft COMMON-Konflikte und weitere symbolbezogene Linkfehler.
**Korrektur:** Ursprung der betroffenen Deklaration bzw. Aufrufstelle mit
in die Linker-Symbolinformationen übernehmen und strukturierte Diagnosen
mit tatsächlicher SourcePos zurückgeben. Keine pauschale Ersatzposition
1:1 eintragen. Probe `v6_linkerdiagnose_nennt_den_urspruenglichen_dateiort`.
## Szenarioabdeckung und positive Nachweise
Alle 21 vorhandenen Delta-Szenarien wurden abgeglichen. Die einfachen
Originalbeispiele besitzen Implementierungs- und Testnachweise; die oben
reproduzierten Randpfade widersprechen den umfassenderen Requirements.
| Requirement / Szenarien | Ergebnis |
| --- | --- |
| Monomorpher Opcode-Satz: gemischter Ausdruck, aufgelöste Namen | Vorhandene Codegen-/HIR-Tests bestanden. Alle 152 Opcodes samt Operandentypen und Encodingbreiten stimmen mit der Dokumentation überein. |
| TBC: Roundtrip, unbekannte Version, Formularwerte | CLI-/VM-Projekttests bestanden: bytegleicher Rundlauf, hello/world aus Designarrays 0/2 nach Entfernen sämtlicher BAS-/FRM-/MAK-/BI-Quellen, Ablehnung alter/höherer Versionen. V2/V3 begrenzen dennoch das vollständige Projektmodell. |
| Quellorte: Fehlerzeile, Bibliotheksfehler | `lib.bas:2` und verschachtelte Include-Fehler bestehen. Unabhängig zusätzlich Fehler in physischer Zeile 42 geprüft. V1 zeigt fehlende Zweig-/Schleifenorte. |
| Benannte Objekte: Lesen/Schreiben, keine Standardeigenschaft, Wertebereich, getrennte Controls | Bestehende Semantik-/VM-/Modelltests und neuer Zwei-FRM-Test bestanden; Check1.Value=3 bleibt Fehler 5. V3 betrifft weitere Formularquellen, V5 die weiteren qualifizierten Zugriffspfade. |
| Diagnosen: Position, Include-Diagnose | Lexer-/Parser-/Semantikpfad trägt Originalpositionen. V6 betrifft die anschließende Linkerphase. |
| Objektgrammatik/-semantik: unbekanntes Objekt/Eigenschaft, Typkonflikt, TYPEOF, gleiche Controls | Bestehende Tests bestanden. V3/V5 verletzen den allgemeinen Modul-/Containervertrag trotz grüner Property-Beispiele. |
| Unterbrechung: Breakpoint, Inspektion, gleiche Zeile in zwei Modulen | Unabhängige Probe: MAIN passiert Zeile 10, LIB hält in Zeile 10; qualifizierte Array-/UDT-Inspektion liefert 7/8; Einzelschritt meldet Spalte 5; ERR/ERL bleiben 6/200. V1 lässt Breakpoints an ELSEIF aus. |
Erneut ausgeführt:
- `cargo test --workspace`: **437 bestanden, 0 fehlgeschlagen, 1 ignoriert**.
Golden-Generator nicht ausgeführt, keine Sollausgaben geändert.
- [Review-Proben](review-probes.rs): **1 bestanden, 6 fehlgeschlagen**.
Jede fehlgeschlagene Probe entspricht einem der sechs Befunde; mehrere
verwandte Reproduktionen werden innerhalb einer Probe gesammelt.
- Zusätzlich zeitlich begrenzte CLI-Proben für die Befunde; temporäre
Quellverzeichnisse anschließend entfernt.
- `cargo bench -p tb-vm --bench compile`: 508 Zeilen **0,54 ms**, 49.760 Zeilen
in 20 verbundenen Modulen **67 ms**; beide Budgets erfüllt.
- Dokumentation: **152/152 Opcodes**, Operanden-/Gesamtbreiten und **9/9
Containerabschnitte** gegen den aktuellen Code abgeglichen.
- `cargo fmt --all -- --check`, `openspec validate projektmodule-und-kompilat --strict`
und `git diff --check`: bestanden.
- Clippy nicht nochmals ausgeführt: keine Produktcodeänderung seit dem
erfolgreichen Clippy-Lauf der Implementierungsabnahme. Kein neuer
interaktiver Originalsystemvergleich und keine Windows-/Linux-Testmatrix.
## Review-Proben reproduzieren
Im Repository-Wurzelverzeichnis ausführen. Der temporäre Treiber hält die
noch roten Vertragsproben außerhalb der normalen Workspace-Suite; ihre
Erwartungen beschreiben das Sollverhalten. Exit-Code ist bis zur Behebung 101.
```sh
python3 - <<'PY'
from pathlib import Path
import json, subprocess, tempfile, os
root = Path.cwd()
with tempfile.TemporaryDirectory(prefix='tb-project-review-') as folder:
p = Path(folder)
manifest = '[package]\nname="tb-project-review"\nversion="0.0.0"\nedition="2021"\n[workspace]\n'
manifest += '[[test]]\nname="probes"\npath=' + json.dumps(str(root / 'openspec/changes/archive/2026-09-05-projektmodule-und-kompilat/review-probes.rs')) + '\n[dependencies]\n'
for name in ['tb-ui', 'tb-vm', 'tb-runtime', 'tb-frontend']:
manifest += name + ' = {path=' + json.dumps(str(root / 'crates' / name)) + '}\n'
(p / 'Cargo.toml').write_text(manifest)
result = subprocess.run(['cargo', 'test', '--manifest-path', str(p / 'Cargo.toml'), '--offline', '--test', 'probes', '--', '--nocapture'], env={**os.environ, 'CARGO_TARGET_DIR': str(root / 'target/review-projektmodule')}, timeout=120)
raise SystemExit(result.returncode)
PY
```
**Abnahmeentscheidung:** V1V4 vor Archivierung beheben; V5/V6 ebenfalls in
die vollständige Umsetzung aufnehmen. Keine zusätzlichen Stilvorschläge.

View File

@@ -0,0 +1,266 @@
//! Unabhängige Vertragsproben; Ausführung mit dem temporären Treiber aus review.md.
use tb_frontend::{
forms::FormCatalog,
source::{SourceSegment, SourceUnit},
};
use tb_runtime::{host::CaptureHost, value::Value};
use tb_vm::{
bytecode::CompiledModule,
compile_project,
interp::{RunEvent, Vm},
};
fn units(files: &[(&str, &str)]) -> Vec<SourceUnit> {
files
.iter()
.map(|(name, code)| SourceUnit::new(name, &format!("{name}.bas"), code))
.collect()
}
fn compile(files: &[(&str, &str)]) -> Result<CompiledModule, Vec<tb_frontend::Diagnostic>> {
compile_project("APP", &units(files), &FormCatalog::default(), &[])
}
fn run(module: CompiledModule) -> (RunEvent, String) {
let bytes = module.to_tbc();
let loaded = CompiledModule::from_tbc(&bytes).unwrap();
assert_eq!(bytes, loaded.to_tbc());
let mut vm = Vm::new(loaded);
let event = vm.run(&mut CaptureHost::default());
(event, tb_runtime::snapshot::text(&vm.rt.screen))
}
#[test]
fn v1_zweige_und_schleifen_behalten_eigene_quellorte() {
let mut failures = vec![];
for (name, code, line) in [
(
"ELSEIF",
"IF 0 THEN\nPRINT 1\nELSEIF 1/0 THEN\nPRINT 2\nEND IF",
3,
),
("CASE", "SELECT CASE 1\nCASE 1/0\nPRINT 2\nEND SELECT", 2),
("NEXT", "FOR i%=32767 TO 32767\nPRINT 1\nNEXT", 3),
("LOOP", "DO\nPRINT 1\nLOOP UNTIL 1/0", 3),
] {
let (event, _) = run(compile(&[("MAIN", code)]).unwrap());
if !matches!(event, RunEvent::Error { line: found, .. } if found == line) {
failures.push(format!("{name}: erwartet Zeile {line}, erhalten {event:?}"));
}
}
let source = SourceUnit {
name: "MAIN".into(),
segments: vec![
SourceSegment {
file: "main.bas".into(),
first_line: 1,
text: "IF 0 THEN\nPRINT 1\n".into(),
},
SourceSegment {
file: "cond.bi".into(),
first_line: 1,
text: "ELSEIF 1/0 THEN\nPRINT 2\n".into(),
},
SourceSegment {
file: "main.bas".into(),
first_line: 4,
text: "END IF\n".into(),
},
],
};
let mut vm = Vm::new(compile_project("APP", &[source], &FormCatalog::default(), &[]).unwrap());
let event = vm.run(&mut CaptureHost::default());
if vm.current_file() != "cond.bi" {
failures.push(format!(
"Include: {event:?} aus {} statt cond.bi",
vm.current_file()
));
}
// Das gleiche Problem darf nicht nur durch Umbenennen der Fehlermeldung behoben werden.
let mut vm = Vm::new(
compile(&[("MAIN", "IF 0 THEN\nPRINT 1\nELSEIF 1 THEN\nPRINT 2\nEND IF")]).unwrap(),
);
vm.add_module_breakpoint(0, 3);
let event = vm.run(&mut CaptureHost::default());
if event != (RunEvent::Breakpoint { line: 3 }) {
failures.push(format!("Breakpoint ELSEIF: {event:?}"));
}
assert!(failures.is_empty(), "{}", failures.join("\n"));
}
#[test]
fn v2_importierte_deklarationen_behalten_kontext_und_abhaengigkeiten() {
let cases = [
(
"DEFINT",
"PRINT Doppelt(3)\nEND",
"DEFINT A-Z\nFUNCTION Doppelt(x)\nDoppelt=x*2\nEND FUNCTION",
" 6 \n",
),
(
"CONST",
"CONST A=3\nPRINT B\nEND",
"CONST A=3\nCONST B=A+1",
" 4 \n",
),
(
"TYPE",
"TYPE Inner\nx AS INTEGER\nEND TYPE\nDIM a AS Outer\na.i.x=3\nPRINT a.i.x\nEND",
"TYPE Inner\nx AS INTEGER\nEND TYPE\nTYPE Outer\ni AS Inner\nEND TYPE",
" 3 \n",
),
];
let mut failures = vec![];
for (name, main, lib, output) in cases {
match compile(&[("MAIN", main), ("LIB", lib)]) {
Err(e) => failures.push(format!("{name}: {e:?}")),
Ok(module) => {
let actual = run(module);
if actual != (RunEvent::Ended, output.into()) {
failures.push(format!("{name}: {actual:?}"));
}
}
}
}
assert!(failures.is_empty(), "{}", failures.join("\n"));
}
#[test]
fn v3_form_metabefehle_erzeugen_getrennte_formularobjekte() {
let module = compile(&[
(
"A",
"'$FORM\nCaption=\"a\"\nCALL SetupB\nPRINT Caption\nEND",
),
("B", "'$FORM\nSUB SetupB\nCaption=\"b\"\nEND SUB"),
])
.unwrap();
let names: Vec<_> = module.objects.iter().map(|o| o.name.clone()).collect();
let actual = run(module);
assert_eq!(
actual,
(RunEvent::Ended, "a\n".into()),
"Objekte: {names:?}"
);
}
#[test]
fn v4_common_behaelt_typidentitaet_und_initialisiert_arrays_einmal() {
let mut failures = vec![];
for (name, files, want) in [
(
"Suffixe",
vec![(
"MAIN",
"COMMON SHARED c%, c$\nc%=7\nc$=\"ok\"\nPRINT c%;c$\nEND",
)],
" 7 ok\n",
),
(
"Array",
vec![
("MAIN", "COMMON SHARED a%(2)\na%(1)=7\nCALL F\nEND"),
("LIB", "COMMON SHARED a%(2)\nSUB F\nPRINT a%(1)\nEND SUB"),
],
" 7 \n",
),
] {
match compile(&files) {
Err(e) => failures.push(format!("{name}: {e:?}")),
Ok(module) => {
let actual = run(module);
if actual != (RunEvent::Ended, want.into()) {
failures.push(format!("{name}: {actual:?}"));
}
}
}
}
assert!(failures.is_empty(), "{}", failures.join("\n"));
}
#[test]
fn v5_load_und_unload_loesen_den_expliziten_container_auf() {
use tb_frontend::forms::ObjectClass;
let mut catalog = FormCatalog::default();
for name in ["Form1", "Form2"] {
catalog.add(name, ObjectClass::Form, None, false);
catalog.add("Text1", ObjectClass::TextBox, Some(name), true);
}
let source = units(&[("MAIN", "LOAD Form2!Text1(3)\nForm2!Text1(3).Text=\"b\"\nPRINT Form2!Text1(3).Text\nUNLOAD Form2!Text1(3)\nEND")]);
let module = compile_project("APP", &source, &catalog, &[]).unwrap();
assert_eq!(run(module), (RunEvent::Ended, "b\n".into()));
}
#[test]
fn v6_linkerdiagnose_nennt_den_urspruenglichen_dateiort() {
let errors = compile(&[
("MAIN", "DECLARE SUB F(x%)\nCALL F(1)\nEND"),
("LIB", "SUB F(x$)\nEND SUB"),
])
.unwrap_err();
assert!(
errors
.iter()
.any(|d| d.message.contains("Parameter type mismatch")),
"{errors:?}"
);
assert!(
errors
.iter()
.all(|d| d.file.is_some() && d.pos.line > 0 && d.pos.column > 0),
"{errors:?}"
);
}
#[test]
fn modulbreakpoint_rundlauf_inspektion_und_numerische_erl_bleiben_getrennt() {
let main = format!(
"DIM a%(2)\nTYPE T\nx AS INTEGER\nEND TYPE\nDIM r AS T\na%(1)=7\nr.x=8\n{}CALL F\nEND",
"\n\nn%=1\n"
);
let lib = format!("SUB F\n{}200 ERROR 6\nEND SUB", "\n".repeat(8));
let module = compile(&[("MAIN", &main), ("LIB", &lib)]).unwrap();
let bytes = module.to_tbc();
let loaded = CompiledModule::from_tbc(&bytes).unwrap();
assert_eq!(bytes, loaded.to_tbc());
let mut vm = Vm::new(loaded);
vm.add_module_breakpoint(1, 10);
let mut host = CaptureHost::default();
assert_eq!(vm.run(&mut host), RunEvent::Breakpoint { line: 10 });
assert_eq!(vm.current_module(), 1);
assert_eq!(vm.current_file(), "LIB.bas");
assert!(matches!(
vm.inspect_element("MAIN!a%", &[1]),
Some(Value::Int(7))
));
assert!(matches!(
vm.inspect_field("MAIN!r", &[0]),
Some(Value::Int(8))
));
vm.remove_module_breakpoint(1, 10);
vm.set_step(true);
assert_eq!(vm.run(&mut host), RunEvent::Stepped { line: 10 });
assert_eq!(vm.current_source_pos().column, 5);
vm.set_step(false);
assert!(matches!(
vm.run(&mut host),
RunEvent::Error {
code: 6,
line: 10,
..
}
));
let source = format!("{}ERROR 6", "\n".repeat(41));
assert!(matches!(
run(compile(&[("MAIN", &source)]).unwrap()).0,
RunEvent::Error {
code: 6,
line: 42,
..
}
));
let module = compile(&[(
"MAIN",
"ON ERROR RESUME NEXT\n200 ERROR 6\nPRINT ERR;ERL\nEND",
)])
.unwrap();
assert_eq!(run(module), (RunEvent::Ended, " 6 200 \n".into()));
}

View File

@@ -0,0 +1,79 @@
# Verifikation: projektmodule-und-kompilat
Stand: 05.09.2026, Arbeitsbaum auf Basis von `58b1f62`.
**Keine offenen Befunde: 0 CRITICAL, 0 WARNING, 0 SUGGESTION.**
Die sechs Befunde des [ersten Reviews](review-initial.md) sind behoben.
Weitere bei der Nachprüfung reproduzierte Abweichungen wurden ebenfalls
korrigiert und mit Regressionen abgesichert. Der Change ist abnahmebereit.
| Dimension | Ergebnis |
| --- | --- |
| Vollständigkeit | 12/12 Tasks umgesetzt; alle 7 Requirements und 21 Delta-Szenarien abgeglichen. |
| Korrektheit | 453 Workspace-Tests bestanden, 0 fehlgeschlagen, 1 vorhandener Golden-Generator ignoriert. Die 7 unveränderten ursprünglichen Review-Proben bestehen auch im unabhängigen temporären Treiber. |
| Kohärenz | D1D4 erfüllt: physische Quellorte, getrennte Scopes, selbstständiges TBC 4 und dokumentierte Inline-Instruktionen. |
## Geschlossene Befunde
| Befund | Korrektur und Nachweis |
| --- | --- |
| V1: Zweig-/Schleifenorte | Parser und HIR erhalten ELSEIF-, CASE-, NEXT-, LOOP- und WEND-Positionen. Codegen emittiert die tatsächlichen Anweisungsgrenzen. Fehler, Breakpoints, WHILE-Rücksprung, LOOP-RESUME, NEXT-RESUME NEXT und Include-Enden in Zeile 42/Spalte 3 sind geprüft. Numerische ERL-Labels bleiben getrennt. |
| V2: Importkontext | Exportierte Signaturen erhalten die DEFtype-Typen ihres Ursprungs. Konstanten werden dort gefaltet, einschließlich transitiver Abhängigkeiten über drei Module. TYPE-Abhängigkeiten werden geordnet und bei abweichenden lokalen Layouts intern qualifiziert. Echte lokale Duplikate bleiben Fehler. |
| V3: `$FORM`-Identitäten | FRM- und `$FORM`-Objekte stehen vor jeder Modulübersetzung in einem gemeinsamen Katalog. Getrennte Captions, gegenseitige Referenzen und unterschiedliche Form_Load-Bindungen sind auch für gemischte FRM/BAS-Projekte nach TBC-Rundlauf geprüft. |
| V4: COMMON | HIR übergibt Block, vollständigen Variablenschlüssel, Elementtyp und Arraygrenzen an den Linker. Typen, Ränge und bekannte Grenzen werden geprüft. `CommonArr` initialisiert einmal und prüft spätere dynamische Grenzen ohne Zurücksetzen des Handles oder der Werte. Offene Arraydeklarationen und gerundete Grenzen bleiben unterstützt. |
| V5: LOAD/UNLOAD | Beide Anweisungen verwenden dieselbe explizite Containerauflösung und Indexprüfung wie andere Objektzugriffe. `Form2!Text1(3)` wird geladen, beschrieben, gelesen und entladen, auch wenn Form1 ebenfalls Text1 besitzt. |
| V6: Diagnosen | Der Linker gibt strukturierte Diagnosen an den ursprünglichen Deklarationen zurück; Source-ID, Datei, Zeile und Spalte bleiben erhalten. Signaturkonflikte, COMMON-Konflikte im Include sowie doppelte Module sind geprüft. |
Die erweiterten Proben fanden zusätzlich folgende inzwischen behobene
Abweichungen: transitive Konstantenimports, fremde TYPE-Layouts bei lokal
abweichenden Definitionen, prozedurübergreifendes Durchsickern von DEFtype,
Verwechslung typisierter Debuggernamen, Verwechslung von Formnamen mit
suffixdeklarierten Variablen und fehlende Literalpositionen in Diagnosen.
Letzteres ist durch `CALL F("wrong")` in `args.bi:42:10` abgesichert.
Auch Konflikte erst in der dritten COMMON-Deklaration werden erkannt.
Die [16 dauerhaften Regressionstests](../../../../crates/tb-vm/tests/project_regressions.rs)
enthalten die sieben ursprünglichen Proben mit unveränderten Erwartungen
sowie neun ergänzende Tests mit mehreren verwandten Fällen. Die ursprüngliche
[Probe-Datei](review-probes.rs) wurde nicht geändert. Es wurden keine Goldens
neu erzeugt und keine Fehlererwartungen abgeschwächt.
## Abgleich aller Requirements und Szenarien
| Requirement | Szenarien / Nachweise |
| --- | --- |
| Monomorpher Opcode-Satz | Gemischter Ausdruck und aufgelöste Namen: vorhandene HIR-/Codegentests; numerische Projektverknüpfung. Alle 153 Opcode-Kennungen, Operandenfolgen und Encodingbreiten gegen die Dokumentation geprüft. |
| TBC-Container | Bytegleicher Roundtrip; klare Ablehnung alter/unbekannter Versionen; vollständige Formularwerte und Designarrays. CLI-Projekttests entfernen sämtliche BAS-/BI-/FRM-/MAK-Quellen und führen das TBC selbstständig aus. |
| Quellorte | Fehler in physischer Zeile 42; Fehler in lib.bas:2; verschachtelte Includes; zusätzliche ausführbare Zweig-/Schleifengrenzen und Initialisierungen. |
| Benannte Objekte | Eigenschaften lesen/schreiben; keine Standardeigenschaft; Check1.Value=3 erzeugt Fehler 5; voneinander unabhängige Werte gleichnamiger Controls. Zwei FRM- und gemischte FRM/`$FORM`-Projekte geprüft. |
| Diagnostik | 1-basierte Datei-/Zeilen-/Spaltenangaben in Lexer, Parser, Semantik und Linker; Include-Fehler einschließlich Literalargumenten. |
| Objektgrammatik/-semantik | Unbekanntes Objekt und unbekannte Eigenschaft benennen; Eigenschaftstyp prüfen; TYPEOF; expliziter Form1/Form2-Container bei gleichnamigen Controls. Qualifiziertes LOAD/UNLOAD nutzt dieselbe Auflösung. |
| Unterbrechung/Inspektion | Modulbezogener Breakpoint; scalar-, array- und UDT-Inspektion; Fortsetzen und Einzelschritt. Gleiche physische Zeile in MAIN und LIB bleibt getrennt, explizite Typ-Suffixe unterscheiden Variablen. ERR/ERL bleiben 6/200. |
Die konkreten ursprünglichen Szenario-/Testnamen und weitere Containerproben
stehen im [Implementierungsnachweis](verification.md), den
[VM-Projekttests](../../../../crates/tb-vm/tests/project.rs) und den
[CLI-Projekttests](../../../../crates/tb-cli/tests/project.rs).
## Abschließende Prüfläufe
- `cargo test --workspace`: **453 bestanden, 0 fehlgeschlagen, 1 ignoriert**.
- Unabhängiger Treiber aus [review-initial.md](review-initial.md): **7 bestanden**.
- `cargo clippy --workspace --all-targets --all-features -- -D warnings`: bestanden.
- `cargo fmt --all -- --check`: bestanden.
- `cargo bench -p tb-vm --bench compile`: 508 Zeilen **0,56 ms** (Budget 50 ms);
49.760 Zeilen in 20 verbundenen Modulen **77 ms** (Budget 1000 ms).
- Formatabgleich: **153/153 Opcodes**, Operandentypen und Gesamtbreiten;
**9/9 Abschnittskennungen** stimmen zwischen Writer, Reader und Dokumentation überein.
- `openspec validate projektmodule-und-kompilat --strict`: bestanden.
- `git diff --check`: bestanden.
Die Verifikation prüft den aktuellen Code und die gesamten Change-Artefakte
lokal und headless auf macOS. Ein neuer interaktiver Originalsystemvergleich
oder eine Windows-/Linux-Testmatrix war nicht Teil dieses Prüflaufs.
Die vier betroffenen Hauptspecs wurden am 05.09.2026 synchronisiert und
mit `openspec validate --specs --strict` validiert (15/15 bestanden).
Alle sieben Delta-Requirements stimmen vollständig mit den Hauptspecs
überein; alle zuvor vorhandenen Szenarien und unberührten Requirements
bleiben erhalten. Der Change ist unter
`openspec/changes/archive/2026-09-05-projektmodule-und-kompilat` archiviert.

View File

@@ -0,0 +1,59 @@
## MODIFIED Requirements
### Requirement: Monomorpher Opcode-Satz
Der Codegenerator SHALL jedes HIR-Konstrukt in typisierte, monomorphe
Opcodes übersetzen: Arithmetik, Vergleiche und Konvertierungen tragen den
Operandentyp im Opcode (kein Tag-Dispatch über Werte zur Laufzeit);
implizite Konvertierungen erscheinen als explizite Konvertierungs-Opcodes
an den vom HIR bestimmten Stellen. Der Opcode-Satz SHALL in
docs/tbvm-design.md vollständig dokumentiert sein.
Die Dokumentation SHALL sämtliche tatsächlich serialisierbaren Instruktionen einschließlich Forms, Ereignissteuerung und Dateioperationen mit Operanden und Encoding führen.
#### Scenario: Gemischter Ausdruck wird monomorph
- **WHEN** `d# = i% + 1.5#` übersetzt wird (INTEGER-Variable, DOUBLE-Ziel)
- **THEN** enthält der Bytecode einen Konvertierungs-Opcode INTEGER→DOUBLE und eine DOUBLE-Addition, keinen generischen Additions-Opcode
#### Scenario: Namen sind zur Laufzeit aufgelöst
- **WHEN** eine Variable oder Prozedur im Bytecode referenziert wird
- **THEN** geschieht das über Slot- bzw. Tabellenindizes, nicht über Namens-Lookups
### Requirement: `.tbc`-Containerformat
Das Kompilat SHALL als `.tbc`-Datei serialisierbar und wieder ladbar sein:
Magic `TBC\0`, Formatversion und dokumentierte Abschnittstabelle.
Der Container SHALL Quellen und Module, Konstantenpool, TYPE-Layouts,
modulqualifizierte Prozeduren mit vollständiger Signatur, Locals und Code,
DATA sowie die vollständigen Forms-Anfangsbeschreibungen führen.
Code und Quellortinformationen SHALL inline in den Prozeduren liegen
dürfen; eigenständige CODE-/LINES-Abschnitte sind nicht erforderlich. Laden und erneutes Serialisieren MUST verlustfrei sein;
eine unbekannte Formatversion MUST mit einer klaren Fehlermeldung
abgewiesen werden.
Ein aus FRM- oder Projektquellen gebautes Kompilat SHALL ohne erneuten Zugriff auf diese Quellen ausführbar sein und ihre Anfangseigenschaften und Designzeit-Control-Arrays bewahren.
#### Scenario: Roundtrip
- **WHEN** ein kompiliertes Modul als `.tbc` geschrieben und wieder geladen wird
- **THEN** ist das geladene Kompilat funktional identisch (gleiche Ausführung, gleiche Zeilenzuordnung)
#### Scenario: Unbekannte Version
- **WHEN** eine `.tbc`-Datei mit höherer Formatversion geladen wird
- **THEN** wird das Laden mit einer Meldung abgelehnt, die die Version nennt
#### Scenario: Formularwerte nach Kompilat-Rundlauf
- **WHEN** ein Formular mit Text1.Text = "hello" kompiliert, gespeichert und ohne Quelldatei geladen wird
- **THEN** liefert der erste Zugriff auf Text1.Text weiterhin hello
### Requirement: Zeilentabelle für Fehlerortung
Der Bytecode SHALL jede Anweisung ihrem Ursprung (Moduldatei, Zeile)
zuordnen, sodass Laufzeitfehlermeldungen, `ERL`, Breakpoints und
Einzelschritt die Quellzeile exakt benennen können.
Die Zuordnung SHALL auch nach Include-Expansion und bei mehreren Projektmitgliedern die ursprüngliche Datei und physische Zeile bewahren. Numerische BASIC-Zeilenlabels für ERL SHALL davon getrennt bleiben.
#### Scenario: Fehler nennt Zeile
- **WHEN** in Zeile 42 eines Programms ein Laufzeitfehler ohne Handler auftritt
- **THEN** nennt die Fehlermeldung Zeile 42
#### Scenario: Fehler aus Projektbibliothek
- **WHEN** lib.bas in physischer Zeile 2 einen Fehler auslöst und über app.mak aufgerufen wird
- **THEN** nennt die Meldung lib.bas und Zeile 2 statt einer verketteten Projektzeile

View File

@@ -0,0 +1,27 @@
## MODIFIED Requirements
### Requirement: Formulare und Steuerelemente als benannte Objekte
Ein Formular SHALL unter seinem Namen ansprechbar sein, ein
Steuerelement unter seinem Namen innerhalb seines Formulars. Eigenschaften
SHALL über `objekt.eigenschaft` gelesen und geschrieben werden, mit dem
in der Forms-Referenz festgelegten Typ und Wertebereich. Es SHALL keine
Standardeigenschaft geben: `Text1 = "x"` MUST abgewiesen werden. Das
Schreiben eines Wertes außerhalb des Wertebereichs MUST Laufzeitfehler 5
auslösen; eine zur Laufzeit schreibgeschützte Eigenschaft MUST bei
Zuweisung einen Fehler auslösen.
#### Scenario: Eigenschaft lesen und schreiben
- **WHEN** `Text1.Text = "hallo"` ausgeführt und danach `Text1.Text` gelesen wird
- **THEN** liefert der Zugriff `hallo`
#### Scenario: Keine Standardeigenschaft
- **WHEN** ein Programm `Text1 = "x"` enthält
- **THEN** wird es abgewiesen und die Meldung nennt `Text1`
#### Scenario: Wert außerhalb des Bereichs
- **WHEN** `Check1.Value = 3` ausgeführt wird
- **THEN** tritt Laufzeitfehler 5 auf
#### Scenario: Getrennte Werte gleichnamiger Controls
- **WHEN** Form1!Text1.Text auf a und Form2!Text1.Text auf b gesetzt werden
- **THEN** liefern die beiden Zugriffe unabhängig a und b

View File

@@ -0,0 +1,50 @@
## MODIFIED Requirements
### Requirement: Diagnostik mit Vorbild-Meldungen
Diagnosen SHALL exakte 1-basierte Quelltextpositionen (Zeile, Spalte)
tragen; die Meldungstexte SHALL dem Katalog des Vorbilds folgen, wo eine
Entsprechung existiert (z. B. „Type mismatch", „Duplicate definition",
„Label not defined", „Subprogram not defined", „Overflow").
Bei Projekten und Includes SHALL jede Diagnose den ursprünglichen Dateinamen mit dessen physischer Zeile und Spalte tragen.
#### Scenario: Position in der Diagnose
- **WHEN** in Zeile 3 ein Typkonflikt auftritt
- **THEN** nennt die Diagnose Zeile 3 und die Spalte des Ausdrucks
#### Scenario: Include-Diagnose
- **WHEN** ein eingebundenes Include einen Typfehler enthält
- **THEN** nennt die Diagnose die Include-Datei und ihre eigene Quellposition
### Requirement: Objektzugriffe in Grammatik und Semantik
Der Parser SHALL Punktpfade auf Formulare und Steuerelemente
(`objekt.eigenschaft`), den Container-Operator
`form!steuerelement[.eigenschaft]`, `IF TYPEOF ausdruck IS klasse`, den
Metabefehl `$FORM` und die Datentypen `FORM` und `CONTROL` als
Parametertypen erkennen. Die Semantik SHALL Eigenschaften gegen die
Klasse des Objekts prüfen und ihren Typ bestimmen. Ein Punktpfad, dessen
Basis weder ein benutzerdefinierter Typ noch ein bekanntes Objekt ist,
MUST eine Diagnose erzeugen, die Basis und Eigenschaft namentlich nennt;
er MUST NOT zu einer impliziten Variablen mit Punkt im Namen werden.
Gleichnamige Steuerelemente unterschiedlicher Formulare SHALL im jeweiligen Formularnamensraum aufgelöst werden; die explizite Containerangabe SHALL vor der lokalen Namenssuche wirken.
#### Scenario: Unbekanntes Objekt wird benannt
- **WHEN** ein Modul `Text9.Text = "a"` ohne Steuerelement `Text9` enthält
- **THEN** nennt die Diagnose `Text9` als unbekanntes Objekt statt „Type mismatch" zu melden
#### Scenario: Unbekannte Eigenschaft wird benannt
- **WHEN** ein Modul `Text1.Farbe = 3` enthält
- **THEN** nennt die Diagnose `Farbe` als unbekannte Eigenschaft von `TextBox`
#### Scenario: Eigenschaftstyp wird geprüft
- **WHEN** ein Modul `Text1.Text = 5` enthält
- **THEN** meldet die Semantik einen Typkonflikt mit Nennung der Eigenschaft
#### Scenario: TYPEOF
- **WHEN** ein Modul `IF TYPEOF Ziel IS CommandButton THEN` enthält
- **THEN** wird es übersetzt und die Klasse gegen die bekannten Steuerelementklassen geprüft
#### Scenario: Gleichnamige Controls verschiedener Formulare
- **WHEN** Form1 und Form2 jeweils Text1 enthalten und Form2!Text1.Text gelesen wird
- **THEN** wird Text1 in Form2 aufgelöst und das Programm übersetzt diagnosefrei

View File

@@ -0,0 +1,24 @@
## MODIFIED Requirements
### Requirement: Unterbrechbare step()-Ausführung
Die VM SHALL als zustandsbehaftete, vom Einbetter getriebene Struktur
ausführbar sein: An Anweisungsgrenzen prüft sie ein Flag-Wort und kann
anhalten (Breakpoint auf (Modul, Zeile), Einzelschritt, Abbruchwunsch).
Im angehaltenen Zustand MUST der Einbetter Variableninhalte (Skalare,
Arrayelemente, UDT-Felder) inspizieren und die Ausführung fortsetzen
können. Die Tick-Prüfung erfolgt nur an Anweisungsgrenzen, nicht pro
Opcode.
Ein Breakpoint SHALL durch Modulidentität und physische Quellzeile bestimmt sein. Gleiche Zeilennummern anderer Module MUST NOT denselben Breakpoint auslösen.
#### Scenario: Breakpoint hält an
- **WHEN** ein Breakpoint auf Zeile 10 gesetzt ist und die Ausführung Zeile 10 erreicht
- **THEN** hält die VM vor der Anweisung an und meldet Modul und Zeile
#### Scenario: Inspektion im Halt
- **WHEN** die VM an einem Breakpoint steht
- **THEN** liefert die Inspektions-API den aktuellen Wert einer benannten Variablen
#### Scenario: Gleiche Zeile in zwei Modulen
- **WHEN** ein Breakpoint auf Modul A, Zeile 10 gesetzt ist und Modul B Zeile 10 ausführt
- **THEN** läuft B weiter; erst A Zeile 10 hält an

View File

@@ -0,0 +1,23 @@
## 1. Quellen und Scopes
- [x] 1.1 Quell-/Modulidentitäten durch Include-Expansion und Frontend erhalten; verifizieren mit Compile- und Runtimefehlern aus lib.bas:2 und verschachtelten Includes.
- [x] 1.2 Form-/Prozedurscopes korrekt auflösen; verifizieren mit zwei Form_Load-Prozeduren, zwei Text1-Controls und unterschiedlichen Form1!Text1/Form2!Text1-Werten sowie echten Duplikaten im selben Scope.
## 2. Kompilat und Debugger
- [x] 2.1 Vollständige Forms-Anfangsdaten, Quelltabelle und Prozedursignaturen serialisieren; verifizieren durch bytegleichen Kompilat-Rundlauf und gleiche Ausführung inklusive Designzeit-Control-Arrays.
- [x] 2.2 Modulbezogene Breakpoints/Quellortmeldungen ergänzen; verifizieren mit identischen physischen Zeilennummern in zwei Modulen, Include-Fehlern, Einzelschritt und unverändertem ERR/ERL.
- [x] 2.3 CLI-run für TBC anschließen und Versionsgrenze anheben; verifizieren mit build, Entfernen aller Quellen, run des Kompilats und klarer Ablehnung inkompatibler Versionen.
## 3. Abnahme
- [x] 3.1 Container und vollständige Opcode-/Operandentabelle dokumentieren; verifizieren mit Format-/Opcode-Abgleich, cargo test --workspace und dem Compile-Budget.
- [x] 3.2 Delta-Specs vollständig gegen die erhaltenen Szenarien prüfen; verifizieren mit openspec validate projektmodule-und-kompilat --strict und git diff --check.
## 4. Reviewbefunde vollständig beheben
- [x] 4.1 V1: Physische Quellpositionen aller Zweige und Schleifenenden erhalten; Fehler, Breakpoints und RESUME prüfen.
- [x] 4.2 V2/V6: Importierte Deklarationen im Ursprungskontext auflösen und Linkdiagnosen an Originalstellen melden.
- [x] 4.3 V3/V5: Gemeinsame Formidentitäten und qualifiziertes LOAD/UNLOAD korrigieren.
- [x] 4.4 V4: Vollständige COMMON-Identitäten und kompatible einmalige Arrayinitialisierung sicherstellen.
- [x] 4.5 Unveränderte Reviewproben, ergänzende Nachbarfälle und sämtliche Change-Szenarien erneut prüfen; alle weiteren Befunde beheben.

View File

@@ -0,0 +1,106 @@
# Implementierungsnachweis — 05.09.2026
Change: `projektmodule-und-kompilat`, Ausgangscommit `58b1f62`.
**Abschließende Verifikation:** [review.md](review.md) bestätigt die Behebung
aller sechs ursprünglichen und aller zusätzlich reproduzierten Befunde.
Der [erste Review](review-initial.md) bleibt als historischer Nachweis erhalten.
Alle zwölf Tasks (sieben Implementierungs- und fünf Review-Aufgaben) sind
umgesetzt. F09, F10, F11 und F24 sind im Arbeitsbaum bearbeitet; die
Hauptspec-Synchronisierung und Archivierung sind am 05.09.2026 abgeschlossen. Die Abnahme
umfasst alle sieben Delta-Requirements mit ihren 21 Szenarien.
## Umsetzung
| Task / Befund | Umsetzung und Nachweis |
| --- | --- |
| 1.1 / F10 | `SourceUnit`/`SourceSegment` erhalten Modul, Datei und physische Zeile vor dem Parsen. Lexer- und Parserpositionen werden vor der Semantik den Originalquellen zugeordnet. CLI-Tests prüfen Compile-/Runtimefehler aus `lib.bas:2`, verschachtelten Includes und dem Codeabschnitt eines entfernten FRM. Auch leere Quellen funktionieren. |
| 1.2 / F09 | Module werden getrennt aufgelöst und danach numerisch verbunden. Lokale Prozeduren und Controls haben Vorrang, `Form!Control` sucht direkt im angegebenen Formular. Eltern-IDs ersetzen Namensvergleiche auch bei Frames, Menüs, Optionsgruppen und Geometrie. Zwei Form_Load-Prozeduren initialisieren ihre jeweiligen Text1-Controls unabhängig; echte SUB-/CONST-/TYPE-Duplikate im selben Modul bleiben Fehler. |
| 2.1 / F11 | TBC 4 enthält Module/Quellen, vollständige Parameter- und Rückgabetypen, Locals, Forms-Instanzen mit Eigenschaften, Designindizes und Eltern-IDs sowie das Startformular. `Vm::new` stellt diesen Anfangszustand her. Bytegleicher Rundlauf und Ausführung mit zwei Formularen und Designarrays 0/2 sind geprüft. |
| 2.2 / F10 | `Source` plus `Stmt` liefern Datei/Spalte/physische Zeile. `InitStmt` erhält dieselben Informationen für globale Initialisierungen, ohne vorzeitig Ereignisse zuzustellen. Breakpoints verwenden Modul-ID und Zeile. Einzelschritt, Variableninspektion und Fortsetzen bleiben möglich. Numerische Labels führen separat `SetErl`; DEF FN erhält den tatsächlichen Quellort. |
| 2.3 / F11 | `tbc run` lädt TBC direkt. Der CLI-Test baut ein Projekt, entfernt alle BAS-/BI-/FRM-/MAK-Dateien und vergleicht die Ausgabe des Kompilats mit der Quellausführung (`hello`, `world`, `7`). Ein zweiter Fall erhält nach Entfernen des FRM den Fehlerort `error.frm:6:1`. Versionen 1, 2, 3, 5 und 32767 werden ausdrücklich benannt und abgewiesen. |
| 3.1 / F24 | [TBVM-Format](../../../../docs/tbvm-design.md) dokumentiert alle neun Abschnitte und alle 153 Opcodes einzeln mit Operanden, Encodingbreiten und Kennungen. Der Compile-Benchmark übersetzt und verbindet nun tatsächlich ein Projekt mit 20 Modulen. |
| 3.2 | Alle Delta-Requirement-Blöcke behalten ihre bisherigen Szenarien. Neue Szenarien und die Implementierungsentscheidungen D1D4 wurden gegen die unten genannten Tests und Codepfade abgeglichen. OpenSpec- und Diff-Prüfung bestanden. |
## Projektgrenzen und ergänzende Regressionen
Der Projektlink versetzt Prozedur-, Slot-, TYPE-, String-, DATA- und
Sprungtabellenreferenzen. Hauptprogramminitialisierungen aller Mitglieder
laufen vor der ersten Ereigniszustellung; die übrigen Modulrümpfe folgen der
Projektreihenfolge. Eindeutige externe Prozeduren werden als Deklarationen
importiert, lokale Namen bleiben erhalten. Eindeutige fremde Konstanten und
TYPEs bleiben als Kompatibilitätsregel sichtbar. Es wird kein Quelltext zur
Unterdrückung von Duplikaten gelöscht.
Geprüft sind wiederholt eingebundene Konstanten, gleichnamige lokale SUBs,
modullokales SHARED, gemeinsame COMMON-Skalare und unterschiedliches OPTION
BASE. Modulhandler werden getrennt gespeichert: ON ERROR GOTO 0 in der
Bibliothek deaktiviert den Handler des Aufrufers nicht. Bestehende ERR-/ERL-
und RESUME-Korpustests bestehen unverändert.
Der Signaturtest übergibt ein INTEGER-Array, einen UDT und einen Skalar an
eine Funktion in einem anderen Modul. Er prüft Rückgabetyp, Array-/BYREF-
Merkmale, Ausgabe und Rückwirkung einer UDT-Feldänderung auf den Aufrufer.
Die dabei gefundene Inkonsistenz zwischen UDT-Argument und Parameter wurde
im gemeinsamen Semantikpfad behoben. Bekannte externe Dialogdeklarationen
funktionieren auch mit qualifizierten Prozedurnamen. ISAM-OPEN verwendet mit
`PushUdtId` eine explizit versetzbare TYPE-Referenz; der Test vergleicht sie
nach dem Rundlauf mit dem tatsächlichen UDT-Index.
Der Loader weist abgeschnittene Dateien an jedem Byteoffset, ungültige
Flags/Abschnittsgrößen, doppelte Abschnitte sowie ungültige Quell-, Modul-,
Prozedur- und Elternreferenzen ab. Inkonsistente Signaturen, doppelte
Anfangseigenschaften, falsche Property-Typen und abweichende Designindizes
werden vor dem VM-Start geprüft. Das ersetzt keinen vollständigen
Stackverifizierer für beliebigen fremden Bytecode.
## Requirement- und Szenarioabgleich
Die neuen ausführbaren Nachweise liegen in
[VM-Projekttests](../../../../crates/tb-vm/tests/project.rs) und
[CLI-Projekttests](../../../../crates/tb-cli/tests/project.rs).
Die folgende Tabelle führt auch die erhaltenen Szenarien auf; bestehende
Tests werden durch neue Mehrmodul- und Include-Proben ergänzt.
| Requirement | Szenarien und Belege |
| --- | --- |
| Monomorpher Opcode-Satz | Gemischter Ausdruck: `codegen::tests::ausdruck_monomorph_mit_conv`; aufgelöste Namen: `sema::tests::hir_slots_aufgeloest` und die numerisch verbundenen Projektaufrufe. Vollständigkeit der Dokumentation zusätzlich gegen das `instrs!`-Makro geprüft. |
| .tbc-Containerformat | Roundtrip: mehrere VM-Projekttests vergleichen die gesamten Bytes; unbekannte Version: CLI-Versionstests; Formularwerte: CLI-Ausgabe `hello` und `world` nach Entfernen sämtlicher Quellen. |
| Zeilentabelle für Fehlerortung | Physische Fehlerzeile: Stmt-/Source-Zuordnung und CLI-Fehlerproben; Projektbibliothek: `lib.bas:2:5`, Include: `inner.bi:2:5`; zusätzlich InitStmt-, DEF-FN- und FRM-Codepositionen. |
| Formulare und Steuerelemente als benannte Objekte | Lesen/Schreiben: bestehender VM-Test `objektzugriff_laeuft_ueber_objekt_und_eigenschaftsindex`; keine Standardeigenschaft: `sema::tests::eigenschaftstyp_schreibbarkeit_bang_und_hir_index`; Wertebereich: `forms::tests::defaults_bereich_und_implizites_laden` prüft Check1.Value = 3 → Fehler 5; unabhängige Werte: zwei Formulare mit Text1 und eigenem Form_Load im neuen Projekttest. |
| Diagnostik mit Vorbild-Meldungen | Exakte Position: SourcePos bleibt 1-basiert; neue Tests prüfen Datei, Zeile und Spalte. Include-Diagnose: sowohl direktes SourceSegment als auch verschachtelte CLI-Expansion liefern die ursprüngliche Position des Typfehlers. |
| Objektzugriffe in Grammatik und Semantik | Unbekanntes Objekt/Eigenschaft und Eigenschaftstyp: bestehende Semantiktests für Text9.Text, Text1.Farbe und Text1.Text = 5; TYPEOF: `typeof_form_control_und_form_metabefehl`; gleichnamige Controls: Form1!Text1/Form2!Text1 sowie lokale Text1-Zugriffe beider Form_Load-Rümpfe. |
| Unterbrechbare step()-Ausführung | Breakpoint und Inspektion: vorhandene VM-Tests plus neuer Modulbreakpoint; gleiche Zeile in zwei Modulen: Modul A passiert Zeile 2, Modul B hält dort. Fortsetzen im Einzelschritt erhält `nested.bi:2`, physische Spalte 5 und getrenntes ERL = 200. |
## Ausgeführte Abnahme
- `cargo test --workspace`: **453 bestanden, 0 fehlgeschlagen, 1 ignoriert**.
Der ignorierte Golden-Generator wurde nicht ausgeführt; Sollausgaben blieben unverändert.
- `cargo clippy --workspace --all-targets --all-features -- -D warnings`: bestanden.
- `cargo fmt --all -- --check`: bestanden.
- `cargo bench -p tb-vm --bench compile`: 508 Zeilen **0,56 ms** (Budget 50 ms);
49.760 Zeilen in 20 verbundenen Modulen **77 ms** (Budget 1000 ms),
335.421 Instruktionen. Release-Messung auf diesem Rechner.
- Formatabgleich: **153/153 Opcodezeilen** einschließlich Operandentypen und
Bytebreiten sowie **9/9 Abschnittskennungen** gegen Writer/Reader abgeglichen.
- `openspec validate projektmodule-und-kompilat --strict`: bestanden.
- `git diff --check`: bestanden.
Die Abnahme ist eine lokale Code-/Testprüfung auf macOS; sie ist kein neuer
interaktiver Originalsystemvergleich und keine Windows-/Linux-Testmatrix.
## Reviewkorrekturen
Die ursprünglichen sieben Reviewproben bestehen unverändert, sowohl im
separaten Treiber als auch in der Workspace-Suite. Die dauerhafte Datei
`crates/tb-vm/tests/project_regressions.rs` ergänzt sie um neun Tests mit
verwandten Fällen. [review.md](review.md) ordnet die Korrekturen V1V6 und
alle zusätzlich reproduzierten Abweichungen den Nachweisen zu.
`CommonArr` (`0x3B`) initialisiert gemeinsame Arrays einmal und prüft spätere
Laufzeitgrenzen auf Kompatibilität; vorab bekannte Konflikte meldet der
Linker an der ursprünglichen COMMON-Deklaration. Die neue Instruktion ist
in TBC 4 serialisiert, validiert und vollständig dokumentiert. Typ-Suffixe
bleiben in Debugger-Slotnamen erhalten. AST-Literale und sämtliche ausführbaren
Zweig-/Schleifenteile tragen ihre tatsächlichen Quellpositionen.