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

6.0 KiB

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.