Delphi Runtime-Compiler-Compiler (zur Laufzeit erstellter Compiler)
Hab mir mal die Mühe gemacht ein Delphi-Runtime-Compiler-Compiler-Framework zu programmieren. Damit sollte es möglich sein, viele verschiedene Grammatiken damit zu definieren und verwenden, die ohne Backtracking (eindeutig mit LR(1) lösbar, ohne Linksrekursionen) geparst werden können. Ich habe das eigentlich aus eigenem Interesse gebaut, ob sowas jemand direkt so brauchen kann ist eine andere Frage. Der Source wurde unter Delphi 2006 erstellt.
Das ganze besteht im Wesentlichen aus 2 Units:
RcCompiler
RcCompiler ist eine Implementierung von EBNF. Sie ermöglicht die Definition einer formalen Sprache. Das ganze ist noch auf einer relativ abstrakten Ebene weswegen die Unit auch nicht sehr viel macht.
RcEBNF
Da die Definition einer formalen Sprache mittels RcCompiler etwas umständlich ist ermöglicht diese Unit die Spezifikation einer Grammtik durch Eingabe eines Strings in EBNF-Syntax. RcEBNF verwendet selbst RcCompiler um den Eingabestring in eine Sprachdefinition für RcCompiler zu parsen und zusammenzustellen.
Allgemeines
Natürlich ist die Implementierung einer Sprache ein grosser Aufwand. Die Definition selbst ist zwar schnell gemacht, man muss natürlich aber noch angeben, was die einzelnen Bausteine machen. Man muss also noch Handlers programmieren (was die machen ist vollig frei: Bytecode erzeugen, direkt interpretieren, in eine andere Sprache übersetzen).
Beispiel der Demo
In der Demo kann man dann in einer zur Laufzeit definierten (extrem einfachen) Programmiersprache programmieren. Man kann dort nur Ausdrücke auswerten und diese einer Variablen zuweisen. Die Sprache wird zur Laufzeit über folgende Strings definiert:
Definition der Demosprache
1: 2: 3: 4: 5: 6: 7:
| befehl = "begin" [ befehl { ";" [ befehl ] } ] "end" | zuweisung. zuweisung = bezeichner ":=" bool. bool = summe [ ( "=" | "<>" | ">" | "<" | "=>" | "<=" ) summe ]. summe = produkt { ( "+"|"-") produkt }. produkt = term { ("*"|"") term }. term = termmitvorz | "(" bool ")" | bezeichner | zahl. termmitvorz = ( "+"|"-" ) term. |
(Der Tokenizer liefert die Terminal Symbols
bezeichner und
zahl als Tokens. Diese sind über Regular Expressions definiert)
Zu jeder Zeile muss man im Framework noch einen Handler angeben, der eine konkrete Zeile verarbeitet. Z.B. für "Summe" einen Interpreter-Handler, der mehrere Zahlen entgegennimmt, diese addiert und die Summe zurückgibt. Oder ein Bytecode-Handler würde z.B. den entsprechenden Code zusammenstellen und diesen an die nächste Stufe weitergeben. Pro Zeile kann man noch einige Optionen einstellen wie z.B. Optimierung und andere.
Ein gültiges Programm wäre nun z.B.:
Testprogramm
1: 2: 3: 4: 5:
| begin sieben := 1+2*3; result := 1+(-9+sieben)*(sieben+3); test := result < -10; end |
Hier noch vollständigkeitshalber die Definition von EBNF in EBNF (implementiert in RcEBNF):
Definition von EBNF in EBNF
1: 2: 3: 4: 5:
| Factor = Identifier | String | "(" Expression ")" | "[" Expression "]" | "{" Expression "}". Term = Factor { Factor }. Expression = Term { "|" Term }. Statement = Identifier "=" Expression ".". Syntax = { Statement }. |
Todo:
Lookahead Spezifikationen; Syntax Fehlermeldungen, Optimierung.
[meta]parser,parsing,lexer,typarser,compiler[/meta]
NEUE VERSION: Sources | [url=www.tyberis.com/down...cDemo.exe]Demo[/url]