Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


LINGUAGGI FORMALI PROGRAMMAZIONE JAVA, Appunti di Tecniche E Linguaggi Di Programmazione

Studio degli automi a stati finiti in grado di riconoscere e interpretare il linguaggio di programmazione java.

Tipologia: Appunti

2018/2019

Caricato il 18/03/2019

MarcoTilocca
MarcoTilocca 🇮🇹

5 documenti

1 / 17

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
AUTOMI A STATI FINITI RICONOSCITORI E INTERPRETI IN JAVA
Domenico Saccà
In questa dispensa è mostrato come implementare in Java gli esempi di riconoscitori di linguaggi
regolari. Inoltre in alcuni dei riconoscitori sono aggiunte azioni di semantica.
1. Automa a stati finiti deterministico: semplice esempio
Si#consideri#l'espressione#regolare#(ab)+b*#c+.#Un#semplice#automa#a#stati#finiti#riconoscitore#
del#linguaggio#generato#dall'espressione#regolare#è:##
#
Fig.#1#
L'automa#è#ovviamente#deterministico#e#possiamo#implementarlo#in#Java#nel#seguente#modo:#
#
import java.util.Scanner;
public class ASF_Ex1 {
public static void main ( String [] args ) {
Scanner read = new Scanner (System.
in
);
String s;
System.
out
.print("\ninserisci una stringa\n<<");
s=read.nextLine();
int q=0;
boolean errore = false;
int i = 0;
while ( i < s.length() && !errore )
switch (q) {
case 0:
if (s.charAt(i)=='a') {
q=1; i++;
}
else
errore=true;
break;
case 1:
if (s.charAt(i)=='b') {
q=2; i++;
}
else
errore=true;
break;
case 2:
if (s.charAt(i)=='a') {
q=1; i++;
}
else
if ( s.charAt(i)=='b' ) {
q=3; i++;
}
else
if ( s.charAt(i)=='c' ) {
q=4; i++;
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff

Anteprima parziale del testo

Scarica LINGUAGGI FORMALI PROGRAMMAZIONE JAVA e più Appunti in PDF di Tecniche E Linguaggi Di Programmazione solo su Docsity!

AUTOMI A STATI FINITI RICONOSCITORI E INTERPRETI IN JAVA

Domenico Saccà

In questa dispensa è mostrato come implementare in Java gli esempi di riconoscitori di linguaggi

regolari. Inoltre in alcuni dei riconoscitori sono aggiunte azioni di semantica.

1. Automa a stati finiti deterministico: semplice esempio

Si consideri l'espressione regolare (ab)+b* c+. Un semplice automa a stati finiti riconoscitore

del linguaggio generato dall'espressione regolare è:

Fig. 1

L'automa è ovviamente deterministico e possiamo implementarlo in Java nel seguente modo:

import java.util.Scanner; public class ASF_Ex1 { public static void main ( String [] args ) { Scanner read = new Scanner (System. in); String s; System. out.print("\ninserisci una stringa\n<<"); s=read.nextLine(); int q=0; boolean errore = false; int i = 0; while ( i < s.length() && !errore ) switch (q) { case 0: if (s.charAt(i)=='a') { q=1; i++; } else errore=true; break; case 1: if (s.charAt(i)=='b') { q=2; i++; } else errore=true; break; case 2: if (s.charAt(i)=='a') { q=1; i++; } else if ( s.charAt(i)=='b' ) { q=3; i++; } else if ( s.charAt(i)=='c' ) { q=4; i++;

else errore=true; break; case 3: if (s.charAt(i)=='b') i++; else if ( s.charAt(i)=='c' ) { q=4; i++; } else errore=true; break; case 4: if (s.charAt(i)=='c') i++; else errore=true; break; default: errore=true; } if (!errore && i==s.length() && q==4 ) System. out.println("\nOK"); else System. out.println("\nNOK"); } }

2. Automa a stati finiti deterministico: riconoscimento di numeri con virgola mobile

Un’espressione regolare che definisce i numeri con virgola mobile è:

( + ∪ -­‐ ∪ ε ) (^1. (0 ∪… ∪ 9)+^ ∪ (0 ∪… ∪ 9)+^ (^2 ε ∪. (^3 ε ∪ (0 ∪… ∪ 9)+^ )^3 )^2 )^1

(^4 ε ∪ e ( + ∪ -­‐ ∪ ε ) (0 ∪… ∪ 9)+^ )^4

Riportiamo di seguito l'automa a stati finiti riconoscitore è:

Figura 2

Implementiamo di seguito l’automa in Java secondo uno schema che permette di riconoscere

più numeri interi – per concludere basterà inserire una stringa vuota:

import java.util.Scanner;

if ( c>='0' && c <='9' ) i++; else if ( c=='e' || c=='E' ) { q=5; i++; } else errore=true; break; case 4: if ( c>='0' && c <='9' ) i++; else if ( c=='e' || c=='E' ) { q=5; i++; } else if ( c== '.' ) { q=8; i++; } else errore=true; break; case 5: if ( c>='0' && c <='9' ) { q=6; i++; } else if ( c=='+' || c=='-' ) { q=7; i++; } else errore=true; break; case 6: if ( c>='0' && c <='9' ) i++; else errore=true; break; case 7: if ( c>='0' && c <='9' ) { q=6; i++; } else errore=true; break; case 8: if ( c>='0' && c <='9' ) i++; else if ( c=='e' || c=='E' ) { q=5; i++; } else errore=true; break; default: errore=true;

return !errore && i==s.length() && (q==3 || q==4 || q==6 || q==8); } }

3. Automa a stati finiti non-deterministico: esempio

Consideriamo ora l'espressione regolare (ab*)+a (c ∪ d+)+. L'espressione è equivalente (cioè

genera lo stesso linguaggio) all'espressione (ab*)+a (c ∪ d)+. L'automa a stati finiti

riconoscitore, costruito a partire dalla seconda espressione, è:

Figura 3

Questo automa è ovviamente non deterministico (in ciascuno degli stati 1 e 2, vi sono due

archi uscenti con la stessa etichetta “a”). Ne possiamo costruire uno deterministico

introducendo degli stati composti, in particolare

  • poiché dallo stato 1 leggendo 'a' si può restare nello stato 1 o andare allo stato 3, la

mossa dallo stato 1 con simbolo 'a' porta allo stato composto 1_3.

  • poiché dallo stato 2 'a' si può andare sia allo stato 1 sia allo stato 3, la mossa dallo stato

2 con simbolo 'a' porta allo stato composto 1_3.

L'automa deterministico riconoscitore è:

Figura 4

L'automa può essere implementato in Java nel seguente modo (lo stato “1_3” è indicato con

import java.util.Scanner; public class ASF_Ex3 { public static void main ( String [] args ) { Scanner read = new Scanner (System. in); String s; System. out.print("\ninserisci una stringa del linguaggio (ab*)+a (c ∪ d+)+"); System. out.print("e premere return (o solo return per uscire)\n<<"); s=read.nextLine(); while ( s.length()>0 ) { if ( riconosci(s) ) System. out.println("\nOK");

if ( c== 'c' || c=='d' ) i++; else errore=true; break; default: errore=true; } } return !errore && i==s.length() && q==4 ; } }

4. Utilizzo della Classe RegExp di Java

E’ possibile in Java introdurre una espressione regolare come “pattern” da utilizzare per

verificare l’’appartenenza di una stringa al linguaggio definito dalla espressione regolare.

Per dettagli sull’utilizzo delle espressioni regolari in Java si può leggere un qualche tutorial

disponibile in rete, ad esempio:

http://www.vogella.com/tutorials/JavaRegularExpressions/article.html#pattern-­‐and-­‐

matcher

L’esempio svolto a lezione è il seguente:

import java.util.regex.; import java.util.Scanner; public class RegExp { // pattern es1: (ab)+bc+ // pattern es2: [+-]?((.\d+)|(\d+(.(\d+)?)?))(e[+-]?\d+)? public static void main (String[] args) { Scanner read = new Scanner (System. in); System. out.println("\ninserisci 'pattern' (solo return per finire)"); String sp = read.nextLine(); while ( sp.length() >0 ) { Pattern p = Pattern. compile(sp); System. out.println("\nstringa (solo return per finire)"); String s = read.nextLine(); while (s.length()>0 ) { Matcher m = p.matcher(s); boolean b = m.matches(); System. out.println("riconosciuto="+b); System. out.println("\nstringa (solo return per finire)"); s = read.nextLine(); } System. out.println("\n\ninserisci 'pattern' (solo return per finire"); sp = read.nextLine(); } System. out.println("\nBye");

read.close(); } }

Di seguito un’esempio di esecuzione del programma:

5. Automa a stati finiti non-deterministico: esercizio

Dato il seguente automa a stati finiti deterministico:

System. out.println("\nBye"); } static double q0() throws SyntaxError { if ( ioh.currSymb() == '+' ) { ioh.next(); return q2(); } if ( ioh.currSymb() == '-' ) { ioh.next(); return - q2(); } if ( '0' <= ioh.currSymb() && ioh.currSymb() <= '9' ) { int digit = ioh.currSymb()-'0'; ioh.next(); return q4(digit); } if ( ioh.currSymb() == '.') { ioh.next(); return q1(); } throw new SyntaxError(); } static double q1() throws SyntaxError { if ( '0' <= ioh.currSymb() && ioh.currSymb() <= '9' ) { double valD = ( ioh.currSymb()-'0')/10.0; ioh.next(); return q3(valD,100); } throw new SyntaxError(); } static double q2() throws SyntaxError { if ( '0' <= ioh.currSymb() && ioh.currSymb() <= '9' ) { int digit = ioh.currSymb()-'0'; ioh.next(); return q4(digit); } throw new SyntaxError(); } static double q3(double valD, double potD) throws SyntaxError { if ( '0' <= ioh.currSymb() && ioh.currSymb() <= '9' ) { valD += ( ioh.currSymb()-'0')/potD; ioh.next(); return q3(valD, potD10); } if ( Character. toUpperCase( ioh.currSymb()) == 'E' ) { ioh.next(); return valDMath. pow(10, q5()); } if ( ioh.currSymb() == IOH. END ) return valD; throw new SyntaxError(); }

static double q4(int valI) throws SyntaxError { if ( '0' <= ioh.currSymb() && ioh.currSymb() <= '9' ) { int digit = ioh.currSymb()-'0'; ioh.next(); return q4(valI10+digit); } if ( Character. toUpperCase( ioh.currSymb()) == 'E' ) { ioh.next(); return valIMath. pow(10, q5()); } if ( ioh.currSymb() == '.') { ioh.next(); return q8(valI,0,10); } if ( ioh.currSymb() == IOH. END ) return valI; throw new SyntaxError(); } static int q5() throws SyntaxError { if ( ioh.currSymb() == '+' ) { ioh.next(); return q7(); } if ( ioh.currSymb() == '-' ) { ioh.next(); return - q7(); } if ( '0' <= ioh.currSymb() && ioh.currSymb() <= '9' ) { int digit = ioh.currSymb()-'0'; ioh.next(); return q6(digit); } throw new SyntaxError(); } static int q6(int valI) throws SyntaxError { if ( '0' <= ioh.currSymb() && ioh.currSymb() <= '9' ) { int digit = ioh.currSymb()-'0'; ioh.next(); return q6(valI*10+digit); } if ( ioh.currSymb() == IOH. END ) return valI; throw new SyntaxError(); } static int q7() throws SyntaxError { if ( '0' <= ioh.currSymb() && ioh.currSymb() <= '9' ) { int digit = ioh.currSymb()-'0'; ioh.next(); return q6(digit); } throw new SyntaxError(); } static double q8(int valI, double valD, double potD) throws SyntaxError { if ( '0' <= ioh.currSymb() && ioh.currSymb() <= '9' ) { valD += ( ioh.currSymb()-'0')/potD;

Il codice Java del riconoscitore, che fa uso delle espressioni regolari gestite dalle classi Pattern e

Matcher, è il seguente:

import java.util.Scanner; import java.util.regex.; public class LF_Date_Parsing { static Pattern pat = Pattern. compile("([A-Za-z]+)|(\d+)|([^ ])"); static String expInp; static Matcher match; static boolean missingToken; static boolean mustBeLeapYear; static boolean isNotLeapYear; static int startDay; public static void main ( String [] args ) { Scanner read = new Scanner (System. in); System. out.print("\nEnter Date as: dd month yyyy" + "(type '.' to quit)\n<<"); expInp = read.nextLine(); while ( expInp.charAt(0)!='.' ) { match = pat.matcher( expInp); missingToken=false; boolean ok = q1( match.find()); if ( ok ) System. out.println("Date OK"); else { int k = missingToken? expInp.length(): mustBeLeapYear && isNotLeapYear? startDay: match.start(); for ( int i = 0; i <k+2; i++) System. out.print("-"); System. out.println("^"); System. out.println("*Wrong Date"); } System. out.print("\nEnter Date as: dd month yyyy"+ "(type '.' to quit)\n<<"); expInp = read.nextLine(); } System. out.println("\nBye"); } static boolean q1( boolean foundToken ){ if ( !foundToken ) { missingToken=true; return false;} String sday= match.group(); if ( match.group(2) != null ) { int day = parseInt(sday); if ( day > 0 && day <= 31 ) { startDay = match.start(); return q3( match.find(),day); } else return false; } else return false; } static boolean q3( boolean foundToken, int day ){ if ( !foundToken ) { missingToken=true; return false;} String month= match.group();

if ( match.group(1) != null ) if ( isMDcorrect(month,day) ) return q5( match.find()); else return false; else return false; } static boolean q5( boolean foundToken ){ if ( !foundToken ) { missingToken=true; return false;} String syear= match.group(); if ( match.group(2) != null ) { int year= parseInt(syear); if ( isYcorrect(year) ) return q3( match.find()); else return false; } else return false; } static boolean q3( boolean foundToken ){ return !foundToken; } private static int parseInt(String s ) { int val =0; for ( int i=0; i <s.length(); i++ ) val = val*10+s.charAt(i)-'0'; return val; } static boolean isMDcorrect(String month, int day ){ Pattern patM = Pattern. compile ( "(gennaio)|(febbraio)|(marzo)|(aprile)" +"|(maggio)|(giugno)|(luglio)|(agosto)"+ "|(settembre)|(ottobre)|(novembre)|(dicembre)"); Matcher matchM = patM.matcher(month.toLowerCase()); mustBeLeapYear=false; if ( matchM.find() ) { if ( matchM.group(4)!=null || matchM.group(6)!=null || matchM.group(9)!=null || matchM.group(11)!=null) return day <= 30; else if ( matchM.group(2)!=null ) if ( day == 29 ) { mustBeLeapYear=true; return true; } else return day <= 28; else return true; // case of month with 31 days } else return false; } private static boolean isYcorrect(int year) { if ( mustBeLeapYear && year%4 != 0 ) { isNotLeapYear=true; return false;

valore in cima allo stack) e ADD (somma i due numeri in cima allo stack e li sostituisce con la loro

somma). Ad esempio, +2- 3 - 4+25 viene tradotta in:

PUSH 2 PUSH 3 CHS ADD PUSH 4 CHS ADD PUSH 25 ADD

L’implementazione in Java illustrata a lezione, che fa uso delle classi Pattern e Matcher, è la

seguente:

import java.util.Scanner; import java.util.regex.; public class LF_Traduttore_Espr_Aritmetica { static Pattern pat = Pattern. compile("(\-)|(\+)|(\d+)|([^\+\-\d ])"); static String expInp; static Matcher match; static String expOut; static String token; static boolean missingToken; public static void main ( String [] args ) { Scanner read = new Scanner (System. in); System. out.print("\nEnter Arithmetic Expession (type '.' to quit)\n<<"); expInp = read.nextLine(); while ( expInp.charAt(0)!='.' ) { match = pat.matcher( expInp); missingToken=false; boolean ok = q0( match.find()); if ( ok ) System. out.println("Translation>>"+ expOut); else { int k = missingToken? expInp.length(): match.start(); for ( int i = 0; i <k+2; i++) System. out.print("-"); System. out.println("^"); System. out.println("*Wrong Expression"); } System. out.print( "\nEnter Arithmetic Expession (type '.' to quit)\n<<"); expInp = read.nextLine(); } System. out.println("\nBye"); } static boolean q0( boolean foundToken ){ if ( !foundToken ) { missingToken=true; return false;} token= match.group(); if ( token.equals("+") ) return q1( match.find(),false); else if ( token.equals("-") ) return q1( match.find(),true); else if ( match.group(3) != null ) return q2( match.find(),true); else return false; } static boolean q1( boolean foundToken, boolean isCHS ){ if ( !foundToken ) { missingToken=true; return false;} token= match.group();

if ( match.group(3) != null ) return q2( match.find(),isCHS); else return false; } static boolean q2( boolean foundToken, boolean isCHS ){ if (!isCHS) expOut="PUSH "+ token; else expOut="PUSH "+ token+" CHS "; if ( !foundToken ) return true; token= match.group(); if ( token.equals("+") ) return q3( match.find(),false); else if ( token.equals("-") ) return q3( match.find(),true); else return false; } static boolean q3( boolean foundToken, boolean isCHS ){ if ( !foundToken ) { missingToken=true; return false;} token= match.group(); if ( match.group(3) != null ) return q4( match.find(),isCHS); else return false; } static boolean q4( boolean foundToken, boolean isCHS ){ if (!isCHS) expOut +=" PUSH "+ token +" ADD "; else expOut +=" PUSH "+ token+" CHS ADD "; if ( !foundToken ) return true; token= match.group(); if ( token.equals("+") ) return q3( match.find(),false); else if ( token.equals("-") ) return q3( match.find(),true); else return false; } }

Il pattern "(\-)|(\+)|(\d+)|([^\+\-\d ])" comprende 4 gruppi: i primi due riconoscono i segni, il

terzo riconosce un intero e il quattro riconosce come errore qualsiasi carattere diverso da un segno o

una cifra decimale o lo spazio.