/* Nuova versione della calcolatrice Scopo di questa versione e' mostrare la costruzione di un albero a partire dall'espressione. L'albero viene poi utilizzato per mostrare l'espressione in notazione postfissa e per valutarla. La struttura dell'albero viene fornita nella classe astratta Expr, con il metodo di calcolo calcola() */ import lt2.calc.Scanner; import lt2.calc.Token; import static lt2.calc.TipoToken.*; import java.io.*; import java.util.Stack; public class Calcolatrice { private static Token t; //variabile destinata a contenere il riferimento //al prossimo token da esaminare private static Scanner scanner; //riferimento all'analizzatore lessicale public static void main(String[] args) throws IOException { //creazione del canale di input BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); //lettura della stringa da esaminare System.out.print("Espressione? "); String s = in.readLine(); //creazione dell'analizzatore lessicale: la stringa riferita da s //e' vista come file di input scanner = new Scanner(new StringReader(s)); //preleva il primo token t = scanner.getProssimo(); try { Expr e = espressione(); //alla fine dell'espressione i token dovrebbero essere finiti if (t.getTipo() == FINE) { System.out.println("Notazione postfissa: " + e.toString()); System.out.println("Risultato: " + e.calcola()); } else throw new EspressioneException("Stringa non aspettata: " + scanner.yytext()); } catch (EspressioneException e) { System.err.println(e.toString()); } } public static Expr espressione() throws IOException { Expr alberoSx, alberoDx; boolean piu; alberoSx = termine(); while ((t.getTipo() == PIU) || (t.getTipo() == MENO)) { piu = t.getTipo() == PIU; t = scanner.getProssimo(); alberoDx = termine(); //crea un nuovo albero collegando il sottoalbero sx e dx //e memorizzando l'operatore if (piu) alberoSx = new PiuExpr(alberoSx, alberoDx); else alberoSx = new MenoExpr(alberoSx, alberoDx); } return alberoSx; } public static Expr termine() throws IOException { Expr alberoSx, alberoDx; boolean per; alberoSx = fattore(); while ((t.getTipo() == PER) || (t.getTipo() == DIVISO)) { per = t.getTipo() == PER; t = scanner.getProssimo(); alberoDx = fattore(); //crea un nuovo albero collegando il sottoalbero sx e dx //e memorizzando l'operatore if (per) alberoSx = new PerExpr(alberoSx, alberoDx); else alberoDx = new DivisoExpr(alberoSx, alberoDx); } return alberoSx; } public static Expr fattore() throws IOException { Expr albero; Stack unari = new Stack(); while (t.getTipo() == MENO || t.getTipo() == PIU) { unari.push(t); t = scanner.getProssimo(); } if (t.getTipo() == NUMERO) { albero = new NumExpr((Integer) t.getValore()); t = scanner.getProssimo(); } else if (t.getTipo() == TONDA_APERTA) { t = scanner.getProssimo(); albero = espressione(); if (t.getTipo() == TONDA_CHIUSA) t = scanner.getProssimo(); else throw new EspressioneException("Parentesi chiusa attesa"); } else throw new EspressioneException("Stringa non aspettata: " + scanner.yytext()); while (!unari.empty()) { Token op = unari.pop(); if (op.getTipo() == PIU) albero = new UnPiuExpr(albero); else albero = new UnMenoExpr(albero); } return albero; } /* Nota relativa all'implementazione del metodo fattore. E' stato utilizzato uno stack per memorizzare gli operatori unari che si trovano all'inizio del fattore e poi porli nell'albero nella posizione corretta. Si potrebbe fornire una soluzione alternativa che anziche' introdurre uno stack esplicito, ricorra allo stack utilizzato per implementare la ricorsione. A tale scopo occorre modificare lo schema di riconoscimento utilizzato dal metodo, sostituendo il ciclo while iniziale con una ricorsione. Il metodo dovrebbe essere sviluppato tenendo conto che un fattore e' formato da un operatore unario seguito da un fattore (chiamata ricorsiva) oppure da un numero oppure da un'espressione racchiusa tra parentesi. */ } class EspressioneException extends RuntimeException { public EspressioneException(String m) { super(m); } }