Question
Java Only Skeleton code: Evaluator.java: package evaluator; import java.util.*; import operand.Operand; import operator.Operator; public class Evaluator { private Stack operandStack; private Stack operatorStack; private StringTokenizer
Java Only
Skeleton code:
Evaluator.java:
package evaluator;
import java.util.*; import operand.Operand; import operator.Operator;
public class Evaluator {
private Stack
private StringTokenizer tokenizer; private static final String DELIMITERS = "+-*^/() ";
public Evaluator() { operandStack = new Stack
public int eval(String expression) { String token;
// The 3rd argument is true to indicate that the delimiters should be used // as tokens, too. But, we'll need to remember to filter out spaces. this.tokenizer = new StringTokenizer(expression, DELIMITERS, true);
while (this.tokenizer.hasMoreTokens()) { // filter out spaces if (!(token = this.tokenizer.nextToken()).equals(" ")) { // check if token is an operand if (Operand.check(token)) { operandStack.push(new Operand(token)); } else { if (!Operator.check(token)) { System.out.println("*****invalid token******"); System.exit(1); }
// TODO Operator is abstract - this line will need to be fixed: // ( The Operator class should contain an instance of a HashMap, // and values will be instances of the Operators. See Operator class // skeleton for an example. ) Operator newOperator = null; // new Operator( token );
while (operatorStack.peek().priority() >= newOperator.priority()) { // note that when we eval the expression 1 - 2 we will // push the 1 then the 2 and then do the subtraction operation // This means that the first number to be popped is the // second operand, not the first operand - see the following code Operator oldOpr = operatorStack.pop(); Operand op2 = operandStack.pop(); Operand op1 = operandStack.pop(); operandStack.push(oldOpr.execute(op1, op2)); }
operatorStack.push(newOperator); } } }
// Control gets here when we've picked up all of the tokens; you must add // code to complete the evaluation - consider how the code given here // will evaluate the expression 1+2*3 // When we have no more tokens to scan, the operand stack will contain 1 2 // and the operator stack will have + * with 2 and * on the top; // In order to complete the evaluation we must empty the stacks (except // the init operator on the operator stack); that is, we should keep // evaluating the operator stack until empty // Suggestion: create a method that takes an operator as argument and // then executes the while loop; also, move the stacks out of the main // method
return 0; }
/** * Class to help test your Evaluator: * javac EvaluatorTester * java EvaluatorTester "1+2" "3*5" */ public static void main(String[] args) { Evaluator evaluator = new Evaluator();
for (String arg : args) { System.out.format("%s = %d ", arg, evaluator.eval(arg)); } } }
Operand.java
package operand;
public class Operand {
public Operand( String token ) {
}
public Operand( int value ) {
}
public int getValue() { return 0; }
public static boolean check( String token ) { return false; } }
Operator.java:
package operator;
import operand.Operand;
public abstract class Operator {
// The Operator class should contain an instance of a HashMap // This map will use keys as the tokens we're interested in, // and values will be instances of the Operators.
// Example: // Where does this declaration go? What should its access level be? // Class or instance variable? Is this the right declaration? // HashMap operators = new HashMap(); // operators.put( "+", new AdditionOperator() ); // operators.put( "-", new SubtractionOperator() );
public abstract int priority();
public abstract Operand execute(Operand leftOperand, Operand rightOperand);
public static boolean check(String token) { return false; } }
Evaluatortest.java:
package tests;
import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals;
import evaluator.Evaluator;
public class EvaluatorTest { @Test void testSimpleAddition() { Evaluator evaluator = new Evaluator();
assertEquals(3, evaluator.eval("1 +2")); }
@Test void testSimpleDivision() { Evaluator evaluator = new Evaluator();
assertEquals(0, evaluator.eval("1/2")); }
@Test void testSimpleExpression() { Evaluator evaluator = new Evaluator();
assertEquals(7, evaluator.eval("1+2*3")); }
@Test void testSimpleParenthesizedExpression() { Evaluator evaluator = new Evaluator();
assertEquals(9, evaluator.eval("(1+2)*3")); }
@Test void testComplexExpressionWithNegativeResult() { Evaluator evaluator = new Evaluator();
assertEquals(-1, evaluator.eval("2-(3/10)+2-5")); }
@Test void testAnotherComplexExpressionWithNegativeResult() { Evaluator evaluator = new Evaluator();
assertEquals(-6, evaluator.eval("(6-12*2)/3")); }
@Test void testSimpleExponentiation() { Evaluator evaluator = new Evaluator();
assertEquals(9, evaluator.eval("3^2")); }
@Test void testSlightlyMoreComplexExponentiation() { Evaluator evaluator = new Evaluator();
assertEquals(4, evaluator.eval("3^2/2")); }
@Test void testHardMode() { Evaluator evaluator = new Evaluator();
assertEquals(1176, evaluator.eval("2+3-5*((2-3)*2-5*2+3*(2-3-5-5*6)+4/2)*2-9")); }
@Test void testProperStackUsage() { Evaluator evaluator = new Evaluator();
// Stacks should be emptied and in a valid state after the first evaluation occurs, // so the second evaluation should run without exception and provide assertEquals(6, evaluator.eval("1+2+3")); assertEquals(1, evaluator.eval("10-8-1")); } }
Operandtest.java:
package tests;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList; import java.util.Arrays;
import org.junit.jupiter.api.Test; import operand.Operand;
public class OperandTest { @Test void testCheck() { ArrayList
validOperands.forEach(operand -> assertTrue(Operand.check(operand))); assertFalse(Operand.check("a")); }
@Test void testGetValueFromOriginalString() { Operand operandOne = new Operand("3"); assertEquals(3, operandOne.getValue()); } @Test void testGetValueFromOriginalInt() { Operand operandTwo = new Operand(7); assertEquals(7, operandTwo.getValue()); } }
Operatortest.java:
package tests;
import org.junit.jupiter.api.Test; import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse;
import java.util.ArrayList; import java.util.Arrays;
import operator.Operator;
public class OperatorTest { @Test void testCheck() { ArrayList
validOperators.forEach(operator -> assertTrue(Operator.check(operator))); assertFalse(Operator.check("1")); } }
The expressions are composed of integer operands and operators drawn from the set +. Requirement 3 Implement the following classes: The algorithm that is partially implemented in nval processes the tokens in the expression string using two stacks; one tor operators and one for operands (alcorithm reproduced here from htt sicsis.pace.edu' murthy/ProgrammingProblemsi 16 Evaluation of infix expressions): - If an operand token is scanned: an operand object is created from the token, and pushed to the operand Strack - If an operator token is scanned, and the operator stack is empty, then an operator object is created from the token, and pushed to the operator stack - If an operator token is scanned, and the cperator stack is not empty, and the operator's precedence is gregter than the precedence of the opcrat or at the top of the stack, then an operator object is created trom the token, and pushed to the operator stack - If the token is 6 , an oparator object is created from the token, and pushed to the operator stack - If the token is \}, then process Operators until the corresponding \{ is encountered. The Oyere_or class is the abstract base class for all of our operators. This class Pop the ( operator. serves as the root of our Strategy Pattern, as well as a repository that allows the wuluator class to retrieve a concrete instance of an Operatar. - If none of the above cases apply, proceas an operator. Note that the res shvap that stores instances of cperators is private! You must provide a means by which the Iva-uator can ask the oparator for concrete oparator instances whour wolating encepsulation. Processing an Dperator means to: In addition, the following methods must be implemented: - Pop the operand stack twice (tor each operand - note the orderili) - boolean checki sering token I - Pop the operator stack This method should retum true if the token passed as a parameter is a valid operator token - Execute the operator with the two operands - atitract int priuzityoj - Push the result orito the operand Stack This method gives us a way to specity the priarity tor each of the subclasses created from Operatcr When all tokens are read, process operators until the operator stack is empty. rtchtoperand ! This method is the strategy in our Strategy Pattern, where we will define the specific algorithm (addition, subtraction, etc.) for the subclass, and return the gperand Requirement 1 resulting from the operation. Implement the above algorithm within the Evaluator class. Individual orerator classes must be subclassed trom ope rat or to implement each of the operations allowed in the expressions defined above. Pemember to properly Requirement 2 organize your code! (Multiple classes should not be detined in a single file, and classes Test this implementation with expressions that test all possible cases (you may use the should be crganized into packages when appropriate.)
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started