You are on page 1of 52

Introduzione a JavaCC

(Parte 1: Generazione di analizzatori lessicali)


Valeria Carofiglio
Tratto da: Modern Compiler Design David Galles

Le fasi del processo di compilazione

Le fasi del processo di compilazione

Cos JavaCC
partendo da una specifica di una grammatica, genera le classi Java che realizzano un analizzatore sintattico top-down per tale grammatica

Installazione
Scaricare JavaCC da: https://javacc.dev.java.net/servlets/ProjectDocumentList Decomprimere in una locazione
Es: C:\Programmi\JavaCC\javacc-4.0

Settare la propria variabile path:


Es: C:\Programmi\JavaCC\bin

Installazione (cont.)

Analizzatore Lessicale
Convertire file di Input in sequenze di token
class data{ int mese; int giorno; int anno; } void main(){ data oggi = new data(); oggi.mese = 9; oggi.giorno = 1; oggi.anno = 2007; } Sequenza di caratteri di input (es: programma sorgente Java)

class Identifier (data) left-brace Identifier (int) Identifier (mese) semicolon identifier (int) identifier (giorno) semicolon identifier (int) identifier (anno) semicolon right-brace Identifier (void) ..

Sequenza di Token (es: token del Java)

Come passare dalla sequenza di caratteri a quella di token


Gli automi Deterministici a Stati Finiti (alcuni esempi)
ESEMPIO1: Un automa deterministico a stati finiti che accetta la stringa ''new'' L'automa ha quattro stati: 0, 1 2 3 0 lo stato iniziale 3 lo stato finale Le transizioni sono tre.

Come passare dalla sequenza di caratteri a quella di token ( cont.)


Gli automi Deterministici a Stati Finiti (alcuni esempi)
ESEMPIO2: Un automa deterministico a stati finiti che accetta ''new'', ''null'' e tutti gli identificatori legali in simpleJava

Il passaggio da stringhe di caratteri a sequenze di token potrebbe essere definito da un automa a stati finiti deterministico davvero molto complesso. (si pensi a quanto complesso possa essere un linguaggio di programmazione)

Generazione di Analizzatori Lessicali


Come fare Dato un linguaggio di programmazione: Descrivere i token del linguaggio Automaticamente generare un automa per quei token Automaticamente generare codice che implementi lautoma Cosa serve

Un metodo per descrivere i token


Le Espressioni Regolari

Descrivere i token in JavaCC


Espressioni Regolari per alcuni token di uso comune (esempi)
Token Parola riservata else Parola riservata for Token Plus Identificatore SimpleJava Letterale intero simpleJava Letterale Reale simpleJava Espressione Regolare else for + [a-z A-Z_$]([0-9a-zA-Z_$])* [0-9]+ ([0-9]*.[0-9]+) | ([0-9]+.[0-9]*)

Tutti i caratteri e le stringhe devono essere racchiuse tra apic i (mancano per

mancanza di spazio) Tutte le espressioni che includono * o + devono essere incluse tra parentesi

Descrivere i token in JavaCC(cont)


Convenzioni per le espressioni regolari in JavaCC

["a","b","c","d"] = ("a"|"b"|"c"|"d") ["b"-"g"] = ["b","e","f","g"] = ("b"|"e"|"f"|"g") ["b"-"f","M"-"O"] = ["b","e","f","M","N","O"] = ("b"|"e"|"f"|"M"|"N"|"O") ()? = opzionale ()+ = ()* (~["a","b"]) = qualunque carattere eccetto a o b. pu essere usato solo con [].

JavaCC: un generatore di analizzatori lessicali


Input/Output

Input: Un insieme di espressioni regolari (una per ogni token del linguaggio) Output: Un analizzatore lessicale in Java che legge un file di input e lo separa in token

Struttura di un file JavaCC


Estensione dei file JavaCC: .jj
options{ /* codice per settare varie opzioni */ } PARSER_BEGIN(nomeParser) public class nomeParser { /* segmento spesso vuoto*/ } PARSER_END(nomeParser) TOKEN_MGR_DECLS : { /* dichiarazioni usate dallanalizzatore lessicale*/ } /* Regole per i token & azioni */

nomeParser.jj Lista di opzioni per JavaCC

Unit di compilazione Java

Regole lessicali e sintattiche

Regole di tipo TOKEN


Struttura di un file JavaCC

TOKEN : { <TOKEN_NAME: RegularExpression> }

TOKEN_NAME il nome del token che deve essere descritto RegularExpression lespressione regolare che lo descrive

Regole di tipo TOKEN


ESEMPI
TOKEN : { <ELSE: else> }

TOKEN : { <INTEGER_LITERAL: ([0-9])+> }

Regole di tipo TOKEN (cont.)


Regole di tipo Token
Pi token possono essere descritti nello stesso blocco di tipo token Con le descrizioni dei token separate da |

TOKEN: { <ELSE: ''else''> | <SEMICOLON: '';''> | <FOR: ''for''> | <INTEGER LITERAL: ([''0''- ''9''])+> | <IDENTIFIER: [''a''-''z'']([''a''-''z'',''0''-''9''])*> }

Regole di tipo Token (cont.)


Con uno sguardo alle regole dellesempio
Tutti i caratteri nelle espressioni regolari, in JavaCC, devono

essere racchiuse tra apici.


<INTEGER LITERAL: <([0 - 9])+> errata

Gli elementi racchiusi tra [ e ] devono essere separati da u na virgola (,) oppure dal segno meno (-).
<IDENTIFIER:<[''a''-''z'']([''a''-''z'' ''0''-''9''])*> errata

Le espressioni che fanno uso di + e - devono essere racchiuse tra parentesi.


<INTEGER LITERAL: [''0''- ''9'']+> errata

I caratteri spazio che nelle regole non sono inclusi tra apici vengono ignorati.

Le classi Java prodotte da JavaCC


Classi generiche:
SimpleCharStream.java Token.java ParseException.java TokenMgrError.ja

Implementa lanalizzatore lessicale

Contiene Classi specifiche per il parser: il metodo getNextToken()

nomeParser.java nomeParserTokenManager.java nomeParserConstants.java

Metodo getNextToken()
JavaCC Il metodo getNextToken implementa lautoma a stati finiti deterministico che riconosce i token descritti dalle espressioni regolari Quando il metodo getNextToken() viene chiamato, viene ricercata lespressione che corrisponde ai successivi caratteri nelle sequenza di input e restituito il token (oggetto) per quella regola.

Le classi Java prodotte da JavaCC


Classi generiche:
SimpleCharStream.java Token.java ParseException.java TokenMgrError.ja

Classi specifiche per il parser:


nomeParser.java nomeParserTokenManager.java nomeParserConstants.java

Le classi Java al metodo Ogni chiamata getNextToken() prodotte da JavaCC Restituisce


Classi generiche:
SimpleCharStream.java Token.java ParseException.java TokenMgrError.ja
un oggetto di questa classe

Classi specifiche per il parser:


nomeParser.java nomeParserTokenManager.java nomeParserConstants.java

public int kind; Le classi Java public int beginLine; prodotteint beginColumn; public da JavaCC
// tipo del token // posizione Classi generiche:

public int endLine, endColumn;


del token nel file di input affinch il

SimpleCharStream.java deve corrispondere /* testo che Token.java token venga creato*/ ParseException.java TokenMgrError.ja

public String image;

Classi specifiche per il parser:


nomeParser.java nomeParserTokenManager.java nomeParserConstants.java

public int kind; Le classi Java public int beginLine; prodotteint beginColumn; public da JavaCC
// tipo del token // posizione Classi generiche:

public int endLine, endColumn;


del token nel file di input

SimpleCharStream.java deve corrispondere affinch il /* testo che Token.java token venga creato*/ ParseException.java TokenMgrError.ja public interface nomeParserConstants {
int int int int EOF = 0; CLASSS = 8; DO = 9; ELSE = 10;

public String image;

Classi specifiche per il parser:


nomeParser.java nomeParserTokenManager.java nomeParserConstants.java

Esempio1
Si consideri il seguente file sempliceFile.jj
PARSER BEGIN(sempliceFile) public class sempliceFile { } PARSER END(sempliceFile) TOKEN: { <ELSE: ''else''> | <SEMICOLON: '';''> | <FOR: ''for''> | <INTEGER LITERAL: ([''0''- ''9''])+> | <IDENTIFIER: [''a''-''z'']([''a''-''z'',''0''-''9''])*> }

Esempio (cont.)
Si consideri il seguente input :
else;else21;

La chiamata al metodo getNextToken()tenta di risolvere la corrispondenza tra linput e le espressioni regolari presenti ne lle regole che definiscono i token.
TOKEN: { <ELSE: ''else''> | <SEMICOLON: '';''> | <FOR: ''for''> | <INTEGER LITERAL: ([''0''- ''9''])+> | <IDENTIFIER: [''a''-''z'']([''a''-''z'',''0''-''9''])*> }

Esempio (cont.)
Si consideri il seguente input :
else;else21;

La chiamata al metodo getNextToken()tenta di risolvere la corrispondenza tra linput e le espressioni regolari presenti ne lle regole che definiscono i token.
TOKEN: corrisponde!! { <ELSE: ''else''> | <SEMICOLON: '';''> | <FOR: ''for''> | <INTEGER LITERAL: ([''0''- ''9''])+> | <IDENTIFIER: [''a''-''z'']([''a''-''z'',''0''-''9''])*> }

Pi di una regola

Metodo getNextToken()(cont.)
JavaCC Quando pi di una espressione regolare corrisponde allinput, JavaCC adotta la seguente strategia:
Usa lespressione regolare per la corrispondenza pi lunga con linput. In caso di stessa lunghezza: usa la regola che appare per prima nel blocco TOKEN del file .jj

Esempio
Si consideri il seguente file File2.jj
PARSER BEGIN(File2) public class File2{ } PARSER END(File2) SKIP: { <'' ''> | <'\n''> | <'\t''> } Per linput: Else ; 23 for

TOKEN: { <ELSE: ''else''> | <SEMICOLON: '';''> | <FOR: ''for''> | <INTEGER LITERAL: ([''0''- ''9''])+> | <IDENTIFIER: [''a''-''z'']([''a''-''z'',''0''-''9''])*> }

Esempio
Si consideri il seguente file File2.jj
PARSER BEGIN(File2) public class File2{ } PARSER END(File2) SKIP: { <'' ''> | <'\n''> | <'\t''> } Per linput: Else ; 23 for

Oggetti restituiti da getNextToken():

<ELSE> < ><SEMICOLON>. TOKEN: { <ELSE: ''else''> | <SEMICOLON: '';''> | <FOR: ''for''> | <INTEGER LITERAL: ([''0''- ''9''])+> | <IDENTIFIER: [''a''-''z'']([''a''-''z'',''0''-''9''])*> }

Esempio
Si consideri il seguente file File2.jj
PARSER BEGIN(File2) public class File2{ } PARSER END(File2) SKIP: { <'' ''> | <'\n''> | <'\t''> } Per linput: Else ; 23 for

getNextToken():

<ELSE> < ><SEMICOLON>. TOKEN: { <ELSE: ''else''> | <SEMICOLON: '';''> | <FOR: ''for''> | <INTEGER LITERAL: ([''0''- ''9''])+> | <IDENTIFIER: [''a''-''z'']([''a''-''z'',''0''-''9''])*> }

Le regole SKIP per ignorare i commenti


Esempio Si consideri un file scritto in linguaggio C++. Un commento allinterno del file sarebbe una riga che cominci per //.

regola di tipo SKIP


SKIP: { <//(~[\n])* \n> } una sequenza qualunque di caratteri
(eccetto quello di fine-linea), preceduta da // e terminante con un carattere di fine-linea.

Le regole SKIP per ignorare i commenti


Esempio Si consideri un file scritto in linguaggio C++. Un commento allinterno del file sarebbe una riga che cominci per //. Poco Espressive!!

regola di tipo SKIP


SKIP: { <//(~[\n])* \n> } una sequenza qualunque di caratteri
(eccetto quello di fine-linea), preceduta da // e terminante con un carattere di fine-linea.

Luso degli Stati Lessicali per ignorare i commenti


Ogni regola di tipo TOKEN o SKIP pu essere etichettata con uno Stato Lessicale . Le regole non etichettate sono considerate regole nello stato lessicale DEFAULT. Lanalizzatore lessicale potr, ad ogni istante,

trovare corrispondenze con le sole regole etichettate con il medesimo stato lessicale
in cui si trova lanalizzatore stesso.
Etichetta

<STATOVECCHIO> SKIP: { <EspressioneRegolare>:STATONUOVO }

Trovata la corrispondenza tra lespressione regolare e una porzione dellinput, lanalizzatore passer dallo stato STATOVECCHIO allo stato STATONUOVO.

Esempio3
Si consideri il seguente file File3.jj
PARSER BEGIN(File3) public class File3{ } PARSER END(File3) SKIP: { < > | <\n> | <\t> } SKIP: { </*> : IN COMMENTO } <IN COMMENTO>: SKIP: { </*> : DEFAULT | <~[ ]> } Regole etichettate DEFAULT

Esempio3
Si consideri il seguente file File3.jj
PARSER BEGIN(File3) public class File3{ } PARSER END(File3) SKIP: { < > | <\n> | <\t> } SKIP: { </*> : IN COMMENTO } <IN COMMENTO>: SKIP: { </*> : DEFAULT |<~[ ]> }

Regole etichettate IN_COMMENTO

Esempio3
Si consideri il seguente file File3.jj
PARSER BEGIN(File3) public class File3{ } PARSER END(File3) SKIP: { < > | <\n> | <\t> }

SKIP: { </*> : IN COMMENTO } <IN COMMENTO>: SKIP: { </*> : DEFAULT |<~[ ]> }

Passaggio da stato lessicale DEFAULT a stato lessicale IN_COMMENTO

Esempio3
Si consideri il seguente file File3.jj
PARSER BEGIN(File3) public class File3{ } PARSER END(File3) SKIP: { < > | <\n> | <\t> }

SKIP: { </*> : IN COMMENTO } Passaggio da stato lessicale IN_COMMENTO a <IN COMMENTO>: stato lessicale DEFAULT SKIP: { <*/> : DEFAULT |<~[ ]> }

Passaggio da stato lessicale DEFAULT a stato lessicale IN_COMMENTO

Esempio3 Si consideri il seguente file File3.jj


PARSER BEGIN(File3) public class File3{ } PARSER END(File3) SKIP: { < > | <\n> | <\t> } SKIP: { </*> : IN COMMENTO } <IN COMMENTO>: SKIP: { <*/> : DEFAULT |<~[ ]> } TOKEN: { <ELSE: else> | <SEMICOLON: ;> | <FOR: for> | <INTEGER LITERAL: ([0 -9])+> | <IDENTIFIER: [a-z]([a z, 0- 9])*> }

Per linput:

else /* commento*/ 12/* commento +/else

Stato Analizzatore

getNextToken()

Regole possibili

DEFAULT

Esempio3 Si consideri il seguente file File3.jj


PARSER BEGIN(File3) public class File3{ } PARSER END(File3) SKIP: { < > | <\n> | <\t> } SKIP: { </*> : IN COMMENTO } <IN COMMENTO>: SKIP: { <*/> : DEFAULT |<~[ ]> } TOKEN: { <ELSE: else> | <SEMICOLON: ;> | <FOR: for> | <INTEGER LITERAL: ([0 -9])+> | <IDENTIFIER: [a-z]([a z, 0- 9])*> }

Per linput:

else /* commento*/ 12/* commento +/else

Stato Analizzatore

getNextToken()

Regole possibili

DEFAULT DEFAULT

ELSE

Esempio3 Si consideri il seguente file File3.jj


PARSER BEGIN(File3) public class File3{ } PARSER END(File3) SKIP: { < > | <\n> | <\t> } SKIP: { </*> : IN COMMENTO } <IN COMMENTO>: SKIP: { <*/> : DEFAULT |<~[ ]> } TOKEN: { <ELSE: else> | <SEMICOLON: ;> | <FOR: for> | <INTEGER LITERAL: ([0 -9])+> | <IDENTIFIER: [a-z]([a z, 0- 9])*> }

Per linput:

else /* commento*/ 12/* commento +/else

Stato Analizzatore

getNextToken() return

Regole possibili

DEFAULT DEFAULT

ELSE

Esempio3 Si consideri il seguente file File3.jj


PARSER BEGIN(File3) public class File3{ } PARSER END(File3) SKIP: { < > | <\n> | <\t> } SKIP: { </*> : IN COMMENTO } <IN COMMENTO>: SKIP: { <*/> : DEFAULT |<~[ ]> } TOKEN: { <ELSE: else> | <SEMICOLON: ;> | <FOR: for> | <INTEGER LITERAL: ([0 -9])+> | <IDENTIFIER: [a-z]([a z, 0- 9])*> }

Per linput:

else /* commento*/ 12/* commento +/else

Stato Analizzatore

getNextToken() return

Regole possibili

DEFAULT DEFAULT DEFAULT

ELSE
Spazio ignorato

Esempio3 Si consideri il seguente file File3.jj


PARSER BEGIN(File3) public class File3{ } PARSER END(File3) SKIP: { < > | <\n> | <\t> } SKIP: { </*> : IN COMMENTO } <IN COMMENTO>: SKIP: { <*/> : DEFAULT |<~[ ]> } TOKEN: { <ELSE: else> | <SEMICOLON: ;> | <FOR: for> | <INTEGER LITERAL: ([0 -9])+> | <IDENTIFIER: [a-z]([a z, 0- 9])*> }

Per linput:

else /* commento*/ 12/* commento +/else

Stato Analizzatore

getNextToken() return

Regole possibili

DEFAULT DEFAULT DEFAULT

ELSE
Spazio ignorato

Esempio3 Si consideri il seguente file File3.jj


PARSER BEGIN(File3) public class File3{ } PARSER END(File3) SKIP: { < > | <\n> | <\t> } SKIP: { </*> : IN COMMENTO } <IN COMMENTO>: SKIP: { <*/> : DEFAULT |<~[ ]> } TOKEN: { <ELSE: else> | <SEMICOLON: ;> | <FOR: for> | <INTEGER LITERAL: ([0 -9])+> | <IDENTIFIER: [a-z]([a z, 0- 9])*> }

Per linput:

else /* commento*/ 12/* commento +/else

Stato Analizzatore

getNextToken() return

Regole possibili

DEFAULT DEFAULT DEFAULT


IN_COMMENTO

ELSE
Spazio ignorato

/*

Esempio3 Si consideri il seguente file File3.jj


PARSER BEGIN(File3) public class File3{ } PARSER END(File3) SKIP: { < > | <\n> | <\t> } SKIP: { </*> : IN COMMENTO } <IN COMMENTO>: SKIP: { <*/> : DEFAULT |<~[ ]> }

Per linput:

else /* commento*/ 12/* commento +/else

Stato Analizzatore

getNextToken() return

Regole possibili

DEFAULT DEFAULT

ELSE
Spazio ignorato

() TOKEN: { <ELSE: else> | <SEMICOLON: ;> | <FOR: for> | <INTEGER LITERAL: ([0 -9])+> | <IDENTIFIER: [a-z]([a z, 0- 9])*> }

Pi chiama te del metodo getNextTok en

DEFAULT

/*
ignorati

IN_COMMENTO Caratteri

..

Luso delle azioni nelle regole SKIP e TOKEN


E possibile aggiungere codice Java alle regole SKIP e TOKEN. Tale codice verr eseguito ogni volta che verr trovata una corrispondenza tra sequenze di caratteri in input ed espressioni regolari nella regola (SKIP o TOKEN). Il codice Java potr fare uso di una qualunque dichiarazione (metodo/variabile) inserita nel blocco TOKEN_MGR_DECLS (si veda slide 12 ).

Esempio4 Si consideri il seguente file rimCommenti.jj


PARSER_BEGIN(rimCommenti) public class rimCommenti {} PARSER_END(rimCommenti) TOKEN_MGR_DECLS: { public static int numCommenti = 0; } SKIP: { </*> : IN_COMMENTO } SKIP: { < // (~[\n])* \n > { numCommenti++;} }

Esempio4 (cont.)
<IN_COMMENTO> SKIP : { < "*/" > { numcomments++; SwitchTo(DEFAULT);} } <IN_COMMENTO> SKIP : { < ~[] > } TOKEN : { <ANY: ~[]>

Luso dellanalizzatore lessicale generato tramite JavaCC, in un programma Java


nomeParser.jj
Classi generate

Per creare un programma java che fa uso dellanalizzatore lessicale:


Creare un oggetto della classe nomeParserTokenMenager.java
Il costruttore di questa classe richiede un oggetto della classe SimpleCharStream .java

Classi generiche: SimpleCharStream.java Token.java ParseException.java TokenMgrError.java Classi specifiche: nomeParser.java nomeParserTokenManager.java nomeParserConstants.java

Creare un oggetto della classe SimpleCharStream.java


Il costruttore di questa classe richiede un Java.io.InputStream standard

Creare un oggetto della classe Java.io.InputStream.java

Luso dellanalizzatore lessicale generato tramite JavaCC, in un programma Java (cont.)?

token t; nomeParser tm; java.io.InputStream infile; infile = new Java.io.FileInputStream("fileName"); tm = new nomeParserTokenMenager(new SimpleCharStream(infile)); t=tm.getNextToken(); while(t.Kind != nomeParserConstants.EOF){ /*processa t */ t = tm.getNextToken(); }

Esempio
Si vuole usare un analizzatore lessicale generato da JavaCC per contare ed eliminare commenti da un file in input.

Esempio4 (cont.)
un programma java che usa lanalizzatore lessicale generato per contare ed eliminare i commenti da un file di input
class rimuoviCommenti{ public static void main(string args[]){ token t; java.io.inputStream infile; rimuoviCommentiTokenMenager tm; if (args.length < 1){ System.out.print("Inserisci un nome di file"); return;} try { infile = new java.io.FileInputStream(args[0]); }catch (Java.io.FileNotFoundException e) { System.out.println("File" + args[] + "non trovato"); return; } tm = new rimuoviCommentiTokenMenager(new SimpleCharStream(in file)); t = tm.getNextToken(); while (t.kind != rimCommentiConstants.EOF){ System.out.print(t); t=tm.getNextToken();} System.out.println("/* Numero di commenti rimossi = " + Integer.toString(tm.numCommenti) + " */"); }

You might also like