XML <-> Code Transformation

The Gunner

Ensign
Registriert
Aug. 2012
Beiträge
168
Hallo

Ich habe hier XML Files, welche den Abstract Syntax Tree von Code darstellen. Die XML files habe ich erfolgreich in einen DOM-tree geparsed und kann diesen auch traversieren. Hier ist eine solche XML Datei:

http://img217.imageshack.us/img217/8365/xmlv.jpg

Diese XML Datei repräsentiert den folgenden Code (Eiffel):

prune_first (n: INTEGER)
do
prune (n, l)
end

Nun möchte ich zuerst das XML File wieder in Code zurückverwandeln. Das möchte ich machen, indem ich den Baum traversiere. Zudem gibt es noch eine Visitor Klasse (Visitor Pattern), bei der man Funktionen selber impelementieren kann, die man dann bei jedem Knoten aufrufen kann.

Mir ist nun aber nicht ganz klar, wie ich am besten vorgehen soll. Wie man im Beispiel sieht, sind alle Codestücke nur in Textelementen des XML Files vorhanden, ich könnte also alle Textelemente ausgeben, allerdings hätte ich da dann z.B. mit der Klammer () Probleme, da die Ausgabe dann prune()n,l anstatt von prune(n,l) wäre und zudem hätte ich dann alles auf einer Zeile und keine Einrückungen etc.

Wie würdet ihr da vorgehen? Die XML files werde ich übrigens später noch kürzen, so dass wirklich nur die wichtigen Nodes übrig bleiben.



Weiterhin würde ich auch gerne eine Transformation in die andere Richtung machen, also von Code (Feature = Methode oder Klasse) zu einem XML File. Man könnte da wohl einen parser generator verwenden.

Wie würdet ihr das aber ohne parser generator machen oder ist das zu aufwendig? Die erste Fragen oben (Umwandlung von XML nach Code) hat jetzt hier aber gerade Priorität. ;)
 
In welcher Sprache willst du das eigentlich machen?


Ich würde hier alle Informationen mit XPath auslesen. Das ist ja genau dafür gedacht, sich durch den Baum einer XML-Datei zu hangeln. Je nach Sprache wird man allerdings eine XPath-Bibliothek installieren müssen.

Edit:
Mir fällt gerade noch auf, dass du Trennzeichen wie das ":" im Methodenkopf und das Komma später im Code etwas unglücklich in XML transformierst.
Dafür würde ich einen Knoten "<ast:separator>" einführen, der die aufgezählten Elemente als Children hat.
"n: INTEGER" würde dann etwa zu so was:
Code:
<ast:separator type=":">
	<ast:operant>
		n
	</ast:operant>
	<ast:operant>
		INTEGER
	</ast:operant>
</ast:separator>
Das erlaubt auch Code auseinander zu nehmen, der zB so geschrieben ist:
Int a, b, c;

Oder hast du schon Logik drin, der in der Lage ist, diesen Fall zu behandeln?
 
Zuletzt bearbeitet:
Solche Klammerprobleme kannst relativ elegant mit Patterns lösen. D.h. du holst dir die Daten wie du es beschrieben hast und definierst z.B. ein Pattern das diese falsche Klammerung abdeckt. Für das jeweilige Pattern musst du dann noch eine passende Umformatierung definieren.

Aber vielleicht ist der Ansatz auch zu kompliziert. Daher wie e-Laurin es schon vorgeschlagen hat, einfach X-Path oder ähnliches verwenden. Mit Java ist das übrigens sehr einfach und die notwendigen Bibliotheken sind recht Benutzerfreundlich.
 
In welcher Sprache willst du das eigentlich machen?

Die Sprache ist Eiffel. Kennt hier wohl niemand. ;)

Ich würde hier alle Informationen mit XPath auslesen. Das ist ja genau dafür gedacht, sich durch den Baum einer XML-Datei zu hangeln. Je nach Sprache wird man allerdings eine XPath-Bibliothek installieren müssen.

Die XML Files werden bereits in einen DOM Tree geparsed, da brauche ich wohl XPath nicht mehr. Zudem gibt es wohl XPath für Eiffel nicht. ;) Mit dem Dom Tree habe ich auch einen Baum den ich traversieren kann (siehe weiter unten).

Edit:
Mir fällt gerade noch auf, dass du Trennzeichen wie das ":" im Methodenkopf und das Komma später im Code etwas unglücklich in XML transformierst.
Dafür würde ich einen Knoten "<ast:separator>" einführen, der die aufgezählten Elemente als Children hat.
"n: INTEGER" würde dann etwa zu so was:

Ich habe keinen eigenen Code to XML parser, die XML files bzw. der Parser sind mir vorgegeben, daher kann ich das nicht anpassen.

Solche Klammerprobleme kannst relativ elegant mit Patterns lösen. D.h. du holst dir die Daten wie du es beschrieben hast und definierst z.B. ein Pattern das diese falsche Klammerung abdeckt. Für das jeweilige Pattern musst du dann noch eine passende Umformatierung definieren.

Das tönt interessant, allerdings verstehe ich den Ansatz nicht ganz. Von Design Pattern habe ich aber schon gehört. ;) Vielleicht kannst du dein Ansatz ein klein wenig ausführen.


Wenn man alle Textelemente auf einer neuen Zeile ausgibt, kommt folgende Darstellung raus (Man beachte die enthaltenen Leerzeichen am Anfang gewisser Textelemente) :

Code:
prune_first
        (
                n:
INTEGER
        )
                do
                        prune (
n
                        ,
l
                        )
        end

Allerdings möchte ich eben folgendes erhalten:

Code:
prune_first (n: INTEGER)
     do
          prune (n, l)
     end

So sieht ein korrektes Feature (= Methode) in der Programmiersprache Eiffel aus. Mein Problem ist nun, wie ich vom DOM Tree eine solche Darstellung bekomme. Für den DOM Tree habe ich einen Parser. im DOM Tree sind alle Elemente des XML Files als Knoten vorhanden. Attribute und Textelemente gehören zu den jeweiligen Knoten (werden in Listen gehalten). Ich habe da v.a. folgende Probleme:

- Wie erkenne ich wann eine neue Zeile anfängt und wie kriege ich die Einrückungen korrekt hin. Deine Ausgabe oben ist ja von der Reihenfolge gleich wie meine Ausgabe, allerdings mit anderen Einrückungen und Zeilenumbrüchen.

- prune() ist das Textelement eines Knoten, wobei "prune(" das erste Textelement in der Liste ist und ")" das zweite. Würde ich nun einfach das Textelement des Knotens ausgeben hätte ich prune() etc. anstatt prune(n,l) etc.

Ich hoffe, dass du mir da vielleicht weiterhelfen kannst. :)

Der Hintergrund ist folgender: Ich habe erfolgreich einen Diff Algorithmus geschrieben, der auf den DOM Tree angewendet werden kann. Der Dom Tree soll nun in einem GUI als Code (wie oben) ausgegeben werden und die jeweiligen Differenzen markiert werden.

Das ergibt gleich noch eine kleine Frage: Ausgegeben werden ja nur Textelemente, allerdings hat es ja auch viele Elementknoten ohne Textelemente (z.B. <ast:feature_name>). Nun ist es ja z.B. möglich, dass nach Anwedung des diff Algorithmus ein Textelement z.B. als 'changed' markiert wird, das parent Element (welches keinen Text enthält) aber als 'added'. Soll dann nun der ausgegebene Text als 'changed' oder als 'added' markiert werden? Bzw. muss man da nur auf edit operationen bei Elementen mit Text schauen?


Ich möchte ja zudem noch unwichtige Knoten aus dem DOM Tree löschen (damit der diff alg schneller läuft). Da kann ich eigentlich alle internen Knoten löschen, welche kein Text enthalten und nur ein Kind haben, oder?
 
Also in vielen Programmiersprachen ist es so, dass du ein Zeilenumbruch nach bestimmten Syntaxelementen machst, in C z.B. nach ; { } und der letzten ) bei if's.
Bei den Einrückungen ist's da noch einfacher: {=>+1 }=>-1 (aber da wird jetzt ein if(a)foo(); nicht eingerückt.

Zu den unwichtigen Knoten: "Kein Text" => also keine syntaktische Bedeutung, dann ja, sonst eher nicht^^.

Zu added<=> changed: Ich denke mal, vom Sinn her würde ein added im parent ein changed im child immer "übertreffen". Ein hinzugefügtes parent hat Kindelemente, die schon geändert werden? Ein neues Element hat doch nur neue Inhalte, sonst ist's nicht neu.
 
Das tönt interessant, allerdings verstehe ich den Ansatz nicht ganz. Von Design Pattern

ich dachte eher an Patternmatching unter Verwendung von regulären Ausdrücken (falls Eiffel so etwas überhaupt kennt) als an Design Patterns, denn die dürften dir bei dem Problem kaum weiter helfen ;)

Hier eine kleine Erklärung aus der Java-Welt:
http://openbook.galileocomputing.de/javainsel9/javainsel_04_007.htm#mj26fc5cf60311afbddd72295cdd646a48

Die Idee ist wie gesagt, dass du die falsche Formatierung in Form eines oder mehrerer regulärerer Ausdrücke, also als Pattern, definierst und auf dem String anwendest. Wird ein solches Pattern erkannt, dann wird eine von dir definierte Änderung an dem String durchgeführt. Z.B. könntest du leicht definieren, dass nach einer Klammer keine Leerzeichen stehen sollen bis zum Parameterbezeichener. Das geht aber natürlich nicht nur mit Leerzeichen, du kannst es beliebig kompiziert gestalten ;) Beispielsweise kannst du so leicht Schlüsselwörter erkennen und eine entsprechende Umformatierung im String direkt durchführen lassen.

Generell würde ich dir zu folgendem Vorgehen raten:
Zunächst alle mehrstelligen Leerzeichen durch einstellige ersetzen. Das dürfte kein Problem sein. Dann die Schlüsselwörter wie do, end begin etc. als Patterns definieren und entsprechend Zeilenumbrüche setzen im String.
 
Zuletzt bearbeitet:
Zurück
Oben