From: <cr...@us...> - 2009-08-20 15:32:02
|
Revision: 5657 http://jnode.svn.sourceforge.net/jnode/?rev=5657&view=rev Author: crawley Date: 2009-08-20 15:31:54 +0000 (Thu, 20 Aug 2009) Log Message: ----------- More work on the expression evaluator. Modified Paths: -------------- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneArithmeticEvaluator.java trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneArithmeticEvaluatorTest.java Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneArithmeticEvaluator.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneArithmeticEvaluator.java 2009-08-20 11:24:38 UTC (rev 5656) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneArithmeticEvaluator.java 2009-08-20 15:31:54 UTC (rev 5657) @@ -90,6 +90,15 @@ public long getValue() throws ShellSyntaxException { return name != null ? evalName(name) : value; } + + @Override + public String toString() { + try { + return Long.toString(getValue()); + } catch (ShellException ex) { + return "OOPS"; + } + } } private final BjorneContext context; @@ -112,6 +121,7 @@ } private Primary evalExpression(CharIterator ci) throws ShellException { + int mark = opStack.size(); int ch = skipWhiteSpace(ci); while ((ch = skipWhiteSpace(ci)) != -1 && ch != ')') { int prefixOp = parseExpressionOperator(ci); @@ -129,16 +139,17 @@ } skipWhiteSpace(ci); pushOperand(evalPrimary(ci)); + if (prefixOp != NONE) { + pushOperator(prefixOp, mark); + } skipWhiteSpace(ci); int op = parseExpressionOperator(ci); - if (prefixOp != NONE) { - if (op == PLUSPLUS || op == MINUSMINUS) { - pushOperator(op); - skipWhiteSpace(ci); - op = parseExpressionOperator(ci); - } - pushOperator(prefixOp); + if (op == PLUSPLUS || op == MINUSMINUS) { + pushOperator(op, mark); + skipWhiteSpace(ci); + op = parseExpressionOperator(ci); } + ch = skipWhiteSpace(ci); if (op == NONE) { if (ch != -1 && ch != ')') { @@ -150,19 +161,19 @@ } else if (ch == ')') { throw new ShellSyntaxException("Expected a number or variable name in expression"); } - pushOperator(op); + pushOperator(op, mark); } if (valStack.size() == 0) { throw new ShellSyntaxException("No expression within \"$((...))\""); } - while (!opStack.isEmpty()) { + while (opStack.size() > mark) { evalOperation(); } - return valStack.getFirst(); + return valStack.removeFirst(); } - private void pushOperator(int op) throws ShellException { - while (!opStack.isEmpty() && opStack.getFirst() <= op) { + private void pushOperator(int op, int mark) throws ShellException { + while (opStack.size() > mark && precedence.get(opStack.getFirst()) <= precedence.get(op)) { evalOperation(); } opStack.addFirst(op); @@ -180,7 +191,6 @@ operand1 = valStack.removeFirst(); operand2 = null; } else { - System.err.println(op); operand2 = valStack.removeFirst(); operand1 = valStack.removeFirst(); } @@ -198,7 +208,7 @@ if (operand1.name == null) { throw new ShellSyntaxException("Cannot apply ++ or -- to a number or a subexpression"); } - value = evalName(operand1.name) + (op == PLUSPLUS ? 1 : -1); + value = evalName(operand1.name) + (op == PLUSPLUS + PREFIX ? 1 : -1); context.setVariable(operand1.name, Long.toString(value)); res = new Primary(null, value); break; @@ -207,8 +217,8 @@ if (operand1.name == null) { throw new ShellSyntaxException("Cannot apply ++ or -- to a number or a subexpression"); } - value = evalName(operand1.name) + (op == PLUSPLUS ? 1 : -1); - context.setVariable(operand1.name, Long.toString(value)); + value = evalName(operand1.name); + context.setVariable(operand1.name, Long.toString(value + (op == PLUSPLUS ? 1 : -1))); res = new Primary(null, value); break; case PLUS: @@ -233,7 +243,7 @@ case PERCENT: value = operand2.getValue(); if (value == 0) { - throw new ShellException("Divide by zero in expression"); + throw new ShellException("Remainder by zero in expression"); } res = new Primary(null, operand1.getValue() % value); break; @@ -252,7 +262,8 @@ } else if (ch == '(') { ci.nextCh(); Primary res = evalExpression(ci); - if (ci.nextCh() != ')') { + skipWhiteSpace(ci); + if ((ch = ci.nextCh()) != ')') { throw new ShellSyntaxException("Unmatched \"(\" (left parenthesis) in arithmetic expression"); } return res; @@ -304,7 +315,12 @@ return SLASH; case '*': ci.nextCh(); - return STAR; + if (ci.peekCh() == '*') { + ci.nextCh(); + return STARSTAR; + } else { + return STAR; + } case '%': ci.nextCh(); return PERCENT; @@ -315,9 +331,11 @@ private long parseNumber(CharIterator ci) { StringBuilder sb = new StringBuilder(); - int ch; - while (Character.isDigit((char) (ch = ci.nextCh()))) { + int ch = ci.peekCh(); + while (ch != -1 && Character.isDigit((char) ch)) { + ci.nextCh(); sb.append((char) ch); + ch = ci.peekCh(); } return Long.parseLong(sb.toString()); } Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java =================================================================== --- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-08-20 11:24:38 UTC (rev 5656) +++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-08-20 15:31:54 UTC (rev 5657) @@ -951,8 +951,7 @@ return sb.toString(); } - - String variable(String parameter) throws ShellSyntaxException { + protected String variable(String parameter) throws ShellSyntaxException { if (BjorneToken.isName(parameter)) { VariableSlot var = variables.get(parameter); return (var != null) ? var.getValue() : null; Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneArithmeticEvaluatorTest.java =================================================================== --- trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneArithmeticEvaluatorTest.java 2009-08-20 11:24:38 UTC (rev 5656) +++ trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneArithmeticEvaluatorTest.java 2009-08-20 15:31:54 UTC (rev 5657) @@ -23,6 +23,7 @@ import junit.framework.TestCase; import org.jnode.shell.ShellException; +import org.jnode.shell.ShellSyntaxException; import org.jnode.shell.bjorne.BjorneArithmeticEvaluator; import org.jnode.shell.bjorne.BjorneContext; import org.jnode.shell.io.CommandIOHolder; @@ -44,13 +45,15 @@ super(null, null); } - /** - * Expose method for testing - */ @Override protected void setVariable(String name, String value) { super.setVariable(name, value); } + + @Override + protected String variable(String name) throws ShellSyntaxException { + return super.variable(name); + } } private static class TestBjorneArithmeticEvaluator extends BjorneArithmeticEvaluator { @@ -97,4 +100,65 @@ assertEquals("0", ev.evaluateExpression(" - B")); } + public void testInfixOperators() throws ShellException { + TestBjorneContext context = new TestBjorneContext(); + context.setVariable("A", "1"); + TestBjorneArithmeticEvaluator ev = new TestBjorneArithmeticEvaluator(context); + assertEquals("2", ev.evaluateExpression("1 + 1")); + assertEquals("2", ev.evaluateExpression("A + 1")); + assertEquals("0", ev.evaluateExpression("1 - 1")); + assertEquals("0", ev.evaluateExpression("1 - A")); + assertEquals("4", ev.evaluateExpression("2 * 2")); + assertEquals("2", ev.evaluateExpression("4 / 2")); + assertEquals("1", ev.evaluateExpression("4 % 3")); + assertEquals("27", ev.evaluateExpression("3 ** 3")); + try { + ev.evaluateExpression("4 / 0"); + fail("no exception for '4 / 0'"); + } catch (ShellException ex) { + // expected + } + try { + ev.evaluateExpression("4 % 0"); + fail("no exception for '4 % 0'"); + } catch (ShellException ex) { + // expected + } + } + + public void testInfixPrecedence() throws ShellException { + TestBjorneContext context = new TestBjorneContext(); + context.setVariable("A", "1"); + TestBjorneArithmeticEvaluator ev = new TestBjorneArithmeticEvaluator(context); + assertEquals("0", ev.evaluateExpression("-1 * 2 + 2")); + assertEquals("4", ev.evaluateExpression("1 * 2 + 2")); + assertEquals("5", ev.evaluateExpression("1 + 2 * 2")); + assertEquals("9", ev.evaluateExpression("1 + 2 * 2 ** 2")); + assertEquals("8", ev.evaluateExpression("1 + 2 * 2 ** 2 + -A")); + } + + public void testParentheses() throws ShellException { + TestBjorneContext context = new TestBjorneContext(); + context.setVariable("A", "1"); + TestBjorneArithmeticEvaluator ev = new TestBjorneArithmeticEvaluator(context); + assertEquals("-4", ev.evaluateExpression("-1 * (2 + 2)")); + assertEquals("4", ev.evaluateExpression("(1 * 2 + 2)")); + assertEquals("6", ev.evaluateExpression("((1) + 2) * 2")); + assertEquals("17", ev.evaluateExpression("1 + (2 * 2) ** 2")); + assertEquals("10", ev.evaluateExpression("1 + 2 * 2 ** 2 + -(-1)")); + } + + public void testIncDec() throws ShellException { + TestBjorneContext context = new TestBjorneContext(); + context.setVariable("A", "1"); + TestBjorneArithmeticEvaluator ev = new TestBjorneArithmeticEvaluator(context); + assertEquals("1", ev.evaluateExpression("A++")); + assertEquals("2", context.variable("A")); + assertEquals("3", ev.evaluateExpression("++A")); + assertEquals("3", context.variable("A")); + assertEquals("3", ev.evaluateExpression("A--")); + assertEquals("2", context.variable("A")); + assertEquals("1", ev.evaluateExpression("--A")); + assertEquals("1", context.variable("A")); + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |