antlr - Altervista

annuncio pubblicitario
www.antlr.org
Renzi Alberto




Strumento automatico che permette di generare
parser ricorsivi discendenti usando LL(*) (un
estensione dell’LL(k) che usa un lookahead arbitrario
per prendere decisioni)
Sviluppato in Java permette di generare codice in
Java, C#, Python, Ruby, C e C++.
Mette a disposizione uno strumento grafico di
sviluppo chiamato ANTLR-Works con il quale è
possibile anche debuggare I parser generati.
Supporta grammatiche EBNF e la generazione di
alberi.

Questo strumento può produrre in automatico il
codice relativo a:
 Lexer: parte lessicale dove si definiscono i token e i
separatori da ignorare
 Parser: parte grammaticale contenente le regole di
produzione
 TreeParser: un parser che riconosce un albero

Ognuno di questi oggetti può contenere azioni
semantiche
Fasi:
 Il lexer prepara i token da passare al parser
 Il parser accetta uno stream dei token preparati dal lexer e cerca
di riconoscere la struttura della frase. Se non vengono prodotti
AST può semplicemente eseguire azioni in accordo al significato
della frase. In alternativa può produrre un albero da passare ad
un TreeParser
 TreeParser: è un parser che accetta un AST che se richiesto
viene prodotto dal parser previa descrizione nella grammatica
del parser.
ANTLR al posto di creare un oggetti stringa per ogni token crea token contenenti i
riferimenti che permettono di recuperare il valore del token.
Per l’AST (nella versione base “CommonTree” fornita con ANTLR) i nodi possono
semplicemente puntare al token dal quale sono stati creati.
Sezioni importanti:
1.
2.
3.
4.
Dichiarazione del nome della classe che deve perforza coincidere con il nome della grammatica del
lexer (in questo caso NostroLexer.g dovrà essere il nome del file che specifica la grammatica del lexer e
NostroLexer.java sarà la classe generata)
lexer grammar NostroLexer;
Nella sezione degli options possiamo indicare il linguaggio di output , in questo caso Java
options
{
language=Java;
}
Nella sezione @Header possiamo indicare il package e gli import necessari
@header
{
package miopackage;
import java.util.*;
}
Nella sezione @members possiamo specificare metodi e variabili che saranno disponibili nella classe
“NostroLexer.java”
@members
{
public void metodo() { ..... }
}

Successivamente è possibile elencare i token
INT: ('0'..'9')+ ;
PLUS: '+' ;
COMMENT: '//'(~('\r'|'\n'))+ {skip();};

E i separatori da ignorare:
WS : ( ' ' | '\t' | '\r' | '\n' )+ {skip();} ;
Sezioni importanti:
1.
2.
3.
4.
Come per il lexer il file della grammatica deve incominciare con la dichiarazione del nome del parser:
parser grammar NostroParser;
Nella sezione degli options possiamo indicare il linguaggio di output, il vocabolario dei token da utilizzare, il K della
grammatica e se il parser dovrà generare l’AST
options
{
tokenVocab = CalendarLexer;
language=Java;
k=1;
output = AST;
}
Nella sezione @Header possiamo indicare il package e gli import necessari
@header
{
package miopackage;
import java.util.*;
}
Nella sezione @members possiamo specificare metodi e variabili che saranno disponibili nella classe
“NostroParser.java”
@members
{
public void metodo() { ..... }
}
5.
6.
Nella sezione @rulecatch possiamo indicare una strategia
personalizzata in caso di errore durante il riconoscimento di una
regola
@rulecatch {
catch (RecognitionException ex) { …. }
}
Elenco delle produzioni della grammatica (metasimboli in
minuscolo, Token in maiuscolo):
expr: expr PLUS term | term ;
term: term TIMES factor | factor ;
factor: INT ;
Metasimbolo:
{operazioni preliminari}
: lato destro della produzione {azioni semantiche} ;
Esempio
expr returns [int res] {//Eventuali inizializzazioni}
:
a:expr PLUS b:term {$res = $a+$b;} | c:term {$res = $c}
;
E’ anche possibile associare azioni semantiche per ogni simbolo che si incontra nella parte destra
expr returns [int res] {//Eventuali inizializzazioni}
:
a:expr {$res=$res+$a; } PLUS b:term {$res = $res+$b;} | c:term {$res = $c;}
;
Inoltre per accedere al payload di un token richiamando la proprietà “.text”.
term returns [int res]
:
INT {$res = $INT.text;}
;



ANTLR dalla versione 3 da la possibilità di creare
alberi omogenei e eterogenei.
Per gli alberi omogenei fornisce un
implementazione di base chiamata CommonTree
con il relativo CommonTreeAdaptor che funge
sia da Factory che da Adaptor allo stesso tempo.
Per gli alberi eterogenei è necessario
implementare l’interfaccia TreeAdaptor che da la
possibilità ad antlr di poter creare e navigare
l’albero senza conoscerne l’implementazione.
Il tipo di AST generato dal parser avviene indicando per ogni regola il
tipo di albero generato. Ciò e possibile specificarlo in due modi
all’interno della grammatica del parser:
1. Tramite i simboli ^ e !:

 Con ^: si indica quale token farà da radice
 Con !: possiamo indicare i token che non andranno a far parte dell’albero
 Es la regola
width:
WIDTH^ DUEP! INT SEMICOLON! ;
genera un albero con WIDTH come radice e un solo figlio INT perchè è l’unico
senza !.
2.
Tramite una riscrittura indicata con “->” al termine della produzione:
width:
WIDTH DUEP INT SEMICOLON -> ^(WIDTH INT);
dopo la “->” si aspetta che si elenchino radice e figli dell’albero
^( radice figlio1 figlio2 ....)


La specifica del TreeParser avviene in modo
analogo alla specifica del lexer e del parser con le
sezioni “options” “@header” “@members” ecc..
Mentre per le regole della grammatica che
descrivono l’albero si possono “estrarre” dopo
aver eseguito un copia e incolla delle regole del
parser semplicemente cancellando le regole
sintattiche e lasciando solo le riscritture che
descrivono l’AST:
Es. Width: WIDTH^ INT; //Per il primo tipo
Width: ^(WIDTH INT); //Per il secondo tipo


Antlr site: www.antlr.org
The Definitive ANTLR Reference, Terence Parr.
Scarica