001    package de.hska.java.aufgaben.rekursion;
002    
003    /**
004     * Implementiert einen sehr einfachen Taschenrechner mit einem
005     * Parser auf Basis des rekursiven Abstiegs.
006     * Die zugrundliegende Grammatik in EBNF ist
007     * <pre>
008     * ausdruck = term, [ "+" | "-" , term ] ;
009     * term = "(",  term, ")" | "0" | "1" | ... | "9" ;
010     * </pre>
011     * <p>
012     *   <a href="http://www.home.hs-karlsruhe.de/~pach0003/informatik_1/aufgaben/rekursion.html#taschenrechner">Zurück zum Aufgabentext</a>
013     * </p>
014     * 
015     * @author Christian Pape
016     *
017     */
018    public class Taschenrechner {
019    
020        private char[] ausdruck;
021    
022        private char aktuellesZeichen;
023        
024        private int indexAktuellesZeichen = 0;
025        
026        /**
027         * Erzeugt einen neuen Taschenrechner mit dem gegebenen
028         * auszuwertenden Ausdruck.
029         */
030        public Taschenrechner(String ausdruck) {
031            this.ausdruck = ausdruck.toCharArray();
032            naechstesZeichenEinlesen();
033        }
034        
035        /**
036         * Liesst das nächste Zeichen ein. Falls keins mehr existiert,
037         * dann ist das Zeichen 0. Leerzeichen werden überlesen.
038         */
039        private void naechstesZeichenEinlesen() {
040            while ( indexAktuellesZeichen < ausdruck.length
041                    && Character.isWhitespace(ausdruck[indexAktuellesZeichen]) ) {
042                indexAktuellesZeichen++;
043            }
044            
045            if ( indexAktuellesZeichen < ausdruck.length) {
046                aktuellesZeichen = ausdruck[indexAktuellesZeichen++];
047            } else {
048                aktuellesZeichen = 0;
049            }
050        }
051        
052        /**
053         * Gibt den Wert des Ausdrucks zurück.
054         */
055        public int evaluiereAusdruck() {
056            int ergebnis = evaluiereTerm();
057            
058            while ( aktuellesZeichen == '+' || aktuellesZeichen == '-') {
059                char operator = aktuellesZeichen;
060                naechstesZeichenEinlesen();
061                int term = evaluiereTerm();
062                switch (operator) {
063                case '+': ergebnis += term;
064                          break;
065                case '-': ergebnis -= term;
066                          break;
067                }
068            }
069             
070            return ergebnis;
071        }
072    
073        /**
074         * Liesst einen Term ein und gibt den Wert zurück.
075         * Ein Term ist ein geklammerter Term oder eine Ziffer.
076         */
077        private int evaluiereTerm() {
078            int ergebnis = 0;
079            
080            if (aktuellesZeichen == '(') {
081                naechstesZeichenEinlesen();
082                ergebnis = evaluiereAusdruck();
083                if (aktuellesZeichen != ')') {
084                    System.out.println("Schliessende Klammer erwartet");
085                } else {
086                    naechstesZeichenEinlesen();
087                }
088            } else if ( Character.isDigit(aktuellesZeichen)) {
089                ergebnis = aktuellesZeichen - '0';
090                naechstesZeichenEinlesen();
091            }
092            
093            return ergebnis;
094        }
095    }