compilerbau:lexer:start
Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende Überarbeitung | ||
compilerbau:lexer:start [2021/10/28 20:11] – Martin Pabst | compilerbau:lexer:start [2021/12/29 11:29] (aktuell) – Externe Bearbeitung 127.0.0.1 | ||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
====== Lexer ====== | ====== Lexer ====== | ||
+ | |||
+ | <WRAP center round tip 60%> | ||
+ | Im folgenden findest Du die Beschreibung des Lexers. Den [[.test: | ||
+ | </ | ||
+ | |||
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: | 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: | ||
Zeile 39: | Zeile 44: | ||
Da wir die TokenTypen der Einfachheit halber auch als Typen der Knoten des vom Parser generierten Syntaxbaums verwenden, gibt es einen zusätzlichen Typ '' | Da wir die TokenTypen der Einfachheit halber auch als Typen der Knoten des vom Parser generierten Syntaxbaums verwenden, gibt es einen zusätzlichen Typ '' | ||
+ | ===== Die Klasse Token ===== | ||
+ | |||
+ | 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 '' | ||
+ | |||
+ | <code java> | ||
+ | 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.text; | ||
+ | } | ||
+ | |||
+ | 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 = "" | ||
+ | |||
+ | switch(tokenType) { | ||
+ | | ||
+ | s += " | ||
+ | | ||
+ | | ||
+ | s += " | ||
+ | | ||
+ | | ||
+ | | ||
+ | } | ||
+ | |||
+ | return s; | ||
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Funktionsweise des Lexers ===== | ||
+ | Die Klasse Lexer bekommt im Konstruktor den Programmtext übergeben und speichert ihn im Attribut '' | ||
+ | |||
+ | <code java> | ||
+ | public class Lexer { | ||
+ | |||
+ | /** | ||
+ | * Die Zeichenkette wird von links nach rechts abgearbeitet. Hier ist | ||
+ | * gespeichert, | ||
+ | */ | ||
+ | private int position; | ||
+ | |||
+ | /** | ||
+ | * Liste zur Speicherung der ermittelten Tokens | ||
+ | */ | ||
+ | private ArrayList< | ||
+ | |||
+ | /** | ||
+ | * Wir speichern den Programmtext in einem Attribut der Klasse, damit er | ||
+ | * nachher nicht von Methode zu Methode weitergereicht werden muss. | ||
+ | */ | ||
+ | private String text; | ||
+ | |||
+ | /** | ||
+ | * | ||
+ | * @param text | ||
+ | * Programmtext, | ||
+ | */ | ||
+ | public Lexer(String text) { | ||
+ | |||
+ | this.text = text; | ||
+ | |||
+ | position = 0; // Wir beginnen mit dem Zeichen ganz links | ||
+ | |||
+ | tokenListe = new ArrayList< | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | Die Methode '' | ||
+ | |||
+ | <code java> | ||
+ | /** | ||
+ | * Zerlegt den Programmtext, | ||
+ | * | ||
+ | * @return Liste mit Tokens | ||
+ | * @throws Exception | ||
+ | * Eine Exception wird ausgelöst, wenn der Programmtext Passagen | ||
+ | * | ||
+ | */ | ||
+ | public ArrayList< | ||
+ | |||
+ | /** | ||
+ | * Wiederholung, | ||
+ | */ | ||
+ | while(position < text.length()) { | ||
+ | |||
+ | /** | ||
+ | * peek() liest das nächste Zeichen im Programmtext, | ||
+ | * Variable position aber nicht. Ruft man peek() mehrmals | ||
+ | * hintereinander auf, liefert es also immer dasselbe Zeichen. | ||
+ | */ | ||
+ | char c = peek(); | ||
+ | |||
+ | if(istZiffer(c)) { | ||
+ | lexZahl(); | ||
+ | } else if(istBuchstabe(c)) { | ||
+ | lexText(); | ||
+ | } else { | ||
+ | switch(c) { | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | // Leerzeichen werden einfach überlesen | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | System.exit(0); | ||
+ | } | ||
+ | |||
+ | position++; | ||
+ | // verarbeitet, | ||
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | return tokenListe; | ||
+ | |||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | ===== Die Hilfsmethoden lexZahl() und lexText() ===== | ||
+ | <code java> | ||
+ | /** | ||
+ | * Die Methode lexText geht davon aus, dass das nächste zu verarbeitende | ||
+ | * Zeichen ein Buchstabe ist. Solange weitere Buchstaben oder Ziffern | ||
+ | * kommen, liest sie sie und setzt sie zu einem Variablennamen zusammen. | ||
+ | */ | ||
+ | private void lexText() { | ||
+ | |||
+ | String variablenBezeichner = ""; | ||
+ | |||
+ | do { | ||
+ | char c = peek(); | ||
+ | variablenBezeichner += c; | ||
+ | position++; | ||
+ | } while(Character.isLetter(peek()) || Character.isDigit(peek()) || peek() == ' | ||
+ | |||
+ | tokenListe.add(new Token(variablenBezeichner)); | ||
+ | |||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Die Methode lexZahl liest eine Zahl | ||
+ | */ | ||
+ | private void lexZahl() { | ||
+ | |||
+ | String zahlAlsString = ""; | ||
+ | |||
+ | do { | ||
+ | char c = peek(); | ||
+ | zahlAlsString += c; | ||
+ | position++; | ||
+ | } while(Character.isDigit(peek()) || peek() == ' | ||
+ | |||
+ | /** | ||
+ | * Hier machen wir es uns leicht und lassen Java den String in eine Zahl | ||
+ | * konvertieren. Die Methode parseDouble ist für sich genommen natürlich | ||
+ | * auch ein Lexer. | ||
+ | */ | ||
+ | double zahl = Double.parseDouble(zahlAlsString); | ||
+ | |||
+ | tokenListe.add(new Token(zahl)); | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | Zum einfachen Hinzufügen eines Tokens, das weder eine Zahl noch Text darstellt, wird eine kleine Hilfsmethode definiert: | ||
+ | <code java> | ||
+ | /** | ||
+ | * Fügt der tokenListe das übergebene Token hinzu | ||
+ | * | ||
+ | * @param tokenType | ||
+ | */ | ||
+ | private void addToken(TokenType tokenType) { | ||
+ | tokenListe.add(new Token(tokenType)); | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | Der Zugriff auf den Programmtext geschieht immer über die Methode peek(), damit weniger Fehler beim Programmieren passieren: | ||
+ | <code java> | ||
+ | /** | ||
+ | * peek() liest das nächste Zeichen im Programmtext, | ||
+ | * position aber nicht. Ruft man peek() mehrmals hintereinander auf, liefert | ||
+ | * es also immer dasselbe Zeichen. | ||
+ | | ||
+ | * @return nächstes Zeichen im Programmtext | ||
+ | */ | ||
+ | private char peek() { | ||
+ | | ||
+ | return text.charAt(position); | ||
+ | } else { | ||
+ | return 0x; | ||
+ | } | ||
+ | } | ||
+ | </code java> | ||
+ | |||
+ | Die restlichen Methoden erklären sich von selbst: | ||
+ | <code java> | ||
+ | /** | ||
+ | * Vorsicht: Die übergebene Liste ist nur dann gefüllt, wenn zuvor die | ||
+ | * Methode lex aufgerufen wurde. | ||
+ | | ||
+ | * @return Liste mit den Tokens, in die der Lexer den Programmtext zerlegt | ||
+ | | ||
+ | */ | ||
+ | public ArrayList< | ||
+ | return tokenListe; | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Nur zu Debuggingzwecken | ||
+ | */ | ||
+ | public String toString() { | ||
+ | |||
+ | | ||
+ | |||
+ | | ||
+ | s += token.toString() + " "; | ||
+ | } | ||
+ | |||
+ | | ||
+ | s = s.substring(0, | ||
+ | } | ||
+ | |||
+ | | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | [[..: | ||
compilerbau/lexer/start.txt · Zuletzt geändert: 2021/12/29 11:29 von 127.0.0.1