Files
TerminalBasic/openspec/changes/archive/2026-09-04-phase-4-objektmodell/design.md

141 lines
6.0 KiB
Markdown

# Design — Formularmodell und Objektsprache
## Context
Siehe proposal.md — Why. Drei Bestandsbefunde schneiden den Entwurf zu:
1. **Der Punkt gehört heute zum Bezeichner.** Der Lexer nimmt `.` in den
Namen auf (`a.b` ist ein Token), die Semantik zerlegt ihn nur, wenn
die Basis ein UDT ist. Ein Objektzugriff fällt deshalb durch bis zur
impliziten Variablen.
2. **`!` ist heute immer Typsuffix.** `Form1!Text1` liest sich als
`Form1` (SINGLE) gefolgt von `Text1`.
3. **Der Rückrufweg existiert bereits** aus `phase-4-ereignisschleife`:
die VM kann ein Ziel anspringen und zurückkehren. Ereignisprozeduren
brauchen davon die Variante „Prozedur mit Argumenten" statt
`GOSUB`-Ziel".
## Goals / Non-Goals
**Goals:**
- Objektzugriffe, die zur Übersetzungszeit gegen die Klasse geprüft
werden — kein Nachschlagen von Eigenschaftsnamen zur Laufzeit im
Normalfall.
- Ein Formularmodell, das die Steuerelemente des Folge-Changes nur noch
füllen, nicht umbauen müssen.
- Modales `SHOW` ohne zweiten Ausführungsstrang.
**Non-Goals:**
- Darstellung und Eingabebehandlung der Steuerelemente.
- Ein offenes Objektsystem für Anwenderklassen (Stufe 2).
## Decisions
### D1 — Objektnamen werden zur Übersetzungszeit aufgelöst
Formulare und Steuerelemente sind zur Übersetzungszeit bekannt: sie
stammen aus der `.FRM`-Beschreibung bzw. dem Formular-Designer. Der
Compiler löst `Text1.Text` deshalb in „Objekt 7, Eigenschaft 12" auf und
prüft Typ und Schreibbarkeit sofort.
| Alternative | Warum nicht |
|---|---|
| Eigenschaftsnamen zur Laufzeit nachschlagen | Verlagert jeden Tippfehler in eine Laufzeitmeldung — genau die stille Lücke, die der Guiding Principle verbietet; zudem eine Hashsuche pro Zugriff |
Der Zugriff über `CONTROL`-Parameter (`SUB Setze (c AS CONTROL)`) bleibt
dynamisch; dort prüft die Laufzeit und `TYPEOF` fragt die Klasse ab.
### D2 — Der Punkt wird im Lexer nicht mehr gefaltet, sondern im Parser zerlegt
Der Lexer liefert weiterhin ein Token, das Punkte enthalten kann (die
Rückwärtskompatibilität für Variablennamen mit Punkt bleibt: klassische
Programme nutzen `Kunde.Name` als gewöhnlichen Variablennamen). Die
Auflösung entscheidet die Semantik in dieser Reihenfolge:
```
Basis ist UDT-Variable -> Feldzugriff wie bisher
Basis ist Formular/Control -> Eigenschaftszugriff
Basis ist SCREEN -> Bildschirmobjekt
sonst -> Diagnose mit Basis- und Gliedname
(heute: implizite Variable)
```
Damit ändert sich für bestehende Programme nichts, solange kein Objekt
den Namen trägt; Objekte gewinnen gegen implizite Variablen, weil
implizite Variablen mit Punkt im Namen ohnehin ein Bug waren.
### D3 — `!` entscheidet sich am folgenden Token
Folgt auf `!` unmittelbar ein Bezeichner, ist es der Container-Operator;
sonst das Typsuffix. `Wert! = 1.5` und `Form1!Text1.Text = "a"` sind
damit unterscheidbar, ohne dass der Lexer wissen muss, was `Form1` ist.
Der Fall `Wert!Text` — SINGLE-Variable direkt gefolgt von einem
Bezeichner — ist in keiner gültigen Syntax des Dialekts erreichbar.
### D4 — Modales SHOW ist ein Frame-Zustand, kein zweiter Strang
`SHOW 1` markiert den aufrufenden Frame als „wartet auf Formular X" und
gibt an die Ereignisschleife zurück. Die Schleife stellt weiter zu; wird
X entladen oder verborgen, läuft der markierte Frame an der Anweisung
nach `SHOW` weiter.
```
Form1.SHOW 1
|
+-- Frame markieren (wartet auf Form1) --> Ereignisschleife
|
Ereignisse zustellen <--+
|
UNLOAD Form1 ------------+
|
PRINT "danach" <-- Marke faellt, Frame laeuft weiter
```
Eine geschachtelte Schleife innerhalb des `SHOW`-Aufrufs wäre die
naheliegende Variante, macht aber die Stapeltiefe von der Zahl
verschachtelter modaler Formulare abhängig und verträgt sich nicht mit
dem Einzelschritt des Debuggers in Phase 5.
### D5 — Ereignisprozeduren sind gewöhnliche Prozeduren mit fester Signatur
Der Dispatch ruft `Command1_Click` wie jeden anderen Prozeduraufruf, nur
angestoßen von der Schleife statt von einer Anweisung. Damit gelten
Frames, `ON ERROR`, `EXIT SUB` und der Debugger unverändert. Die
Zuordnung Name → Objekt/Ereignis entsteht zur Übersetzungszeit; zur
Laufzeit ist es ein Index.
### D6 — Das Inventar bekommt eine zweite Quelle
Die Themenliste der Original-Hilfe führt das Objektmodell nicht. Statt
es unerfasst zu lassen — und damit die Abnahme der Phase 6 blind zu
machen —, führt das Inventar Eigenschaften, Methoden und Ereignisse je
Klasse mit der Forms-Referenz als Quelle. Der bestehende Abgleichstest
prüft sie mit denselben Regeln.
Größenordnung: 17 Klassen, in Summe einige hundert Einträge. Das ist der
Preis dafür, dass „Vollständigkeit ist das Soll" für Forms überhaupt
messbar wird.
## Risks / Trade-offs
- **Namenskollision Objekt gegen Variable** → Objekte gewinnen; ein
Programm mit einer Variablen `Text1` und einem Steuerelement `Text1`
bekommt eine Diagnose statt einer stillen Umdeutung.
- **Der Inventarzuwachs erschlägt die Tabelle** → eigene Gruppe je
Klasse, damit die bestehenden Gruppen lesbar bleiben; der Abgleichstest
nennt weiterhin einzelne Einträge.
- **Modale Marke und Debugger** → der Fortsetzungspunkt ist derselbe
Mechanismus wie `STOP`/`CONT`; er wird zusammen mit Phase 5 geprüft.
- **`.FRM` fehlt noch** → bis dahin entstehen Formulare im Test aus einer
im Code aufgebauten Beschreibung; das Objektmodell hängt nicht am
Dateiformat.
## Open Questions
- Z-Reihenfolge bei überlappenden Steuerelementen (offene Detailfrage der
Forms-Referenz) — betrifft die Darstellung, nicht das Objektmodell.
- Ob `ControlPanel` alle 18 Systemfarbwerte des Vorbilds abbildet oder
eine Teilmenge, entscheidet sich mit der Darstellung im Folge-Change.