diff --git a/src/Core/Evaluation.vala b/src/Core/Evaluation.vala index 1d442ca7..2ed38ef4 100644 --- a/src/Core/Evaluation.vala +++ b/src/Core/Evaluation.vala @@ -24,7 +24,8 @@ namespace PantheonCalculator.Core { private errordomain EVAL_ERROR { NO_FUNCTION, NO_OPERATOR, - NO_CONSTANT + NO_CONSTANT, + NO_NUMBER } private errordomain SHUNTING_ERROR { @@ -61,7 +62,11 @@ namespace PantheonCalculator.Core { Operator () { symbol = "mod", inputs = 2, prec = 2, fixity = "LEFT", eval = (a, b) => a % b }, Operator () { symbol = "^", inputs = 2, prec = 3, fixity = "RIGHT", eval = (a, b) => Math.pow (a, b) }, Operator () { symbol = "E", inputs = 2, prec = 4, fixity = "RIGHT", eval = (a, b) => a * Math.pow (10, b) }, - Operator () { symbol = "%", inputs = 1, prec = 5, fixity = "LEFT", eval = (a, b) => b / 100.0 } + //Internal use only + //Hgh precedence multiply and divide for percentage evaluation + //Percentage always evaluates first as if it has parens around it + Operator () { symbol = "<*>", inputs = 2, prec = 7, fixity = "LEFT", eval = (a, b) => a * b }, + Operator () { symbol = "<÷>", inputs = 2, prec = 6, fixity = "LEFT", eval = (a, b) => a / b } }; private struct Function { string symbol; int inputs; Eval eval;} @@ -127,6 +132,9 @@ namespace PantheonCalculator.Core { case TokenType.CONSTANT: output.append (t); break; + case TokenType.CURRENT_LEFT_VALUE: + output.append (t); + break; case TokenType.FUNCTION: op_stack.push_tail (t); break; @@ -208,6 +216,20 @@ namespace PantheonCalculator.Core { foreach (Token t in token_list) { if (t.token_type == TokenType.NUMBER) { stack.push_tail (t); + } else if (t.token_type == TokenType.CURRENT_LEFT_VALUE) { + var t1 = stack.pop_tail (); + Token t2; + if (!stack.is_empty () && stack.peek_tail ().token_type == TokenType.NUMBER) { + t2 = stack.peek_tail ().dup (); + } else if (stack.is_empty ()) { + t2 = new Token ("1", TokenType.NUMBER); + } else { + throw new EVAL_ERROR.NO_NUMBER (""); + } + + stack.push_tail (t2); + stack.push_tail (t1); + } else if (t.token_type == TokenType.CONSTANT) { try { Constant c = get_constant (t); @@ -306,6 +328,7 @@ namespace PantheonCalculator.Core { if (fabs (d) - 0.0 < double.EPSILON) { d = 0.0; } + return new Token (d.to_string (), TokenType.NUMBER); } diff --git a/src/Core/Scanner.vala b/src/Core/Scanner.vala index 8fdaa871..f66862b4 100644 --- a/src/Core/Scanner.vala +++ b/src/Core/Scanner.vala @@ -22,7 +22,8 @@ namespace PantheonCalculator.Core { public errordomain SCANNER_ERROR { UNKNOWN_TOKEN, ALPHA_INVALID, - MISMATCHED_PARENTHESES + MISMATCHED_PARENTHESES, + INVALID_PERCENT } public class Scanner : Object { @@ -70,7 +71,7 @@ namespace PantheonCalculator.Core { } else if (t.token_type == TokenType.OPERATOR && (t.content in "-−")) { /* Define last_tokens, where a next minus is a number, not an operator */ if (last_token == null || ( - (last_token.token_type == TokenType.OPERATOR && last_token.content != "%") || + (last_token.token_type == TokenType.OPERATOR) || (last_token.token_type == TokenType.FUNCTION) || (last_token.token_type == TokenType.P_LEFT) )) { @@ -82,6 +83,20 @@ namespace PantheonCalculator.Core { //Insert a leading zero to make complete number e.g. .5 -> 0.5 t.content = "0" + t.content; t.token_type = TokenType.NUMBER; + } else if (t.token_type == TokenType.PERCENT) { + if (last_token == null || ( + (last_token.token_type == TokenType.OPERATOR) || + (last_token.token_type == TokenType.FUNCTION) || + (last_token.token_type == TokenType.P_LEFT))) { + throw new SCANNER_ERROR.INVALID_PERCENT (_("'%' must follow a value.")); + } else { + token_list.append (new Token ("<*>", TokenType.OPERATOR)); + token_list.append (new Token ("", TokenType.CURRENT_LEFT_VALUE)); + token_list.append (new Token ("<÷>", TokenType.OPERATOR)); + last_token = new Token ("100", TokenType.NUMBER); + token_list.append (last_token); + continue; + } } if (next_number_negative && t.token_type == TokenType.NUMBER) { @@ -122,7 +137,6 @@ namespace PantheonCalculator.Core { private Token next_token () throws SCANNER_ERROR { ssize_t start = pos; TokenType type; - if (uc[pos] == decimal_symbol.get_char (0)) { pos++; while (uc[pos].isdigit () && pos < uc.length) { @@ -141,10 +155,13 @@ namespace PantheonCalculator.Core { } type = TokenType.NUMBER; } else if (uc[pos] == '+' || uc[pos] == '-' || uc[pos] == '*' || - uc[pos] == '/' || uc[pos] == '^' || uc[pos] == '%' || + uc[pos] == '/' || uc[pos] == '^' || uc[pos] == '÷' || uc[pos] == '×' || uc[pos] == '−') { pos++; type = TokenType.OPERATOR; + } else if (uc[pos] == '%') { + pos++; + type = TokenType.PERCENT; } else if (uc[pos] == '√') { pos++; type = TokenType.FUNCTION; diff --git a/src/Core/Token.vala b/src/Core/Token.vala index 77a3fbdb..a2d3a935 100644 --- a/src/Core/Token.vala +++ b/src/Core/Token.vala @@ -24,6 +24,8 @@ namespace PantheonCalculator.Core { NUMBER, OPERATOR, FUNCTION, + PERCENT, + CURRENT_LEFT_VALUE, SEPARATOR, CONSTANT, P_LEFT, @@ -42,5 +44,9 @@ namespace PantheonCalculator.Core { token_type: in_token_type ); } + + public Token dup () { + return new Token (content, token_type); + } } } diff --git a/test/CoreTest.vala b/test/CoreTest.vala index 8ab28572..0f7352cf 100644 --- a/test/CoreTest.vala +++ b/test/CoreTest.vala @@ -74,8 +74,8 @@ class PantheonCalculator.Core.CoreTest : Object { assert_equal ("e^5.25 / exp(5.25)", "1"); assert_equal ("10^(log(2.2))", "2.2"); assert_equal ("3.141592654*3.141592654", "9.869604404"); // https://github.com/elementary/calculator/issues/7 - assert_equal ("10 + 5 - 10%", "14.9"); // https://github.com/elementary/calculator/issues/44 - assert_equal ("10 - 10% + 5", "14.9"); // https://github.com/elementary/calculator/issues/44 + assert_equal ("10 + 5 - 10%", "13.5"); // https://github.com/elementary/calculator/issues/44 + assert_equal ("10 - 10% + 5", "14"); // https://github.com/elementary/calculator/issues/44 assert_equal ("25,123 - 234,2", "-209,077", ",", "."); // https://github.com/elementary/calculator/issues/48 assert_equal ("25.000,123 - 234000,2", "-209.000,077", ",", "."); // https://github.com/elementary/calculator/issues/48