To receive notifications about scheduled maintenance, please subscribe to the mailing-list gitlab-operations@sympa.ethz.ch. You can subscribe to the mailing-list at https://sympa.ethz.ch

Commit 60b4a4c1 authored by Luzian Bieri's avatar Luzian Bieri
Browse files

BNF cookbook

parent 5319cf72
Pipeline #96899 failed with stages
in 45 seconds
# (E)BNF Kochrezept
Zu [BNF](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form), resp. [EBNF](https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form): (E)BNF definiert - durch Rekursion - Regeln (die sog. Grammatik), wieein Set aus Elementen eines sogenannten Alphabetes zu einem Ausdruck (→Satz,bzw. word) zusammengesetzt werden darf.
- [(E)BNF Kochrezept](#ebnf-kochrezept)
- [BNF](#bnf)
- [EBNF](#ebnf)
- [Kochrezepte](#kochrezepte)
- [Erster Schritt](#erster-schritt)
- [Auswahl an Zeichen](#auswahl-an-zeichen)
- [Abfolge von eigenen BNF Sätzen](#abfolge-von-eigenen-bnf-sätzen)
- [Wiederholung](#wiederholung)
- [Option](#option)
- [Auswahl zwischen mehreren eigenen BNF Sätzen](#auswahl-zwischen-mehreren-eigenen-bnf-sätzen)
- [Kochrezepte Beispiel](#kochrezepte-beispiel)
- [Erster Schritt (Beispiel)](#erster-schritt-beispiel)
- [Auswahl an aufeinanderfolgenden Buchstaben in ASCII (Beispiel)](#auswahl-an-aufeinanderfolgenden-buchstaben-in-ascii-beispiel)
- [Auswahl an Zeichen (Beispiel)](#auswahl-an-zeichen-beispiel)
- [Abfolge von eigenen BNF Sätzen (Beispiel)](#abfolge-von-eigenen-bnf-sätzen-beispiel)
- [Wiederholung (Beispiel)](#wiederholung-beispiel)
- [Option (Beispiel)](#option-beispiel)
- [Auswahl zwischen mehreren eigenen BNF Sätzen (Beispiel)](#auswahl-zwischen-mehreren-eigenen-bnf-sätzen-beispiel)
- [Definierte Funktionen (gegeben)](#definierte-funktionen-gegeben)
- [Consume](#consume)
- [Lookahead](#lookahead)
Eine komplette (E)BNF besteht immer aus einem Alphabet und einem Set Regeln.Das Alphabet kann auch implizit durch die Regeln gegeben werden. Beim Zusammensetzen aller gegebenen Regeln erhalten wir eine Grammatik.
## BNF
BNF kennt grundsätzlich nur ein Zeichen: das `|` (=oder).
```c++
seq = term | term "_" seq
term = "A" | "A" lowerterm | lowerterm
lowerterm = "a" | "a" lowerterm
```
## EBNF
Um BNF ein bisschen schöner darzustellen wurde EBNF definert und 2 weitere Zeichen hinzugefügt: `[...]` (=Option, 0 oder einmal) und `{...}` (=Wiederholung, 0-unendlich oft).
```c++
seq = term ["_" seq ]
term = "A" {"a"} | "a" {"a"}
```
## Kochrezepte
>Disclaimer: Ich kann nicht garantieren, dass dieses Kochrezept alle einzelnen Fälle abdeckt aber sicher den grössten Teil.
Ein Kapitel weiter findet ihr die Korchrezepte mal angewandt.
### Erster Schritt
Für jeden Satz eine leere Funktion erstellen. Tipp klein geschrieben returned bool, nimmt einen `std::istream` as Argument.
```c++
// X = ...
bool x(std::istream& is){
...
}
```
### Auswahl an Zeichen
Durch mehrere `if`s oder mehrer `||` alle Optionen auprobieren. Für Buchstaben und Zahlen gibs eine Abkürzung (siehe Beispiel).
```c++
// X = 'a' | 'b' | 'c'
bool x(std::istream& is){
return consume(is, 'a') || consume(is, 'b') || consume(is, 'c');
}
```
### Abfolge von eigenen BNF Sätzen
Für jedes Element ein `if`, wenns `false` zurück gibt, dann `return false`. Ganz am Schluss ein `return true`.
```c++
// X = Y Z
bool x(std::istream& is){
if (!y(is)) return false;
if (!z(is)) return false;
return true;
}
// schöner
bool x(std::istream& is){
return y(is) && z(is);
}
```
### Wiederholung
Mit `while` all Elemente, die passen, löschen. 0 ist ja auch richtig für die Anzahl Wiederholungen, daher immer `true`;
```c++
// X = {Y}
bool x(std::istream& is){
while(!y(is));
return true;
}
```
Andere Bedingungen nicht vergessen!
```c++
// X = W {Y} Z
bool x(std::istream& is){
if (!w(is)) return false;
while(!y(is));
return z(is);
}
```
### Option
Was genau in der Option drin ist, ist uns egal, consumen nicht vergessen! Wenn die option angefangen hat, dann muss sie auch fertig gemacht werden!
```c++
// X = [Y]
bool x(std::istream& is){
y(is);
return true;
}
// X = W [Y Z]
bool x(std::istream& is){
if (!w(is)) return false;
if (y(is)) return z(is); // Wenn y kommt, dann MUSS auch z vorkommen.
return true;
}
```
Andere Bedingungen nicht vergessen!
```c++
// X = W [Y] Z
bool x(std::istream& is){
if (!w(is)) return false;
y(is);
return z(is);
}
```
### Auswahl zwischen mehreren eigenen BNF Sätzen
**Schwierigster Fall!** Wir müssen eine Lösung finden um zu entscheiden Welcher der jeweiligen Sätze wir anwenden können. --> Sätze genau studiere und schauen ob und wie sie sich **am Anfang unterscheiden**. Dann `lookahead` verwenden.
```c++
// X = Y | Z | W
// W = '+' ...
// Z = '1' ...
// Y = 'a' ...
bool x(std::istream& is){
if (lookahead(is) == '+') return w(is); // wenn ein + kommt muss auch ein W kommen
if (lookahead(is) == '1') return z(is); // wenn ein 1 kommt muss auch ein Z kommen
return y(is); // ist die letzte möglichkeit.
}
```
## Kochrezepte Beispiel
Wir verwenden folgende BNF als Beispiel:
```c++
Expression = Operator Operand Operand
Operator = + | - | / | *
Operand = Number | Expression
Number = Integer | Integer . Integer
Integer = Digit | Digit Integer
Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
```
### Erster Schritt (Beispiel)
Für jeden Satz eine leere Funktion erstellen:
```c++
bool Expression(std::istream& is){}
bool Operator(std::istream& is){}
bool Operand(std::istream& is){}
bool Number(std::istream& is){}
bool Integer(std::istream& is){}
bool Digit(std::istream& is){}
```
### Auswahl an aufeinanderfolgenden Buchstaben in ASCII (Beispiel)
```c++
Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
```
Liest sich: `Digit` ist eine Ziffer zwischen `0` und `9`, resp. `Digit` ist `0` oder `1` oder, ...
```c++
bool digit(std::istream& is){
char next = lookahead(is);
if(next >= '0' && next <= '9'){
return consume(is, next); // immer true
}
return false;
}
```
Gleiches mit Buchstaben:
```c++
Example = a | b | c | d | e | f
...
if(next >= 'a' && next <= 'f'){
...
```
### Auswahl an Zeichen (Beispiel)
```c++
Operator = + | - | / | *
```
Liest sich: `Operator` ist eins der Zeichen `+`, `-`, `*`, `/`.
```c++
bool operator_(std::istream& is){
return consume(is, '+') ||
consume(is, '-') ||
consume(is, '*') ||
consume(is, '/');
}
```
### Abfolge von eigenen BNF Sätzen (Beispiel)
```c++
Expression = Operator Operand Operand
```
Liest sich: `Expression` ist ein `Operator` gefolgt von einem `[space]`, gefolgt von einem `Operand`, gefolgt von einem `[space]`, gefolgt von einem `Operand`.
```c++
bool expression(std::istream& is){
if(!operator_(is)) return false; // wenn kein Operator kommt dann fail
if(!consume(is,' ')) return false; // wenn kein [space] kommt dann fail
if(!operand(is)) return false; // wenn kein Operand kommt dann fail
if(!consume(is,' ')) return false; // wenn kein [space] kommt dann fail
if(!operand(is)) return false; // wenn kein Operand kommt dann fail
return true; // alle anforderungen erfüllt
}
```
### Wiederholung (Beispiel)
```c++
BNF:
Integer = Digit | Digit Integer
EBNF:
Integer = Digit { Digit }
```
Liest sich: `Integer` besteht aus mindestens einem `Digit`, resp. einem `Digit` und einer Wiederholung von `Digit`s
```c++
//Integer = Digit | Digit Integer
bool integer(std::istream& is){
if(!digit(is)){
return false; // mindestens ein Digit brauchen wir
}
integer(is); // Rekursion schnapp sich alle weitern Digits
return true;
}
```
```c++
//Integer = Digit { Digit }
bool integer(std::istream& is){
if(!digit(is)){
return false; // mindestens ein Digit brauchen wir
}
while(digit(is)); // Schnappt sich alle weitern Digits ohne Rekursion
return true;
}
```
### Option (Beispiel)
```c++
BNF:
Number = Integer | Integer . Integer
EBNF:
Number = Integer [ . Integer ]
```
Liest sich: `Number` besteht aus einem `Integer` eventuell gefolgt von einem `.` UND einem `Integer`, resp. einer `Number` ODER einer `Number`, einem `.` und einem `Integer`
```c++
//Number = Integer | Integer ’.’ Integer
//Number = Integer [ ’.’ Integer ]
bool number(std::istream& is){
if(!integer(is)){
return false; // Mindestens ein Integer brauchen wir
}
if(lookahead(is) == '.'){ // falls wir in der Option sind ...
consume(is, '.');
return integer(is); // brauchen wir auch ein Integer
}
return true;
}
// schöner geht aber nur wenn wir den character direkt auch löschen dürfen
bool number(std::istream& is){
if(!integer(is)){
return false; // Mindestens ein Integer brauchen wir
}
if(consume(is, '.')){
return integer(is);
}
return true;
}
// funktion da oben würde failen wenn z.B. Number = Integer [ Expression ] gelten würde
```
### Auswahl zwischen mehreren eigenen BNF Sätzen (Beispiel)
```c++
Operand = Number | Expression
```
Liest sich: `Operand` ist entweder eine `Number` oder eine `Expression`
```c++
// operand = number | expression
bool operand(std::istream& is){
char next = lookahead(is);
if(next == '+' || next == '-' || // rausfinden ob wir auf der Expression-...
next == '*' || next == '/'){ // ...oder der Number-Seite sind
return expression(is);
}
else{
return number(is);
}
}
```
### Definierte Funktionen (gegeben)
#### Consume
```c++
// PRE: Valid input stream is.
// POST: Reads char from stream and returns true if that char matches
// the expected char, otherwise false is returned.
bool consume(std::istream& is, char expected) {
char actual;
is >> actual;
return is.good() && actual == expected;
}
```
#### Lookahead
Gibt den nächsten Buchstaben im stream zurück.
```c++
// PRE: Valid input stream is.
// POST: Leading whitespace characters are extracted from is, and the
// first non-whitespace character is returned (0 if there is none).
char lookahead(std::istream& is) {
is >> std::ws; // skip whitespaces
char next = is.peek();
if (is.good()) return next;
else return 0;
}
```
......@@ -40,7 +40,7 @@ def create_readme():
mdFile.new_header(level=3, title='PVK')
mdFile.new_line()
table_from_array(mdFile, config.pvk)
mdFile.new_paragraph("Slides zum PVK: [slides.com](https://slides.com/luzianbieri/informatik-i-pvk-2019)")
mdFile.new_paragraph("Slides zum PVK: [slides.com](https://slides.com/luzianbieri/informatik-i-pvk-2020)")
mdFile.new_header(level=3, title='Zusammenfassungen')
mdFile.new_line()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment