Compilerbau. Einführung in Bison

Ähnliche Dokumente
Definition Compiler. Bekannte Compiler

Kapitel 5: Syntax-Analyse

Compilerbau Syntaxanalyse 68. LR(1)-Syntaxanalyse

LR(1) Itemmengenkonstruktion

Syntaxanalyse Ausgangspunkt und Ziel

Lexikalische Programmanalyse der Scanner

9 Compilerbau-Werkzeuge

Inhalte der Vorlesung. 6. Syntaxgesteuerte Übersetzung. 6. Syntaxgesteuerte Übersetzung. Attributierter Syntaxbaum

Was ist ein Compiler?

Fachseminar Compilerbau

Alphabet, formale Sprache

Inhalte der Vorlesung. 4. Der Scanner-Generator lex. 4. Der Scanner-Generator lex. 4.1 lex: Grundlagen

Einführung - Parser. Was ist ein Parser?

FACHHOCHSCHULE MANNHEIM

Von der Grammatik zum AST

Syntax von Programmiersprachen

Praktikum Funktionale Programmierung Teil 1: Lexen und Parsen

Einführung in die Informatik Grammars & Parsers

9.4 Grundlagen des Compilerbaus

Grammatiken und ANTLR

Programmierung 2. Übersetzer: Das Frontend. Sebastian Hack. Klaas Boesche. Sommersemester

Grundlagen der Informatik. Prof. Dr. Stefan Enderle NTA Isny

Programmiersprachen und Übersetzer

Lexikalische Analyse, Tokenizer, Scanner

4 Lexikalische Analyse und Parsing

Inhalt Kapitel 5: Syntax

Kellerautomat (1/4) Kellerautomat (2/4) Kellerautomat (3/4) Kellerautomat (4/4)

Software Entwicklung 2. Übersetzerbau 1

Interpreter - Gliederung

Fachseminar WS 2008/09

Formale Sprachen, reguläre und kontextfreie Grammatiken

Werkzeuge zur Programmentwicklung

Kapitel 4. Einführung in den Scannergenerator Flex. Einführung in den Scannergenerator Flex Wintersemester 2008/09 1 / 9

Anwenundg regulärer Sprachen und endlicher Automaten

Automaten und formale Sprachen Klausurvorbereitung

1. Der Begriff Informatik 2. Syntax und Semantik von Programmiersprachen. I.2. I.2. Grundlagen von von Programmiersprachen.

Programmiersprachen und Übersetzer

Teil 7: Interpreterbau Prof. Dr. Max Mühlhäuser FG Telekooperation TU Darmstadt

Vorlesung Informatik I

VU Software Paradigmen / SS 2014

Programmieren in Java

Programmieren in C. Macros, Funktionen und modulare Programmstruktur. Prof. Dr. Nikolaus Wulff

1 Grammar Engineering. 2 Abstrakte Syntax als abstrakte Algebra. 3 LL(1)-Parser. 4 LR Parser. 5 Fehlerbehandlung. 6 Earley Parser

Grammatiken in Prolog

Interdisziplinäre fachdidaktische Übung: Modelle für Sprachen in der Informatik. SS 2016: Grossmann, Jenko

Definition von domänenspezifischen Sprachen mit Xtext: Einführung. 19. November 2014

Vorlesung Programmieren

Theoretische Grundlagen des Software Engineering

Grundlagen der Programmierung 2 (Comp-D)

7. Formale Sprachen und Grammatiken

Grundlagen der Informatik Vorlesungsskript

9 Theoretische Informatik und Compilerbau

6 Kontextfreie Grammatiken

Übungsaufgaben zu Formalen Sprachen und Automaten

Übersetzergenerierung mit lex und yacc

Was bisher geschah Chomsky-Hierarchie für Sprachen: L 0 Menge aller durch (beliebige) Grammatiken beschriebenen Sprachen L 1 Menge aller monotonen

ARBEITSBLATT ZU FORMALEN SPRACHEN

1 Syntax von Programmiersprachen

Algorithmen und Datenstrukturen I - Exkurs Formale Sprachen -

Kapitel 2. Methoden zur Beschreibung von Syntax

kontextfreie Grammatiken Theoretische Informatik kontextfreie Grammatiken kontextfreie Grammatiken Rainer Schrader 14. Juli 2009 Gliederung

Java für Fortgeschrittene Proseminar im Sommersemester 2009 Compilertechnik - Parser, Scanner & Co.

Deklarationen in C. Prof. Dr. Margarita Esponda

Das erste C++ Programm

Formale Sprachen und Automaten

Grammatik Prüfung möglich, ob eine Zeichenfolge zur Sprache gehört oder nicht

LR(k)-Parser. CYK-Algorithmus ist zu langsam.

Compiler, Übersetzer. Allgemeine Erklärung / Definition

Kontextfreie Sprachen

Theorie der Informatik. Theorie der Informatik. 6.1 Einführung. 6.2 Alphabete und formale Sprachen. 6.3 Grammatiken. 6.4 Chomsky-Hierarchie

Automaten und Formale Sprachen alias Theoretische Informatik. Sommersemester 2013

Fachseminar. Semantische Analyse

2.6 Deterministisches Top-Down-Parsen

Begriffe (Wiederholung)

Algorithmen & Programmierung. Ausdrücke & Operatoren (1)

Grundlagen der Informatik III Wintersemester 2010/ Vorlesung Dr.-Ing. Wolfgang Heenes

Propädeutikum. Dipl.-Inf. Frank Güttler

Einleitung Entwicklung in C Hello-World! Konstrukte in C Zusammenfassung Literatur. Grundlagen von C. Jonas Gresens

Algorithmen und Formale Sprachen

Java für Anfänger Teil 2: Java-Syntax. Programmierkurs Manfred Jackel

Algorithmen & Programmierung. Formale Sprachen Die Logik hinter dem Compiler

Formale Sprachen. Eine Einführung. Tim Lethen Düsseldorf Version II (04-06)

3.4 Struktur von Programmen

Umformung NTM DTM. Charakterisierung rek. aufz. Spr. Chomsky-3-Grammatiken (T5.3) Chomsky-0-Grammatik Rek. Aufz.

2.11 Kontextfreie Grammatiken und Parsebäume

Programmierung WS12/13 Lösung - Übung 1 M. Brockschmidt, F. Emmes, C. Otto, T. Ströder

Grundlagen der Theoretischen Informatik

Grundbegriffe. Grammatiken

10 Die Programmiersprache C99: Zusammenfassung

Übung 9. Quellcode Strukturieren Rekursive Datenstrukturen Uebung 9

Transkript:

Compilerbau Einführung in Bison

Überblick 1. Bison a) Eingabespezifikation 1. Prolog 2. Definitionsteil 3. Regelteil 4. Epilog b) Bison-Bezeichner und -Funktionen c) Konflikte und Mehrdeutigkeiten d) Debug-Funktionalität e) Designprozess der syntaktischen Analyse 2. Beispielprogramme Compilerbau 2

Analysephase (Frontend) Analysephase zerlegt in 3 Phasen: 1. Lexikalische Analyse Zerlegung des Quelltextes in zusammengehörende Token 2. Syntaktische Analyse Überprüfung, ob eingelesener Quellcode der Syntax (Grammatik) der Quellsprache entspricht Eingabe wird in Syntaxbaum umgewandelt 3. Semantische Analyse Überprüfung der statischen Semantik Variablendeklaration und Zuweisungen werden geprüft (attributierter Syntaxbaum) Compilerbau 3

Parsergenerator Konstruiert automatisch Code aus Grammatik Schritte für Bottom-up-Parser Konstruiere aus Grammatik den Zustandsübergangsgraph/CFSM Z. B.: LALR(1), LR(1) Erzeuge Code für Parser Wichtig Nutzung einer Standardschnittstelle zum Scanner Yacc Yet another compiler compiler Parsergenerator für UNIX Entwickelt Mitte der 70er Bison Auch nur ein Tier Weiterentwicklung von YACC durch GNU Abwärtskompatibel zu Yacc Entwickelt Ende der 80er Compilerbau 4

Bison Einführung ist ein Parsergenerator erzeugt C/C++ Programm, das die syntaktische Analyse eines Tokenstroms durchführt ist optimiert für deterministische, kontextfreie LALR(1)-Grammatiken nächste anwendbare Regel eindeutig durch schon gelesenen Eingabe und Lookahead der restlichen Eingabe benötigt eine Spezifikation (Grammatik) in Backus-Naur-Form (BNF) Compilerbau 5

Flex und Bison Quellcode Scanner Parser Zwischencode Flex Spezifikation lexikalischer Einheiten (Token) reguläre Ausdrücke Lexer Spezifikationsdatei (example.l) Übersetzen nach example.c Bison Spezifikation der syntaktischen Struktur kontextfreie Grammatik Bison Spezifikationsdatei (example.y) Einbinden des Scanners in den Parser Übersetzen nach example.tab.c Compilerbau 6

Bison Kompilierung (1) Zwischencode Lex- Spezifikation Lex-Compiler Automat mit Übergangstabelle (C-Programm) C-Compiler Bison- Spezifikation Bison-Compiler Bison-Parser (C-Programm) C-Compiler Festlegung der Regeln & dazugehörigen Aktionen C-Programm main() yyparse() yylex() yyerror() Quellcode Scanner *.tab.h Token Parser Compilerbau 7

Bison Kompilierung (2) Bison- Programm parser.y bison C-Programm parser.tab.c gcc Ausführbares Programm Parser bison d parser.y gcc c parser.tab.c gcc o parser parser.tab.o Kompilierung und Start bison -<option> <dateiname> d erstellt Headerdatei *.tab.h t setzt Debugfunktionalität (#define YYDEBUG 1) v erstellt Infodatei zum generierten Parser parser.tab.c enthält yyparse() zum Starten der Syntaxanalyse Compilerbau 8

Bison Eingabespezifikation Vier Teile %{ <Prolog> %} <Definitionsteil> %% <Regelteil> %% <Epilog> %{ Einbindung von C-Header-Dateien Definitionen für Makros Deklaration von Funktionen und Variablen %} Definition von Terminal- und Nichtterminalsymbolen Festlegen von Präzedenzen Festlegen des Startsymbols lsym: rsym 11 {<Aktion 11 >} rsym 1n {<Aktion 1n >} rsym 21 {<Aktion 21 >} rsym 2n {<Aktion 2n >} rsym 31 {<Aktion 31 >} rsym 3n {<Aktion 3n >} ; Definition von Funktionen wie z. B. main, yyerror, yylex hier möglich Compilerbau 9

Prolog Einbindung von C-Header-Dateien Deklaration von Konstanten Deklaration von Funktionen Beispiel %{ %} #include <stdio.h> #include <string.h> void yyerror(char* msg); extern int yylex(); yyerror(char*) Fehlerausgabefunktion von bison yylex() Aufruf des Scanners Compilerbau 10

Definitionsteil 1. Definition von Token 2. Festlegen des Startsymbols 3. Festlegung von Präzedenz- und Assoziationsbedingungen 4. Einbinden von C/C++-Code 5. Definition von beliebigen Rückgabetypen später bei semantischer Analyse Compilerbau 11

Definitionsteil Definition von Token (1) Syntax %token <name> {" " <name>} Gültige Namen für Token sind alle C-Identifier Konvention Tokennamen in Großbuchstaben Werden in #define konvertiert Beispiel %token NAME1 %token NAME2 %token NAMEn %token NAME1 NAME2 NAMEn Compilerbau 12

Definitionsteil Option d (1) Generierung von symbolischen Konstanten Option d beim Aufruf von bison erzeugt zusätzlich zu *.tab.c die Headerdatei *.tab.h *.tab.h enthält C-Konstantendefinitionen für definierte Token Stehen auch in der *.tab.c-datei Verwendung in der Lexer-Spezifikationsdatei *.tab.h im Definitionsteil der Lex-Spezifikationsdatei einbinden Rückgabewert der Aktionen bei matchenden RA: symbolische Konstante für den erkannten Token Compilerbau 13

Definitionsteil Option d (2) Generierung von symbolischen Konstanten numid.y %token NUMBER %token ID %% %% #define NUMBER 257 #define ID 258 numid.tab.h Verwendung in der Lexer-Spezifikationsdatei %{ #include "numid.tab.h" %} %% [+-]?(0 [1-9][0-9]*) {printf("%d\n", NUMBER); return(number);} [a-za-z_][a-za-z0-9_]* {printf("%d\n", ID); return(id);} numid.l Compilerbau 14

Definitionsteil Definition von Token (2) flex liefert bison Token als Integer-Werte int yylex() Zum konsistenten Gebrauch der Tokenkodierung in flex und bison gelten folgende Konventionen: Token ist ein einzelnes Zeichen (Literal) ASCII-Wert wird verwendet (1 256) Token ist eine Zeichenkette, die von flex durch einen regulärer Ausdruck erkannt wird Tokenname in bison definieren und entsprechende symbolische Konstanten erzeugen ( 257) Konstanten flex zugänglich machen und dort als Rückgabewert verwenden Token 0 signalisieren Ende des Eingabetextes Compilerbau 15

Definitionsteil Festlegen des Startsymbols Syntax %start <name> Definition nicht zwingend, aber empfohlen Default Linke Seite der ersten Regel als Startsymbol Beispiel %start nonterminal Compilerbau 16

Definitionsteil Präzedenz- und Assoziationsbedingungen (1) Präzedenzen/Prioritäten von Operatoren um Mehrdeutigkeiten zu vermeiden Beispiel Keller Eingabe Aktion $ E+E*E+E^E$ Shift $E +E*E+E^E$ Shift $E+ $E+E 5 + 1 * 3 + 2 ^ 2 = (5 + (1 * 3)) + (2^2) = 12 E*E+E^E$ Shift *E+E^E$ Shift/Reduce Shift, um gewünschte Ableitung zu erhalten Nicht aus Grammatik erkennbar Lösung 1: Grammatik umstellen (siehe VL) Lösung 2: Präzedenzen einfügen expr ::= expr '+' expr expr '-' expr expr '*' expr expr '/' expr expr '^' expr E: expr Compilerbau 17

Definitionsteil Präzedenz- und Assoziationsbedingungen (2) Syntax %left <name> {" " <name>} linksassoziativ %right <name> {" " <name>} rechtsassoziativ %nonassoc <name> {" " <name>} keine Assoziativität Reihenfolge der %left und %right Angaben bestimmen die Präzedenzen der Token Präzedenz nimmt von oben nach unten zu Mehrere Operatoren pro Deklaration möglich gleiche Präzedenz Gleichzeitig auch Tokendeklaration %left, %right, %nonassoc anstelle von %token Compilerbau 18

Definitionsteil Präzedenz- und Assoziationsbedingungen (3) Beispiel 1 %left op a op b op c = (a op b) op c %right op a op b op c = a op (b op c) %nonassoc op a op b op c ist ein Fehler Beispiel 2 %left '+' '-' %left '*' '/' %right '^' '+' ' ' '*' '/' linksassoziativ '^' rechtsassoziativ '*' und '/' höhere Präzedenz als '+' und ' ' '^' höchste Präzedenz '*' und '/' sowie '+' und ' ' jeweils gleiche Präzedenz Compilerbau 19

Regelteil 1. Spezifikation der Produktionen und der dazugehörigen Regeln 2. Festlegung der Nichtterminale (implizit) 3. Semantische Aktionen später bei semantischer Analyse Compilerbau 20

Regelteil Spezifikation der Regeln Syntax (vereinfacht) <Metasymbol> ":" {<Symbol>} {" " {<Symbol>}} ";" Rekursion möglich (rechts- oder links) Überführung der Regeln A ::= X 11 X 12 X 1n X 21 X 22 X 2n X m1 X m2 X mn Grammatik A Nichtterminale X ij Nichtterminale Terminale A : X 11 X 12 X 1n X 21 X 22 X 2n X m1 X m2 X mn ; Bisonregeln (Aktionen später) Compilerbau 21

Regelteil Epsilonregel Regel lsym ::= ε rsym 1 rsym 2 lsym : /* leer */ rsym 1 rsym 2 ; oder lsym : epsilon rsym 1 rsym 2 ; epsilon : /* leer */ ; Compilerbau 22

Regelteil Festlegung der Nichtterminale Terminalsymbole Token aus dem Definitionsteil Literale in Hochkommata Nichtterminalsymbole Implizit definiert durch linke Seite einer Regel Jedes Nichtterminalsymbol muss auf der linken Seite von mindestens einer Regel stehen Compilerbau 23

Regelteil Grammatikvereinfachungen (1) anythings ::= thing* somethings ::= thing+ maybething ::= [thing] Grammatik anythings: /* EMPTY */ anythings thing ; somethings: thing somethings thing ; maybething: /* EMPTY */ thing ; Bisonregeln Compilerbau 24

Regelteil Grammatikvereinfachungen (2) inopt ::= a [b] c trailrep ::= a b+ anyrep ::= a b* c Grammatik inopt : a /* EMPTY */ c a b c ; trailrep: a b trailrep b ; anyrep : a b_rep c ; b_rep : /* EMPTY */ b_rep b ; Bisonregeln Compilerbau 25

Regelteil Präzedenzen von Regeln (1) Problem Präzedenzen eines Operators oftmals vom Kontext abhängig Beispiel 8 / - 2 * 2 = (8 / (-2)) * 2 = -8 E: expr Keller Eingabe Aktion $ E/ E*E$ Shift $E / E*E$ Shift $E/ E*E$ Shift $E/ E*E$ Shift $E/ E *E$ Shift $E/ E* E$ Shift $E/ E*E $ Reduce $E/ E $ Reduce $E/E $ Reduce $E $ Accept %left '+' '-' %left '*' '/' expr : expr '*' expr expr '-' expr '-' expr da '*' höhere Präzedenz als ' ' = 8 / (-(2 * 2)) = -2 Compilerbau 26

Regelteil Präzedenzen von Regeln (2) Syntax %prec <Terminalsymbol> Am Ende einer Regel (vor einer eventuellen Aktion) Effekt Präzedenz + Assoziativität der Regel d. h. des rechtesten Terminalsymbol der Regel = Präzedenz + Assoziativität des definierten Tokenplatzhalters Terminalsymbol %left '+' '-' %left '*' '/' %nonassoc UMINUS expr : expr '*' expr expr '-' expr '-' expr %prec UMINUS Compilerbau 27

Regelteil Fehlerbehandlung Default Beim ersten Fehler abbrechen Aufruf einer Funktion yyerror(char*) mit syntax error Größere Fehlermeldung mittels (im Definitionsteil) #define YYERROR_VERBOSE yes Wünschenswert präzise Information über aufgetretenen Fehler Grund, Zeilennummer Fortsetzung der Syntaxanalyse an geeigneten Punkt Dazu gibt es das spezielle Nichtterminal error Später mehr Compilerbau 28

Epilog Code wird unverändert nach *.tab.c kopiert Einbinden von benutzerdefinierten Funktionen, die in den Aktionen benutzt werden können Bereitstellung der Funktionen int yylex() (falls nicht von Flex) int main() Überschreiben der Funktion void yyerror(char*) Compilerbau 29

Bison-Bezeichner und -Funktionen int yyparse() liest Tokenstrom mittels yylex yylex kann von Flex generiert sein Rückgabewert ist Fehlerstatus 0: alles OK Ermittelt Ableitungsbaum der Eingabe void yyerror(char* s) Zuständig für die Ausgabe der Fehlermeldung Anpassung der Fehlermeldung durch Überschreiben der Funktion Muss implementiert werden Compilerbau 30

Konflikte und Mehrdeutigkeiten (1) Konflikte Shift/Reduce-Konflikt Reduce/Reduce-Konflikt Im Konfliktfall ist Generierung einer Aktionsund Goto-Tabelle hilfreich Mittels Option v beim Kompilieren Erzeugt Tabelle *.output Konflikte lassen sich direkt aus der Tabelle ablesen Compilerbau 31

Konflikte und Mehrdeutigkeiten (2) Default-Konfliktlösung Shift/Reduce-Konflikt es wird shift angewendet Reduce/Reduce-Konflikt es wird erste Regel im Regelteil zur Reduktion verwendet Besser: Konflikte selbst auflösen durch Umformung der Grammatik oder Festlegung von Präzedenz- und Assoziativitätsbedingungen Compilerbau 32

Debug-Funktionalität Im Definitionsteil #define YYDEBUG 1 Oder mittels Option t beim Kompilieren Im Quellcode Ein: yydebug = 1; Aus: yydebug = 0; Ausgabe welches Token von yylex gelesen wurde bei Shift: Tiefe und Inhalt des Stacks bei Reduce: angewendete Regel und Inhalt des Stacks Debug-Fehlermeldung im Fehlerfall #define YYERROR_VERBOSE yes Compilerbau 33

Designprozess der syntaktischen Analyse Übersicht 1. Spezifikation der Grammatik für jede Regel eine Aktion 2. Implementierung eines Scanners für die Erkennung der Token *.tab.h inkludieren 3. Implementierung einer Kontrollfunktion Funktion zum Aufruf des Parsers 4. Implementierung von Fehlerbehandlungsroutinen falls beim Parsen Fehler auftreten yyparse() yylex() main() yyerror() Compilerbau 34

Beispielprogramme ETF Parser für die Expression/Term/Faktor-Grammatik Compilerbau 35