Progetto Automi e Linguaggi Parser svliluppato con JLex e cup Sviluppato da Santoro Carlo Maurizio Matricola:0108/528 Sviluppo terminato il: 18/06/06
TRACCIA DEL PROGETTO Si costruisca, utilizzando la coppia di programmi JLex e CUP, un parser in grado di riconoscere un linguaggio che descrive dei capi di abbigliamento ed attribuisca loro delle valutazioni. Il linguaggio consente delle categorie di capi di abbigliamento; per ognuna è possibile definire un insieme di attributi che si applicano ad una categoria ed assegnare agli attributi un peso, utile per la valutazione complessiva di un abbigliamento. Dopo la definizione delle categorie dei capi d abbigliamento possono comparire le descrizioni dei prodotti che assegnano una valorizzazione agli attributi. Una valorizzazione è formata da un valore, descritto con un simbolo ( '-', '+', '*'), e da un nome di attributo. Il significato dei simboli è descritto nella seguente tabella: Simbolo Significato Valore * Eccellente 3 + Buono 2 - Sufficiente 1 Il programma deve riconoscere il linguaggio descritto precedentemente e deve stampare, suddivisi per categorie, l elenco dei prodotti con il loro punteggio. Se nella descrizione non è presente un attributo presente nella definizione del capo di abbigliamento allora a tale attributo viene assegnato il valore di default 1. Quando il programma incontra un errore, sia sintattico che semantico, deve segnalarlo e terminare l'esecuzione: non è richiesta la gestione ed il recupero degli errori. Dato il seguente ingresso: // Definizione dei capi di abbigliamento: abito = [rifiniture->10, tessuto->8]; camicia = [rifiniture->10, tessuto->8];? // Descrizione dei capi di abbigliamento : abito di BOSS = rifiniture *, tessuto -: abito; abito di Valentino = rifiniture *, tessuto +: abito; camicia di Armani = rifiniture +, tessuto +: camicia; Esempio Il programma dovrebbe produrre il seguente output; il formato non è vincolante, invece le informazioni contenute lo sono: Punteggi raggiunti: Abito di Boss, 38 Abito di Valentino, 46 Camicia di Armani, 36
DESCRIZIONE GENERALE DEL PROGETTO Data una sequenza di simboli in ingresso, si vuole sviluppare un programma che verifica se la sequenza appartiene o no al linguaggio definito dalla grammatica. E necessario un analizzatore sintattico (parser) : un programma che è in grado di associare alla sequenza di simboli in ingresso il relativo albero di derivazione. Deve essere di tipo bottom-up (dalle foglie alla radice), un software di questo tipo è CUP. CUP è un generatore di analizzatori sintattici o parser che trasforma la descrizione di una grammatica context-free in un programma Java per riconoscere ed analizzare la grammatica stessa. Oltre alle regole sintattiche, è possibile specificare quali azioni devono essere eseguite in corrispondenza del riconoscimento dei vari simboli della grammatica. E necessario integrare il parser così generato con un analizzatore lessicale: alcune convenzioni permettono di eseguire l integrazione di Cup con lo scanner JLex in modo semplificato. Ed è stato creato uno script che effettua questo compito. Setup, verrà impostata l integrazione con JLex e le funzioni e variabili per i calcoli Terminali e Non Terminali, tutti i simboli da utilizzare nel programma Precedenze - Regole, espressioni che legano i simboli terminali e non terminali Dopo aver prodotto il sorgente si opererà in questo modo: JLEX : E un Analizzatore lessicale o scanner che si occupa di analizzare uno stream di caratteri in input e produrre in uscita uno stream di tokens(hanno un tipo e un valore) che costituiscono gli elementi base su cui andrà ad operare un analizzatore sintattico cup. I sorgenti hanno la seguente struttura User code, verranno inserite le informazioni per l integrazione con cup %% Jlex directives %% Regolar Expression Rules, tutti I simboli da intercettare Dopo aver prodotto il sorgente si opererà in questo modo:
INTEGRAZIONE DI CUP E JLEX I due programmi vengono interfacciati secondo il seguente schema : I passaggi sono stati automatizzati da uno script che esegue le compilazioni dei codici e alla fine esegue il codice. Script.bat del Yylex.java del sym.java del parser.java rmdir /S /Q Example La prima parte si occupa di rimuovere i codici compilati eventualmente già presenti nella directory java JLex.Main minimal.lex move minimal.lex.java Yylex.java java java_cup.main < minimal.cup javac -d. *.java java Example.parser La seconda parte segue lo schema precedente Alla fine viene lanciato il codice prodotto Lo script in formato.bat è eseguibile in sistemi di tipo DOS\WINDOWS ma con le dovute modifiche si può eseguire su sistemi GNU\Linux. Lo script richiede che sia inserito in una directory del tipo.../myjlex/j e che al suo interno vi siano le directory con i sorgenti compilati di JLEX e CUP, è anche necessario che sia impostato nel path la cartella.../myjlex/j. I due file che l utente dovrà produrre saranno esclusivamente minimal.lex e minimal.cup scelti in questo modo perché disponibili direttamente sul sito ufficiale di cup.
DEFINIZIONE DEL SORGENTE CUP Descrizione della grammatica Il file cup dovrà prendere in ingresso i simboli TERMINALI forniti da JLex e riconoscere se appartenenti alla grammatica tramite le produzioni dei simboli NON TERMINALI. E anche necessario compiere delle operazioni che consentano di effettuare gli opportuni calcoli previsti dalla traccia, a questo scopo sono state definite delle strutture dati e delle funzioni che agevolano tele compito. Sono proposti : in blu il codice cup, in nero le produzioni della grammatica, in corsivo i simboli non terminali e in corsivo grassetto i simboli terminali //SIMBOLI TERMINALI terminal paperta,pchiusa,pvirgola,uguale,virgola,freccia,pinter,duepunti,asterisco,piu,meno; terminal String stringa; terminal Integer numero; Questi simboli sono intercettati da JLex e costituiscono la base per la creazione di espressioni con i simboli non terminali Stringa e numero hanno una definizione propria perché dovranno essere associate a delle variabili. //SIMBOLI NON TERMINALI non terminal radice,interrog,listadef,definizione,listaattrib,attrib,listadescrizione,descrizione,listavalorattrib,valorattrib; I simboli non terminali verranno usati nelle produzioni //PRODUZIONE PRINCIPALE start with radice; Rappresenta la radice dell albero di derivazione E lecita una sola occorrenza di questa parola chiave //DEFINIZIONE DELLA GRAMMATICA radice ::= interrog listadef interrog listadescrizione interrog; radice? listadefinizioni? listadescrizioni? //PRODUZIONE DI DEFINIZIONI listadef ::= listadef definizione definizione ; definizione ::= stringa uguale paperta listaattrib pchiusa pvirgola; listaattrib ::= listaattrib virgola attrib attrib ; attrib ::= stringa freccia numero ; listadefinizioni listadefinizioni definizione definizione definizione stringa = [ ListaAttibuti ]; ListaAttrubuti ListaAttributi, attributo attributo attributo stringa -> numero //PRODUZIONE DI DESCRIZIONE listadescrizione ::= listadescrizione descrizione descrizione ; descrizione ::= stringa uguale listavalorattrib duepunti stringa pvirgola; listavalorattrib ::= listavalorattrib virgola valorattrib valorattrib ; valorattrib ::= stringa asterisco stringa piu stringa meno ; ListaDescrizione ListaDescrizione descrizione descrizione descrizione stringa = ListaValoreAttributo : stringa ; ListaValoreAttributo ListaValoreAttributo, ValoreAttributo ValoreAttributo ValoreAttributo stringa * stringa + stringa -
DEFINIZIONE DEL SORGENTE CUP Definizione delle strutture dati Dopo aver riconosciuto la grammatica si devono effettuare i calcoli per produre l output desiderato. La parte delle definizioni e la parte delle descrizioni creano due strutture dati sulle quali si effettueranno i dovuti calcoli per produrre la sequenza di uscita. DEFINIZIONI Dato un input come: abito = [rifiniture->10, tessuto->8]; camicia = [rifiniture->10, tessuto->8]; Dovrà produrre una tabella come: Rifiniture Tessuto Abito 10 8 Camicia 10 8 Tale tabella dovrà essere implementata fisicamente come: String capi[]; //Vettore colonna a sinistra della matrice String attibuti[]; //Vettore riga sopra alla matrice int [][]matrice ; //Memorizza informazione sui pesi dato che gli attributi e i capi sono vettori di stringhe e i pesi sono inseriti in una matrice di interi DESCRIZIONI Dato un input come: abito di BOSS = rifiniture *, tessuto -: abito; abito di Valentino = rifiniture *, tessuto +: abito; camicia di Armani = rifiniture +, tessuto +: camicia; Dovrà produrre una tabella come: Nome capo Tipo capo rifiniture tessuto abito di BOSS Abito (1) * (3) - (1) abito di Valentino Abito (1) * (3) + (2) camicia di Armani Camicia (2) + (2) + (2) Tale tabella dovrà essere implementata fisicamente come: String vettdescr[]; //Vettore dei nomi delle descrizioni int tipodescr[]; //Contiene la tipologia dei capi int [][]matriceattr; //Matrice che contiene il valore degli attributi
Occorre produrre un output del tipo: Punteggi raggiunti: Abito di Boss, 38 Abito di Valentino, 46 Camicia di Armani, 36 DEFINIZIONE DEL SORGENTE CUP Calcoli per produrre l output Per realizzarlo si dovrà procedere ad operare parallelamente sulle due matrici, scorrendo tutte le righe della tabella 2, per ogni riga si dovrà proporre un risultato. Per calcolare il risultato di ogni riga si dovranno allineare le tabelle con : l indice di riga della tabella 1 e il valore della riga considerata del vettore TipoCapo nella tabella 2 Dopo avere allineato le due righe delle tabelle opportunamente, si potrà effettuare la sommatoria per tutte le righe utili : Σ( Tabella1[descrizione]*Tabella2[descrizione] ) Il codice che effettua tale operazione è : int i=0; //indice di riga int k; //indice di colonna int risultato; //accumulatore dei risultati parziali for (k=1;k<=9;k++){ risultato=0; for (i=1;i<=9;i++) risultato = risultato+(matrice[tipodescr[k]][i]*matriceattr[k][i]); Stampa (risultato);
DEFINIZIONE DEL SORGENTE JLEX Lo scanner dovrà intercettare i simboli in input da tastiera, in caso di stringa o numero dovrà essere istanziata una nuova variabile che verrà trattata in modo opportuno da cup SIMBOLI SEMPLICI stringa=[a-za-z] %% "[" { return new Symbol(sym.paperta); "]" { return new Symbol(sym.pchiusa); ";" { return new Symbol(sym.pvirgola); "=" { return new Symbol(sym.uguale); "," { return new Symbol(sym.virgola); "->" { return new Symbol(sym.freccia); "?" { return new Symbol(sym.pinter); ":" { return new Symbol(sym.duepunti); "*" { return new Symbol(sym.asterisco); "+" { return new Symbol(sym.piu); "-" { return new Symbol(sym.meno); SIMBOLI COME VARIABILE {stringa* { return new Symbol(sym.stringa, new String(yytext())); [0-9]+ { return new Symbol(sym.numero, new Integer(yytext())); [ \t\r\n\f] { /* ignora spazi bianchi. */. { System.err.println("Carattere non consentito: "+yytext()); L integrazione con cup e in generale con il progetto è effettuata all inizio del codice con : package Example; import java_cup.runtime.symbol; %% %cup
package Example; import java_cup.runtime.*; action code {: SORGENTE CUP : minimal.cup pag1 //DEFINIZIONE VARIABILI GLOBALI int sezione=0; int capi_now=0; //Variabile di stato che indica a quale capo ci si riferisce(indice vet) int attrib_now=0; //Variabile di stato che indica a quale attributo ci si riferisce(indice vet) //VARIABILI PER LA DEFINIZIONE //Tabella 1 int [][]matrice ; //Memorizza informazione sui pesi String capi[]; //Vettore colonna a sinistra della matrice String attibuti[]; //Vettore riga sopra alla matrice String nomedescr; //Il nome della descrizione visualizzato in output //VARIABILI PER LA DESCRIZIONE //Formano tabella 2 String vettdescr[]; //Vettore dei nomi delle descrizioni ex AbitoDiUgoBoss int tipodescr[]; //Contiene la tipologia dei capi int [][]matriceattr; //Matrice che contiene gli attributi int postabdue = 1; //Indica in che posizione ci si trova della tabella 2 nell'input //Inizializza ed istanzia i dati void gestiscisezione() { if (sezione==1) { int i; //Scorre le colonne int k; //Scorre le righe //Tabella 1 matrice = new int[10][10]; capi = new String[10]; attibuti = new String[10]; //Tabella 2 vettdescr = new String[10]; matriceattr = new int[10][10]; tipodescr = new int[10]; //inizializza dati //Tabella 1 for (i=1;i<=9;i++){capi[i]="nullo"; for (i=1;i<=9;i++){attibuti[i]="nullo"; for (i=1;i<=9;i++){ for (k=1;i<=9;i++){matrice[i][k]=0; //Tabella 2 for (i=1;i<=9;i++){vettdescr[i]="nullo"; for (i=1;i<=9;i++){tipodescr[i]=0; for (i=1;i<=9;i++){ for (k=1;i<=9;i++){matriceattr[i][k]=0; //End SEZIONE 1 //END funzione gestiscisezione
SORGENTE CUP : minimal.cup pag 2 //Calcola e visualizza i risultati void stampa(){ int i=0; //indice di riga int k; //indice di colonna int risultato; //accumulatore dei risultati parziali System.out.println(""); for (k=1;k<=9;k++){ risultato=0;ù for (i=1;i<=9;i++){risultato=risultato+(matrice[tipodescr[k]][i]*matriceattr[k][i]); if(risultato==0)system.out.print(""); else {System.out.print(vettdescr[k]);System.out.print(",");System.out.println(risultato); //End funzione stampa void addattrib(string s,int n) { int i=0; int postrovato=0; //indica dove è stato trovato while (postrovato==0){ i=i+1; if (s.equals(attibuti[i])) postrovato=i; else if (attibuti[i].equals("nullo")) {attibuti[i]=s; postrovato=i; attrib_now=postrovato; matrice[capi_now][attrib_now]=n; //Riceve in input una descrizione del tipo NOME VALORE(*+-) void nuovadescr(string s,int valore) { int i=0; int postrovato=0; //indica dove è stato trovato //Trova l'indice vettore dove si trova l'attributo s while (postrovato==0){ i=i+1; if (s.equals(attibuti[i])) postrovato=i; matriceattr[postabdue][postrovato]=valore; //Da una stringa restituisce la posizione del vettore CAPO int trovacapo(string s) { int i=0; int postrovato=0; //indica dove è stato trovato //Trova l'indice vettore dove si trova l'attributo s while (postrovato==0){ i=i+1; if (s.equals(capi[i])) postrovato=i; return(postrovato);
SORGENTE CUP : minimal.cup pag3 //Aggiunge un attributo alla lista degli attributi void addcapo(string s) { int i=0; int postrovato=0; //indica dove è stato trovato while (postrovato==0){ i=i+1; if (s.equals(capi[i])) postrovato=i; else if (capi[i].equals("nullo")) {capi[i]=s; postrovato=i; capi_now=postrovato; :;//FINE ACTIONE CODE parser code {: public static void main(string args[]) throws Exception { new parser(new Yylex(System.in)).parse(); : //SIMBOLI TERMINALI terminal paperta,pchiusa,pvirgola,uguale,virgola,freccia,pinter,duepunti,asterisco,piu,meno; terminal String stringa; terminal Integer numero; //SIMBOLI NON TERMINALI non terminal radice,interrog,listadef,definizione,listaattrib,attrib,listadescrizione,descrizione,listavalorattrib,valorattrib; //PRODUZIONE PRINCIPALE start with radice; //DEFINIZIONE DELLA GRAMMATICA radice ::= interrog listadef interrog listadescrizione {: System.out.println(""); System.out.println("GRAMMATICA RICONOSCIUTA"); stampa(); :interrog; //PRODUZIONE DI DEFINIZIONI interrog ::= {:sezione=sezione+1; gestiscisezione(); : pinter ; listadef ::= listadef definizione definizione ; definizione ::= stringa:sdefin{: addcapo(sdefin); : uguale paperta listaattrib pchiusa pvirgola; listaattrib ::= listaattrib virgola attrib attrib ; attrib ::= stringa:sattrib freccia numero:n {: addattrib(sattrib,n); :; //PRODUZIONE DI DESCRIZIONE listadescrizione ::= listadescrizione descrizione descrizione ; descrizione ::= stringa:s1 {: vettdescr[postabdue]=s1; : uguale listavalorattrib duepunti stringa:s2{: tipodescr[postabdue]=trovacapo(s2); postabdue=postabdue+1; : pvirgola; listavalorattrib ::= listavalorattrib virgola valorattrib valorattrib ; valorattrib ::= stringa:s1 asterisco {:nuovadescr(s1,3); : stringa:s2 piu {: nuovadescr(s2,2); : stringa:s3 meno {: nuovadescr(s3,1); : ;
SORGENTE JLlex : minimal.lex package Example; import java_cup.runtime.symbol; %% %cup stringa=[a-za-z] %% "[" { return new Symbol(sym.paperta); "]" { return new Symbol(sym.pchiusa); ";" { return new Symbol(sym.pvirgola); "=" { return new Symbol(sym.uguale); "," { return new Symbol(sym.virgola); "->" { return new Symbol(sym.freccia); "?" { return new Symbol(sym.pinter); ":" { return new Symbol(sym.duepunti); "*" { return new Symbol(sym.asterisco); "+" { return new Symbol(sym.piu); "-" { return new Symbol(sym.meno); {stringa* { return new Symbol(sym.stringa, new String(yytext())); [0-9]+ { return new Symbol(sym.numero, new Integer(yytext())); [ \t\r\n\f] { /* ignora spazi bianchi. */. { System.err.println("Carattere non consentito: "+yytext());