Warning: include_once(/var/www/html/pmwiki-2.2.86/cookbook/soap4pmwiki/soap4pmwiki.php): failed to open stream: No such file or directory in /var/www/html/fields/dbp15/local/config.php on line 4

Warning: include_once(): Failed opening '/var/www/html/pmwiki-2.2.86/cookbook/soap4pmwiki/soap4pmwiki.php' for inclusion (include_path='.:/opt/php/lib/php') in /var/www/html/fields/dbp15/local/config.php on line 4

Warning: Cannot modify header information - headers already sent by (output started at /var/www/html/fields/dbp15/local/config.php:4) in /var/www/html/pmwiki-2.2.86/pmwiki.php on line 1250
Datenbankpraktikum SS 2015 - D - Lexer

Der Lexer

Der Lexer oder auch Tokenizer ist eine Vorstufe des Parsers wo die SQL-Abfragen vor der semantischen Interpretation verarbeitet werden, indem der Query-String in unterschiedliche Token eingeteilt wird. Der Lexer unserer Datenbank ist als Iterator implementiert. Bei der Initialisierung eines Lexers im Parser wird der Input-Query-String mitgegeben und es ist fortan möglich am Lexer entweder .next() oder .next_real() aufzurufen. Ersteres gibt jedes Token zurück, das der Lexer dem Query-String zuordnet, letzteres lässt die Leerzeichen-Token raus, da diese dem Parser keine neuen Informationen geben würden.

Der Lexer gibt die Option auf einen Token-Span zurück. Gibt es keinen nächsten Token, wird None zurückgegeben, wenn eines vorhanden ist, wird nicht nur das Token sondern auch die Position des Tokens angegeben um es ggf. zu markieren.

Der Lexer nimmt den Query-String als Input und verarbeitet diesen zu Token

Unterstützte Token

Token, die zurückgegeben werden können, beinhalten Literale (Rückgabe von Strings, Integers und Floats) mit ihren Werten, Wörter mit Werten und Token ohne Wert. Token ohne eigenen Wert sind Trennungszeichen (Klammern und Anführungszeichen), mathematische Operatoren, Leerzeichen und den Stern, der in SQL-Query-Strings als Multiplikation oder als Wildcard z.B. in SELECT-Abfragen gedeutet werden kann. Diese Interpretation erfolgt jedoch noch nicht im Lexer. Für alle sonstigen Zeichen, die nicht in SQL-Abfragen verwendet werden, wird ein Unknown-Token zurückgegeben.

Ablauf

Um die gesamte syntaktische und semantische Verarbeitung getrennt von der restlichen Verarbeitung des Query-Strings zu halten, ist der Lexer dem Parser vorangestellt. Der Input des Users, der SQL-Query-String, wird dem Lexer gegeben und dieser teilt diesen in sinnvoll zusammenhängende Einheiten ein, die in Token eingeordnet werden und an den Parser zur weiteren Interpretation gereicht werden.

Der Lexer merkt sich dafür eine aktuellen und den nächsten Character.

Die Hauptarbeit des Lexers findet in dessen Implementation von .next() statt, die der Lexer als Iterator bestizen muss. Hier wird der aktuelle Character aus dem Query-String gesucht und gegebenenfalls, falls keiner vorhanden ist, eine None-Option zurückgegeben, sodass der Parser weiß, dass keine Token mehr folgen. Wenn ein Character gefunden wird, wird dieser analysiert.

Bei einfachen Satzzeichen und mathematischen Operatoren, die nur aus einem Zeichen bestehen, ist die Analyse einfach. Passt der Character auf eines dieser Zeichen, wird ein zugehöriges Token ohne einen Wert in den Tokenspan gegeben und als Rückgabewert gesetzt.

Um mehrstellige Operatoren zu erkennen, wird bei möglichen Kandidaten (z.B. <, >) auch noch der nächste Character untersucht: Ist dieser die Vervollständigung des Operatoren, wird dieser als Token zurückgegeben, ansonsten der einstellige. Leerzeichen werden als ein Whitespace-Token gesetzt, jedoch in einer Komprimierung. Egal wie viele Leerzeichen hintereinander folgen, es wird nur ein Whitespace-Token zurückgegeben, das dann in der .next_real()-Methode ebenfalls übersprungen wird und damit keine Leerzeichen mehr zwischen den Token vorhanden sind.

Um Zahlen und Nummern als syntaktische Einheit zu erkennen, werden unterschiedliche Methoden aufgerufen, wenn der aktuelle Character ein Buchstabe oder eine Zahl ist.

fn scan_words(&mut self) -> String {
	let mut s = String::new();
	// Loop until the end of a word (only letters, numbers and _)
	loop {
	    // Take current char
	    match self.curr.unwrap_or(' ') {
		c @ 'a' ... 'z' |
		c @ 'A' ... 'Z' |
		c @ '0' ... '9' |
		c @ '_' => {
		    // Push letter into return string
		    s.push(c);
		},
		// If no letter, then stop loop
		_ => break
	    }
	    // Next char
	    self.bump();
	}
	s
}

Bei einem Word-Token wird die .scan_words()-Methode aufgerufen. In dieser Methode wird eine Schleife durchlaufen und an den zurückgegebenen String werden solange Character hinzugefügt, wie diese ein Buchstabe oder eine Zahl oder ein Unterstrich sind, ansonsten wird abgebrochen. Das daraus resultierende Wort wird dem Word-Token als Wert mitgegeben.

Ist der aktuelle Character eine Zahl, wird die Methode .scan_nums() aufgerufen. Diese gibt einen String zurück und lässt neben Zahlen auch Punkte zu, prüft an dieser Stelle aber noch nicht die Validität der Zahl. Dies wird gemacht, bevor das Token gesetzt wird. Zuerst wird geprüft, ob die Zahl als ein Integer geparst werden kann, schlägt das fehl, wird versucht es als float auszuwerten. Ist eines davon erfolgreich, wird entweder ein Literal(Lit::Int())- oder ein Literal(Lit::Float())-Token mit dem jeweiligen Wert zurückgegeben. Ist das Parsen nicht erfolgreich, wird ein Unknown-Token als Rückgabewert gesetzt.

Die letzte Einteilung, die der Lexer treffen kann, ist die in String-Literale. Literale, die der Lexer erkennen kann, sind alle Zahlen, ebenso Booleans und Strings, die in Anführungszeichen stehen. Booleans werden jedoch noch nicht im Lexer als solche Literale erkannt, da dies eine semantische Verarbeitung bedeuten würde. Die Zahlen werden wie oben beschrieben behandelt. Für die String-Literale wird bei der Erkennung des aktuellen Characters als Anführungszeichen (dabei ist es egal ob es ein einfaches oder ein doppeltes Anführungszeichen ist) die Methode .scan_lit() aufgerufen. In dieser Methode wird alles in einen String gebaut, das nach dem Anführungszeichen folgt, bis das schließende Anführungszeichen gesetzt wird. Sollte der Benutzer der Datenbank bei seiner Eingabe das schließende Anführungszeichen vergessen, wird ein Error geworfen, damit der Lexer nicht in einer Endlos-Schleife endet. Sollte jedoch alles ordnungsgemäß eingegeben worden sein, wird das schließende Anführungszeichen übersprungen, um nach der Methode auf dem nächsten Character zu stehen. Als Rückgabe eines String-Literals wird ein Literal(Lit::String())-Token gegeben, in dem der Wert des Literals ohne die Anführungszeichen eingetragen ist, sodass der Parser den Wert direkt verarbeiten kann.


Autor: Stephanie Heyderich
Gruppe: Stephanie Heyderich, Lisa Konieczny


Page last modified on September 24, 2015, at 02:36 PM