Dies ist eine alte Version des Dokuments!
Inhaltsverzeichnis
Erweiterung der Sprache
Bisher kennt unsere Programmiersprache nur Terme (expressions), keine Anweisungen (statements). Wir wollen sie daher erweitern, so dass beispielsweise folgendes Programm kompiliert und ausgeführt werden kann:
a = 1; b = 2; while(a < 10){ a = a + 1; b = b * 2; print(b); }
Erweiterung der Grammatik
Die Grammatik dieser Sprache in EBNF sieht so aus:
Ziffer = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ; Buchstabe = "A" | "B" | "C" | "D" | … | "Z" | "a" | "b" | … | "z" ; Zahl = Ziffer { Ziffer | "." } ; Text = Buchstabe { Buchstabe | Ziffer | "_" } ; TrueFalse = "true" | "false" ; Term = "(" Aussage ")" | Zahl | Text | TrueFalse | "-" Term ; Produkt = Term | Term "*" Term ; Quotient = Term | Term "/" Term ; ProduktQuotient = Produkt | Quotient ; Summe = ProduktQuotient | ProduktQuotient "+" ProduktQuotient ; Differenz = ProduktQuotient | ProduktQuotient "-" ProduktQuotient ; SummeDifferenz = Summe | Differenz ; Vergleichsoperator = "<" | ">" | "==" | "<=" | ">=" | "!=" ; Aussage = SummeDifferenz | SummeDifferenz Vergleichsoperator SummeDifferenz ; Zuweisung = Text "=" Aussage ";" Wiederholung = "while" "(" Aussage ")" "{" Sequenz "}" ; Ausgabe = "print" "(" Aussage ")" ; Anweisung = Zuweisung | Wiederholung | Ausgabe ; Sequenz = { Anweisung }; Programm = Sequenz ;
Neue Tokentypen
Für die neuen syntaktischen Elemente brauchen wir zusätzliche Tokentypen:
public enum TokenType { zahl, text, plus, minus, mal, geteilt, klammerAuf, klammerZu, geschweifteKlammerAuf, geschweifteKlammerZu, whileKeyword, printKeyword, kleiner, groesser, identisch, kleinergleich, groessergleich, ungleich, zuweisung, trueKeyword, falseKeyword, strichpunkt, /** * Nur als Knotentyp für Knoten des Syntaxbaums: */ negation }
Erweiterung des Lexers
Damit der Lexer die Schlüsselwörter while
, print
, true
und false
erkennt, erweitern wir einfach die Methode lexText()
:
/** * Die Methode lexText lext Variablenbezeichner und Schlüsselwörter (keywords) */ private void lexText() { String text = ""; do { char c = peek(); text += c; position++; } while(istBuchstabe(peek()) || istZiffer(peek()) || peek() == '_'); switch(text) { case "true" : tokenListe.add(new Token(TokenType.trueKeyword)); break; case "false" : tokenListe.add(new Token(TokenType.falseKeyword)); break; case "while" : tokenListe.add(new Token(TokenType.whileKeyword)); break; case "print" : tokenListe.add(new Token(TokenType.printKeyword)); break; default : tokenListe.add(new Token(text)); break; } }
Etwas trickreicher sind die neuen Zeichen. Um etwa <
und < =
unterscheiden zu können, muss der Lexer ein Zeichen nach vorne blicken können. Wir brauchen daher eine zusätzliche peek(int n)
-Methode, die n
Zeichen nach vorne blickt:
/** * peek(n) liest das Zeichen im Programmtext an (aktuelle Position + n). Die * aktuelle Position (Attribut position) wird nicht verändert. * * @return Das Zeichen, das n Zeichen weiter steht als die aktuelle Position */ private char peek(int n) { if(position + n < text.length()) { return text.charAt(position + n); } else { return(char) 0; } }
Damit sieht die Erkennung von >
, >=
und !=
so aus:
case '>' : if(peek(1) == '=') { addToken(TokenType.groessergleich); position++; } else { addToken(TokenType.groesser); } break; case '!' : if(peek(1) == '=') { addToken(TokenType.ungleich); position++; } else { println("Das Zeichen = wird erwartet.", Color.red); System.exit(1); } break;