Syntax arithmetischer Ausdrücke

Mit Hilfe einer Syntaxbeschreibung in BNF können wir, wie das folgende Beispiel zeigt, formal festlegen, welche Zeichenketten vollständig geklammerten arithmetischen Ausdrücken in Python-Schreibweise entsprechen. Üblicherweise beginnen Nichtterminalsymbole mit Großbuchstaben und Terminalsymbole sind zwischen Hochkommata notiert.

Exp   ::= Var
        | Val
        | '(' Exp Op Exp ')'
        | Fun '(' Exps ')'
      
Var   ::= 'x' | 'y' | 'z'

Val   ::= Num | '-' Num

Num   ::= Digit | Digit Num

Digit ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'

Op    ::= '+' | '-' | '*' | '/' | '**'

Fun   ::= 'sqrt' | 'sin' | 'cos' 

Exps  ::= Exp | Exp ',' Exps

Die Ableitungsregeln einer BNF geben für jedes Nichtterminalsymbol hinter dem Zeichen ::= an, wie dieses abgeleitet werden kann. Alternative Ableitungsmöglichkeiten werden dabei durch einen senkrechten Strich | getrennt.

Aus einer BNF kann man die in der beschriebenen Sprache enthaltenen Wörter schrittweise ableiten. Dazu beginnt man mit einem Nichtterminalsymbol und ersetzt schrittweise Nichtterminalsymbole durch eine mögliche Ableitung, bis alle Nichtterminalsymbole ersetzt sind. Zum Beispiel zeigt die folgende Ableitung, dass (sqrt(((x**2)+1))/x) ein Wort in der beschriebenen Sprache vollständig geklammerter arithmetischer Ausdrücke ist, da wir es aus dem Nichtterminalsymbol Exp ableiten können:

Exp
(Exp Op Exp)
(Exp/Exp)
(Exp/Var)
(Exp/x)
(Fun(Exps)/x)
(sqrt(Exps)/x)
(sqrt(Exp)/x)
(sqrt((Exp Op Exp))/x)
(sqrt((Exp+Exp))/x)
(sqrt((Exp+Val))/x)
(sqrt((Exp+Num))/x)
(sqrt((Exp+Digit))/x)
(sqrt((Exp+1))/x)
(sqrt(((Exp Op Exp)+1))/x)
(sqrt(((Exp**Exp)+1))/x)
(sqrt(((Exp**Val)+1))/x)
(sqrt(((Exp**Num)+1))/x)
(sqrt(((Exp**Digit)+1))/x)
(sqrt(((Exp**2)+1))/x)
(sqrt(((Var**2)+1))/x)
(sqrt(((x**2)+1))/x)

Wie wir sehen, kann eine solche Ableitung aufwendig werden, insbesondere deshalb, weil sich von einem Schritt zum nächsten nur wenig ändert und der Rest des bisher abgeleiteten Wortes unverändert übernommen werden muss. Statt Ableitungen wie oben gezeigt zu notieren, können wir sie auch mit einem sogenannten Ableitungsbaum darstellen. Zum Beispiel beschreibt der folgende Ableitungsbaum die Ableitung des Wortes (3+x) aus dem Nichtterminalsymbol Exp. Hierbei sind Nichtterminalsymbole in abgerundete und Terminalsymbole in eckige Kästen gesetzt.

graph TD e0(["Exp"]) --> t0["("] & e1(["Exp"]) & o0(["Op"]) & e2(["Exp"]) & t1[")"] e1 --> f1(["Val"]) --> t4["3"] o0 --> t5["+"] e2 --> v3(["Var"]) --> t15["x"]

Die Wurzel des Ableitungsbaums ist mit dem Nichtterminalsymbol Exp beschriftet, aus dem das Wort (3+x) abgeleitet wird.

Im Allgemeinen bilden die Nichtterminalsymbole die inneren Knoten des Baums. Die Kindknoten eines inneren Knotens entsprechen der rechten Seite der Regel, die im entsprechenden Ableitungsschritt angewendet wurde. So hat die Wurzel des gezeigten Ableitungsbaums fünf Kinder, die der rechten Seite der als Erstes angewendeten Regel Exp ::= '(' Exp Op Exp ')' entsprechen.

Die Blätter des Ableitungsbaums sind mit Terminalsymbolen beschriftet. Das abgeleitete Wort ergibt sich, indem man die Blätter des Baums (die sogenannte Front) von links nach rechts liest. Hier ergibt sich das Wort (3+x).