Benutzer-Werkzeuge

Webseiten-Werkzeuge


compilerbau:erweiterung:start

Dies ist eine alte Version des Dokuments!


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;

Test des Lexers

Der Lexer lässt sich wieder mit der Klasse LexerTest testen. Den Programmtext oben zerlegt er beispielsweise in folgende Token:

text[a] zuweisung zahl[1.0] strichpunkt text[b] zuweisung zahl[2.0] strichpunkt whileKeyword klammerAuf text[a] kleiner zahl[10.0] klammerZu geschweifteKlammerAuf text[a] zuweisung text[a] plus zahl[1.0] strichpunkt text[b] zuweisung text[b] mal zahl[2.0] strichpunkt printKeyword klammerAuf text[b] klammerZu strichpunkt geschweifteKlammerZu

Erweiterung der Klasse Knoten

Auch Anweisungen (Wiederholung, Zuweisung, Print-Anweisung) werden im Syntaxbaum als Knoten dargestellt. Dafür wird neben den Kindknoten rechts und links noch ein dritter KindKnoten naechsteAnweisung benötigt:

public class Knoten {
 
	/**
	 * Im Token steckt der Inhalt des Knotens drin, also ein Operator, eine Zahl oder ein 
	 * Variablenbezeichner. Der Einfachheit halber verwenden wir hier die Klasse Token. 
	 */
	private Token token; 
 
	/**
	 * Kindknoten linkerhand
	 */
	private Knoten links;
 
	/**
	 * Kindknoten rechterhand
	 */
	private Knoten rechts;
 
	/**
	 * Im Falle einer Anweisung: nächstfolgende Anweisung
	 */
	private Knoten naechsteAnweisung;
 
 
...

Die Kindknoten der Anweisungen haben für verschiedene Arten von Anweisungen verschiedene Bedeutung:

wiederholungs-Knoten:

  • links: Aussage innerhalb der Klammer
  • rechts: erste Anweisung innerhalb des while-Blocks (d.h. innerhalb der geschweiften Klammern)
  • naechsteAnweisung: Erste Anweisung, die der while-Anweisung folgt (d.h. erste Anweisung nach der schließenden geschweiften Klammer)

print-Knoten:

  • links: Aussage, deren wert ausgegeben werden soll
  • rechts: null
  • naechsteAnweisung: Die Anweisung, die auf die print-Anweisung folgt.

Zuweisungs-Knoten:

  • links: text-Knoten, der den Bezeichner der Variablen enthält, der etwas zugewiesen werden soll.
  • rechts: Aussage, deren Wert der Variablen zugewiesen werden soll.
  • naechsteAnweisung: Die Anweisung, die auf die Zuweisung folgt.
compilerbau/erweiterung/start.1635449036.txt.gz · Zuletzt geändert: 2021/12/29 11:29 (Externe Bearbeitung)

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki