Übersicht über die Schritte des Compilers
Lexer
Der Lexer liest den Programmtext Zeichen für Zeichen und fasst diese zu kleinsten syntaktischen Einheiten, sogenannten Tokens, zusammen. Unsere Beispielsprache besteht aus folgenden 8 Tokens:
- +
- -
- *
- /
- (
- )
- Zahlen (die aus Ziffern und Kommata bestehen), z.B.
1,278
- Variablenbezeichnern, die mit einem Buchstaben des (englischen!) Alphabets beginnen und danach aus Buchstaben, Ziffern oder einem Unterstrich bestehen.
Die Tokens können im Programmtext unmittelbar hintereinander stehen, sie können aber auch durch Leerzeichen oder Zeilenumbrüche ('whitespace') voneinander getrennt sein. Whitespace wird vom Lexer einfach überlesen.
Den Programmtext
1 + 2 * (4 * a1 - 3)
zerlegt der Lexer also in folgende 11 Tokens:
zahl[1.0] plus zahl[2.0] mal klammerAuf zahl[4.0] mal text[a1] minus zahl[3.0] klammerZ
Damit der Parser die Tokens nachher weiterverarbeiten kann, muss zu jedem Token sein Typ (Zahl, plus, text, …) sowie - im Falle von Zahlen und Variablen - auch die konkrete Zahl bzw. die konkrete Variable gespeichert werden. Ein Token wird daher als Objekt der Klasse Token
abgebildet:
package lexer; public class Token { private TokenType tokenType; // Art des Tokens (z.B. zahl, klammerAuf usw.) private double zahl; // Wird nur belegt, wenn tokenType == zahl private String text; // Wird nur belegt, falls tokenType == bezeichner public Token(double zahl){ this.zahl = zahl; tokenType = TokenType.zahl; } public Token(String text){ this.text = text; tokenType = TokenType.variable; } public Token(TokenType tokenType){ this.tokenType = tokenType; } public TokenType getTokenType() { return tokenType; } public double getZahl() { return zahl; } public String getText() { return text; } /** * Die toString()-Methode dient nur Debuggingzwecken */ @Override public String toString() { String s = "" + tokenType; switch (tokenType) { case zahl: s += "[" + zahl + "]"; break; case variable: s += "[" + text + "]"; break; default: break; } return s; } }
Die Klasse TokenType ist einfach eine Enum-Klasse mit den möglichen Typen:
public enum TokenType { zahl, text, plus, minus, mal, geteilt, klammerAuf, klammerZu, /** * Nur als Knotentyp für Knoten des Syntaxbaums: */ negation }
Da wir die TokenTypen der Einfachheit halber auch als Typen der Knoten des vom Parser generierten Syntaxbaums verwenden, gibt es einen zusätzlichen Typ negation
. Dazu später mehr.