6.6 Einen Parser in Java implementieren
6.6 Einen Parser in Java implementieren
Den vollständigen Quelltext, den wir in der Vorlesung geschrieben haben, finden Sie in der Datei ArithmeticGrammar.java.
Ich möchte nun eine kontextfreie Grammatik für
arithmetische Ausdrücke der Form
((31+402)*83)
entwerfen.
Der Einfachheit halber bestehe ich auf strenger Klammerung,
so wäre
(2*(1+2+3))
zum Beispiel nicht erlaubt. Unsere
Grammatik soll allgemeine Dezimalzahlen darstellen können.
Das Alphabet ist somit
$\Sigma =
\{\texttt{0},\texttt{1},\texttt{2},\texttt{3},\texttt{4},
\texttt{5},\texttt{6},\texttt{7},\texttt{8},\texttt{9},
\texttt{+},\texttt{*},\texttt{(},\texttt{)}\}$.
Die Produktionsregeln sind:
Die Nichtterminale sind also $E$ (Expression), $N$
(Number) und $D$ (Digit). Wir haben auch den einzelnen
Produktionen Namen gegeben, bis auf die der Form
$D \rightarrow i$.
Was soll nun unser Parser tun? Er soll,
gegeben ein Eingabewort
$w \in L$,
den
Ableitungsbaum
konstruieren, für
((31+402)*83)
also
Wie wir diesen Baum in Java repräsentieren, darüber
sprechen wir in einer Minute. Zuerst aber: wir wollen mit
diesem Baum etwas Sinnvolles tun. Zum Beispiel
auswerten,
so dass am Ende eine Zahl rauskommt, im obigen Beispiel
also
$(31 + 402) \cdot 83 = 35939$.
Oder den Ausdruck
umformen von Infix-Notation zu Präfixnotation,
also(* (+
31 402) 83).
All dies wird sehr einfach sein, sobald wir
den Ableitungsbaum als Datenstruktur vorliegen haben.
Für meine Implementierung in Java erschaffe ich für jedes Nichtterminal $X$ ein Interface und für jede Produktionsregel $X \rightarrow \alpha$ eine Klasse, die das Interface $X$ implementiert und $\alpha$ als Klassenvariable enthält.
interface Expression
wird implementiert von
class Sum,
die als Klassenvariable
Exrepssion e1, e2
enthält,
class Product,
die als Klassenvariable
Exrepssion e1,
e2
enthält,
class JustNumber,
die als Klassenvariable nur eine
Number numberenthält;
interface Number
wird implementiert von
class MultiDigitNumber,
die als Klassenvariable
eineNumber
und eine
Digit
erhält und
class SingleDigitNumber,
die als Klassenvariable ein
Digitenthält;
interface Digit
wird implementiert
vonclass
DigitOne,class DigitTwo,class DigitThree,class
DigitFour,class DigitFive,class DigitSix,class
DigitSeven,class DigitEight
undclass DigitNine.
In unserem Anwendungsfall hat jedes Interface eine
Methodepublic int toInt().
Interface
Expression
hat
zusätzlich noch die Methode
String toPrefixNotation().
Ich schreibe auch ein Über-Interface
ParseObject,
das
alle Interfaces zusammenfasst. Um uns das Debugging zu
erleichtern, überschreibe ich in jeder Klasse die
Methodepublic String toString().