javacc Analisi Sintattica Nicola Fanizzi Corso di Linguaggi di Programmazione Dipartimento di Informatica Università degli Studi di Bari 15 maggio 2014 Sommario 3 1 2 Introduzione Parsing top-down ricorsivo Costruire un Parser LL(1) Specifica e prodotti File di specifica Metodi del parser Costanti ed Eccezioni Token, scanner e flusso di input Catena dei token N. Fanizzi Linguaggi di prog.+Lab 4 5 Ricorsione e Iterazione Iterazioni Esempi Scelta delle produzioni Lookahead locale Algoritmo di scelta Lookahead Sintattico e Semantico Esempi NL_Xlator Bowd CSV2HTML Calcolatore javacc – Analisi Sintattica 15 maggio 2014 2 / 85 Introduzione Sommario 1 Introduzione Parsing top-down ricorsivo Costruire un Parser LL(1) 2 Specifica e prodotti 3 Ricorsione e Iterazione 4 Scelta delle produzioni 5 Esempi N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 3 / 85 Introduzione Parsing I L’analizzatore sintattico (o parser) verifica se la sequenza di token in input costituisca un programma corretto: Definizione (Obiettivo del Parsing) Determinare se la stringa di token in input può essere generata da una determinata grammatica libera da contesto N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 4 / 85 Introduzione Parsing II Il generatore di parser top-down: produce codice (Java) che si avvale del token manager metodo getNextToken() determina se una sequenza di token possa essere generata da una data grammatica libera da contesto altrimenti segnala l’errore grammatiche LL(k) costruendo l’albero di derivazione a partire dal simbolo iniziale fino alle foglie (token) N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 5 / 85 Introduzione Parsing top-down ricorsivo Parsing top-down ricorsivo Grammatica con produzioni: normalmente se la stringa di token successiva nella sequenza di input può essere derivata da S S −→ print(E); S −→ while(E) do S S −→ { L } E −→ id E −→ num L −→ S L L −→ parseE termina normalmente se la stringa di token successiva può essere derivata da E Implementazione per ogni non-terminale della grammatica, una funzione ricorsiva: N. Fanizzi Linguaggi di prog.+Lab parseS() termina parseL termina normalmente se la stringa di token successiva può essere derivata da L javacc – Analisi Sintattica 15 maggio 2014 6 / 85 Introduzione Parsing top-down ricorsivo Inizializzazione e controllo token 1 Token currentToken = ParseTokenManager.getNextToken(); 2 boolean checkToken(int expectedTokenType) { if (currentToken.kind == expectedTokenType) { 5 currentToken = ParseTokenManager.getNextToken(); 6 } else { 7 error("Parse error"); } 8} 3 4 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 7 / 85 Introduzione Parsing top-down ricorsivo Esempio: Parse.java / I S −→ print(E); | while ( E ) do S | { L } void ParseS() { switch(currentToken.kind) { 3 case ParseConstants.PRINT: 4 checkToken(ParseConstants.PRINT); 5 checkToken(ParseConstants.LEFT_PARENTHESIS); 6 ParseE(); 7 checkToken(ParseConstants.RIGHT_PARENTHESIS); 8 checkToken(ParseConstants.SEMICOLON); 9 break; 10 case ParseConstants.WHILE: 11 checkToken(ParseConstants.WHILE); 12 checkToken(ParseConstants.LEFT_PARENTHESIS); 13 ParseE(); 14 checkToken(ParseConstants.RIGHT_PARENTHESIS); 15 checkToken(ParseConstants.DO); 16 ParseS(); 17 break; 1 2 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 8 / 85 Introduzione Parsing top-down ricorsivo Esempio: Parse.java / II case ParseConstants.LEFT_BRACE: checkToken(ParseConstants.LEFT_BRACE); ParseL(); checkToken(ParseConstants.RIGHT_BRACE); break; default: error("Errore nella derivazione di S"); } 18 19 20 21 22 23 24 } 25 26 } S −→ print(E); | while ( E ) do S | { L } N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 9 / 85 Introduzione Parsing top-down ricorsivo Esempio: Parse.java / III E −→ id | num void ParseE() { switch(currentToken.kind) { 3 case ParseConstants.ID: 4 checkToken(ParseConstants.ID); 5 break; 6 case ParseConstants.NUM: 7 checkToken(ParseConstants.NUM); 8 break; 9 default: 10 error("Errore di parsing"); 11 } 12 } 1 2 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 10 / 85 Introduzione Parsing top-down ricorsivo Esempio: Parse.java / IV L −→ S L | void ParseL() { switch(currentToken.kind) { 3 case RIGHT_BRACE: // simbolo nel FOLLOW 4 /* nessuna azione: stringa vuota */ 5 break; 6 default: 7 ParseS(); 8 ParseL(); 9 } 10 } 1 2 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 11 / 85 Introduzione Costruire un Parser LL(1) Costruire un Parser LL(1) 1 Creare una grammatica EBNF libera per il linguaggio 2 Rimuovere ambiguità, ricorsione sinistra e casi fattorizzabili (a sinistra) 3 Determinare gli insiemi FIRST e FOLLOW 4 Costruire la tabella di parsing 5 Usare la tabella per creare la suite di funzioni che implementano il parser 6 Convertire la grammatica in funzioni di parsing N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 12 / 85 Specifica e prodotti Sommario 1 Introduzione 2 Specifica e prodotti File di specifica Metodi del parser Costanti ed Eccezioni Token, scanner e flusso di input Catena dei token 3 Ricorsione e Iterazione 4 Scelta delle produzioni 5 Esempi N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 13 / 85 Specifica e prodotti File di specifica File di specifica options{ /* codice per settare varie opzioni */ } PARSER_BEGIN(NomeParser) public class NomeParser { /* segmento per la classe del parse, anche vuoto */ } PARSER_END(NomeParser) TOKEN_MGR_DECLS : { /* eventuali dichiarazioni usate dal token manager */ } /* Espr. regolari per i token & azioni */ /* Regole sintattiche & azioni */ N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 14 / 85 Specifica e prodotti File di specifica Regole per il parser I Forma tipica di una regola grammaticale JavaCC: void nonTerminalName() : { /* Dichiarazioni Java */ } { /* Definizione delle regole di produzione */ } sezione /* Dichiarazioni Java */ : anche vuota; usata tipicamente per inizializzare variabili, ad es., per la costruzione di un albero di parsing astratto sezione regole EBNF (parti destre, separate da ”|”) Non-terminali sono seguiti da ”()” corrispondono infatti a metodi Java Terminali (token) compresi tra parentesi angolari < e > es. <WHILE>, <STRINGA>, <MINORE_O_UGUALE> possono contenere anche azione sem.: blocco di codice Java N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 15 / 85 Specifica e prodotti File di specifica Regole per il parser II Espressioni regolari ammesse: [e] o (e)? indicano che e è opzionale (e1 | e2 | e3 | ... ) scelta tra e1, e2, e3, ... (e)+ una o più occorrenze di e (e)* zero o più occorrenze di e N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 16 / 85 Specifica e prodotti File di specifica Regole per il parser III Esempio S −→ while(E) S S −→ V = E; Corrispondente rappresentazione JavaCC: S = statement; E = expression; V = variable void statement() : {} { <WHILE> <LPAREN> expression() <RPAREN> statement() | variable() <GETS> expression() <SEMICOLON> } N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 17 / 85 Specifica e prodotti File di specifica Linea di comando Con debugging del parser: javacc -debug_parser NomeParser.jj javac NomeParser*.java java NomeParser segnala le chiamate dei metodi e l’uscita Con debugging del token manager: javacc -debug_token_manager NomeParser.jj javac NomeParser*.java java NomeParser produce molte informazioni diagnostiche ed è tipicamente usato per osservare il trace, un singolo token per volta N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 18 / 85 Specifica e prodotti File di specifica File prodotti da JavaCC Elaborando il file di specifica Parser1.jj, si ottengono i file: Parser1.java ParseException.java Token.java Parser1Constants.java Parser1TokenManager.java TokenMgrError.java SimpleCharStream.java Parser1.java contiene, nella classe Parser1 il codice Java fornito nel blocco PARSER_BEGIN/PARSER_END il codice prodotto da JavaCC per la grammatica N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 19 / 85 Specifica e prodotti File di specifica Esempi semplici I PARSER_BEGIN(Simple1) public class Simple1 { 3 public static void 4 main(String args[]) throws ParseException { 5 Simple1 parser = new Simple1(System.in); 6 parser.input(); 7 } 8 } 9 PARSER_END(Simple1) 1 2 10 void input() : {} 13 { matchedBraces() ("\n"|"\r")* <EOF> } 11 12 14 void matchedBraces() : {} 17 { "{" [ matchedBraces() ] "}" } 15 16 non ammette spazi N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 20 / 85 Specifica e prodotti File di specifica Esempi semplici II PARSER_BEGIN(Simple2) public class Simple2 { 3 public static void 4 main(String args[]) 5 throws ParseException { 6 Simple2 parser 7 = new Simple2(System.in); 8 parser.input(); 9 } 10 } 11 PARSER_END(Simple2) 1 17 2 18 void input() : {} 21 { 22 matchedBraces() <EOF> 23 } 19 20 24 13 void matchedBraces() : {} 27 { 28 "{" [ matchedBraces() ] "}" 29 } 14 30 12 SKIP : { 15 " " | "\t" | "\n" | "\r" 16 } N. Fanizzi Linguaggi di prog.+Lab 25 26 31 32 // fine Simple2 javacc – Analisi Sintattica 15 maggio 2014 21 / 85 Specifica e prodotti File di specifica Esempi semplici III 1 PARSER_BEGIN(IdList) " " | "\t" 23 | "\n" 24 | "\r" 25 } 21 2 3 4 22 /** ID lister. */ public class IdList { 5 /** Main entry point. */ public static void main(String args[]) throws ParseException { IdList parser = new IdList(System.in); parser.Input(); } 6 7 8 9 10 11 12 13 14 15 20 28 33 /** Top level production. */ void Input() : 36 {} 37 { 38 ( <Id> )+ <EOF> 39 } 35 PARSER_END(IdList) 18 19 TOKEN : { 29 < Id: ["a"-"z","A"-"Z"] 30 ( ["a"-"z","A"-"Z","0"-"9"] )* 31 > 32 } 27 34 } 16 17 26 SKIP : { N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 22 / 85 Specifica e prodotti File di specifica Esempi semplici IV 1 PARSER_BEGIN(FormaPrefissa) public class FormaPrefissa { 3 /* segue */ 4 } 5 PARSER_END(FormaPrefissa) 19 2 20 6 24 SKIP : 8{ 9 " " | "\n" 10 } 7 void formula(): {} 22 { espr() <EOF> 23 } 21 25 void espr(): {} 28 { 11 29 (<PIU> | <MENO> 12 TOKEN : 30 | <PER> | <DIVISO>) 13 { 31 espr() espr() 14 < PIU: "+"> | < MENO: "-"> 32 | <INTERO> 15 | < PER: "*"> | < DIVISO: "/"> 33 } 16 | < INTERO: (["0"-"9"])+ > 34 17 } 35 18 36 // fine FormaPrefissa N. Fanizzi Linguaggi di prog.+Lab 26 27 javacc – Analisi Sintattica 15 maggio 2014 23 / 85 Specifica e prodotti File di specifica Esempi semplici V 1 2 public static void main(String args[]) FormaPrefissa parser; { 3 if (args.length < 1) { System.out.print("Uso: java FormaPrefissa <nomefile>"); return; } try { parser = new FormaPrefissa(new java.io.FileInputStream(args[0])); } catch (java.io.FileNotFoundException e) { System.out.println("File " + args[0] + " non trovato."); return; } try { parser.formula(); System.out.println("Corretta"); } catch (ParseException e) { System.out.println(e.getMessage()); System.out.println("Errata"); } 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 } N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 24 / 85 Specifica e prodotti Metodo getToken() Metodi del parser I Parser1.java contiene anche il metodo getToken() che fornisce token senza fare avanzare l’input getToken(i) restituisce l’i-esimo token: getToken(1) token corrente getToken(2) token successivo, . . . L’argomento passato dev’essere un intero non negativo getToken(0) token precedente N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 25 / 85 Specifica e prodotti Metodo getToken() Metodi del parser II Il token restituito da getToken() dipende dal contesto della chiamata: 1 2 3 4 5 6 7 void printfStatement(): {Token t;} { "printf" ... ")" { t=getToken(1);} "+" } getToken(1) restituisce ")" N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 26 / 85 Specifica e prodotti Costanti ed Eccezioni Costanti ed Eccezioni La classe Parser1 implementa l’interfaccia Parser1Constants; tutte le costanti sono disponibili al parser Quando il parser incontra un errore, genera una ParseException definita nell’omonimo file .java N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 27 / 85 Specifica e prodotti Token, scanner e flusso di input Token Token.java contiene i campi dei token kind, image, next, beginLine, beginColumn, endLine, endColumn campi addizionali che supportano ulteriori funzionalità L’array tokenImage fornisce una stringa stampabile per ogni token includono i doppi apici per facilitare la stampa di errori tranne che per <EOF>, <UNSIGNED>, <ID> in una grammatica, il nome d’un token tra < > denota un oggetto Token; senza, denota il valore del campo kind (o l’indice per tokenImage) N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 28 / 85 Specifica e prodotti Token, scanner e flusso di input Token manager e stream di input Il token manager è definito nella classe Parser1TokenManager contiene il metodo getNextToken() chiamato dal parser per richiedere un token (oggetto) in caso di errore, getNextToken() lancia un’eccezione TokenErrorException, definita in TokenMgrError.java. Si genera anche la classe SimpleCharStream per il flusso usato dal token manager per l’input di caratteri N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 29 / 85 Specifica e prodotti Catena dei token Catena dei token Gli oggetti restituiti al parser dal token manager sono concatenati attraverso il campo next che contiene un riferimento al prossimo token: image "a" "b" next "c" null Questo permette di scorrere la catena in avanti Se il parser salva dei riferimenti ai token potrà sempre avervi accesso più tardi N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 30 / 85 Ricorsione e Iterazione Sommario 1 Introduzione 2 Specifica e prodotti 3 Ricorsione e Iterazione Iterazioni Esempi 4 Scelta delle produzioni 5 Esempi N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 31 / 85 Ricorsione e Iterazione Iterazioni Iterazioni con * e + Per ripetere un pattern senza usare la ricorsione, si possono usare operatori ad hoc, ad es.: expr −→ term ("+"term)* invece di expr −→ term termList termlist −→ "+"term termList termList −→ Con gli operatori * e + il codice diventa più efficiente delle versioni ricorsive equivalenti Inoltre, si può usare anche l’operatore ? N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 32 / 85 Ricorsione e Iterazione Esempi Iterazioni – esempi I Esempio void expr() : { } { 3 term() 4 termList() 5} 1 2 6 void termList(): {} { 9 "+" 10 term() 11 termList() 12 | 13 {} 14 } 7 8 diventa: N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 33 / 85 Ricorsione e Iterazione Esempi Iterazioni – esempi II void expr() : {} { 3 term() 4 ( 5 "+" 6 term() 7 )* 8} 1 2 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 34 / 85 Ricorsione e Iterazione Esempi Iterazioni – esempi III Esempio con addizione e sottrazione void expr() : {} { 3 term() 4 termList() 5} 1 2 6 void termList (): {} { 9 "+" // addizione 10 term () 11 termList () 12 | 13 "-" // sottrazione 14 term () 15 termList () 16 | 17 {} 18 } 7 8 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 35 / 85 Ricorsione e Iterazione Esempi Iterazioni – esempi IV Se si cercasse di semplificare void expr() : {} { 3 term() 4 termList() 5} 1 2 6 void termList (): {} { 9 "+" 10 term () 11 // gen codice per l’addizione 12 termList () 13 | 14 "-" 15 term () 16 // gen codice per la sottrazione 17 termList () 18 | 19 {} 20 } 7 8 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 36 / 85 Ricorsione e Iterazione Esempi Iterazioni – esempi V void expr() : {} { 3 term() 4 ( 5 ("+"|"-") 6 // gen codice ??? 7 term() 8 )* 9} 1 2 generare codice per somma o differenza? Meglio: void expr() : {Token t;} { 3 term() 4 ( 5 (t="+"|t="-") 6 // gen codice sceglie in base a t 7 term () 8 )* 9} 1 2 e.g. sfruttando kind del token e costanti N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 37 / 85 Scelta delle produzioni Sommario 1 Introduzione 2 Specifica e prodotti 3 Ricorsione e Iterazione 4 Scelta delle produzioni Lookahead locale Algoritmo di scelta Lookahead Sintattico e Semantico 5 Esempi N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 38 / 85 Scelta delle produzioni Punti di scelta I Dato il nonterminale da espandere secondo la propria disciplina in generale il parser deve scegliere tra più parti destre delle produzioni del nonterminale, nei cosiddetti punti di scelta ad ogni punto corrisponde un insieme di scelta contenente i (prefissi di) token generabili a partire da quel punto se gli insiemi sono disgiunti, la scelta è deterministica cfr. grammatiche LL(k) N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 39 / 85 Scelta delle produzioni Punti di scelta II Si supponga di specificare la grammatica: S −→ bcd | def PARSER_BEGIN(G2) class G2 3{ 4} 5 PARSER_END(G2) 6 void S(): {} 7{ 8 // qui un punto di scelta 9 "b" "c" "d" // ins. di selezione {"b"} 10 | 11 "d" "e" "f" // ins. di selezione {"d"} 12 } 1 2 Ad un punto di scelta il parser sceglie sulla base del prossimo token e dell’insieme di selezione per ogni produzione N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 40 / 85 Scelta delle produzioni Punti di scelta III Se la grammatica è LL(1), ad ogni punto di scelta, questa è determinata univocamente dal prossimo token. Altrimenti: void S(): {} { 3 "b" "c" "d" 4| 5 "b" "e" "f" 6} 1 2 // ins. di selezione {"b"} // ins. di selezione {"b"} stesso insieme di selezione: un token non basta. Conoscendo in anticipo (look ahead) il token successivo, si potrebbe determinare l’alternativa corretta. N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 41 / 85 Scelta delle produzioni Punti di scelta IV Per default si considera solo il token corrente ad ogni punto di scelta In caso di alternative multiple, si sceglie la prima elencata Esempio per la grammatica precedente: se l’input fosse "b""c""d", il parser sceglierebbe la prima strada e completerebbe il lavoro anche se fosse "b""e""f", il parser seguirebbe la prima strada, ma poi con "e" sarebbe lanciata un’eccezione Sebbene la specifica sia problematica, JavaCC compila comunque un parser ma genera anche messaggi di avvertimento del tipo: Warning: choice conflict... N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 42 / 85 Scelta delle produzioni Lookahead locale Lookahead locale I Per ovviare ai casi precedenti, si usa la direttiva LOOKAHEAD void S(): {} { 3 LOOKAHEAD(2) // nel punto di selezione 4 "b" "c" "d" 5| 6 "b" "e" "f" 7} 1 2 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 43 / 85 Scelta delle produzioni Lookahead locale Lookahead locale II Altra variazione void S(): {} { 3 "bcd" // ins. di selezione {"bcd"}: token unico 4 | 5 "bef" // ins. di selezione {"bef"}: token unico 6} 1 2 nessun problema: grammatica LL(1) N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 44 / 85 Scelta delle produzioni Lookahead locale Lookahead locale III La direttiva LOOKAHEAD va solo nei punti di scelta: 1 quando si usa | 2 quando si usano *, +, ? void S(): {} { 3 // qui NO 4 B() 5 // qua NO 6 ( 7 // qui OK 8 C() 9 | 10 D() 11 ) 12 } 1 2 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 45 / 85 Scelta delle produzioni Lookahead locale Lookahead locale IV void S(): {} 2{ 3 ( 4 // p.d.s. primo | 5 B() 6 | 7 // p.d.s. secondo | 8 C() 9 | 10 D() 11 ) 1 12 13 )? N. Fanizzi // p.d.s. per + F() 19 20 )+ 21 22 ( 23 // p.d.s. per * G() 24 25 )* 26 27 } 29 // p.d.s. per ? E() 15 ( 18 28 ( 14 16 17 30 31 32 Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 46 / 85 Scelta delle produzioni Lookahead locale Lookahead locale – esempi I Per il frammento ( "b" "c" )* "d" il parser generato conterrà: while (tokenCorrente == "b") { 3 consuma "b" 4 consuma "c" 5 } 6 consuma "d" 1 2 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 47 / 85 Scelta delle produzioni Lookahead locale Lookahead locale – esempi II inserendo una direttiva LOOKAHEAD(2) nel punto di scelta, la struttura cambia: while (tokenCorrente == "b" && tokenSucc == "c") { 3 consuma "b" 4 consuma "c" 5} 6 consuma "d" 1 2 ma è superflua; N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 48 / 85 Scelta delle produzioni Lookahead locale Lookahead locale – esempi III serve invece nel caso seguente (LOOKAHEAD (2) "b" "c" )* "b" "d" interpretato con while (tokenCorrente == "b" && tokenSucc == "c") { 3 consuma "b" 4 consuma "c" 5 } 6 consuma "b" 7 consuma "d" 1 2 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 49 / 85 Scelta delle produzioni Lookahead locale Lookahead locale – esempi IV Lo stesso vale anche per l’operatore + la specifica (LOOKAHEAD (2) "b" "c" )+ "b" genera un ciclo: do { consuma "b" 3 consuma "c" 4 } while (tokenCorrente == "b" && tokenSucc == "c") 5 consuma "b" 1 2 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 50 / 85 Scelta delle produzioni Lookahead locale Lookahead locale – esempi V Infine nel caso di ?: ("b")? il codice sarebbe del tipo: if (tokenCorrente == "b") { 3 consuma "b" 4 } 1 2 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 51 / 85 Scelta delle produzioni Algoritmo di scelta Algoritmo di scelta – esempi I In una grammatica LL(1) al più una parte destra è annullabile (può derivare ) Consideriamo una grammatica LL(1) P −→ Q P −→ R P −→ T Supponiamo sia annullabile T. Quindi l’insieme di selezione per T è FIRST(T) + FOLLOW (P). Per le altre, gli ins. corrispondono ai FIRST delle parti destre. N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 52 / 85 Scelta delle produzioni Algoritmo di scelta Algoritmo di scelta – esempi II In un parser ricorsivo discendente (pseudo-codice) si avrebbe: void P() { 3 switch(tokenCorrente) { 4 case tokenCorrente in FIRST(Q): 5 Q(); break; 6 case tokenCorrente in FIRST(R): 7 R(); break; 8 case tokenCorrente in FIRST(T)+FOLLOW(P): 9 T(); break; 10 default: 11 solleva eccezione; 12 } 13 } 1 2 Nota: Ultimo test inutile, si può semplificare il default: N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 53 / 85 Scelta delle produzioni Algoritmo di scelta Algoritmo di scelta – esempi III void P() { 3 switch(tokenCorrente) { 4 case tokenCorrente in FIRST(Q): 5 Q(); break; 6 case tokenCorrente in FIRST(R): 7 R(); break; 8 default: 9 T(); break; 10 } 11 } 1 2 I test precedono le chiamate di Q() e R(), ma non di T(). Il default deve sempre corrispondere alla derivazione di : quindi non servirà calcolare i FOLLOW , bastano i FIRST N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 54 / 85 Scelta delle produzioni Algoritmo di scelta Algoritmo di scelta in JavaCC I JavaCC adotta la seconda strategia: il codice generato non esegue test sui token nei FOLLOW i test sulle alternative annullabili vanno previsti per ultimi altrimenti il funzionamento del parser sarebbe compromesso Esempio PARSER_BEGIN(Gram) import java.io.*; 3 class Gram 4{ 5 public static void main (String[] args) 6 throws IOException, ParseException { 7 Gram parser = new Gram(FileInputStream(args[0])); 8 parser.S(); 9 } 10 } 11 PARSER_END(Gram) 12 // da saltare 1 2 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 55 / 85 Scelta delle produzioni Algoritmo di scelta Algoritmo di scelta in JavaCC II SKIP: { 15 " " | "\n" | "\r" | "\t" 16 } 17 // grammatica 18 void S() : {} 19 { 20 { System.out.println("fine");} 21 | 22 "z" S() // ins. sel. {"z"} 23 } 13 14 // ins. sel. {EOF} Prima alternativa: produzione vuota (solo un’azione sem.) si doveva mettere alla fine JavaCC dà un warning: can expand to the empty token sequence il parser viene generato ma la parte relativa a "z" viene eliminata perché irraggiungibile N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 56 / 85 Scelta delle produzioni Algoritmo di scelta Algoritmo di scelta in JavaCC III L’assenza di warning da parte di JavaCC non garantisce che la specifica sia corretta Esempio JavaCC compila senza warning void S() : {} { 3 Q() "z" 4} 5 // -------------------6 void Q() : {} 7{ 8 "z" // ins. selezione {"z"} 9 | 10 {} // ins. selezione {"z"} 11 } 1 2 tuttavia il parser ottenuto risconosce "zz" ma non "z" (pur essendo entrambe accettabili) N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 57 / 85 Scelta delle produzioni Algoritmo di scelta Algoritmo di scelta in JavaCC IV Per l’input "z", il parser usa la prima alternativa di Q(), ma poi non ci sono altre occorrenze di "z" nell’input per il consumo della "z" nella prod. di S() si doveva perseguire la seconda alternativa in Q() che richiede e non consuma la "z" sull’input, lasciandola disponibile per la regola di S() La grammatica non è LL(1) Gli ins. di selezione delle alternative in Q() coincidono JavaCC non calcola tale insieme per la seconda, quindi non si accorge della violazione della proprietà delle grammatiche LL(1) N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 58 / 85 Scelta delle produzioni Lookahead Sintattico e Semantico Lookahead Sintattico e Semantico Oltre alla direttiva LOOKAHEAD(n) che specifica il numero n di token da esaminare in anticipo per decidere in un punto di scelta: lookahead sintattico specifica sintattica dell’espansione attesa per la scelta della prima alternativa se si verifica, si prende la prima strada altrimenti si procede alla prossima scelta lookahead semantico specifica una condizione booleana (tra graffe) true il parser segue la prima alternativa false considera la prossima N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 59 / 85 Scelta delle produzioni Lookahead Sintattico e Semantico Lookahead Sintattico I Esempio lookahead sintattico void S() : {} { 3 LOOKAHEAD(C() "a") 4 A() 5 | 6 B() 7} 1 13 2 14 8 void A() : {} { 11 C() "a" "a" 12 } 9 10 N. Fanizzi Linguaggi di prog.+Lab void B() : {} 15 { 16 C() "b" "b" 17 } 18 void C() : {} 20 { 21 "c" "c" 22 } 19 23 24 javacc – Analisi Sintattica 15 maggio 2014 60 / 85 Scelta delle produzioni Lookahead Sintattico e Semantico Lookahead Sintattico II Data l’espansione specificata C()"a" il parser cerca sul flusso di input una sottostringa che corrisponde a C() seguita da un token "a" In caso di successo, si segue la prima alternativa A() altrimenti si considera la successiva Esempio se si avesse in input "c""c""a""a", si sceglierebbe la prima prod. Usando invece la direttiva LOOKAHEAD(3) non vi sarebbero problemi perché la stringa per C() ha lunghezza 2. N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 61 / 85 Scelta delle produzioni Lookahead Sintattico e Semantico Lookahead Sintattico III Se, invece, la regola di C() fosse: void C (): {} { 3 ("c")+ 4} 1 2 nessuna direttiva di lookahead numerico funzionerebbe, al contrario di quello sintattico N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 62 / 85 Scelta delle produzioni Lookahead Sintattico e Semantico Lookahead Semantico I Esempio lookahead semantico void S() : {} 2{ 3 LOOKAHEAD({ getToken(1).kind == ID && 4 getToken(2).kind == ASGN }) 5 A() 6 | 7 D() 8} 1 9 void A() : {} { 12 <ID> <ASGN> E() 13 } 10 11 14 void D() : {} { 17 <ID> <ID> 18 } 15 16 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 63 / 85 Scelta delle produzioni Lookahead Sintattico e Semantico Lookahead Semantico II Nota il metodo getToken() non consuma token in input getToken(1) restituisce il token corrente; getToken(2) restituisce il token che lo segue N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 64 / 85 Esempi Sommario 1 Introduzione 2 Specifica e prodotti 3 Ricorsione e Iterazione 4 Scelta delle produzioni 5 Esempi NL_Xlator Bowd CSV2HTML Calcolatore N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 65 / 85 Esempi NL_Xlator Esempi: NL_Xlator I 1 PARSER_BEGIN(NL_Xlator2) 2 3 4 /** New line translator. */ public class NL_Xlator2 { 5 /** Main entry point. */ public static void main(String args[]) throws ParseException { NL_Xlator parser = new NL_Xlator(System.in); parser.ExpressionList(); } 6 7 8 9 10 11 12 } 13 14 PARSER_END(NL_Xlator) 15 SKIP : { 18 " " | "\t" | "\n" | "\r" 19 } 16 17 20 21 TOKEN : N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 66 / 85 Esempi NL_Xlator Esempi: NL_Xlator II 22 { < ID: ["a"-"z","A"-"Z","_"] ( ["a"-"z","A"-"Z","_","0"-"9"] )* > | < NUM: ( ["0"-"9"] )+ > 25 } 23 24 26 /** Top level production. */ void ExpressionList() : { String s; } 29 { 30 { 31 System.out.println("Please type in an expression 32 followed by a \";\" or ^D to quit:"); System.out.println(""); 33 } 34 ( s=Expression() ";" 35 { 36 System.out.println(s); System.out.println(""); 37 System.out.println("Please type in another expression 38 followed by a \";\" or ^D to quit:"); System.out.println(""); 39 } 40 )* 41 <EOF> 42 } 27 28 43 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 67 / 85 Esempi NL_Xlator Esempi: NL_Xlator III /** An Expression. */ String Expression() : 46 { 47 java.util.Vector termimage = new java.util.Vector(); 48 String s; 49 } 50 { 51 s=Term() 52 { termimage.addElement(s); } 53 ( "+" s=Term() 54 { termimage.addElement(s); } 55 )* 56 { 57 if (termimage.size() == 1) { 58 return (String)termimage.elementAt(0); 59 } else { 60 s = "the sum of " + (String)termimage.elementAt(0); 61 for (int i = 1; i < termimage.size()-1; i++) { 62 s += ", " + (String)termimage.elementAt(i); 63 } 64 if (termimage.size() > 2) { s += ","; } 65 s += " and " + (String)termimage.elementAt(termimage.size()-1); 44 45 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 68 / 85 Esempi NL_Xlator Esempi: NL_Xlator IV return s; 66 } 67 } 68 69 } 70 /** A Term. */ String Term() : 73 { 74 java.util.Vector factorimage = new java.util.Vector(); 75 String s; 76 } 77 { 78 s=Factor() 79 { factorimage.addElement(s); } 80 ( "*" s=Factor() 81 { factorimage.addElement(s); } 82 )* 83 { 84 if (factorimage.size() == 1) { 85 return (String)factorimage.elementAt(0); 86 } else { 87 s = "the product of " + (String)factorimage.elementAt(0); 71 72 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 69 / 85 Esempi NL_Xlator Esempi: NL_Xlator V for (int i = 1; i < factorimage.size()-1; i++) { s += ", " + (String)factorimage.elementAt(i); } if (factorimage.size() > 2) { s += ","; } s += " and " + (String)factorimage.elementAt(factorimage.size()-1); return s; 88 89 90 91 92 93 } 94 } 95 96 } 97 /** A Factor. */ String Factor() : { Token t; String s; } 100 { 101 t=<ID> { return t.image; } 102 | t=<NUM> { return t.image; } 103 | "(" s=Expression() ")" { return s; } 104 } 98 99 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 70 / 85 Esempi Bowd Esempi: Bowd I 1 2 PARSER_BEGIN(Bowd) import java.io.* ; 3 class Bowd { public static void main(String args []) 6 throws IOException { 7 System.out.print:("Lettura da standard input"); 8 BufferedReader in = new BufferedReader( 9 new InputStreamReader(System.in)); 10 String linea = in.readLine(); 11 System.out.print(sostituzione(linea)); 12 } 4 5 13 14 15 16 17 N. Fanizzi static String sostituzione(String inStringa) { Reader sr = new StringReader(inStringa); Bowd parser = new Bowd(sr); StringBuffer buffer = new StringBuffer(); Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 71 / 85 Esempi Bowd Esempi: Bowd II try { parser.inizio(buffer); } catch(TokenMgrError e) 20 {throw new IllegalStateException();} 21 catch(ParseException e) 22 {throw new IllegalStateException();} 23 return buffer.toString(); 24 } // sostituzione 25 } // classe 26 PARSER_END(Bowd) 18 19 27 TOKEN : { 30 < PAROLA_DI_4_LETTERE : (<LETTERA>){4}> 31 | < PAROLA_DI_PIU_DI_4_LETTERE : 32 (<LETTERA>){5} (<LETTERA>)* > 33 | < #LETTERA : ["a"-"z","A"-"Z"] > 34 | < ALTRO : ~[] > 28 29 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 72 / 85 Esempi Bowd Esempi: Bowd III 35 } 36 void inizio(StringBuffer buffer) : { Token t; } 39 { 40 ( <PAROLA_DI_4_LETTERE> 41 { buffer.append("****"); } 42 | ( t=<PAROLA_DI_PIU_DI_4_LETTERE> | t=<ALTRO>) 43 { buffer.append(t.image); } 44 )* 45 <EOF> 46 } 37 38 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 73 / 85 Esempi CSV2HTML Esempi: CSV2HTML I CSV: Formato standard (export da fogli elettronici) Esempio STUDENT, ID NAME, DATE OF BIRTH 129384, Davy Jones, 03/04/81 3 328649, Clare Manstead, 30/11/81 4 237090, Richard Stoppit, 22/06/82 5 523343, Brian Hardwick, 15/11/81 6 908423, Sally Brush, 06/06/81 7 328453, Elisa Strudel, 12/09/82 8 883632, Peter Smith, 03/05/83 9 542033, Ryan Alcot, 04/12/80 1 2 Grammatica file: riga() ( <FINELINEA> riga())* [<FINELINEA>)] <EOF> linea: (record)+ 3 record: <RECORD> [<VIRGOLA>] 1 2 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 74 / 85 Esempi CSV2HTML Esempi: CSV2HTML II 1 2 PARSER_BEGIN(CSV2HTML) import java.io.*; 3 public class CSV2HTML { public static void main(String args[]) { 6 if(args.length!=2) { errore(); } 7 FileInputStream myIn = null; 8 PrintStream myOut = null; 9 try { 10 myIn = new FileInputStream(args[0]); 11 myOut = new PrintStream(args[1]); 12 } catch(Exception e) { 13 System.err.println("errore apertura file."); 14 System.exit(0); 15 } 4 5 16 17 18 19 String p = "<html>\n <head>\n" + "<title>Tabella generata da CSV2HTML </title>\n" + " </head>\n <body>\n"; N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 75 / 85 Esempi CSV2HTML Esempi: CSV2HTML III try { CSV2HTML parser = new CSV2HTML(myIn); 22 p+= parser.file(); 23 } catch(ParseException e) { 24 System.err.println("errore parsing"); 25 System.exit(0); 26 } 27 p += " </body>\n</html>"; 28 System.out.print("Tabella convertita:\n"+p); 29 myOut.print(p); myOut.close(); 30 } // main 31 private static void errore() { 32 System.err.println("USO: CSV2HTML inFile outFile"); 33 System.exit(0); 34 } // errore 35 } // classe 20 21 36 37 PARSER_END(CSV2HTML) 38 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 76 / 85 Esempi CSV2HTML Esempi: CSV2HTML IV SKIP : { 41 " " | "\t" 42 } 39 40 43 TOKEN : { 46 <VIRGOLA: ","> 47 | <RECORD: (~[",","\r","\n"])+ > 48 | <FINELINEA: "\r\n" | "\r" | "\n" > 49 } 44 45 50 String file() : { String tab=" <table border=’1’>\n", r; } { 53 r=riga() <FINELINEA> { tab+=r; } 54 ( r=riga() <FINELINEA> { tab+=r; } )* 55 ( <FINELINEA> )? <EOF> 56 { return tab+=" </table>\n"; } 57 } 51 52 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 77 / 85 Esempi CSV2HTML Esempi: CSV2HTML V 58 String riga() : { String d, r="\t<tr>\n"; } { 61 ( d=record() { r+=d; } )+ 62 { return r+="\t</tr>\n"; } 63 } 59 60 64 String record() : { Token t; } { 67 t=<RECORD> ( <VIRGOLA> )? 68 { return "\t\t<td> " + t.image + " </td>\n";} 69 } 65 66 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 78 / 85 Esempi Calcolatore Esempi: Calcolatore I PARSER_BEGIN(Calcolatore) public class Calcolatore { 3 public static void main (String args []) { 4 Calcolatore parser = new Calcolatore(System.in); 5 double v = 0; 6 System.out.println("Immettere espressione"); 7 System.out.print("[op.ammessi:"); 8 System.out.print("+,-,*,/,abs,exp,log,sin,cos,tan]"); 9 System.out.print("\n> "); 10 try { 11 v = parser.start(); 12 } catch (Exception e) { 13 System.err.println("impossibile calcolare il valore."); 14 System.exit(1); 15 } // catch 16 System.out.print("Valore:"+v); 17 } // main 18 } // class 1 2 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 79 / 85 Esempi Calcolatore Esempi: Calcolatore II 19 PARSER_END(Calcolatore) 20 SKIP: { " " | "\r" | "\t" 23 } 21 22 24 TOKEN: { 27 < EOL: "\n" > 28 | < #CIFRA: ["0" - "9"] > 29 | < NUMFP: 30 (<CIFRA>)+ "." (<CIFRA>)* (<ESP>)? 31 | "." (<CIFRA>)+ (<ESP>)? 32 | (<CIFRA>)+ <ESP> 33 | (<CIFRA>)+ (<ESP>)? 34 > 35 | < #ESP: ["e","E"] (["+","-"])? (<CIFRA>)+ > 36 } 25 26 37 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 80 / 85 Esempi Calcolatore Esempi: Calcolatore III double start(): { double r; } { 40 r=espressione() <EOL> 41 { return r; } 42 } 38 39 43 double espressione(): { double t,r; } { // E ::= T RE 46 t=termine() r=restoE(t) 47 { return r; } 48 } 44 45 49 50 double restoE(double accu): { double t,r; } { // RE ::= "+" T RE | "." T RE | epsilon 53 "+" t=termine() r=restoE(accu+t) 54 { return r; } 55 | 56 "-" t=termine() r=restoE(accu-t) 51 52 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 81 / 85 Esempi Calcolatore Esempi: Calcolatore IV { return r; } | // vuota { return accu; } 57 58 59 60 } 61 double termine() : { double f,r; } { // T ::= F RT 64 f=fattore() r=restoT(f) 65 { return r; } 66 } 62 63 67 double restoT(double accu): { double f,r; } { // RE ::= "*" F RT | "/" F RT | epsilon 70 "*" f=fattore() r=restoT(accu*f) 71 { return r;} 72 | 73 "/" f=fattore() r=restoT(accu/f) 74 { return r; } 75 | // vuota 68 69 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 82 / 85 Esempi Calcolatore Esempi: Calcolatore V { return accu; } 76 77 } 78 double fattore(): {double f; Token t; } { // F ::= nfp | "-" F | "fn" F | "(" E ")" 81 t=<NUMFP> 82 { return Double.parseDouble(t.image); } 83 | "-" f=fattore() 84 { return -f; } 85 | "abs" f=fattore() 86 { return Math.abs(f); } 87 | "exp" f=fattore() 88 { return Math.exp(f); } 89 | "log" f=fattore() 90 { return Math.log(f); } 91 | "sin" f=fattore() 92 { return Math.sin(f); } 93 | "cos" f=fattore() 94 { return Math.cos(f); } 79 80 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 83 / 85 Esempi Calcolatore Esempi: Calcolatore VI "tan" f=fattore() { return Math.tan(f); } 97 | "(" f=espressione() ")" 98 { return f; } 99 } 95 | 96 N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 84 / 85 Riferimenti Riferimenti D. Galles: Modern Compiler Design. Scott/Jones Publishing A.J. Dos Reis: Compiler Construction Using Java, JavaCC, and Yacc, Wiley-IEEE Computer Society Press T. S. Norvell. JavaCC Tutorial N. Fanizzi: Manualetto JavaCC N. Fanizzi Linguaggi di prog.+Lab javacc – Analisi Sintattica 15 maggio 2014 85 / 85