| Mit diesem Wissen geht es jetzt stark in Richtung Taschenrechner. Als ersten neuen Punkt führen wir Zeichendefinitionen ( Characters ) ein, also Bezeichner für die kleinsten Datenbausteine. Wir arbeiten hier mit Zahlen und deren kleinste Bestandteile sind Ziffern (Digits): COMPILER Expr
CHARACTERS
digit = '0'..'9'.
END Expr. Listing 5: Eine Zeichenklasse für Ziffern definieren Solche Zeichenmengen werden klein geschrieben und können auf verschiedene Weise gebildet und kombiniert werden, z.B. mit dem Bereichsoperator (..), als einfacher String oder mit Plus und Minus (Vereinigung/Differenz). Ein weiteres Beispiel für ein paar Zeichemengen: digit = "0123456789".
hexDigit = digit + "ABCDEF".
letter = 'A' .. 'Z'.
eol = '\r'.
noDigit = ANY - digit. Als nächstes spezifizieren wir zusammenhängende Einheiten ( Tokens ) als Verbund von Characters. Eine Zahl besteht aus mindestens einer Ziffer: COMPILER Expr
CHARACTERS
digit = '0'..'9'.
TOKENS
number = digit { digit }.
END Expr. Listing 6: Token-Deklarationen Genau genommen steht dort nun: Eine Zahl besteht aus einer ersten Ziffer und beliebig vielen folgenden. Ein solches Token tritt in der weiteren Verarbeitung immer als zusammenhängende, also nicht weiter zerlegte Einheit (Terminalsymbol) auf. Und jetzt können wir mit den Produktionen loslegen. Das gestaltet sich erstaunlich einfach. Lassen wir Punktrechnung erstmal außen vor und betrachten Terme wie: 1 - 2 - 3 + 4 Das könnten wir doch so beschreiben, dass eine Zahl vorkommt und dann ggf. immer wieder ein Rechenzeichen mit einer nächsten Zahl. COMPILER Expr
CHARACTERS
digit = '0'..'9'.
TOKENS
number = digit { digit }.
PRODUCTIONS
Expr = number { ("+" | "-") number }.
END Expr. Listing 7: Rechnungen mit Plus und Minus beschreiben Damit sind solche Ausdrücke grammatikalisch beschrieben. Jetzt beziehen wir in diese Überlegung die Punktrechnung mit ein: 1 * 2 * 3 + 2 + 3 / 4 Wir sehen: Unser Ausdruck mit allen Operatoren ist im Grunde eine Menge aus Produkten (Punktrechnungen), zwischen denen wiederum Plus oder Minus steht. COMPILER Expr
CHARACTERS
digit = '0'..'9'.
TOKENS
number = digit { digit }.
PRODUCTIONS
Expr = Prod { ("+" | "-") Prod }.
...
END Expr. Listing 8: Punktrechnungen mitdefinieren Ein Ausdruck ist also immer ein Produkt und ggf. viele weitere mit Rechenzeichen dazwischen. Ein Produkt können wir in genau der selben Weise weiter zerlegen. Ein Produkt ist eine Menge aus Faktoren, zwischen denen Mal oder Geteilt stehen. Ein Faktor ist eine einfache Zahl oder ein geklammerter Ausdruck. COMPILER Expr
CHARACTERS
digit = '0'..'9'.
TOKENS
number = digit { digit }.
PRODUCTIONS
Expr = Prod { ("+" | "-") Prod }.
Prod = Fact { ("*" | "/") Fact }.
Fact = number | "(" Expr ")".
END Expr. Listing 9: Vollständige Grammatik Das war's! Die komplette Grammatik für arithmetische Ausdrücke. Der Parser ist übrigens vollständig rekursiv, Verschachtelung von Ausdrücken ist daher kein Problem. Prüfen, ob's stimmt: Abbildung 3: Arithmetische Ausdrücke getestet Verdächtig korrekt ;) Alle richtigen Ausdrücke werden vom Parser angenommen, alle falschen zurückgewiesen. Für motivierte Leser stelle ich einfach mal die Übung in den Raum, einen Potenzoperator oder eine Funktionssyntax wie sin(x) einzubauen. |