Grundlagen der modellgetriebenen Softwareentwicklung Teil ...drachen/mgse/MGSE-Teil-3.pdf · Prof....
Transcript of Grundlagen der modellgetriebenen Softwareentwicklung Teil ...drachen/mgse/MGSE-Teil-3.pdf · Prof....
Prof. Dr. H. Drachenfels Version 11.0 Hochschule Konstanz 13.2.2019
Grundlagen der modellgetriebenen Softwareentwicklung
Teil 3: Domänenspezifische Sprachen
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-1
Hochschule Konstanz
Domänen
Domäne Arbeits-, Wissensgebiet, auf dem jemand besonders gut Bescheid weiß, auf dem er sich speziell und besonders intensiv betätigt ... (Duden Fremdwörterbuch, 1982) Jede Software wird für eine bestimmte Domäne entwickelt, z.B.:
• Übersetzung von Programmiersprachen
• Kommunikation in verteilten System
• Grafische Benutzeroberflächen
• Web-Shop
• Buchhaltung
• Waschmaschinensteuerung
• ...
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-2
Hochschule Konstanz
Domänenspezifische Bibliotheken
Traditionell wird Domänenwissen in Bibliotheken (und Frameworks) erfasst, die ihrerseits in einer universellen Programmiersprache implementiert sind.
Die Aufteilung Sprachen universell und Bibliotheken domänenspezifisch ist im Prinzip ein Erfolgsmodell, hat aber trotzdem auch Nachteile:
• Das Domänenwissen muss mit Hilfe der Konzepte der Programmiersprache ausgedrückt werden (z.B. mit Funktionen oder Objekten).
Die dadurch bedingte semantische Lücke ist eine Fehlerquelle und führt zu Redundanz in Form von sich wiederholendem Standardcode (Boilerplate). Beispiele: GUI-Programmierung mit Java Swing, Datenverwaltung mit Java Collections
• Bibliotheksimplementierungen binden Domänenwissen an bestimmte Plattformen und an bestimmtes Laufzeitverhalten.
macht die Integration mehrerer Bibliotheken für unterschiedliche Domänen in einer Anwendung mitunter schwierig
erfordert gegebenenfalls viele aufwendig zu pflegende Varianten einer Bibliothek
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-3
Hochschule Konstanz
Domänenspezifische Sprachen
DSL (Domain Specific Language, auch: Fachsprache)
Eine formale Sprache, die speziell für ein bestimmtes Problemfeld (die Domäne) entworfen und implementiert wird.
Potenzielle Vorteile einer DSL:
• die relevanten Aspekte einer Domäne (und möglichst nichts darüber hinaus) lassen sich kompakt und korrekt beschreiben und können auf unterschiedliche Implementierungen abgebildet werden
wenige spezialisierte Konzepte mit möglichst wenig syntaktischem Ballast
• Domänenexperten können damit umgehen, d.h. sie können zumindest mit der DSL verfasste Beschreibungen verstehen
Anlehnung an die in der Domäne üblichen Notationen
Entwurfsschritte für eine DSL:
• Domänenanalyse zum Bestimmen der relevanten Aspekte
• Sprachklasse, Syntax und Semantik festlegen
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-4
Hochschule Konstanz
Formale Sprachen
Formale Sprache
Menge von Symbolen mit Regeln für zulässige Kombinationen der Symbole
z.B. Programmiersprachen
Metasprache
formale Sprache zum Beschreiben formaler Sprachen (d.h. zum Definieren der Symbole und Regeln)
eine unendliche Meta-Rekursion ist durch selbstbeschreibende Metasprachen vermeidbar
z.B. erweiterte Backus-Naur-Form (EBNF)
Sprache Metasprache
«beschreibt» «beschreibt»
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-5
Hochschule Konstanz
DSL: Domänenanalyse
Möglichkeiten zum Erfassen der relevanten Konzepte einer Domäne:
• Glossar
Liste von Begriffen mit Erklärungen
• Taxonomie
hierarchischer Einordnung von Begriffen in Kategorien
• Thesaurus
Taxonomie mit zusätzlichen Synonym-/Verwandtschafts-Beziehungen
• Ontologie (Begriffsnetz, semantisches Netz)
Begriffe mit Eigenschaften, beliebigen Relationen und Axiomen
Axiome sind Aussagen, die in der Domäne immer wahr sind
Taxonomien und Thesauren sind im Prinzip Spezialfälle von Ontologien mit fest vorgegebenen Relationstypen und ohne Axiome
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-6
Hochschule Konstanz
DSL: Sprachklassen
Klassifizierung nach Notation:
• rein textuell
• grafisch mit textuellen Beschriftungen
Klassifizierung nach Implementierung:
• Spezialisierung einer Universalsprache
z.B. UML-Profile, XML-Dialekte
• Black-Box-Einbettung in eine Wirtssprache
Kapselung in Strings oder Kommentaren der Wirtssprache z.B. reguläre Ausdrücke, printf-Formatstrings, Dokumentationskommentare in Java
• White-Box-Einbettung in eine Wirtssprache (interne DSL) Realisierung als Ausdrücke in der Wirtssprache z.B. EasyMock mit Wirtssprache Java
• eigenständige Sprache (externe DSL) z.B. HTML, Unix shell script, Makefile
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-7
Hochschule Konstanz
DSL: Syntax
• die konkrete Syntax einer DSL legt fest, wie etwas formuliert werden kann:
spezifiziert im Detail die Notation der Sprache (Schlüsselwörter, Symbole, ...), z.B. bei textuellen DSLs mit Hilfe von regulären Ausdrücken und/oder EBNF
die konkrete Syntax ist die Benutzerschnittstelle der DSL und ist entscheidend für die Lesbarkeit der DSL durch den Menschen
• die abstrakte Syntax einer DSL legt fest, was formuliert werden kann:
spezifiziert die Struktur der Sprache (Konzepte und deren Beziehungen ...), z.B. mittels eines Metamodells, das als abstrakter Syntaxgraph (ASG) bzw. abstrakter Syntaxbaum (AST) instanziert wird
zu ein und derselben abstrakten Syntax kann es mehrere konkrete Syntaxformen geben z.B. eine textuelle und eine grafische Notation
die abstrakte Syntax ist entscheidend für die automatisierte Verarbeitung dessen, was der Benutzer mit der DSL formuliert
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-8
Hochschule Konstanz
DSL: Semantik
• die statische Semantik regelt die Wohlgeformtheit von Formulierungen:
explizit in Form von Konsistenzregeln für abstrakte Syntaxgraphen
z.B. eindeutige Namen für Entitäten verlangen
z.B. bei Programmiersprachen: Variablen müssen vor ihrer Verwendung definiert werden
• die dynamische Semantik regelt die Bedeutung von Formulierungen:
implizit in Form von Transformatoren für abstrakte Syntaxgraphen
Transformatoren erzeugen aus jedem Sprachkonzept etwas Bestimmtes
z.B. erzeugt der Compiler einer Programmiersprache aus einer Variablendefinition eine Speicherreservierung
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-9
Hochschule Konstanz
DSL: Werkzeug-Unterstützung
Werkzeuge machen eine DSL erst praktisch einsetzbar:
• Parser
wandelt konkrete Syntax in abstrakte Syntax
muss nur bei externen DSLs jeweils neu implementiert werden
• Serialisierer
wandelt abstrakte Syntax in konkrete Syntax
ist nicht immer erforderlich
• Editor
ermöglicht das interaktive Bearbeiten von konkreter und/oder abstrakter Syntax
je nach Sprachklasse und gewünschtem Komfort entweder Wiederverwendung oder spezielle Implementierung
• Interpreter / Transformatoren / Generatoren
verarbeiten die in abstrakter Syntax vorliegende Information
legen die dynamische Semantik der DSL fest
die Implementierung der Werkzeuge kann zu großen Teilen automatisiert werden.
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-10
Hochschule Konstanz
UML-Spezialisierung: Profile
UML kann mittels Profil zu einer DSL spezialisiert werden.
Ein UML-Profil besteht aus
• Stereotypen Spezialisierungen von Modellelementen
• Tagged Values Attribute der Stereotypen
• Constraints Konsistenzregeln für mit den Stereotypen markierte Modellelemente
Ein UML-Profil modifiziert
• die abstrakte Syntax von UML mittels Stereotypen und Tagged Values
als Metasprache wird im Prinzip das UML-Klassendiagramm verwendet, siehe dazu auch den MOF-Standard (Meta Object Facility)
• die statische Semantik von UML mittels Constraints
als Metasprache wird die Object Constraint Language (OCL) verwendet
• die konkrete Syntax von UML mittels graphischer Symbole
einem Stereotyp kann ein graphisches Symbol zugeordnet werden
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-11
Hochschule Konstanz
UML-Spezialisierung: OCL (1)
Constraints werden mit OCL (Object Constraint Language) formal spezifiziert. Sie machen Aussagen über die Gültigkeit von Modellen (statische Semantik).
Arten von Constraints:
• Invarianten sind Bedingungen, die bei jedem Objekt einer bestimmten Klasse zu jedem Zeitpunkt erfüllt (true) sein müssen
• Vorbedingungen (Preconditions) und Nachbedingungen (Postconditions) sind Bedingungen, die vor bzw. nach Aufruf einer bestimmten Operation erfüllt (true) sein müssen
• Anfangswerte, die bei bestimmten Attributen einzuhalten sind
• Ergebniswerte, die bestimmte Abfrageoperationen zu liefern haben
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-12
Hochschule Konstanz
UML-Spezialisierung: OCL (2)
Syntax von Constraints:
• Invarianten
context Klasse inv: Bedingung
• Vor- und Nachbedingungen:
context Operation pre: Bedingung post: Bedingung
• Anfangswerte:
context Attribut init: Ausdruck
• Ergebniswerte:
context Operation body: Ausdruck
Wertebereich von Attributen beschränken Beziehungen zwischen Klassen / Objekten beschränken
Anwendbarkeit einer Operation beschränken Auswirkung einer Operation spezifizieren
Implementierung von Abfrageoperationen spezifizieren
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-13
Hochschule Konstanz
Beispiel UML-Spezialisierung (1)
UML-Profile sind z.B. gut auf UML-Klassendiagramme anwendbar:
• auf der Metaebene Definition eines Profils Joi (Java Objects by Interface) mit Stereotyp Component, Tagged Value singleton und Constraint,
anwendbar auf Klassen
• konforme Verwendung des obigen Profils:
<<metaclass>>
UML::Class
<<stereotype>>
Joi::Component
singleton: boolean
{ eine Joi-Komponente darf weder Ober- noch Unterklasse haben und muss mindestens ein Interface implementieren }
<<Joi::Component>>
{ singleton = false }
EinModell::EineKlasse
<<interface>>
EinModell::EinInterface
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-14
Hochschule Konstanz
Beispiel UML-Spezialisierung (2)
• die Constraints für mit Stereotyp Joi::Component markierte Klassen können mit OCL wie folgt formalisiert werden:
context Component
inv: self.base_Class.superClass->isEmpty()
inv: Generalization.allInstances().general ->excludes(self.base_Class)
inv: self.base_Class.clientDependency ->select(oclIsKindOf(InterfaceRealization)) ->notEmpty()
(getestet mit Papyrus UML 1.0.2 in Eclipse Luna 4.4.2)
keine Oberklasse
Navigation im UML-Metamodell
keine Unterklasse
mindestens ein Interface
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-15
Hochschule Konstanz
XML-Spezialisierung: DTD und XSD
XML kann als DSL verwendet werden.
Der Sprachumfang kann (und sollte unbedingt) formal spezifiziert werden:
• entweder mittels Metasprache DTD (Document Type Definition)
• oder mittels Metasprache XSD (XML Schema Definition).
Die XML-Spezialisierung betrifft:
• Elemente und deren syntaktische Kombination (Schachtelung, Reihenfolge, ...) <element>...</element>
• Elementattribute und deren Wertebereiche <element attribut="wert">
• Entities (Textbausteine, nur mittels DTD definierbar) &entity;
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-16
Hochschule Konstanz
Beispiel DTD (1)
Beispiel zur Verwendung einer DTD (Document Type Definition):
• auf der Metaebene legt joi.dtd den Sprachumfang fest
<!ELEMENT joi (component)*> <!ELEMENT component (interface+, body?)> <!ATTLIST component name ID #REQUIRED singleton (true|false) "false" > <!ELEMENT interface EMPTY> <!ATTLIST interface name NMTOKEN #REQUIRED > <!ELEMENT body (#PCDATA)>
Das Wurzelelement joi enthält beliebig viele component-Elemente, diese wiederum mindestens ein interface-Element sowie eine optionales body-Element.
ID ist ein eindeutiger Name, NMTOKEN ein Name, der mehrfach vorkommen darf.
#REQUIRED kennzeichnet ein Pflichtattribut.
singleton hat den Standardwert false.
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-17
Hochschule Konstanz
Beispiel DTD (2)
• konforme Verwendung der DTD in einmodell.joi
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE joi SYSTEM "joi.dtd">
<joi>
<component name="C">
<interface name="I" />
<body>
Beliebiger Text, auch mit Entities
</body>
</component>
</joi>
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-18
Hochschule Konstanz
Beispiel XSD (1)
Das gleiche Beispiel wie zuvor mit XSD (XML Schema Definition):
• auf der Metaebene legt joi.xsd den Sprachumfang fest
<?xml version="1.0" encoding="ISO-8859-1"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="joi"> <xs:complexType> <xs:sequence> <xs:element name="component" type="componenttype" minOccurs="0" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> </xs:element>
...
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-19
Hochschule Konstanz
Beispiel XSD (2)
• Fortsetzung joi.xsd ... <xs:complexType name="componenttype"> <xs:sequence> <xs:element name="interface" type="interfacetype" maxOccurs="unbounded" /> <xs:element name="body" type="xs:string" minOccurs="0" /> </xs:sequence> <xs:attribute name="name" type="xs:ID" use="required" /> <xs:attribute name="singleton" type="xs:boolean" default="false" /> </xs:complexType>
<xs:complexType name="interfacetype"> <xs:attribute name="name" type="xs:string" use="required" /> </xs:complexType>
</xs:schema>
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-20
Hochschule Konstanz
Beispiel XSD (3)
• konforme Verwendung in einmodell.joi wie bei der DTD-Version,
nur ohne <!DOCTYPE ...>-Zeile
die Schemadatei wird dann als Parameter an den Parser übergeben
• oder mit Angabe der Schemadatei im Wurzelelement:
<?xml version="1.0" encoding="ISO-8859-1"?>
<joi xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="joi.xsd">
<component name="C">
<interface name="I" />
<body>
Beliebiger Text, auch mit Entities
</body>
</component>
</joi>
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-21
Hochschule Konstanz
Interne DSL: Java
In Java können APIs (Application Programming Interfaces) so definiert werden, dass sie den Charakter einer DSL bekommen:
• Method-Chaining
API-Methoden liefern als Rückgabewert ihre this-Referenz,
um Aufrufketten innerhalb eines Ausdrucks zu ermöglichen
Beispiel: append-Methoden der Klasse java.lang.StringBuilder
String s = new StringBuilder()
.append("Hallo").append(" World!").toString();
• Method-Nesting
Rückgabewerte von API-Methoden als Argumente für API-Methodenaufrufe
dadurch sind rekursive Strukturen beschreibbar
• Object-Scoping
API-Methoden liefern als Rückgabewert jeweils ein Kontext-Objekt, das die als nächstes aufrufbaren Methoden festlegt und Zwischenergebnisse speichert
dadurch sind bestimmte Aufrufreihenfolgen für das Method-Chaining definierbar
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-22
Hochschule Konstanz
Beispiel Interne DSL in Java: EasyMock
Beispiel: EasyMock-Bibliothek für die Domäne Unit-Tests
import static org.easymock.EasyMock.*;
public final class BeispielTest {
public interface Beispiel { boolean methode(); }
public static void main(String[] args) {
Beispiel mock = createMock(Beispiel.class);
expect(mock.methode()).andReturn(true).times(2);
replay(mock); mock.methode(); mock.methode(); verify(mock); }
}
Verwendung der internen DSL, um das Verhalten des Mock-Objekts zu spezifizieren:
• es sollen genau zwei Aufrufe von Beispiel.methode akzeptiert werden, die jeweils true zurückliefern
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-23
Hochschule Konstanz
Beispiel Interne DSL in Java: DOM-Baum (1)
• Konstruktion eines DOM-Baums (XML Document Object Model) mit konventioneller Aufrufsequenz:
org.jdom2.Element root = new org.jdom2.Element("joi");
org.jdom2.Element c = new org.jdom2.Element("component"); c.setAttribute("name","C"); c.setAttribute("singleton","false"); root.addContent(c);
org.jdom2.Element i = new org.jdom2.Element("interface"); i.setAttribute("name","I"); c.addContent(i);
org.jdom2.Element b = new org.jdom2.Element("body"); b.setText("Beliebiger Text"); c.addContent(b);
org.jdom2.Document d = new org.jdom2.Document(root);
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-24
Hochschule Konstanz
Beispiel Interne DSL in Java: DOM-Baum (2)
• Konstruktion des gleichen DOM-Baums mit interner DSL:
import static joi.JdomBuilder.joi;
...
org.jdom2.Document d = joi().component("C").interfaces("I").body("Beliebiger Text").end();
• Implementierung der internen DSL in JdomBuilder.java:
package joi;
public final class JdomBuilder {
private final ComponentScope componentScope; private final InterfaceScope interfaceScope; private final BodyScope bodyScope;
... // Hilfsvariablen und privater Konstruktor ...
component() liefert ein Objekt, auf dem nur interfaces()aufrufbar ist
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-25
Hochschule Konstanz
Beispiel Interne DSL in Java: DOM-Baum (3)
...
public static ComponentScope joi() { return new JdomBuilder().componentScope; } public class ComponentScope {
public InterfaceScope component(String name) { ... }
public InterfaceScope singleton(String name) { ... }
public org.jdom2.Document end() { ... } } public class InterfaceScope { public BodyScope interfaces(String name, String... more) { ... } } public class BodyScope extends ComponentScope { public ComponentScope body(String s) { ... } } }
Endesymbol der internen DSL
Aufruf wegen Oberklasse optional
Startsymbol der internen DSL
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-26
Hochschule Konstanz
Externe DSL: Reguläre Ausdrücke
Mit regulären Ausdrücken als Metasprache kann die konkrete Syntax einfacher textueller DSLs definiert werden (einfach heißt ohne rekursive Verschachtelungskonstrukte)
• Zeichenklassen:
• Quantifizierung:
• Verknüpfung:
r{2} zweimal hintereinander r{1,3} ein bis drei Wiederholungen r? entspricht r{0,1} r* entspricht r{0,}, d.h. beliebig oft inklusive keinmal r+ entspricht r{1,}, d.h. mindestens einmal
r ist ein beliebiger regulärer Ausdruck
r und s beliebige reguläre Ausdrücke
a das Zeichen 'a'
[abc-e] eines der Zeichen 'a', 'b', 'c', 'd' oder 'e'
[^abc-e] keines der Zeichen 'a', 'b', 'c', 'd' oder 'e'
. beliebiges Zeichen (außer Zeilenwechsel)
rs Konkatenation, d.h. erst r, dann s
r|s Alternative, d.h. r oder s
(r) Klammerung zum Überschreiben der Vorrangregeln (Quantifizierung vor Konkatenation vor Alternative)
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-27
Hochschule Konstanz
Externe DSL: EBNF
Mit der Erweiterten Backus-Naur-Form (EBNF) als Metasprache können kontextfreie Grammatiken für textuelle DSLs definiert werden
• Terminale:
• Nichtterminale:
• Symbolfolgen:
• Produktionsregeln:
s und t beliebige Symbolfolgen mit Terminalen und Nichtterminalen
"abc" die Zeichenfolge abc
Name metasprachliche Variable
st s gefolgt von t
s|t entweder s oder t
{s} beliebig viele s inklusive keinmal [s] kein oder ein s
(s) Klammerung zum Überschreiben der Vorrangregeln
Nichtterminal = Symbolfolge ;
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-28
Hochschule Konstanz
Externe DSL: Parse-Tree versus AST
• ein Parse-Tree zeigt, mit welchen Regeln ein Parser seine Eingabe zerlegt hat
• ein AST (Abstract Syntax Tree) entsteht aus einem Parse-Tree, indem man die Knoten der Nicht-Terminale und der rein syntaktisch motivierten Terminale daraus entfernt
• Beispiel: Zuweisung in einer Programmiersprache
x = 0;
Parse-Tree: AST:
=
x 0
statement
assignment ;
name = expression
x 0
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-29
Hochschule Konstanz
Externe DSL: Implementierung
• mit reguläre Ausdrücken und EBNF die konkrete Syntax der DSL definieren
reguläre Ausdrücke für die Definition der Tokens (Schlüsselwörter, Zahlen, Namen, ...) und EBNF für die Definition der zulässigen Token-Folgen
daraus (in der Regel automatische) Erzeugung eines Parsers, der die Syntax prüft
• zusätzlich Rückgriff auf eine Programmiersprache, um die abstrakte Syntax und die statische Semantik einer DSL zu definieren
als Programmiersprache empfiehlt sich die Implementierungssprache des Parsers
EBNF und die erforderlichen Programmfragmente werden oft in einer Quelle gemischt, dann sogar automatische Erzeugung eines Parsers, der einen konsistenten abstrakten Syntaxgraph liefert
• Beispiele für Parser-Generatoren:
flex und bison (ehemals lex und yacc) Klassiker unter Linux / Unix
ANTLR bzw. ANTLWorks aktuell viel verwendet, besonders im Java-Umfeld
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-30
Hochschule Konstanz
Externe DSL: Scanner-Generator flex
flex (fast lexical analyser) generiert einen Scanner, der Symbole (Tokens)
in einem Text findet
• Dateiformat der Scanner-Spezifikation:
%{ Anwendungscode
%}
Definitionen
%%
Regeln
%%
Anwendungscode
Anwendungscode und Aktionscode der Regeln wird in C geschrieben und wörtlich in den generierten Scanner übernommen
Name Wert
%s Startbedingung(en)
%x Startbedingung(en)
%option Option(en)
Textmuster Aktion
Textmuster { Aktion }
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-31
Hochschule Konstanz
Beispiel flex
/* number.l - einfache Scanner-Spezifikation für Zahlen und Einzelzeichen */ %{ enum token {NUMBER = 256};
%} %option noyywrap %% [+-]?[0-9]+("."[0-9]+)? return NUMBER;
[ \t\n] /* ignorieren */;
. return yytext[0];
%% int main (int argc, char *argv[]) {
int t;
while ((t = yylex()) != 0)
if (t == NUMBER)
printf("NUMBER(%s)", yytext);
else
printf("%c", t);
return 0;
}
Anwendungscode: NUMBER wird in einer Regel und in main gebraucht
Option: Scanner soll bei Eingabeende terminieren
Anwendungscode: yylex() ist der Aufruf des generierten Scanners
Regeln: Zahlen und Einzelzeichen sind Token, Zwischenraum nicht
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-32
Hochschule Konstanz
Externe DSL: Parser-Generator bison
bison generiert einen Parser, der Tokenfolgen auf syntaktische Korrektheit prüft
• Dateiformat der Parser-Spezifikation:
%{ Prolog
%}
%union { Typdeklarationen
}
Definitionen
%%
Regeln
%%
Epilog
Prolog, Typdeklarationen, Aktionen der Regeln und Epilog werden in C geschrieben und wörtlich in den generierten Parser übernommen.
%token Terminalsymbol(e)
%left Terminalsymbol(e)
%right Terminalsymbol(e)
%type <TYP> Nichtterminal(e)
...
Nichtterminal: Symbole { Aktion } | Symbole { Aktion } ;
...
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-33
Hochschule Konstanz
Beispiel bison (1)
/* formel-parser.y - einfache Parser-Spezifikation für arithmetische Ausdrücke */ %{ #include <stdio.h>
int yylex(void);
void yyerror(const char *);
%} %token NUMBER %left '+' '-' %left '*' '/' %% expr: NUMBER { printf("NUMBER\n"); } | expr '+' expr { printf("+\n "); } | expr '-' expr { printf("-\n "); } | expr '*' expr { printf("*\n "); } | expr '/' expr { printf("/\n "); } | '(' expr ')' ; ;
Prolog: benötigte Prototypen.
Definitionen: Terminalsymbole mit aufsteigendem Vorrang und gegebenenfalls Assoziativität
Regel: zulässige Tokenfolgen
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-34
Hochschule Konstanz
Beispiel bison (2)
/* Fortsetzung formel-parser.y ... */
%% int main(void) {
return yyparse();
}
void yyerror(const char *s) {
fprintf(stderr, "%s\n", s);
}
Anwendungscode:
yyparse() ist der Aufruf des generierten Parsers.
yyerror ruft der generierte Parser beim Entdecken eines Syntaxfehlers auf.
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-35
Hochschule Konstanz
Externe DSL: Parser-Generator ANTLR
ANTLR 4 (Another Tool for Language Recognition Version 4) generiert Klassen für
die Erkennung von Tokens (Lexer statt Scanner genannt) und die Syntaxanalyse.
Aus einer Grammatikbeschreibung Name.g4 entstehen im Falle von Java:
NameLexer.java NameParser.java
NameListener.java NameBaseListener.java
Anders als bei flex und bison (und älteren Versionen von ANTLR) soll die Grammatikbeschreibung keine Aktionen mit Programmcode enthalten.
Die Aktionen sollen statt dessen getrennt von der Grammatik in einer Listener-Implementierung bereitgestellt werden (alternativ auch Visitor-Implementierung).
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-36
Hochschule Konstanz
Beispiel ANTLR (1)
// Joi.g4 grammar Joi;
joi : component* ; component : componentHead 'implements' componentInterface (',' componentInterface)* componentBody? ; componentHead : 'component' NAME # ComponentName | 'singleton' NAME # SingletonName ; componentInterface : NAME ; componentBody : BODY ;
BODY : '{' .*? '}' ; NAME : ('A'..'Z') ('a'..'z'|'A'..'Z'|'0'..'9'|'_')* ; NEWLINE : '\r'? '\n' -> skip ; WHITESPACE : [ \t]+ -> skip ;
Namen der Lexer-Regeln in Großbuchstaben
Namen der Parser-Regeln in Kleinbuchstaben
Namen für die Regel-Alternativen
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-37
Hochschule Konstanz
Beispiel ANTLR (2)
Beispiel-Eingabe mit zugehörigem Parse-Tree:
component C implements I {
Beliebiger Text
}
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-38
Hochschule Konstanz
Beispiel ANTLR (3)
Der Parse-Tree kann über eine Listener-Implementierung in einen Jdom-Baum transformiert und dann als XML serialisiert werden:
// JoiToXml.java import org.antlr.v4.runtime.*; // ANTLRInputStream, CommonTokenStream import org.antlr.v4.runtime.tree.*; // ParseTree, ParseTreeWalker import org.jdom2.*; // Document, Element import org.jdom2.output.*; // XMLOutputter
public final class JoiToXml { private JoiToXml() { }
public static void main(final String[] args) throws java.io.Exception { JoiLexer lexer = new JoiLexer(new ANTLRInputStream(System.in)); JoiParser parser = new JoiParser(new CommonTokenStream(lexer)); ParseTree tree = parser.joi();
JdomBuilder builder = new JdomBuilder(); new ParseTreeWalker().walk(builder, tree); new XMLOutputter().output(builder.document, System.out); } ...
Eingabe in Syntax der externen DSL
Ausgabe im XML-Format
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-39
Hochschule Konstanz
Beispiel ANTLR (4)
... private static final class JdomBuilder extends JoiBaseListener { private final Element root = new Element("joi"); private final Document document = new Document(root); private Element component;
public void enterComponent(JoiParser.ComponentContext c) { component = new Element("component"); root.addContent(component); }
public void exitComponentName(JoiParser.ComponentNameContext c) { component.setAttribute("name", c.NAME().getText()); component.setAttribute("singleton", "false"); }
public void exitSingletonName(JoiParser.SingletonNameContext c) { component.setAttribute("name", c.NAME().getText()); component.setAttribute("singleton", "true"); } ...
Alternative SingeltonName
Alternative ComponentName der Regel componentHead
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-40
Hochschule Konstanz
Beispiel ANTLR (5)
...
public void exitComponentInterface(JoiParser.ComponentInterfaceContext c) { component.addContent( new Element("interface") .setAttribute("name", c.NAME().getText()) ); }
public void exitComponentBody(JoiParser.ComponentBodyContext c) { String t = c.BODY().getText(); t = t.substring(1, t.length() - 1).trim();
component.addContent(new Element("body").setText(t)); } } }
Für jede Parser-Regel bzw. benannte Regelalternative generiert ANTLR im Interface JoiListener eine enter- und eine exit-Methode und in der Klasse JoiBaseListener leere Default-Implementierungen dazu.
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-41
Hochschule Konstanz
DSLs: Empfehlungen (1)
• Modelle bevorzugt mit DSLs formulieren!
Modellerstellung wird kompakter, weniger fehleranfällig und für Domänenexperten verständlicher
aber: DSL niemals als Selbstzweck, denn das Entwickeln einer DSL kann je nach Sprachklasse sehr aufwendig sein, und für die Anwender bringt das Erlernen einer neuen Sprachen ebenfalls Aufwand mit sich
• Graphische oder textuelle DSL?
wenn vor allem komplexe Beziehungen zu modellieren sind, ist eine Graphik tendenziell anschaulicher
Werkzeugunterstützung für Text ist wesentlich einfacher
vor der Entscheidung für die eine oder andere konkrete Syntax sollten erst einmal abstrakte Syntax und statische Semantik entworfen werden, denn die sind entscheidend für die Nützlichkeit der DSL
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-42
Hochschule Konstanz
DSLs: Empfehlungen (2)
• UML-Spezialisierung als DSL?
- Profile durch UML-Werkzeuge nicht wirklich gut unterstützt
- Einschränkung der Mächtigkeit von UML mittels OCL sehr mühsam
Profile sind eher geeignet, UML-Modelle mit Annotationen anzureichern (wir kommen darauf bei den Modelltransformationen zurück)
• XML-Spezialisierung als DSL?
- wenig Spielraum beim Sprachdesign
- großer syntaktischer Ballast
+ sehr gute und reichhaltige Werkzeugunterstützung vorhanden
XML-Dialekte sind eher als Datenaustausch- und Speicherungsformat geeignet, wegen des syntaktischen Ballasts nur in einfachen Fällen menschenlesbar/schreibar
Prof. Dr. H. Drachenfels Grundlagen der modellgetriebenen Softwareentwicklung 3-43
Hochschule Konstanz
DSLs: Empfehlungen (3)
• interne DSL?
- wenig Spielraum beim Sprachdesign
- beschränkt auf Zielgruppe Programmierer, die die Wirtssprache beherrschen
+ geringer Implementierungsaufwand durch Wiederverwendung der Werkzeuge
der Wirtssprache
+ geringer Lernaufwand für die Zielgruppe
• externe DSL?
+ maximaler Spielraum beim Sprachdesign
- Implementierungs-, Pflege- und Lernaufwand kann sehr groß werden
deshalb eher für kleine Domänen geeignet
erstrebenswert: Komposition von DSLs aus wiederverwendeten Teil-DSLs