141 lines
6.0 KiB
Markdown
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.
|