// Questa classe implementa un riconoscitore di espressioni aritmetiche // intere completamente parentesizzate, involgenti costanti naturali e // operatori di somma, differenza, prodotto e divisione. Nel corso del // riconoscimento, le espressioni vengono anche valutate; quindi il // risultato del riconoscimento non sara` un boolean, ma un Integer. // Il risultato null sara` il responso che segnala un errore di // sintassi oppure un tentativo di dividere per 0; quando, invece, // il risultato sara` diverso da null, esso rappresentera` il valore // dell'espressione. class Espress{ public static Integer espr( String ex ){ // Questo metodo da` avvio al riconoscimento/valutazione di // un'espressione, ma delega il grosso del lavoro al metodo // ricorsivo e privato che ha il suo stesso nome. if ( ex == null ) return null; // Inizializza una testina di scansione che poi si addentrera` nella // stringa ex grazie ai sottostanti metodi: numerale, prox, prossimoCar. int[] scans = { 0 }; // testina di scansione, // posta sul primo carattere di ex // Determina il valore, val, della stringa ex, se questa e` // un'espressione ben formata e completamente parentesizzata; // inoltre rimuovi eventuali spazi vuoti che la seguano. // Qualora ex sia malformata oppure la sua valutazione richieda // una divisione per 0, il risultato sara` null. Integer val = espr( ex, scans ); // return ( val == null ) ? null : ( prox( ex, scans, null ) ) ? val : null; // Rispetto all'istruzione return spenta qui sopra, la sottostante e` // piu` permissiva: accetta la mancanza delle parentesi piu` esterne. return ( val == null ) ? null : conAltro( val, ex, scans, null ); } private static Integer espr( String e, int[] s ){ // Le espressioni sono di due tipi: (1) numerali, ossia sequenze // ininterrotte di cifre decimali e (2) espressioni che iniziano // con una parentesi tonda seguita da due parentesi. Spazi vuoti // possono comparire in un'espressione, purche` non // all'interno di un numerale. Integer a; // risultato, eventuale secondo operando if ( prox( e, s, '(' ) ){ if ( ( a = espr( e, s ) ) == null ) return null; // fallimento return conAltro( a, e, s, ')' ); } if (( a = numerale( e, s ) ) == null ) return null; return a; } private static Integer conAltro( int a, String e, int[] s, Character delim ){ // Se al primo operando, a, fa seguito un operatore opt e poi un // secondo operando, b, effettua l'aggiornamento a = a opt b e // restituisci questo valore aggiornato dopo aver consumato il // delimitatore atteso, delim; se opt manca, restituisci a immutato. // Se qualcosa va storto, ad esempio il delimitatore discorda // dalle aspettative, restituisci null. char[] opt = { '*', '/', '+', '-' }; Integer b; for( char c : opt ) if ( prox( e, s, c) ) { if ( ( b = espr( e, s ) ) == null ) return null; // fallim. if ( c == '/' && b == 0 ) return null; a = ( c == '+' ) ? ( a+b ) : ( c == '-' ) ? ( a-b ) : ( c == '*' ) ? ( a*b ) : (a/b); break; } return ( ! prox( e, s, delim ) ) ? null : a; } private static Integer numerale( String e, int[] s ){ // Questo metodo consuma la sequenza iniziale formata di cifre decimali // consecutive dal residuo della stringa e che parte dalla sua posizione // s[0]. Se tale sequenza non e` vuota, restituisce il valore intero // non-negativo espresso da tale sequenza; // in caso contrario, restituisce il valore null. if ( s[ 0 ] >= e.length() || e.charAt( s[ 0 ] ) < '0' || e.charAt( s[ 0 ] ) > '9' ) return null; int acc = 0; do{ acc = acc * 10 + ( e.charAt( s[ 0 ] ) - '0' ); s[0]++; } while( s[0] != e.length() && e.charAt( s[ 0 ] ) >= '0' && e.charAt( s[ 0 ] ) <= '9' ); return acc; } private static boolean prox( String e, int[] s, Character c ){ // Questo metodo ci dice se, una volta sorvolati eventuali spazi iniziali // del residuo di e che inizia in posizione s[0], ci si imbatte nel // carattere atteso c (o, qualora c==null, se non vi sia piu` residuo). Character pross = prossimoCar( e, s ); if ( pross == null ) return c == null; if ( ! pross.equals( c ) ) return false; s[ 0 ]++ ; return true; } private static Character prossimoCar( String e, int[] s ){ // Questo metodo ci dice in quale carattere ci si imbatte, una volta sorvolati // eventuali spazi iniziali del residuo di e che inizia in posizione s[0]. while( s[0] < e.length( ) && e.charAt( s[ 0 ] ) == ' ' ) s[ 0 ]++; return ( s[0] >= e.length( ) ) ? null : e.charAt( s[ 0 ] ); } public static void main( String[] aaa ) { // Programma iniziale usa-e-getta, che serve solo al collaudo di questa classe. String espressione = "(2+2)"; do { System.out.println( espressione+" == "+ espr( espressione ) ); espressione = Leggi.leggi( "espressione" ); } while ( espressione != null ); } }