Lade...
 

Pfad

Path

Path(Filter::Zugriffsausdruck)

Mit Path kann ein Zugriffspfad für SetFormat als Literal auf den Stack gebracht werden. Gegenüber des Zugriffsausdrucks im String hat dies die Vorteile, dass:

  • Die Eclipse-IDE Syntax-Highlighting, Parsing und Auto-Vervollständigung für Ausdrucke in Path() anbieten kann
  • Innerhalb von Providern: Der Kontext für anonyme Prozeduren wird an der Stelle festgelegt, an der das Path-Literal erzeugt wird und nicht beim Aufruf von SetFormat

 

197507

Path legt dabei ein Objekt vom Typ CX_PATH_LITERAL auf den Stack. Das Objekt hält intern zwei Repräsentationen des Zugriffspfades, die originale und eine übersetzte Form. Ein CX_PATH_LITERAL kann anstelle des Zugriffsausdrucks in SetFormat, ClearFormat und SetSort verwendet werden und ist nicht persistierbar und lässt sich nicht wie reguläre Strings verändern. Über den Befehl String erhält man einen String, der dann per Execute wieder zu dem gleichen CX_PATH_LITERAL geparst werden kann.

Beispiel

Path(CX_PERSON::call({ Copy(firstName) String("Person(%s)") })) //-> Path(CX_PERSON::...) String //-> "Path(CX_PERSON::call({ Copy(firstName) String(\"Person(%s)\") }))" Execute //-> Path(CX_PERSON::call({ Copy(firstName) String("Person(%s)") }))

Beispiel

[ Path(CX_CLASS::uniqueID) HEADER "Id "] SetFormat [ Path(CX_CLASS::call({ Drop "42" })) Header "42" ] SetFormat

 

Vor Dll-Version 197507 wurde Path() zur Parse-Zeit in einen String übersetzt und alle anonymen Prozeduren in das aktuelle Modul kompilliert. Das hatte zur Folge, dass anonyme Prozeduren in Zugriffsausdrücken dazu geführt haben, dass der Zugriffsausdruck nicht über Modulgrenzen hinweg verwendet werden konnte (ein Provider konnte eine Liste nicht mit Formaten befüllen) und zum anderen GetFormat nur einen kompillierten Zugriffsausdruck lieferte, der nur innerhalb eines Moduls und einer ClassiX-Instanz gültig war.

Ausführungskontext

Grundsätzlich bestimmt die Liste, in die der Zugriffsausdruck per SetFormat eingefügt wurde, den Modulkontext in dem der Ausdruck ausgewertet wird (Ausnahme: Gebundene Pfadliterale). Ein call(proc) bezieht sich also immer auf die Prozedur proc aus dem Modul des Listen-Widgets, auch wenn das Path-Literal in einem anderen Modul (z.b einem Provider) erzeugt wurde. Auf diese Weise kann ein Provider die Liste eines anderen Moduls mit Zugriffsausdrücken befüllen, die sich auf das Listen-Modul beziehen.

Wird der Zugriffsausdruck per Copy(STACK) ausgewertet, dann bestimmt die Quelle des CX_PATH_LITERAL-Objekts den Auswertungskontext. Wurde das Path-Objekt über GetFormatVector aus einer Liste zurückgegeben, dann ist die Liste der Auswertungskontext dieses Zugriffspfads. Wird ein Pfad per Copy(STACK) ausgewertet, der einfach als Literal auf den Stack gelegt wurde, wird dieser in dem Kontext ausgeführt, in dem dieses Literal erzeugt wurde.

Sollte sich ein solcher gebundener Zugriffsausdruck bei der Auswertung auf das Widget beziehen (Bsp: Path(CX_CLASS::call({Widget Copy(txt)})), dann ist es ein Fehler, wenn dieses Path-Objekt ausgewertet wird, nachdem das Widget geschlossen wurde.

Beispiel (Auswertungskontext von Prozeduren)

Module(listModule) [ INITIALIZE: Widget(win,list) listProvider::SetListBox Define(GetNumber) Drop 42 ; ] ... Module(listProvider) [ Define(SetListBox) LocalVar(list) -> list [ Path(CX_CLASS::uniqueID) HEADER "uniqueID" ] list SetFormat(STACK) [ Path(CX_CLASS::call(GetNumber)) HEADER "Number" ] list SetFormat(STACK) ; ]

 

Dieses Verhalten kann in einigen Fällen nicht erwünscht sein. Über call(provider::proc) kann man in einem Zugriffsausdruck gezielt die Prozedur eines bestimmten Providers aufrufen. Sollte ein Format-Provider-Modul eigene Prozeduren referenzieren wollen, dann muss diesen das Providertag vorangestellt werden. So kann gezielt gesteuert werden, ob eine Prozedur aus dem Provider oder dem Zielmodul aufgerufen werden soll.

Diese Syntax ist nur für Provider zulässig. Prozeduren aus regulären Modulen können auf diese Weise nicht aufgerufen werden.

Beispiel (Expliziter Prozedurkontext)

Module(listModule) [ INITIALIZE: Widget(win,list) listProvider::SetListBox ] ... Module(listProvider) [ Define(GetNumber) Drop 21 ; Define(SetListBox) LocalVar(list) -> list [ Path(CX_CLASS::uniqueID) HEADER "uniqueID" ] list SetFormat(STACK) [ Path(CX_CLASS::call(listProvider::GetNumber)) HEADER "Number" ] list SetFormat(STACK) // Alternativ (siehe nächster Abschnitt) [ Path(CX_CLASS::call({ GetNumber})) HEADER "Number" ] list SetFormat(STACK) ; ]

 

Anonyme Prozeduren in Zugriffspfaden

Anonyme Prozeduren in Zugriffsausdrücken sollten grundsätzlich in dem Modul ausgeführt werden, in welchem sie definiert sind. Dies würde jedoch erfordern, das das Path-Literal und die Formate der Liste das transiente Modul-Objekt, in welchem sie definiert wurden, kennen. Das wiederum würde es unmöglich machen, ein solches Format in GetFormat in eine stringifizierte Form zu überführen, die sie potenziell in einer anderen ClassiX-Instanz mit korrektem Kontext wieder geladen werden soll.

Das Problem wird wie folgt gelöst:

Der Ausdruck in Path() wird zur Laufzeit geparsed, wenn das Path-Literal auf den Stack gelegt wird. Alle frei stehenden anonymen Prozeduren (Bsp: call({ Drop "Test" })) werden in dem aktuellen Modul als reguläre Prozeduren (z.B unter dem Namen lambda_A) definiert und entsprechend im übersetzten Zugriffspfad so ersetzt (Bsp: call(lambda_A)). Das CX_PATH_LITERAL-Objekt merkt sich den Kontext, in dem es angelegt wurde und der so übersetzte Zugriffsausdruck wird bei Copy(STACK) immer in diesem Kontext ausgeführt.
SetFormat überschreibt den Kontext mit dem Kontext des Widgets, auf welches SetFormat angewandt wird. Dadurch wird sichergestellt, dass die Zugriffspfade grundsätzlich immer im Kontext des Listenwidgets ausgewertet werden. Falls der Zugriffspfad eine anonyme Prozedur enthält, dann wird diese in SetFormat in dem Modul des Zielwidgets übersetzt.

Achtung: Diese Übersetzung führt dazu, dass im Gegensatz zu normalen anonymen Prozeduren, die anonymen Prozeduren innerhalb von Path() kein Closure sein können (dafür wäre wieder transienter Zustand notwendig) und damit können diese anonymen Prozeduren keine lokalen Variablen aus der umgebenden Prozedur referenzieren.


Sollte das aktuelle Modul ein Provider-Modul sein, dann werden im original Zugriffsausdruck (den man mit String und GetFormat erhält) und im übersetzten Ausdruck jeder frei stehenden anonymen Prozedur das Provider-Tag vorangestellt. Beispiel:

call({ Drop "Test" }) --> call(provider::{ Drop "Test" }) call(lambda_A) --> call(provider::lambda_A)

 

Anonyme Prozeduren, denen ein Provider-Tag vorangestellt ist, werden in dem angegebenen Provider-Modul kompilliert und wie reguläre Provider-Aufrufe in Zugriffspfaden ausgeführt.

Der Übersetzungsvorgang von Path() dient nur dem Zweck, anonyme Prozeduren im korrekten Kontext zu generieren und im Zugriffsausdruck korrekt zu ersetzen. Hierbei wird nicht geprüft, ob der Zugriffsausdruck generell auswertbar ist. Wird in einem call(proc) eine unbekannte Prozedur aufgerufen, dann wird dieser Fehler erst bei der Auswertung des Pfades auftreten, also beim Anzeigen der entsprechenden Liste. Der Parser führt lediglich einen rudimentären Syntax-Check für den Ausdruck im Path-Literal durch, sodass syntaktisch falsche Ausdrücke bereits zur Parse-Zeit entdeckt werden.

Wird SetFormat mit einem String ausgeführt, dann unterscheidet sich das Verhalten darin, dass der eben beschriebene Übersetzungsvorgang erst bei der Ausführung von SetFormat stattfindet. Intern wird ein String bei der Ausführung von SetFormat in ein Path-Literal umgewandelt. Deshalb liefert GetFormat das Format immer als Path-Literal unabhängig davon, wie es gesetzt wurde.

Gebundene Pfadliterale

208336

Gebundene Pfadliterale sind ein neues Feature, mit dem die Kontextregeln genauer gesteuert werden können. Im Normalfall wird ein Pfadliteral, das per GetFormatVector aus einer Liste geladen wird und per Copy(STACK) ausgewertet wird, in dem Widget-Kontext der Liste ausgewertet, in dem das Pfadliteral definiert wurde. Das heißt, dass alle Modulvariablen und Prozeduren und der Code der anonymen Prozeduren in dem Modul des Widgets ausgeführt werden, damit die Auswertung das gleiche Ergebnis wie in der Liste liefert, unabhängig davon, wo das Pfadliteral ausgewertet wird.

Wird ein solches Pfadliteral jetzt aber per SetFormat in eine andere Liste (in einem anderen Modul) eingefügt, dann greift jedoch die Regel, dass das Zielwidget den Auswertungskontext der Pfadliterale vorgibt. Das führt dann dazu, dass ein Modul andere Ergbnisse erhält, wenn es Pfadliterale aus einer fremden Liste per Copy(STACK) auswertet und andere Ergebnisse erhält, wenn diese Pfadliterale dann in eine eigene Liste gesetzt werden. Womöglich werden dann Prozeduren nicht korrekt gefunden.

Das Problem wird durch kontextgebundene Pfadliterale behoben. Per GetBoundFormatVector erhält man ebenfalls das Format einer Liste, nur dass in dem Fall alle Pfadliterale der Formate an das Quellwidget gebunden sind. Dies wird durch Path(...,BOUND) in der ClassiX-Shell signalisiert. Ein solches Pfadliteral behält seinen Quellkontext auch bei einem SetFormat noch bei und die Prozeduren und Modulvariablen werden immer im Kontext des Quellwidgets ausgewertet. Das Pfadliteral behält den gebundenen Zustand auch nach dem SetFormat bei und GetFormatVector würde ein solches Pfadliteral wieder als gebundenes Pfadliteral rausgeben. 

Ob ein Pfadliteral gebunden ist oder nicht, kann über die MA-Funktion BoundMA() ausgelesen und jederzeit verändert werden. Per ResetContext kann unabhängig davon, ob das Pfadliteral gebunden ist oder nicht der Kontext des Literals mit dem aktuellen Kontext überschrieben werden. Der Zustand, ob das Literal gebunden ist oder nicht, wird dabei nicht verändert.

Ein gebundenes Pfadliteral kann auch direkt als Literal erstellt werden (Bsp: Path(CX_CLASS::call(ModuleProc), BOUND)). In dem Fall ist das Pfadliteral an den Kontext gebunden, in dem es erzeugt wurde.

 

Wichtig: Ein gebundenes Pfadliteral hält den Ausführungskontext als transienten Zustand in dem Objekt. Enthält eine Liste gebundene Pfadliterale, dann können diese Formate nicht per GetFormat aus der Liste geladen, in stringifizierter Form zwischengespeichert und später per SetFormat wieder korrekt geladen werden. Die Pfade würden dann in dem Kontext ausgeführt werden, in welchem der String per Execute wieder in ein gebundenes Pfadliteral übersetzt wird.

Zusammenfassung Auswertungskontext

Da die Regeln recht komplex sind, folgt hier tabellarische Zusammenfassung der Auswertungskontexte.

A und B sind hierbei reguläre Module und P ist ein Provider-Modul. 
A.list und B.list stehen für Listenwidgets aus den jeweiligen Modulen.
A.list ⟹ B steht dann zum Beispiel dafür, dass der Pfad aus der Liste aus Modul A stammt (GetFormatVector) und in Modul B ausgewertet wird.
A ⟹ B steht dann dafür, dass ein Pfad in einem Modul erzeugt wurde und an B zurückgegeben wurde (z.B durch SendMsg) und in B ausgewertet wird.
A.list ⥂  B.list steht dafür, dass ein Zugriffspfad als gebundener Zugriffspfad (GetBoundFormatVector) aus der Liste aus Modul A stammt und per SetFormat in die Liste von Modul B gesetzt und dort ausgewertet wird.
x ist eine Modulvariable, proc eine Prozedur.

 

Herkunft \ Zugriffspfad
Path(var(x))
Path(call(proc))
Path(call({ Module }))
Path(call({ Widget }))
Path(call({ proc }))
Path(call({ x }))
A ⟹ A A.x A.proc A A.win2 A.proc A.x
A.list ⟹ A A.x A.proc A A.list A.proc A.x
A.list ⥂ A A.x A.proc A A.list A.proc A.x
B ⟹ A B.x B.proc B - | B.win3 B.proc B.x
B ⥂ A B.x

B.proc

B - | B.win3 B.proc B.x
B.list ⟹ A B.x B.proc B B.list B.proc B.x
B.list ⥂ A B.x B.proc B B.list B.proc B.x
P ⟹ A P.x P.proc P A.win1 P.proc P.x
P ⥂ A P.x P.proc P A.win1 P.proc P.x
A ⟹ A.list A.x A.proc A A.list A.proc A.x
A.list2 ⟹ A.list A.x A.proc A A.list A.proc A.x
A.list2 ⥂ A.list A.x A.proc A A.list2 A.proc A.x
B ⟹ A.list A.x A.proc A A.list A.proc A.x
B ⥂ A.list B.x B.proc B - | B.win3 B.proc B.x
B.list ⟹ A.list A.x A.proc A A.list A.proc A.x
B.list ⥂ A.list B.x B.proc B B.list B.proc B.x
P ⟹ A.list A.x A.proc P A.list P.proc P.x
P ⥂ A.list P.x P.proc P A.win1 P.proc P.x

Ergänzende Hinweise zur Tabelle

A.win1=  Hier wurde im Kontext von A.win die Providerprozedur aufgerufen, die den Zugriffspfad erzeugt hat. Dadurch wurde zwar der Modulkontext für den Aufruf der anonymen Prozedur auf den Provider gebunden, da sich bei einem Prozeduraufruf (auch einem Providerprozeduraufruf) der Widget-Kontext aber nicht ändert, wurde der Widget-Kontext des Zugriffspfads insgesamt an das aktuelle Kontextwidget gebunden, was in dem Fall A.win war. 
Der Eintrag A.win taucht nur bei Zugriffspfaden auf, die von P stammen, nicht aber von B, weil nur bei Provider-Modulen eine Prozedur über Modulgrenzen hinweg aufgerufen werden kann. Will man einen Zugriffspfad von Modul B erhalten, dann muss dies per SendMsg(...) geschehen und der Empfänger dieser Message definiert dann den Widget-Kontext.

A.win2 = Hier wurde eine Prozedur aus dem Modul A im Widget-Kontext A.win aufgerufen, weshalb dieser auch an das Pfad-Objekt gebunden wurde.

- | B.win3 = Hier wurden die Zugriffspfade per SendMsg(...) aus dem Modul B geholt. Das heißt, dass der hier gebundene Widget-Kontext davon abhängt, ob die Message vom Modul B selbst empfangen wurde (-) oder von einem Widget im Modul B (B.win).

 

Die leicht rötlich hinterlegten Zellen erscheinen auf den ersten Blick vielleicht unlogisch, folgen aber direkt aus den Regeln der Kontextbindung und der Behandlung von anonymen Prozeduren aus Provider-Modulen. Die anonyme Prozedur erhält einen Provider-Präfix und damit entspricht der Modulkontext innerhalb der anonymen Prozeduren dem Provider-Modul P. SetFormat in A.list setzt aber den Auswertungskontext des gesamten Pfads auf (Modul = A / Widget = A.list). Dadurch werden alle Ausdrücke außerhalb von anonymen Prozeduren (also var(x) und call(proc)) innerhalb dieses Kontexts ausgeführt.

 

 

Methodenverzeichnis von CX_PATH_LITERAL (MDI)

Funktion MA Parameter Rückgabe Kurzbeschreibung
BoundMA
208336
* TRUE/FALSE   Lese- & Schreibzugriff auf das Bound-Flag des Pfadliterals.
ResetContext
206803
      Setzt den Ausführungskontext des CX_PATH_LITERAL-Objekts auf den aktuellen Ausführungskontext.

 

Siehe auch: