Ansicht
Dokumentation

CL_ABAP_SYNTAX_TREE - Syntaxbaum eines ABAP-Programms

CL_ABAP_SYNTAX_TREE - Syntaxbaum eines ABAP-Programms

BAL_S_LOG - Application Log: Log header data   ABAP Short Reference  
Diese Dokumentation steht unter dem Copyright der SAP AG.
SAP E-Book

Funktionalität

Allgemeines

Mit der Klasse können Abstrakte Syntaxbäume (Abstract Syntax Trees, ASTs) für ABAP-Quelltexte erzeugt werden. Es wird derselbe (sogenannte "rnd"-) Parser benutzt wie in der für Code Completion und Token-Qualifikation angebotenen Klasse CL_ABAP_PARSER.

Die AST-Abdeckung der "rnd"-Grammatik für ABAP ist derzeit noch partiell. ABAP-Ausdrücke und ein Großteil der operationalen ABAP-Anweisungen sind abgedeckt, aber z.B. deklarative Anweisungen noch nicht. Solche Code-Anteile werden im AST durch sog. "Dirty"-Knoten angezeigt.

ASTs können in effizienter Darstellung als Tabelle vom Typ T_NODEINFOTAB erhalten werden. Die Baumstruktur ist dabei über die Spalte DEPTH (Baumtiefe) abgebildet. Die AST-Namen (z.B. "MCall" für einen Methodenaufruf) sind in dieser Darstellung nicht direkt abgelegt, sondern nur als Indizes (Spalte NAME_INDEX). Diese Namensindizes sind über das Klassenattribut G_ASTNAMES zu Strings auflösbar, etwa mit einer Tabellenselektion wie folgt:
  cl_abap_syntax_tree=>g_astnames[ -name_index ]-name
oder umgekehrt:
  cl_abap_syntax_tree=>g_astnames[ key kn name = `MCall` ]-index
Die Tabelle wird im Klassenkonstruktor aus dem Kernel aufgebaut; die Daten kommen aus der zum Kernel gehörigen ABAP-Grammatikdatei. Die Zuordnung Name - Index ändert sich regelmäßig (mit jeder Änderung der ABAP-Grammatik) und darf daher keinesfalls als fest angenommen werden.
Die weiteren Komponenten von T_NODEINFO werden weiter unten erklärt.

Alternativ können ASTs als XML serialisiert werden.

Erzeugung eines ASTs

Mit der Methode CREATE wird ein AST als Instanz der Klasse erzeugt. Die Methode erwartet als Eingabe zwei Tabellen mit Statements (Zeilentyp SSTMNT) und Tokens (Zeilentyp STOKESX), wie sie vom ABAP-Befehl SCAN ABAP-SOURCE aufgebaut werden.

Durch Versorgung der Parameter für Beginn- und Ende-Statement-Index kann der zu parsende Code-Bereich eingeschränkt werden, z.B. auf ein einzelnes Statement. Bei statement-weiser Verarbeitung kann jedoch kein AST aufgebaut werden, der blockbildende Konstrukte wie IF oder DO adäquat wiedergibt. Wenn man an solchen Strukturen interessiert ist, sollte man also auf kompletten Übersetzungseinheiten arbeiten und beim SCAN die Option WITH INCLUDES verwenden, wodurch INCLUDE-Instruktionen und Makro-Aufrufe aufgelöst werden. Ohne diese Vorverarbeitung sind statement-übergreifende ASTs im allg. strukturelll nicht korrekt (z.B. kann ein Makro ein block-öffnendes Konstrukt wie IF ohne das zugehörige ENDIF enthalten).

Die aufgelösten INCLUDEs und Makros sind im AST nicht mehr sichtbar, außer es wird der Parameter WITH_LEVEL_NODES gesetzt: Dann werden AST-Knoten an ihrer Stelle eingefügt. Diese Option ist z.B. sinnvoll, wenn man (entgegen der obigen Empfehlung) SCAN ohne die Option WITH INCLUDES verwendet hat.

Der Parameter DISCARD_STREAM steuert, ob der für den "rnd"-Parser aufgebaute Eingabestrom nach dem Parsen verworfen oder beibehalten werden soll. Wenn der AST im folgenden nur mit der Methode GET_NODES inspiziert werden soll, kann der Strom verworfen werden. Die Beibehaltung des Stroms ist hingegen sinnvoll, wenn der AST als XML gerendert werden soll, denn das XML-Rendering bezieht Token-Informationen aus dem "rnd"-Eingabestrom, nicht aus der SCAN-Tabelle. (Wird nach dem Verwerfen des Stroms gerendert, so besteht das XML nur aus AST-Namen ohne Token-Bezug, d.h. es fehlen z.B. Bezeichner.)

Der Parameter QUALIFY_TOKENS steuert, ob die Tokens-Tabelle im ausgewählten Bereich auch gleich qualifiziert werden soll. Dies erspart den Aufruf der Funktion RS_QUALIFY_ABAP_TOKENS_STR, wenn sowohl AST-Aufbau als auch Token-Qualifikation gewünscht sind. (Bei der Token-Qualifikation wird die Spalte TYPE mit Werten aus der Typgruppe SANA belegt, z.B. SANA_TOK_FIELD für ABAP-Felder.)

Serialisierung als XML

Die Methoden TO_XML und TO_XML_WITH_SRC serialisieren den AST oder ausgewählte Knoten als XML. Sie unterscheiden sich dadurch, dass TO_XML alle Knoten in einen einzigen XML-Strom schreibt, TO_XML_WITH_SRC hingegen in eine Tabelle von Paaren aus XML-Darstellung und zugehörigem Quelltext. Falls mit der Methode TO_XML mehrere AST-Wurzelknoten gerendert werden sollen, muss man an dem übergebenen XML-Writer-Objekt zuvor ein XML-Wurzelelement geöffnet haben (das man nach der Methode schließt), weill ein gültiges XML-Dokument genau einen Wurzelknoten haben muss.

Ein AST kann durchaus mehr als ein Wurzelelement haben (z.B. weil als Eingabe eine Sequenz von nicht geschachtelten Anweisungen verwendet wurde). Ebenso kann die Seriallisierung mehrere Wurzelknoten liefern, weill darüberliegende AST-Knoten herausgefiltert wurden (siehe Dokumentation der Parameter NAME_FILTER und RULE_PROPS bei der Methode GET_NODES).

Der Parameter WITH_ORPHAN_TEXT steuert, ob durch AST-Knoten nicht abgedeckte ("verwaiste") Quelltext-Abschnitte in den XML-Strom aufgenommen werden.

Der Parameter DISCARD_STREAM, kann wie oben beschrieben, gesetzt werden, wenn keine weitere XML-Serialisierung vorgesehen ist.

Aufbau der AST-Knoteninformation

Die wesentliche Methode zur AST-Verarbeitung ist GET_NODES. Entweder der AST als Ganzes (mit Parameter NODE_INDEX = 0) oder ab einem bestimmten Knoten (mit Parameter NODE_INDEX > 0) kann zurückerhalten werden.

Der Parameter NODE vom Typ T_NODEINFO kann verwendet werden, um nur einen einzelnen AST-Knoten abzuholen. Wird hingegen der tabellenwertige Parameter NODES übergeben, so wird die Tabelle mit allen gefundenen Knoten gefüllt.
Der Typ T_NODEINFO hat, neben INDEX (Knotenindex), DEPTH (Baumtiefe) und NAME_INDEX (AST-Namensindex), mehrere Komponenten, die für Blattknoten den Bezug zu den SCAN-Tokens herstellen (innere AST-Knoten haben aus Effizienzgründen keinen Token-Bezug):

  • TOKEN_INDEX ist der Index des (ersten) Tokens, das dem AST-Knoten entspricht.
  • Wenn MULTI_TOKEN gleich 0 ist, so entspricht dem AST-Knoten entweder das gesamte Token oder ein durch Offset TOKEN_OFF und Länge TOKEN_LEN bezeichneter Teil davon.
  • Wenn MULTI_TOKEN ungleich 0 ist, so entsprechen dem AST-Knoten alle Tokens von TOKEN_INDEX bis TOKEN_INDEX + TOKEN_LEN - 1.
  • In TOKEN_TYPE steht der Tokentyp gemäß Token-Qualifikation.

Nicht jedem syntaktischen Konstrukt ist ein dedizierter AST-Name zugeordnet. So haben etwa alle arithmetischen Operatoren den generischen AST-Namen "Op". Um welchen Operator es sich handelt (z.B. "+" oder "*"), kann dem zugeordneten SCAN-Token entnommen werden. Auch ein einstelliger relationaler Operator wie "IS NOT ASSIGNED" wird durch einen allgemeinen AST-Namen dargestellt, wobei MULTI_TOKEN = 3 anzeigt, dass dem Knoten mehrere Tokens entsprechen.

Die Komponente RULE_PROP von T_NODEINFO enthält die für den AST-Knoten gültigen Regeleigenschaften als "BIT-OR" der binären Konstanten RP_.... Solche Regeleigenschaften ergeben sich aus der ABAP-Grammatik. Folgende Eigenschaften sind unterstützt:

  • RP_IDENT: Bezeichner. Z.B. haben in einer Anweisung "X = Y." beide Seiten der Zuweisung diese Eigenschaft.
  • RP_WRITE_ACCESS: Schreibzugriff. Z.B. für die linke Seite einer Zuweisung gesetzt, aber nicht für die rechte.
  • RP_STATEMENT: ABAP-Anweisung (im Unterschied zu Ausdrücken und anderen Anweisungsbestandteilen).
  • RP_OBSOLETE: Eine ABAP-Konstruktion, die (in diesem Release) obsolet ist.
  • RP_PRIVATE: Reservierte ABAP-Konstruktion (nicht für den allgemeinen Gebrauch).
  • RP_DIRTY_PART: AST resultiert aus einem partiell abgedeckten Teil der ABAP-Grammatik (z.B. Berechnungsausdruck innerhalb einer ansonsten nicht abgedeckten Anweisung).
  • RP_DIRTY_UNCOVERED: Knoten für eine (noch) gar nicht AST-mäßig abgedeckte ABAP-Anweisung.

Navigation im AST

Mit dem Parameter AXIS der Methode GET_NODES wird, in Anlehnung an die Navigationsachsen in XPath, der Suchbereich selektiert. Zulässige Werte sind die Konstanten AX_...:

  • AX_CHILD: Direkte Unterknoten (Kinder) des Ausgangsknotens.
  • AX_DESCENDANT: Knoten im Unterbaum des Ausgangsknotens, diesen ausgeschlossen.
  • AX_DESCENDANT_OR_SELF: Knoten im Unterbaum des Ausgangsknotens, diesen eingeschlossen.
  • AX_PARENT: Direkter Oberknoten des Ausgangsknotens.
  • AX_ANCESTOR: Echte Oberknoten des Ausgangsknotens.
  • AX_ANCESTOR_OR_SELF: Oberknoten des Ausgangsknotens, diesen eingeschlossen.
  • AX_FOLLOWING_SIBLING: Folgeknoten des Ausgangsknotens auf der gleichen Ebene.
  • AX_PRECEDING_SIBLING: Vorgängerknoten des Ausgangsknotens auf der gleichen Ebene.
  • AX_FOLLOWING: Alle Knoten hinter dem Ausgangsknoten.
  • AX_PRECEDING: Alle Knoten vor dem Ausgangsknoten.

Die Reihenfolge der gefundenen Knoten in der Ergebnistabelle wird durch die Navigationsachse bestimmt. Bei Rückwärtsachsen ("Parent", "Ancestor", "Preceding", "Preceding-Sibling") ist dies die umgekehrte Reihenfolge im AST.

Bei den Vorwärtsachsen "Descendant" und "Following" kann die Baumstruktur folgendermaßen aus der Spalte DEPTH erschlossen werden:

  • Die Unterknoten eines Knotens k mit Tiefe d sind alle folgenden Zeilen mit Tiefe > d.
  • Der Elternknoten von k ist die (rückwärts gehend) erste Zeile mit Tiefe = d - 1.
  • Der Folgeknoten (rechte Geschwisterknoten) von k ist die erste Folgezeile mit Tiefe d.

Filter: Regeleigenschaften

Die entlang der Navigationsachse gefundenen Knoten können gefiltert werden. Der Filter-Parameter RULE_PROPS enthält inklusive (Komponenete INCLUSIVE) und exklusive (Komponente EXCLUSIVE) Regeleigenschaften jeweils als "BIT-OR". Die Komponente INCL_OP bzw. EXCL_OP gibt an, ob die Eigenschaften "AND"- verknüpft (Wert FO_AND ) oder "OR"-verknüpft (Wert FO_OR) werden sollen. Ein (inklusiver) Filter für Bezeichner mit Schreibzugriff wäre also etwa so zu definieren:
  value cl_abap_syntax_tree=>t_ruleprop_filter(
    inclusive = cl_abap_syntax_tree=>rp_ident
         bit-or cl_abap_syntax_tree=>rp_write_access
    incl_op = cl_abap_syntax_tree=>fo_and ).
Um obsolete und reservierte Konstrukte herauszufiltern, verwendet man:
  value cl_abap_syntax_tree=>t_ruleprop_filter(
    exclusive = cl_abap_syntax_tree=>rp_obsolete
         bit-or cl_abap_syntax_tree=>rp_private
    excl_op = cl_abap_syntax_tree=>fo_or ).

Filter: AST-Namen

Orthogonal dazu kann über den Parameter NAME_FILTER die Menge der gefundenen Knoten über ihren AST-Namen eingeschränkt werden. Ein Namensfilter besteht aus einer Menge von Namensindex-Intervallen. Im allg. erhält man eine solche Tabelle über die Methode GET_NAME_FILTER. Diese Methode kann mit einer Range-Tabelle über AST-Namen (Parameter NAME_RANGE) und/oder einem Kategorien-Filter (Parameter CATEGORIES) aufgerufen werden. AST-Kategorien sind eine in der ABAP-Grammatik verankerte Gruppierung von AST-Namen. Die Kennungen der verfügbaren Kategorien sind als Konstanten CT_... hinterlegt:

  • CT_CONTROL_FLOW: Alle Kontrollfluss-Anweisungen außer Aufrufen; z.B. DO, IF.
  • CT_INVOCATION: Alle Aufrufe, z.B. Methoden- oder Funktionsaufruf.

Ein Kategorienfilter ist ähnlich aufgebaut wie ein Regeleigenschaften-Filter. Um z.B. alle Kontrollfluss- und Aufruf-Konstrukte inklusiv zu filtern, verwendet man:
  value cl_abap_syntax_tree=>t_category_filter(
    inclusive = cl_abap_syntax_tree=>ct_control_flow &&
                cl_abap_syntax_tree=>ct_invocation
    incl_op = cl_abap_syntax_tree=>fo_or ).

Aus Performance-Gründen empfiehlt es sich, komplexe Filter nicht mehrfach aufzubauen, sondern an geeigneter Stelle zu berechnen und zu speichern (z.B. Klassenkonstruktor, statische Attribute).

Der Parameter FILTER_MODE von GET_NODES bestimmt das Zusammenspiel von Navigationsachse (AXIS) und Filter-Parametern. Die Konstanten FM_... haben folgende Bedeutung:

  • FM_RECURSIVELY: Die Suche wird entlang der Achse rekursiv fortgesetzt. Ist z.B. die Achse "Descendant" und ein inklusiver Filter auf Kontrollfluss-Anweisungen gesetzt, so werden auch geschachtelte DO und IF zurückgeliefert.
  • FM_INCLUDE_SUB: Wird ein Knoten gefunden, der die Filter erfüllt, so wird sein gesamter Unterbaum zurückgeliefert.
  • FM_EXCLUDE_SUB: Es werden nur entlang der Achse gefundene Knoten ohne ihren Unterbaum zurückgeliefert. Im Beispiel wären geschachtelte DO und IF nicht in der Ergebnismenge enthalten.

Man beachte, dass die Baumtiefe (Komponente DEPTH von T_NODEINFO) in folgenden Fällen nicht herangezogen werden kann, um die Schachtelung von Knoten zu entscheiden:

  • Bei Navigation entlang Rückwärtsachsen.
  • Bei Anwendung von Filtern auf tiefen Achsen ("Descendant", "Following"), außer es wird der Filtermodus FM_INCLUDE_SUB verwendet.

In diesen Fällen sollte die Schachtelung entweder irrelevant sein, oder sie ist bei Bedarf z.B. durch Navigation zu "Parent" zu rekonstruieren.

Beziehungen

Beispiel

Hinweise

Weiterführende Informationen






rdisp/max_wprun_time - Maximum work process run time   ABAP Short Reference  
Diese Dokumentation steht unter dem Copyright der SAP AG.

Length: 23120 Date: 20240420 Time: 042604     sap01-206 ( 267 ms )