# Klammerberechnungen bei einem Taschenrechner



## HansUlli383 (19. Feb 2015)

Hey,

Ich programmiere derzeit einen Taschenrechner.
Grafische Oberfläche etc. ist inzwischen alles vorhanden, jedoch habe ich Probleme bei den Berechnungen.
Ich habe aus diesem Forum folgenden Quellcode genommen:







```
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

public class SimpleCalculator {

	public SimpleCalculator() {}

	/**
	 *  + = addition 
         *   - = subtraction * = multiplication
         *  / = division ^ = power (pow) 
         *  % = square-root e.g 2%4 = 2
	 *  
	 */
	public double calculate(String expression) {
		if (expression == null) return Double.MIN_VALUE;
		expression = expression.trim();
		if (expression.equals("")) return Double.MIN_VALUE;		
		StringTokenizer str = new StringTokenizer(expression, "+-*/^%", true);
		List tokens = new ArrayList(str.countTokens());
		w : while (str.hasMoreTokens())
			tokens.add(str.nextToken().trim());
		w : while (tokens.indexOf("%") > -1) {
			f : for (int n = 0; n < tokens.size(); n++)
				calculate("%", tokens, n);
		}
		w : while (tokens.indexOf("^") > -1) {
			f : for (int n = 0; n < tokens.size(); n++)
				calculate("^", tokens, n);
		}
		w : while (tokens.indexOf("*") > -1) {
			f : for (int n = 0; n < tokens.size(); n++)
				calculate("*", tokens, n);
		}
		w : while (tokens.indexOf("/") > -1) {
			f : for (int n = 0; n < tokens.size(); n++)
				calculate("/", tokens, n);
		}
		w : while (tokens.indexOf("-") > -1) {
			f : for (int n = 0; n < tokens.size(); n++)
				calculate("-", tokens, n);
		}
		w : while (tokens.indexOf("+") > -1) {
			f : for (int n = 0; n < tokens.size(); n++)
				calculate("+", tokens, n);
		}
		if (tokens.size() != 1) return Double.MIN_VALUE;
		return toDouble((String) tokens.get(0),  Double.MIN_VALUE);
		
	}

	private void calculate(String calcType, List tokens, int n) {
		String token = (String) tokens.get(n);
		if (!token.equals(calcType)) return;
		double l, r, res;
		int s, e;
		if (n - 1 == -1) {
			s = 0;
			l = 1;
		} else {
			s = n - 1;
			l = toDouble((String) tokens.get(n - 1), 1);
		}
		if (n + 1 == tokens.size()) {
			e = tokens.size() - 1;
			r = 1;
		} else {
			e = n + 1;
			r = toDouble((String) tokens.get(n + 1), 1);
		}
		if (calcType.equals("%"))
			res = Math.sqrt(r);
		else if (calcType.equals("^"))
			res = Math.pow(l, r);
		else if (calcType.equals("*"))
			res = l * r;
		else if (calcType.equals("/"))
			res = l / r;
		else if (calcType.equals("-"))
			res = l - r;
		else if (calcType.equals("+"))
			res = l + r;
		else
			res = 0;
		tokens.add(e + 1, String.valueOf(res));
		f : for (int i = e; i >= s; i--)
			tokens.remove(i);
		n = n + (e - s);

	}

	private double toDouble(String number, double defaultNumber) {
		try {
			return Double.parseDouble(number);
		} catch (NumberFormatException e) {}
		return defaultNumber;
	}	
}
```
(von java-forum.org-Benutzer meez)

Hat jemand eine Idee, wie ich diesem Taschenrechner Klammerberechnungen "beibringen" könnte?


----------



## Tarrew (19. Feb 2015)

Ohne dir was Einreden zu wollen mal ein anderer Vorschlag: 

Ich hab mal was ganz Ähnliches gemacht, aber mit Antlr4. Wenn man das grundsätzliche Prinzip erstmal verstanden hat, ist das echt genial, einfach und effektiv. 
Du kannst dir eine Grammatik bauen und Antlr generiert dir einen Parser, baut dir einen Baum (der die Rechnung darstellt) und iteriert darüber. 

Eine Grammatik besteht aus Lexer- und Parserregeln. Kleines Beispiel: 

Du hast beispielsweise folgende Lexerregeln:

```
MUL :   '*' ;
DIV :   '/' ;
ADD :   '+' ;
SUB :   '-' ;
NUMBER :   [0-9]+ ;
```

Und die Parserregeln:


```
expr:   expr operator=('*'|'/') expr   # MulDiv
    |   expr operator=('+'|'-') expr   # AddSub
    |   NUMBER                   # number
    |   '(' expr ')'          # parens
    ;
```

Wenn du jetzt zB die Rechnung "4+5+2" eingibst und den Parser damit fütterst, dann wird er dir einen Baum bauen, in etwa so: 


Außerdem wurde dir schon ein TreeWalker generiert, der über den Braum drüberläuft. 
Dann hast du zu guter letzt noch einen "Listener" wo das eigentliche Rechnen passiert. 

In dem Beispiel 4+5+2 wird der TreeWalker erstmal die "enterAddition()" Methode aufrufen. 
Da passiert erstmal nichts. Dann wird die "enterNumber()" gerufen, da er in den Knoten mit der 4 geht. Diese 4 kannst du jetzt auf einen Stack pushen. 

Dann wird er wieder in enterAddition() gehen für das 2. "+" wo wieder nichts passieren muss.
Dann wieder in enterNumber() für die 5. Die Number wird wieder auf den Stack gepusht. Das gleiche passiert mit der 2. Ob du bei enterNumber() oder exitNumber() die Zahlen pusht ist eigentlich egal. 

Bei exitAddition() nimmt man sich dann einfach die obersten zwei Zahlen vom Stack (also 5 und 2), addiert sie und packt sie auf den Stack. (Dann liegen noch die 4 und die 7 drauf). 
Dann wird für das weite "+" die exitAddition()-Methode aufgerufen und macht genau wieder das gleiche. Nimmt sich die 7 und die 4 vom Stack und packt eine 11 wieder drauf. 



So jetzt hab ich ziemlich viel geschrieben, wahrscheinlich total unverständlich, aber was ich sagen möchte ist, dass man auf diese Weise extrem leicht Sachen ausrechnen kann. Man kann sehr leicht Punkt vor Strich, Klammerechnung, Potenrechnung etc einfügen. 

Ich hab dann später noch Gleichungen implementiert wie: f(x)=e^x; als Eingabe und konnte mir dann mit "f(10)" das Ergebnis ausgeben lassen, oder auch die Ableitung bzw das Integral. 


Allgemein lassen sich mit Antlr total geile Dinge machen (auch wenn ich es am Anfang nicht gemocht habe), vllt ist das also auch eine Option für dich. 
Ich finde so ein Taschenrechner ist ein sehr gutes Beispiel für die Nutzung von Antlr. 

Grüße 

#Edit: 
Achja es sei noch gesagt, dass die generierten Files, also TreeWalker, Listener etc alles Javaklassen sind. Also könntest du es problemlos für die Berechnung nutzen in deinem bestehendem Java-Projekt ohne dir irgendwas neues bauen zu müssen.


----------



## Tarrew (19. Feb 2015)

Falls es noch jemand anderen interessiert (weil das Thema ja ziemlich interessant ist): 

Hab grad mit HansUlli in Skype geschrieben und wir haben eine Grammatik und einen Listener geschrieben, der Addieren, Subtrahieren, Dividieren, Multiplizieren, Klammerrechnung, Punkt vor Strich, Sinus, Cosinus und Tangens kann. 

Mit folgender Grammatik:

```
grammar example;

prog:   expr+ ;


expr:   expr op=('*'|'/') expr      # MulDiv
    |   expr op=('+'|'-') expr      # AddSub
    |   INT                         # int 
    |   SIN expr      				# sin
    |   COS expr					# cos
    |   TAN expr					# tan
    |   '(' expr ')'                # parens
    ;

MUL :   '*' ; 
DIV :   '/' ;
ADD :   '+' ;
SUB :   '-' ;
INT :   [0-9]+ ;    
NEWLINE:'\r'? '\n' ;    
WS  :   [ \t]+ -> skip ;
SIN : 'sin';
COS : 'cos';
TAN : 'tan';
```

Und folgendem Listener:

```
package test;

import java.util.Stack;

import test.exampleParser.AddSubContext;
import test.exampleParser.CosContext;
import test.exampleParser.IntContext;
import test.exampleParser.MulDivContext;
import test.exampleParser.ProgContext;
import test.exampleParser.SinContext;
import test.exampleParser.TanContext;



public class Listener extends exampleBaseListener {
	public Stack<Double> stack = new Stack<Double>();
	@Override
	public void exitProg(ProgContext ctx) {
		
	}
	
	@Override
	public void enterInt(IntContext ctx) {
		
		stack.push(Double.valueOf(ctx.INT().getText()));
	}
	
	@Override
	public void exitAddSub(AddSubContext ctx) {
		Double number1 = stack.pop();
		Double number2 = stack.pop();
		Double result;
		if (ctx.op.getType() == exampleParser.ADD) {
			result = number2 + number1;
		} else if (ctx.op.getType() == exampleParser.SUB) {
			result = number2 - number1;
		} else {
			throw new AssertionError("Unbekannter Operator "
					+ ctx.op);
		}
		stack.push(result);
	}
	
	@Override
	public void exitMulDiv(MulDivContext ctx) {
		Double number1 = stack.pop();
		Double number2 = stack.pop();
		Double result;
		if (ctx.op.getType() == exampleParser.MUL) {
			result = number2 * number1;
		} else if (ctx.op.getType() == exampleParser.DIV) {
			result = number2 / number1;
		} else {
			throw new AssertionError("Unbekannter Operator "
					+ ctx.op);
		}
		stack.push(result);
	}
	
	public double result(){
		return stack.pop();
	}
	
	@Override
	public void exitSin(SinContext ctx) {
		stack.push(Math.sin(stack.pop()));
	}
	
	@Override
	public void exitTan(TanContext ctx) {
		stack.push(Math.tan(stack.pop()));
	}
	
	@Override
	public void exitCos(CosContext ctx) {
		stack.push(Math.cos(stack.pop()));
	}
}
```
Damit lassen sich sich dann auch kompliziertere Sachen wie: sin(3*tan(sin(2+3*9))) problemlos ausrechnen. Und das mit grade Mal ~100 Zeilen Code. 
Finde Antlr persönlich sehr cool, vllt will irgendwann jemand mal was Ähnliches machen und probierts dann auch über diesen Weg. Denke nur über Java könnte das ziemlich aufwenig werden.


----------

