Generare analizzatori lessicali con Flex

Transcript

Generare analizzatori lessicali con Flex
UNIVERSITÀ DEGLI STUDI DI ROMA
“TOR VERGATA”
Generare analizzatori
lessicali con Flex
Corso di laurea triennale in Informatica
Corso di linguaggi e traduttori
Prof.ssa : Dora Giammarresi
Tutor : Francesca Capri
Anno Accademico 2010/2011
Terminologia
Tre concetti necessari per comprendere la fase di
analisi lessicale:
TOKEN: simbolo che rappresenta uno specifico tipo
di lessema, per esempio una parola chiave o una
sequenza di caratteri che denota un identificatore. In
genere è rappresentato da un nome ed un attributo
opzionale.
LESSEMA: sequenza di caratteri del programma
sorgente che corrisponde al pattern di un token
(istanza specifica di un token).
PATTERN o MODELLO: descrizione compatta delle
possibili forme che il lessema di un token può
assumere. Nel caso di una parola chiave il pattern è
semplicemente la sequenza di caratteri che formano
quella parola chiave.
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
2
Esempi : token, pattern e lessema
TOKEN
PATTERN
if
if
id
count, a123
[A-Za-z] ([A-Za-z]|[0-9])*
0, 3
[0-9]
digit
16/11/2010
LESSEMA
if
Linguaggi e traduttori A.A. 2010/2011
3
L’ analisi lessicale nel compilatore
Fasi di un compilatore:
L’analisi
lessicale è la
prima fase
del processo
di
compilazione
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
4
L’ analisi lessicale nel compilatore
Facendo un ingrandimento della parte cerchiata nell’immagine
relativa al compilatore, si può vedere meglio come lavora un
analizzatore lessicale e come questo interagisce con un
analizzatore sintattico o parser.
Programma
sorgente
Analizzatore
lessicale
token
Analizzatore
sintattico
getNextToken
Tabella
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
5
Ruolo dell’ analizzatore lessicale
Data in input una sequenza di caratteri (stringa
che forma il programma sorgente) di un
alfabeto, un analizzatore lessicale o scanner
o lexer , li raggruppa in lessemi e verifica se
questi possono essere decomposti in una
sequenza di token, tale che ogni token
appartenga al lessico:
in caso positivo, restituisce in uscita la
sequenza di token corrispondente ai lessemi
trovati.
in caso negativo, restituisce un errore
lessicale.
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
6
Ruolo dell’ analizzatore lessicale
L’analizzatore lessicale è in grado di
svolgere ulteriori operazioni e/o
compiti:
Elimina e/o ignora spazi vuoti e commenti(spazi,
tabulazioni, ritorni a capo)
Associa i messaggi di errore prodotti dal
compilatore al programma sorgente
Se nel programma sorgente sono presenti delle
macro potrebbe occuparsi della relativa espansione
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
7
Un po’ di storia………
Esitono molti generatori automatici:
Lex, Flex, ScanGen,.... : generano un codice in C
JLex, Sable, Cup, .... : generano un codice in Java
Lex fu il primo generatore di scanner. Esso fu inventato da Mike
Lesk e Eric Shmidt (AT&T Bell Lab) nel 1975. Lex è distribuito
con il sistema operativo Unix. Esistono tanti software
alternativi al Lex.
Uno dei più conosciuti ed usati è Flex ( Fast Lexical analyser
generator) introdotto da Vern Paxson intorno al 1987 per
risolvere problemi di efficienza. Esso è frequentemente usato
con Bison, un parser generator alternativo a Yacc, ma è
comunque utilizzabile come generatore di programmi standalone. Flex è un prodotto della Free Software Foundation, Inc.
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
8
Flex : introduzione
Flex è quindi un tool che genera
analizzatori lessicali, vediamo in
che modo:
prende in input un file che specifica il lessico di
un certo linguaggio, solitamente nella forma di
espressioni regolari e includendo altre funzioni
ausiliarie, definizioni di token, .....
produce in output un codice (scritto in un certo
linguaggio) che implementa il ruolo dello scanner.
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
9
Flex : generazione di uno scanner
lex.l
Compilatore
Flex
lex.yy.c
lex.yy.c
Compilatore
C
a.exe
File di
INPUT
a.exe
16/11/2010
output
(es. sequenza di
token)
Linguaggi e traduttori A.A. 2010/2011
10
Flex : file lex.l
lex.l: file sorgente in formato Flex, che serve
per avere una descrizione dello scanner da
generare. La descrizione è nella forma di
coppie comprendenti:
espressione regolare : definiscono rigorosamente gli
elementi che devono essere riconosciuti nel flusso di dati.
codice C : azioni da effettuare al verificarsi del match
con una RE.
Le coppie, RE e codice C, formano la sezione
REGOLE DI TRADUZIONE del file lex.l
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
11
Flex : file lex.yy.c
lex.yy.c: file di codice sorgente C, che
definisce la funzione ‘yylex()’. Questa
funzione può essere utilizzata direttamente
da una ipotetica funzione main per il
recupero dei token e non ritorna al chiamante
fino a quando non ha esaurito i suoi dati in
lettura.
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
12
Flex : funzione yylex()
La funzione, ‘yylex()’, scandisce l’input file yyin
e ritorna il prossimo token, ricopiando sul file yyout
il testo non riconosciuto. Per default, i file yyin e
yyout sono inizializzati rispettivamente a stdin e
stdout. Il token riconosciuto (una costante, un
numero, un'istruzione, ecc…), sarà fornito, con
l'indicazione del tipo ,al parser (Bison), ogni volta
che questo la richiamerà. Il parser in base alle
informazioni ricevute, applicherà le regole
opportune. L’implementazione di questa funzione si
basa su un DFA che rappresenta le RE. Al termine di
ogni azione l’automa si ricolloca sullo stato iniziale,
pronto a riconoscere nuovi simboli.
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
13
Flex : file a.exe
a.exe : file eseguibile ottenuto compilando
il file lex.yy.c tramite un compilatore C.
Quando l'eseguibile viene lanciato, analizza il
proprio ingresso in cerca di occorrenze delle
espressioni regolari.
Ogni volta che ne individua una, viene
eseguito il corrispondente codice C.
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
14
Flex : struttura dei programmi
Un file sorgente in formato Flex consiste di 3 sezioni,
separate da %%:
dichiarazioni
%%
regole di traduzione
%%
funzioni ausiliarie
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
15
Flex : struttura dei programmi
Un file sorgente in formato Flex consiste di 3 sezioni,
separate da %%:
dichiarazioni {sezione opzionale}
%%
obbligatorio anche se la sezione precedente è vuota
regole di traduzione {sezione obbligatoria }
%%
possiamo ometterlo se la sezione seguente è vuota
funzioni ausiliarie {sezione opzionale}
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
16
Flex : sezione dichiarazioni (1/3)
Questa sezione contiene:
Definizioni regolari
Ogni definizione è del tipo:
nome definizione
Esempi di definizione:
DIGIT
[0-9]
LETTER
[A-Za-z]
ID {LETTER}({LETTER}|{DIGIT})*
Il nome sarà lo stesso che, nella sezione regole di
traduzione, potrà essere utilizzato per riferirsi alla
specifica definizione, attraverso la dicitura {nome}
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
17
Flex : sezione dichiarazioni (2/2)
Opzioni
Flex permette il passaggio di opzioni che altrimenti sarebbero dovute
essere passate attraverso la riga di comando. Le opzioni vengono
passate attraverso la direttiva:
%option nomeopzione
Esempi di opzioni:
%option main: Flex fornisce un routine main() di default
%option noyywrap: questa direttiva comporta che non
venga chiamata la funzione yywrap() dopo che è stato raggiunto un
carattere di end-of-file (fine file) e si assume, quindi, che non esistano
più file da analizzare. La funzione yywrap() dovrebbe, teoricamente,
impostare correttamente la variabile globale yyin e ritornare il valore
zero se esistono altri file da analizzare, oppure ritornare un valore non
zero nel caso in cui il lavoro da compiere sia giunto al termine (e,
quindi, ottenere anche la terminazione nell'esecuzione dello scanner).
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
18
Flex : sezione dichiarazioni (3/3)
segmento di codice C
Il codice C, delimitato da
%{ %}
sarà ricopiato parola per parola nel file di output
lex.yy.c
Esempio di segmento C:
%{
#include <stdio.h>
#define PAP 1
#define PCH 2
#define SUM 3
%}
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
19
Flex : sezione regole di traduzione (1/3)
Questa sezione è l’unica obbligatoria ed è la più
importante del file Flex, perché è qui che vengono
specificate:
le regole di matching
le azioni da intraprendere per ogni
simbolo riconosciuto
Anche in questa sezione è possibile inserire codice tra
%{ e %}. Ciò deve avvenire prima della prima
regola. Può servire per esempio per dichiarare
variabili locali usate nella routine di scanning.
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
20
Flex : sezione regole di traduzione (2/3)
La sezione regole di traduzione avrà la seguente
struttura:
pattern {action}
pattern
sono espressioni regolari che permettono di avere un
riscontro con determinate stringhe di caratteri del linguaggio
sorgente. Questi pattern possono essere resi più compatti
richiamando in essi i nomi associati alle diverse definizioni come
descritto nella sezione dichiarazioni.
action
la parte action può :
Essere vuota (contiene solo ; ), al match del pattern non succede
nulla.
Contenere porzione di codice C, racchiuso tra {} , associata ad uno
specifico pattern e solo in presenza di esso sarà eseguito
integralmente.
Contenere solo | indica che quell’azione è definita nella stesso
modo della regola che segue.
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
21
Flex : sezione regole di traduzione (3/3)
Esempi di regole di traduzione:
{ID} { printf("IDENTIFICATORE\n"); }
[a-z]+ {ECHO;}
\t |
\n ;
Direttive speciali che possono essere incluse in
un’azione sono:
ECHO: copia yytext nell’output dello scanner
REJECT: cerca un'alternativa al match corrente
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
22
Flex : sezione funzioni ausiliarie
Quest'ultima sezione contiene semplicemente codice C che sarà
copiato così com'è nel file di output lex.yy.c Questa sezione è
opzionale e viene spesso usata per contenere funzioni
richiamate dallo scanner come supporto all'elaborazione. Di
fatto, qua possono essere riportate tutte quelle funzioni utili che
sono richiamate all'interno da qualche azione nella sezione
delle regole di traduzione.
Se si vuole generare uno scanner che possa vivere di vita
propria, bisogna fornirlo di una funzione main, tale funzione
va inserita proprio in questa sezione.
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
23
Flex : osservazioni
Sia nella sezione delle dichiarazioni che in quella delle
regole
di traduzione tutto ciò che si trova all'interno della coppia di
identificatori %{ e %} viene copiato direttamente nel file di output
lex.yy.c senza alcuna modifica. %{ e %} devono apparire a inizio di
linea (non indentati).
Nella sezione regole
di traduzione è possibile inserire codice
tra %{ e %}, basta che ciò avvenga prima della prima regola.
In Flex qualsiasi cosa tra /* e*/ è considerato commento e copiato
direttamente nel file di output lex.yy.c Però ci sono due eccezioni:
1. Nella sezione regole di traduzione i commenti non possono
apparire all’inizio di una linea o immediatamente dopo una lista
di stati dello scanner.
2. Nella sezione dichiarazioni i commenti non possono apparire
sulla linea in cui si trova la direttiva %option
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
24
Flex : come avviene il match?
Il programma finale generato da Flex:
legge il suo input un carattere alla volta da sinistra a destra
finché trova il più lungo prefisso di input (quello sarà il
corrente lessema) che fa match con uno dei pattern della
sezione delle regole di traduzione.
Il testo corrispondente al match viene reso disponibile
attraverso la variabile globale yytext e la sua lunghezza viene
memorizzata in yyleng.
Vengono quindi eseguite le azioni corrispondenti al pattern per
il quale avviene il match.
Prosegue la scansione dell’input alla ricerca di altri match.
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
25
Flex : risoluzione dei conflitti (1/2)
Durante il processo di match si possono
presentare due tipologie di conflitti che Flex
risolve grazie a due regole di cui è fornito:
1.
2.
Se più prefissi della sequenza d’ingresso soddisfano uno
o più pattern, si sceglie sempre il prefisso più lungo
possibile
Se il prefisso più lungo corrisponde a due o più pattern,
si sceglie il pattern elencato per primo nella sezione
delle regole di traduzione.
La seconda regola ci permette di rendere riservate le
parole chiave soltanto se si definiscono prima le
regole per le parole chiave e poi quelle per gli
identificatori.
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
26
Flex : risoluzione dei conflitti (2/2)
Esempio di conflitto
Dato il file :
%%
for {return FOR_CMD;}
format {return FORMAT_CMD;}
[a-z]+ {return GENERIC_ID;}
e la stringa di ingresso “format”, la procedura yylex ritorna il
valore FORMAT_CMD, preferendo la seconda regola alla prima
perché descrive una sequenza più lunga, e la seconda regola
alla terza perché definita prima nel file sorgente.
Se non viene trovato alcun match, viene eseguita la regola di
default : il carattere dell’input non riconosciuto viene
considerato un match e copiato nell’output.
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
27
Flex : come definire i pattern nelle regole?
x
.
[xyz]
[a-z]
[^A-Z]
r*
r+
r?
r{2,5}
r{2,}
r{4}
{nome}
(r)
rs
r|s
r/s
^r
r$
16/11/2010
il carattere 'x'
ogni carattere eccetto '\n'
una classe di caratteri;
in questo caso, 'x', 'y' o 'z'
una classe con un range;
ogni carattere compreso tra 'a' e 'z'
una classe negata:
ogni carattere NON nella classe
zero o più r,
dove r è un'altra espressione regolare
una o più r
zero o una r
tra due a cinque r
due o più r
esattamente 4 r
l'espansione della definizione di nome
r, parentesizzata per raggruppare
concatenazione: r seguita da s
alternativa: r oppure s
restrizione: r ma solo se seguita da s
r ma solo a inizio linea
r ma solo a fine linea
Linguaggi e traduttori A.A. 2010/2011
28
Flex : variabili e routine predefinite
int yylex(void) : routine di scanning
yytext : contiene la stringa che ha appena matchato con
un pattern. Può essere di tipo char * oppure char []
yyleng : contiene il numero dei caratteri riconosciuti
yylval : valore associato al token
FILE *yyin : input file (default: stdin)
FILE *yyout : output file (default: stdout)
unput(c) : rimette il carattere c nella stringa dei simboli da
esaminare
input() : legge il carattere successivo
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
29
Flex : primo programma
Il primo e più piccolo programma Flex ammesso è:
%%
Genera uno scanner che semplicemente prende il suo
input (un carattere per volta) e lo ricopia nel suo
output, per la regola di default.
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
30
Flex : esempio sezione dichiarazioni del file lexer2.l
%option noyywrap
inserisce la direttiva %option che mi permette
di non chiamare la funzione yywrap()
%{
#define PAP 1
#define PCH 2
#define SUM 3
#define MIN 4
#define MUL 5
#define DIV 6
#define ID 7
#define NUM 8
#define MOD 9
#define ASSIGNOP 10
%}
codice C racchiuso tra %{ %} che dichiara
delle costanti che verranno riportate
nel file di output
delim [ \t\n]
ws {delim}+
letter [A-Za-z]
digit [0-9]
number {digit}+(\.{digit}+)?(E[+\-]?{digit}+)?
id {letter}({letter}|{digit})*
16/11/2010
definisco i nomi dei token
Linguaggi e traduttori A.A. 2010/2011
31
Flex : esempio sezione regole di traduzione del file
lexer2.l
%%
richiamo il token ws definito nella sezione dichiarazioni
{ws} {}
{id} {printf("ID \n"); }
{number} {printf("NUM \n");}
"+" {printf("SUM \n");}
"-" {printf("MIN \n");}
"*" {printf("MUL \n");}
"/" {printf("DIV \n"); }
regole di traduzione
"%" {printf("MOD \n"); }
"(" {printf("PAP \n");}
")" {printf("PCH \n");}
"=" {printf("ASSIGNOP \n");}
"$" {return 0;}
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
32
Flex : esempio sezione funzioni ausiliarie del file
lexer2.l
%%
int main()
{
int a;
a = yylex();
system("PAUSE");
return 0;
}
16/11/2010
implemento il main che contiene yylex()
Linguaggi e traduttori A.A. 2010/2011
33
Flex : compilazione dei file
16/11/2010
Per prima cosa inserire i percorsi completi in cui sono installati
Flex e il compilatore C, più la cartella bin, nella variabile di sitema
path.
Esempio:
C:\Dev-Cpp\bin (compilatore C)
C:\Program Files (x86)\GnuWin32\bin (flex)
Posizionarsi tramite shell nella cartella dove vi sono i file da
compilare
Digitando flex nomeFile.l si compila il file flex e si crea il file
lex.yy.c
Esempio:
flex lexer2.l
Digitando gcc lex.yy.c si ottiene il file eseguibile a.exe
Linguaggi e traduttori A.A. 2010/2011
34
Link utili
Dove scaricare Flex:
http://flex.source forge.net/
Manuale di Flex:
http://flex.sourceforge.net/manual/
Compilatore C:
http://www.bloodshed.net/download.html
Materiale relativo alle espressioni regolari:
http://it.wikipedia.org/wiki/Espressione_regolare
16/11/2010
Linguaggi e traduttori A.A. 2010/2011
35