Im folgenden soll anhand der Regeln für die Eingabe von Linien gezeigt werden, wie die vorgestellten Einzelteile zusammenarbeiten.
Als erstes klinken wir das Nonterminal line-mode in das zentrale, wiederholt abgearbeitete Nonterminal main (Regel 6.8) ein:
;; hook line-mode rule to "main" nonterminal (newrule "main" ; the nonterminal derived by this rule NIL ; no special test function '("line-mode")) ; the nonterminal deriving the rule
Für das Nonterminal line-mode gibt es nur eine Ableitung (Regel 6.7):
;; specify syntax and actions for accepting an arbitrary number of single lines (newrule "line-mode" ; the nonterminal derived by this rule NIL '(:LINE-MODE ; the keyword triggering selection of this rule (ped::mode-switch (ped::actual-logical) ped::MODE-GEOMETRIC NIL) ; we want to see points, lines and faces - not segments etc. (mode "Enter single lines" ("first point" ":LINE (or (point %G) :IGNORE)") ("" "") ("" "") #'ped::elastic-point #'ped::elastic-point NIL NIL) NIL ; here is the data collector for the loop - it's not used (:REPEAT ; the loop specifier "line" ; the only syntactic part of the loop is this nonterminal (mode "Enter single lines" ("first point" ":LINE (point %G)") ("" "") ("" "") #'ped::elastic-point #'ped::elastic-point NIL NIL) NIL) NIL))
Um diese Regel aktivieren zu können, wird ein Menüeintrag angelegt, der bei Auslösung das Schlüsselwort :LINE-MODE an den Parser schickt:
;; generate menu-entry for "line-mode" (ped::register-selection "Edit" ; the name of the menu "line" ; the name and text of the menu entry #'ped::logical-command ; the function to be invoked on activation ":LINE-MODE\n") ; the data to be passed to the function
Wenn der Parser auf eine Ableitung von main wartet und dieses
Schlüsselwort
erhält, prüft er, welche der Regeln es akzeptiert. Er wird die
line-mode-Regel finden und aktivieren, worauf
:LINE-MODE sofort akzeptiert wird. Die beiden nächsten Elemente der
Regel sind Operationen, die sofort ausgeführt werden - erst die Umschaltung
in den geometrischen Modus, dann die Konfiguration der Benutzerschnittstelle
über mode.
Uns interessiert hier ganz besonders die Zeichenkette für den linken
Mausknopf:
":LINE (or (point %G) :IGNORE)"
Wenn der Anwender nun die Maus auf die Position mit den Weltkoordinaten (-1,0)
bewegt und den linken Mausknopf drückt, dann wird dieser Text zuerst der
Koordinatenersetzung unterzogen; deren Resultat ist
":LINE (or (point -1 0) :IGNORE)"
Die Funktion ped::logical-command macht daraus den LISP-Ausdruck
(:LINE (or (point -1 0) :IGNORE))
und ruft damit ped::eval-token-list auf. Diese Funktion zerlegt die Liste
in ihre zwei Bestandteile und ruft für jeden ped::eval-token auf.
Dort wird versucht, den jeweilige LISP-Ausdruck auszuwerten, und das
Resultat wird über ped::apply-token an den Parser gesendet.
Das sieht in unserem Beispiel so aus:
:LINE
wertet auf sich selbst aus, wird daher unverändert weitergegeben.
Der Parser wartet auf eine Möglichkeit, das Nonterminal line abzuleiten.
Mit dem nun angekommenen Schlüsselwort aktiviert er daher die Regel 6.3.
Diese akzeptiert nach dem Schlüsselwort zwei Punkte und ist
- mit allen semantischen Regeln - so definiert:
;; specify syntax and actions for interactively creating a line (newrule "line" ; the nonterminal solved by this rule NIL ; no explicit test function '(:LINE ; this terminal-keyword in input-stream ; triggers this rule (mode "Enter first point of line" ("first point" "(or (point %G) :IGNORE)") ("first point" "(or (point %G) :IGNORE)") ("" "") #'ped::elastic-point #'ped::elastic-point NIL NIL) "point" ; nonterminal to get first point - don't care how ; result has index 3 (mode "Enter second point of line" ("second point" "(or (point %G) :IGNORE)") ("second point" "(or (point %G) :IGNORE)") ("cancel" ":CANCEL") #'ped::elastic-line #'ped::elastic-line ($ 3) NIL) "point" ; nonterminal to get second point - don't care how ; result has index 5 (line ($ 3) ($ 5)) ; access results of point nonterminals by ; $-Function with appropriate indices and ; generate the line from the two points ))
Das Schlüsselwort :LINE wird sofort akzeptiert, der Modus neu
konfiguriert (was sich aber in der Anzeige nicht auswirkt),
und ein Punkt erwartet.
Dieser wird sofort nachgeliefert als zweites Wort, das durch den Mausknopf
gesendet wurde:
(or (point -1 0) :IGNORE)
ped::eval-token wertet diesen Ausdruck schließlich aus. Wenn die
point-Funktion erfolgreich war
(was im zweidimensionalen Fall zu erwarten ist), dann ist
das Ergebnis ein CLS-Punkt. Der Parser akzeptiert ihn aufgrund der
Regel 6.4, legt ihn auf den Datenstapel und führt die zweite
mode-Funktion der line-Regel aus. Da keine weiteren Worte mehr
bereitstehen, kehrt der PED, nachdem er die neue Konfiguration realisiert hat,
in seine Warteschleife zurück.
Dem Benutzer präsentiert sich daher, wenn er nun die Maus bewegt, etwa das
Bild nach Abb. 6.5. Eine Linie wird nun ständig zwischen dem ersten
Punkt und dem Mauszeiger geführt. Die interne Konfiguration des linken
Mausknopfes ist jetzt
"(or (point %G) :IGNORE)"
Wenn der Benutzer wiederum den linken Mausknopf drückt, dann wird ähnlich wie beim ersten Mal der Punkt generiert und an den Parser geschickt. Dieser akzeptiert ihn, legt in auf den Datenstapel, und führt gemäß der Regel den Ausdruck (line ($ 3) ($ 5)) aus. ($ 3) wird durch das Resultat des dritten Elementes, also den ersten Punkt, ersetzt, und ($ 5) durch den zweiten. Es bleibt also der Aufruf der Funktion line mit den beiden Punkten als Parameter - das Ergebnis ist die gewünschte Linie, die zwar hier nicht mehr benötigt wird, aber als Seiteneffekt generiert wurde.
Regel 6.3 ist damit erledigt, und wir stehen wieder in der Bearbeitung von line-mode.
Abbildung 6.5: Sichtbare Konfiguration des PED während
Ausführung der Regel line
Einen Punkt in zweidimensionalen Geometrien erhält man, wie gerade gezeigt,
mit
"(point %G)"
.
Während der
Vorverarbeitung erfolgt der Funktionsaufruf (point 1.2 3.4), der
einen Punkt an dieser Position liefert.
Eine andere interessante Art der Vorverarbeitung geschieht mit Funktionen
zum Einfangen (snap) vorhandener Daten. "(snap-line %g)"
versucht mit den resultierenden Koordinaten eine Linie zu finden. Wenn die
Funktion entsprechend definiert ist, funktioniert dies in beliebig
dimensionalen Räumen.
Funktionen zum Einfangen liefern in dem Fall, daß kein Objekt getroffen wurde,
NIL. Um zu verhindern, daß der Parser in diesem Fall Fehler erkennt
oder diesen Wert als gültig ansieht und die Regel weiter abarbeitet,
kann auf das Schlüsselwort :IGNORE ausgewichen werden, damit
der Benutzer die Eingabe wiederholen kann.
"(or (snap-line %g) :IGNORE)"
ist eine entsprechende Konfiguration.