AST und Domain Specific Language

Werbung
Praktikum II zur Vorlesung Informatik III
Domain Specific Language
Prof. Dr. Nikolaus Wulff
Zeitraum: 8. – 15. November 2011
Vorbesprechung: Donnerstag 27. Oktober 2011
Der Integrator aus Praktikum I ist lediglich in der Lage fest im Programm übersetzte Funktionen zu verarbeiten. Nun gilt es, die Funktionen
in einer Textdatei oder als Zeichenkette frei zu spezifizieren und anschließend mit Hilfe eines Parsers in einen Abstrakten Syntax Baum (AST) zu
überführen, der dann numerisch effizient ausgewertet, graphisch gezeichnet
und differenziert oder integriert werden kann.
Dieses Vorgehen ermöglicht es Funktionen frei zu erfinden und zu definieren, ohne das Programm neu übersetzen zu müssen. Hierzu wird eine
eigene Domain Specific Language (DSL) für den Parser entwickelt.
1
Domain Specific Language
Im Listing 1 finden Sie ein einfaches Beispiel einer DSL, die einen modifizierten Sinus darstellt und geeignet in einen AST überführt werden muss.
1
2
3
4
5
6
a = 0.5;
b = 2;
pi = 3.14;
y(x) = 2∗sin(pi∗x) + b;
x = 0.5;
a∗y(x);
//
//
//
//
//
//
specify variable a
specify variable b
specify variable pi
declare function y
specify variable x
evaluate a∗y(x) at x=0.5
Listing 1: Beispiel eines DSL Scripts.
Die DSL soll die wesentlichen mathematischen Operationen {+,-,*,/} mit
den zugehörigen Klammerregeln, sowie ein Konzept zur Definition von Konstanten, Variablen und frei definierbare Funktionen zur Verfügung stellen.
Am einfachsten wird solch eine DSL mit Hilfe der Erweiterten Backus-NaurForm (EBNF) in Form von Produktionsregeln spezifiziert1 . Einen ersten
Entwurf einer solchen DSL des Mathematik Parsers ist in Abbildung (1)
1
Hierbei baut eine Regel auf die andere auf, ähnlich wie bei der DTD Beschreibung
von XML Dokumenten aus Informatik II.
1
letter
digit
sign
:= (’a’..’z’ | ’A’..’Z’)
:= (’0’..’9’);
:= ’+’ | ’-’;
integer
float
number
:= (sign)? (digit)+
:= integer ’.’ (digit)*
:= integer | float;
assign
atom
term
expr
:=
:=
:=
:=
variable ’=’ number;
number | variable;
atom (’*’|’/’) atom;
term (’+’|’-’) term;
Abbildung 1: Erster unvollständiger EBNF Entwurf für die DSL.
zu sehen. Ähnlich zur DTD sind die Multiplizitäten 0:1 als ()?, 1:n als ()+
und 0:n als ()* annotiert. Wie in der Informatik üblich wird eine Alternative durch die oder-Operation | kenntlich gemacht. Obige DSL ist noch nicht
vollständig ausspezifiziert, zeigt aber bereits, wie sich die fundamentalen
Operationen der DSL definieren lassen.
Im Sourcelisting 2 sehen Sie einen einfachen Test, der mit einer Zeichenkette2 als Skript, zunächst die Variable x = −1.5 definiert und den Ausdruck
3.75 + x berechnet.
1
#define EPS 1.E−10
2
3
4
5
6
7
8
9
void testScripting () {
AST ast;
double y;
char script [] = ”x = −1.5; ” \
”3.75 + x;”;
ast = parseScript(script ) ;
preCondition(ast != NULL, ”TEST FAILED: no AST from parser”);
10
y = eval(ast) ;
printf (”eval : %f\n”,y);
if (fabs(2.25 − y)<EPS) {
printf (”TEST OK\n”);
} else {
printf (”TEST FAILED!\n”);
}
11
12
13
14
15
16
17
18
}
Listing 2: DSL Scripts Testlauf.
2
Diese kann natürlich auch aus einer Textdatei eingelesen werden, aber so ist der Test
in sich geschlossen.
2
Das Ergebnis der Parser Analyse und die Auswertung des AST wird dann
anschließend auf numerische Korrektheit überprüft. Ein erfolgreicher Testlauf produziert z. B. die nachfolgende Ausgabe.
parsing line >x=-1.5<
setVariable[x]=-1.5
parsing line >3.75+x<
eval: 2.250000
TEST OK
Aufgabe
Erstellen Sie eine DSL zum Berechnen von mathematischen Formeln. Entwickeln Sie eine geeignete Implementierung eines DSL Parsers zur Auswertung
der Formeln passend zur von Ihnen definierten DSL.
Alle DSL Elemente, wie Variable, Term, etc., werden als Spezialisierungen des generischen Knoten Typen AST definiert. Die Headerdatei 3 zeigt
einen Ausschnitt der entsprechenden Definitionen. Gut zu erkennen ist die in
der Vorlesung besprochene kanonische Form des abstrakten C Datentypen
(ADT). Erweiterungen des AST müssen strukturkonform zu diesem ADT
sein. Hier können entsprechende Makros hilfreich sein. Der Parser analysiert
das Skript und intitialisiert und verkettet die entsprechenden AST Knoten.
Tip
Fangen Sie zunächst an einfache Zuweisungen, wie z.B. x=3, und dann einfache Anweisungen, wie z.B. 2 + 5 oder x + 5, zu implementieren bevor Sie
an den komplizierteren Klammerungen und Formeln verzweifeln :-).
Bauen Sie erst nach dem erfolgreichen Test der einfacheren Syntax das
System weiter aus. Es macht Sinn automatisierte Testtreiber a la Listing 2
zu verwenden.
Für diese Aufgabe ist u.A. Arbeitsteilung und gute Vorbereitung
gefragt, so dass unterschiedliche Module und Quelltexte – Parser, AST, Test,
etc –, parallel von verschiedenen Entwicklern im Team entwickelt werden
können.
3
1
2
#ifndef AST H
#define AST H
3
4
typedef char ∗String;
5
6
7
8
typedef enum { ADD=’+’, SUB=’−’, MUL=’∗’, DIV=’/’,
ASSIGN=’=’, DEFINE=’:’, CONSTANT=’C’, VARIABLE=’V’,
LBRACK=’(’, RBRACK=’)’} Type;
9
10
11
typedef struct node instance struct ∗AST;
typedef struct node class struct ∗ASTClass;
12
13
14
15
16
struct node class struct {
double (∗value)(AST node);
String (∗toString)(AST node);
};
/∗ common methods for AST nodes ∗/
17
18
19
20
21
typedef struct node instance struct { /∗ common attributs of AST nodes ∗/
ASTClass class;
Type type;
};
22
23
24
#define eval(node) ((node)−>class−>value(node))
#define asString(node) ((node)−>class−>toString(node))
25
26
27
/∗∗ parse the given script and return the AST.
AST parseScript(const String script);
∗/
28
29
30
31
32
/∗∗ various constructor methods, for different AST node types. ∗/
AST createConstant(const double val);
AST createOperator(const AST left, const Type op, const AST right);
AST createVariable(const String name);
33
34
35
#endif /∗ AST H is defined ∗/
Listing 3: Mögliche Definition des generischen AST.
4
Herunterladen