Werkzeuge für domänenspezifische Sprachen

Seite 2: DSLs

Inhaltsverzeichnis

Man muss sich bei der Definition einer DSL mindestens um die folgenden drei Aspekte kümmern:

  • Für das verarbeitende Werkzeug (beispielsweise ein Codegenerator) ist die abstrakte Syntax relevant (auch als Metamodell bezeichnet). Sie ist eine Datenstruktur, die den semantisch relevanten Inhalt eines mit der DSL beschriebenen Programms darstellt. Interpreter oder Codegeneratoren arbeiten auf dieser Datenstruktur.
  • Die konkrete Syntax ist die grafische oder textuelle Darstellung der abstrakten Syntax. Sie ist sozusagen das User Interface der Sprache. Anwender, die die Sprache nutzen, interagieren mit der konkreten Syntax. Es ist daher essenziell, dass sie auf die entsprechende Zielgruppe angepasst ist. Oft bezeichnet man die Sprachen mit grafischer Syntax als Modellierungssprachen.
  • Der dritte Aspekt einer Sprache ist ihre Semantik, also die Bedeutung davon, was man mit der konkreten Syntax schreibt und mit der abstrakten Syntax dem Tool zur Verarbeitung gibt. Es gibt unterschiedliche Arten der formalen Semantikdefinition, in der Praxis finden sie allerdings fast keine Verwendung. Entweder definiert eine Dokumentation der Sprache die Semantik in Prosa oder sie wird implizit durch Interpreter und Codegeneratoren definiert. Wenn ein Codegenerator eine DSL auf eine Sprache mit bekannter Semantik abbildet (beispielsweise C#), so lässt sich daraus im Umkehrschluss die Semantik der DSL ableiten.

Zusammenhang zwischen Metamodell, konkreter Syntax, Semantik, Constraints, Modell und Domäne (Abb. 1)

In der Praxis benötigt man zwei weitere Aspekte. Zum einen sogenannte Constraints, zum anderen die Modellverarbeiter, also Codegeneratoren oder Interpreter.

In aller Regel gibt es Korrektheitsregeln für Programme einer DSL, die sich mit gängigen Formalismen zur Beschreibung von abstrakter Syntax definieren lassen. Beispielsweise müssen die Attribute einer Datenstruktur eindeutige Namen haben. Gleiches gilt sinngemäß für die Zustände in einer Zustandsmaschine. Constraints sind Boolsche Ausdrücke und dienen dazu, genau das sicherzustellen. Damit ein Modell als korrekt zu betrachten ist, müssen alle Constraints zu "true" evaluieren.

Im betrachteten Zusammenhang dienen DSLs der Entwicklung von Software. Dies bedeutet, dass man die mit einer DSL erstellten Modelle in existierende Laufzeitinfrastrukturen einfügen muss. Dafür gibt es vorrangig zwei Vorgehensweisen, es existieren aber auch Mischformen.

Ein Interpreter ist ein Programm, das den abstrakten Syntaxbaum eines Modells traversiert und bei der Semantik der Sprache entsprechende Seiteneffekte auslöst. Ein einfacher Interpreter für einen Taschenrechner würde für eine Datenstruktur, die die Addition zweier Zahlen repräsentiert, tatsächlich die beiden Zahlen addieren.

Codegeneratoren sind spezielle Interpreter, die als Seiteneffekt der Modelltraversierung semantisch äquivalenten Quellcode in einer existierenden Programmiersprache ausgeben. Dieser wird nachfolgend interpretiert oder seinerseits kompiliert.

Zusammenhang zwischen dem Modell und den verarbeitenden Werkzeugen (Abb. 2)

Oft benötigt man auch Modelltransformatoren, die entweder Modelle ergänzen (quasi als Prä- oder Postprozessor) oder Instanzen eines Metamodells in Instanzen eines anderen Metamodells überführen. Das ist notwendig, wenn man Modelle abstrakterer DSLs (die beispielsweise Fachlichkeit beschreiben) auf Modelle von DSLs abbildet, die eher technischer Natur sind (zum Beispiel Architektur-DSLs).

Ein weiteres wichtiges Tool ist der Editor. Seine Aufgabe ist, den Umgang mit der konkreten Syntax der DSL für den Anwender effizient zu gestalten. Der Editor baut einen der abstrakten Syntax entsprechenden Baum auf. Bei Sprachen mit grafischer Syntax geschieht das in aller Regel direkt: Wenn man beispielsweise einen Zustand in ein Zustandsdiagramm einfügt, wird im Syntaxbaum auch direkt das passende Modellelement angelegt. Bei Sprachen mit textueller Syntax ist diese Abbildung nicht so einfach, weswegen sie einen Parser benötigt. Seine Aufgabe ist, die textuelle konkrete Syntax eines Programms auf Korrektheit zu prüfen und den zur konkreten Syntax passenden abstrakten Syntaxbaum aufzubauen. Dazu kommt eine sogenannte Grammatik zum Einsatz. Dies ist letztlich eine formale Beschreibung, die Abbildungsregeln von der konkreten auf die abstrakte Syntax definiert.