blob: 5a43570c6bf1ca7f24ced596d7bd7f370d1be14a [file] [log] [blame]
Justin Klaassen4b3af052014-05-27 17:53:10 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.calculator2;
18
Justin Klaassen4b3af052014-05-27 17:53:10 -070019import org.javia.arity.Symbols;
20import org.javia.arity.SyntaxException;
21import org.javia.arity.Util;
22
23public class CalculatorExpressionEvaluator {
Justin Klaassen741471e2014-06-11 09:43:44 -070024
Justin Klaassene61089e2014-12-04 11:31:22 -080025 /**
26 * The maximum number of significant digits to display.
27 */
Justin Klaassen696f2292014-05-30 18:13:04 -070028 private static final int MAX_DIGITS = 12;
Justin Klaassene61089e2014-12-04 11:31:22 -080029
30 /**
31 * A {@link Double} has at least 17 significant digits, we show the first {@link #MAX_DIGITS}
32 * and use the remaining digits as guard digits to hide floating point precision errors.
33 */
34 private static final int ROUNDING_DIGITS = Math.max(17 - MAX_DIGITS, 0);
Justin Klaassen4b3af052014-05-27 17:53:10 -070035
36 private final Symbols mSymbols;
37 private final CalculatorExpressionTokenizer mTokenizer;
38
Justin Klaassen2be4fdb2014-08-06 19:54:09 -070039 public CalculatorExpressionEvaluator(CalculatorExpressionTokenizer tokenizer) {
Justin Klaassen4b3af052014-05-27 17:53:10 -070040 mSymbols = new Symbols();
Justin Klaassen2be4fdb2014-08-06 19:54:09 -070041 mTokenizer = tokenizer;
Justin Klaassen4b3af052014-05-27 17:53:10 -070042 }
43
44 public void evaluate(CharSequence expr, EvaluateCallback callback) {
45 evaluate(expr.toString(), callback);
46 }
47
48 public void evaluate(String expr, EvaluateCallback callback) {
49 expr = mTokenizer.getNormalizedExpression(expr);
50
51 // remove any trailing operators
52 while (expr.length() > 0 && "+-/*".indexOf(expr.charAt(expr.length() - 1)) != -1) {
53 expr = expr.substring(0, expr.length() - 1);
54 }
55
56 try {
zhuqk0617b8a581a2015-05-26 16:48:10 +080057 if (expr.length() == 0 /*|| Double.valueOf(expr) != null*/) {
Justin Klaassen2be4fdb2014-08-06 19:54:09 -070058 callback.onEvaluate(expr, null, Calculator.INVALID_RES_ID);
Justin Klaassen4b3af052014-05-27 17:53:10 -070059 return;
60 }
61 } catch (NumberFormatException e) {
62 // expr is not a simple number
63 }
64
65 try {
66 double result = mSymbols.eval(expr);
67 if (Double.isNaN(result)) {
Justin Klaassen2be4fdb2014-08-06 19:54:09 -070068 callback.onEvaluate(expr, null, R.string.error_nan);
Justin Klaassen4b3af052014-05-27 17:53:10 -070069 } else {
Justin Klaassen696f2292014-05-30 18:13:04 -070070 // The arity library uses floating point arithmetic when evaluating the expression
71 // leading to precision errors in the result. The method doubleToString hides these
72 // errors; rounding the result by dropping N digits of precision.
Justin Klaassen2be4fdb2014-08-06 19:54:09 -070073 final String resultString = mTokenizer.getLocalizedExpression(
74 Util.doubleToString(result, MAX_DIGITS, ROUNDING_DIGITS));
75 callback.onEvaluate(expr, resultString, Calculator.INVALID_RES_ID);
Justin Klaassen4b3af052014-05-27 17:53:10 -070076 }
77 } catch (SyntaxException e) {
Justin Klaassen2be4fdb2014-08-06 19:54:09 -070078 callback.onEvaluate(expr, null, R.string.error_syntax);
Justin Klaassen4b3af052014-05-27 17:53:10 -070079 }
80 }
81
82 public interface EvaluateCallback {
Justin Klaassen2be4fdb2014-08-06 19:54:09 -070083 public void onEvaluate(String expr, String result, int errorResourceId);
Justin Klaassen4b3af052014-05-27 17:53:10 -070084 }
85}