Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Intro Grammatikexempel Syntaxanalys Senast DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Verktyg för Java Avslutning Idag • Grammatiker Intro • Ändliga automater med stack • Grammatikexempel • Backus-Naur-form (BNF), notation för • Att generera syntaxanalysatorer grammatiker • Exempel för Newick-träd • Rekursiv medåkning (recursive descent) • Lexikal analys • DCG i Prolog Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Exempel: Lava program → headers stmnt_list stmnt_list → statement | stmnt_list S EMI C OLON statement statement → B EGIN stmnt_list E ND | assignment | while_statement | if_statement | ... • Lex/Yacc i C/C++ • Lite om JFlex/CUP för Java Avslutning Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Exempel: Lava assignment → VARIABLE A SSIGN expression while_statement → W HILE bool_expr D O statement if_statement → I F bool_expr T HEN statement E LSE statement | I F bool_expr T HEN statement Problem: Grammatiken kräver ”extra lookahead” Terminaler: S EMI C OLON, B EGIN, E ND Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Intro Förbättring av if-satsen Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Att generera syntaxanalysatorer if_statement → I F bool_expr T HEN statement else_clause else_clause → E LSE statement | • Syfte: Att automatisera tråkig programmering • Minskar risken för syntax-buggar • Lättare för andra att arbeta med koden • Kan få snabb parser: Verktyg ofta väloptimerade Kvarstående problem: Hur tolkas if $x>y$ then if $x>z$ then runTests() else runSimpleTests() Grammatiken är tvetydig. Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Intro Grammatikexempel Varför inte rekursiv medåkning? • Rekursiv medåkning kan vara bästa alternativet • RM är för LL(k)-parsers: Läs vänster-till-höger, vänsterhärledning, k ”lookahead”. • LL-grammatik kan vara en begränsning • Vänsterrekursion kan bli farligt: IntList ::= IntList COMMA Int | Int Skriv om för att undvika! Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Alternativ till LL(k) • LR(k) är läsning vänster-till-höger, och • • • • högerhärledning. Jobbigt att implementera för hand: Behöver verktyg Implementeras som en ändlig automat med stack Klarar rekursioner säkrare Högerrekursion riskerar ineffektivitet (många element på stacken) Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Kompilatorkonstruktion Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning DCG för syntaxanalysatorer i Prolog • DCG: Definite Clause Grammar • Syntax för produktioner: • Brett fält • Många verktyg: Vi tittar på de enkla • DCG i Prolog • Lex/Yacc för C/C++ • Cup för Java • • • • Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Exempel: Newickträd i DCG Avslutning Intro Huvud --> Kropp. Syntaktiskt socker för Prolog-satser Utnyttjar Prologs backtracking för syntaxanalysen Begränsning på grammatiken? ”Killer feature”: Jobbar åt båda hållen! Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Lexikal analysator lex([], []). newick → tree semiColon semiColon → SemiColon | tree → Leaf | LeftParen tree Comma tree RightParen Terminaler är Leaf, LeftParen, RightParen, Comma och SemiColon. -- Ta bort mellanslag lex([32|S], Terminals) :- % Oj, alla mellanslag! lex(S, Terminals). -- Här kommer en viktig symbol lex([C|S], [Symbol|Terminals]) :char2symbol(C, Symbol), lex(S, Terminals). -- Ett ord: lagra som sammansatt term lex(S, [w(Symbol)|Terminals]) :firstWord(S, W, NextS), name(Symbol, W), lex(NextS, Terminals). Avslutning Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Intro Grammatikexempel Hjälpande kod 1 Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Hjälpande kod 2 -- Ett ord: lagra som sammansatt term lex(S, [w(Symbol)|Terminals]) :firstWord(S, W, NextS), name(Symbol, W), lex(NextS, Terminals). -- Här kommer en viktig symbol lex([C|S], [Symbol|Terminals]) :char2symbol(C, Symbol), lex(S, Terminals). firstWord([X|Xs], W, Next) :firstWordI([X|Xs], W, Next), length(W, L), L>1. char2symbol(40, char2symbol(41, char2symbol(44, char2symbol(32, char2symbol(59, Intro Grammatikexempel Syntaxanalys openParen). closeParen). comma). space). semicolon). DCG i Prolog % % % % % ’(’ ’)’ ’,’ ’ ’ ’;’ Lex och Yacc Verktyg för Java firstWordI([],[],[]). firstWordI([C|S], [], [C|S]) :\+ character(C). -Eget predikat. Rätt Ascii? firstWordI([C|S], [C|RestOfWord], Next) :character(C), -Eget predikat. Rätt Ascii? firstWordI(S, RestOfWord, Next). Avslutning Intro Grammatikexempel Syntaxanalysatorn BNF newick → tree semiColon semiColon → SemiColon | tree → Leaf | LeftParen tree Comma tree RightParen Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Provkörning | ?- name(’(mus, (apa, jag));’, L), lex(L, Syms), newick(Syms, []). L = [40,109,117,115,44,32,40,97,112,97|...], Syms = [openParen,w(mus),comma,openParen, w(apa),comma,w(jag),closeParen, closeParen,semicolon] ? n no | ?- DCG newick --> tree, semi. semi semi tree tree leaf --> --> --> --> [semicolon]. []. leaf. [openParen], tree, [comma], tree, [closeParen]. --> [w(X)]. Avslutning Observationer • Syntaxanalysatorn kortare än lexern • Inget slutresultat: samlar inte ihop någon data! Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Intro DCG igen, med datastruktur leaf(X) Intro Grammatikexempel tree(T), semi. [semicolon]. []. leaf(X), {L = l(X)}. [openParen], tree(T1), [comma], tree(T2), [closeParen]. --> [w(X)]. DCG i Prolog Lex och Yacc Verktyg för Java DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Intro Grammatikexempel Expansion av DCG-produktioner Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java DCG för er newick --> tree, semi. semi --> [semicolon]. semi --> []. Översättning till predikat • Bra för labben. newick(In, Out) :tree(In, Remaining), semi(Remaining, Out). semi(In, Out) :’C’(In, semicolon, Out). semi(In, Out) :Out = In. • Mycket smidig syntaxanalysator ’C’/3 plockar X från In: ’C’([X|Terminals], X, Terminals). Avslutning | ?- parseTree(T). |: (lasse, (hubba, nisse)); T = b(l(lasse),b(l(hubba),l(nisse))) ? yes | ?- newick(b(l(apa), l(mus)), L, []). L = [openParen,w(apa),comma,w(mus), closeParen,semicolon] ? n L = [openParen,w(apa),comma,w(mus), closeParen] ? n no | ?- --> --> --> --> --> Syntaxanalys Syntaxanalys Exempelkörning igen % Unifiera X till träd läst fr stdin: parseTree(X) :read_line(T), lex(T, Symbols), newick(X, Symbols, []). newick(T) semi semi tree(L) tree(b(T1,T2)) Grammatikexempel • Kan hantera tvetydiga grammatiker. Bra? Dåligt? Avslutning Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Intro Grammatikexempel Lex och Yacc Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Lexer för Newickträd i Lex %% %% Avdelare Användardefinierade funktioner Avslutning Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Lexer för Newickträd i Lex %{ /* Include symbols from parser, generated by yacc/bison */ #include ”newick.h” /* Some supporting C code here */ %} ... %% {WHITE} \( \) \: , \; {STR} %% ... (mera här) Avslutning Regler av formen RegEx { <C-kod> return <en symbol>} Andra delen: /* RegEx for valid leaf names */ STR [a-zA-Z0-9\-\_/+*#!\%&?\.]+ WHITE [ \t\n]+ Verktyg för Java Avdelare Första delen: /* Handle tree comments sensible */ %x comment Lex och Yacc %{ C-definitioner hamnar här %} Diverse deklarationer för lex • GNU har egna varianter: Flex och Bison • (F)Lex: En ”scanner”. • Infil beskriver terminaler • Utfil definierar funktionen yylex. • Kan definiera ett helt eget program. • Yacc/Bison: • ”Yet Another Compiler-Compiler” • Infil beskriver en grammatik med actions • Utfil definierar funktionen yyparse • Använder yylex (kan skrivas av dig) • Genererar LALR-parsers, variant av LR • Implementeras som en automat med stack Grammatikexempel DCG i Prolog Generellt format för (F)Lex • Klassiska Unix-verktyg Intro Syntaxanalys \[ /* Do nothing */ { return LEFT_PAREN; } { return RIGHT_PAREN; } { return COLON; } { return COMMA; } /* Why bother? */ { yytree_lval.str = strdup(yytext); return STRING; } { BEGIN(comment); } /* Special state to read comments in */ <comment>\] { BEGIN(INITIAL); } <comment>. /* eat comment */ %% Avslutning Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Intro Allmänt format för yacc/bison else_statement : ELSE statement | /* nothing */ ; %% Avdelare Användardefinierade funktioner Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Andra delen av Bisongrammatiken %% tree : leaf { $$ = $1; } /* En ”action” */ | LEFT_PAREN tree COMMA tree RIGHT_PAREN { $$ = new_branch($2, $4); } /*”action”*/ ; leaf : STRING { $$ = new_node($1);} /* Mer ”action” */ ; %% /* Even more C functions */ DCG i Prolog Lex och Yacc Verktyg för Java Avslutning %{ /* Here comes quite a few C declarations */ %} /* More Bison/C declarations */ /* Deklarera terminaler */ %token LEFT_PAREN %token RIGHT_PAREN %token SEMICOLON %token COMMA %token <str> STRING Regler som BNF-liknande uttryck if_statement : IF bool_expr THEN else_statement Grammatikexempel Syntaxanalys Första delen av Bisongrammatiken %{ C-definitioner hamnar här %} Diverse deklarationer för bison %% Avdelare Intro Grammatikexempel Intro /* Semantik för variabler: mytree är en typ */ %type <mytree> tree %type <mytree> leaf %% ...(mera här) Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Koppla ihop flex och bison > flex newick.l > bison newick.y > ls lex.yy.c newick.l newick.y newick.tab.c Genererat: lexikal analysator: Funktionen yylex i lex.yy.c syntaxanalysator: Funktionen yyparse i newick.tab.c Läs från stdin med mytree = yyparse(); Avslutning Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Intro Allmänt om Lex/Yacc Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java • Mycket att skriva • JFlex bygger på JLex som bygger på Lex. • Struligt! • JFlex använder samma konstruktioner • Viktigt: Får ej vara vänsterrekursiv. • CUP: Constructor of Useful Parsers • Du implementerar felhantering själv! • CUP liknar Yacc • Svårt att debugga. • Tydligare syntax, ”äkta” BNF • Projektet lever! • CUP är ej (?) installerat på CSC, men lätt Grammatikexempel Syntaxanalys DCG i Prolog Avslutning I Java: JFlex och CUP att ladda ner och testa. • Modernt: Stöd för C++ Intro Grammatikexempel Lex och Yacc Verktyg för Java Läs Newickträd i Java: Början Avslutning Intro Grammatikexempel Syntaxanalys DCG i Prolog /* Declare the terminals */ terminal LEFTPAREN; terminal RIGHTPAREN; terminal COMMA; terminal SEMICOLON; terminal String NAME; // Ett värde följer med terminalen /* Declare non-terminals */ non terminal Tree newick; // Dessa variabler ges ett värde non terminal Tree tree; non terminal Tree leaf; non terminal semicolon; Verktyg för Java Läs Newickträd i Java: Slutet import java_cup.runtime.*; /* Prepare your lexer! (JFlex or your own class) */ init with {: scanner.init(); :}; scan with {: return scanner.next_token(); :}; Lex och Yacc /* Newick grammar */ newick ::= tree semicolon; semicolon ::= SEMICOLON | ; tree ::= leaf | LEFTPAREN tree COMMA tree RIGHTPAREN ; leaf ::= NAME; Avslutning Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Intro Användning Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Lägg till semantik i CUP Början på filen är oförändrad: newick ::= tree:t semicolon {: RESULT = t :} ; semicolon ::= SEMICOLON | ; tree ::= leaf:t {: RESULT = t; :} | LEFTPAREN tree:t1 COMMA tree:t2 RIGHTPAREN {: RESULT = new Branch(t1, t2); :} ; leaf ::= NAME:l {: RESULT = new Leaf(l); :} ; • Skapa parser kod med java -cp java-cup-11a.jar java_cup.Main newick.cup • Två nya klassfiler: sym.java och parser.java • Användning: parser newickparser = new parser(new my_scanner()) • Scanner är ett enkelt interface som du implementerar eller får från JFlex. Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Sammanfattning om CUP Avslutning Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Avslutning Slutligen om syntaxanalysgeneratorer • Yacc/CUP i Haskell: Happy • Gammal god Yacc-tradition, snygg implementation • Delar vissa nackdelar: • Mycket att skriva • Felhantering sker ej enkelt • Är avlusning svårt? • Använd gärna i lab 6 — på egen hand! • Från Chalmers kommer BNFC. BNF Converter: Multilingual front-end generation from labelled BNF grammars. Genererar kod för • • • • C, via Flex och Bison C++, via Flex och Bison Haskell, via Happy Java, via JLex och CUP • BNFC ger dig datastrukturer, lexer, testfall, Makefile, mm. Intro Grammatikexempel Syntaxanalys DCG i Prolog Lex och Yacc Verktyg för Java Mer att läsa, mer att plugga • Andrew Appel: Modern compiler implementation in C • Andrew Appel: Modern compiler implementation in Java • Andrew Appel: Modern compiler implementation in ML • Aho, Sethi, Ullman: Compilers: Principles, Techniqes and Tools Kurser • 2D1418 Språkteknologi • 2D1375 Programspråksimplementation Avslutning