📅  最后修改于: 2023-12-03 14:58:26.936000             🧑  作者: Mango
This problem is related to parsing of arithmetic expressions. The grammar for such expressions is defined as follows:
<expr> ::= <term> {(+|-) <term>}
<term> ::= <factor> {(*|/) <factor>}
<factor> ::= id | <number> | '(' <expr> ')'
Where id
is a variable, and number
is an integer constant.
Write a Java program to parse and evaluate arithmetic expressions. You need to implement two methods in your program:
parseExpression(String expr)
- This method should take an arithmetic expression as a string, and return a parse tree (in an appropriate data structure) for that expression. If the expression is invalid, your method should throw an appropriate exception.evaluateExpression(Node root, Map<String, Integer> variables)
- This method should take a parse tree (rooted at root
) and a map of variable names to values, and return the value of the expression represented by that parse tree. If the expression contains an undefined variable, your method should throw an appropriate exception.Sample Input:
expr = "5 + 3 * 2"
variables = {"x": 2}
Sample Output:
Parse Tree:
+
/ \
5 *
/ \
3 2
Value: 11
The problem can be solved using recursive descent parser. We can write functions to parse each non-terminal symbol of the grammar and recursively invoke these functions to parse sub-expressions.
To evaluate the expression, we need to traverse the parse tree in the postfix order, and use a stack to keep track of intermediate results.
Following is an implementation of the solution:
class Node {
String op;
int val;
Node left, right;
Node(String op) { this.op = op; }
Node(int val) { this.val = val; }
}
public class ArithmeticParser {
int pos;
String input;
public Node parseExpression(String expr) {
this.pos = 0; this.input = expr;
Node root = parseTerm();
while (pos < input.length() && (input.charAt(pos) == '+' || input.charAt(pos) == '-')) {
String op = input.charAt(pos++) + "";
Node right = parseTerm();
Node left = root;
root = new Node(op); root.left = left; root.right = right;
}
return root;
}
Node parseTerm() {
Node root = parseFactor();
while (pos < input.length() && (input.charAt(pos) == '*' || input.charAt(pos) == '/')) {
String op = input.charAt(pos++) + "";
Node right = parseFactor();
Node left = root;
root = new Node(op); root.left = left; root.right = right;
}
return root;
}
Node parseFactor() {
if (Character.isDigit(input.charAt(pos))) {
int val = 0;
while (pos < input.length() && Character.isDigit(input.charAt(pos))) {
val = val * 10 + input.charAt(pos++) - '0';
}
return new Node(val);
} else if (Character.isLetter(input.charAt(pos))) {
int start = pos;
while (pos < input.length() && Character.isLetterOrDigit(input.charAt(pos))) pos++;
String id = input.substring(start, pos);
return new Node(id);
} else if (input.charAt(pos) == '(') {
pos++; Node root = parseExpression(); pos++;
return root;
} else throw new IllegalArgumentException("Invalid input at " + pos);
}
public int evaluateExpression(Node root, Map<String, Integer> variables) {
if (root == null) return 0;
if (root.op == null) {
if (Character.isDigit((char)root.val)) return root.val;
if (variables.containsKey(root.op)) return variables.get(root.op);
throw new IllegalArgumentException("Undefined variable " + root.op);
}
int left = evaluateExpression(root.left, variables);
int right = evaluateExpression(root.right, variables);
switch (root.op) {
case "+": return left + right;
case "-": return left - right;
case "*": return left * right;
case "/":
if (right == 0) throw new IllegalArgumentException("Division by zero");
return left / right;
}
return 0;
}
}
// Usage:
ArithmeticParser parser = new ArithmeticParser();
// Parsing example
Node root = parser.parseExpression("5 + 3 * 2");
// Evaluation example
Map<String, Integer> variables = new HashMap<String, Integer>();
variables.put("x", 2);
int value = parser.evaluateExpression(root, variables);
System.out.println(value);
The ArithmeticParser
class has two methods, parseExpression
and evaluateExpression
, which correspond to the two tasks mentioned in the problem statement. The parseExpression
method uses a recursive descent parser to parse the input expression, while the evaluateExpression
method evaluates the parse tree using a stack-based approach.
To parse a term, we first call the parseFactor
method. If the current character is a digit, we parse the integer value; if it is a letter, we parse a variable name; and if it is '('
, we recursively call parseExpression
to parse the sub-expression enclosed in parentheses.
To parse an expression, we repeatedly call parseTerm
and combine the resulting parse trees using +
or -
operators.
To parse a factor, we first check if the current character is a digit, letter, or '('
. If it is a digit, we parse an integer value. If it is a letter, we parse a variable name. If it is '('
, we recursively parse the enclosed sub-expression.
Evaluation of the parse tree is done recursively, starting from the leaf nodes. If a leaf node is an integer, it is pushed onto a stack; if it is a variable, its value is looked up in the provided dictionary. If an inner node is encountered, its operands are popped from the stack, the corresponding arithmetic operation is performed, and the result is pushed back onto the stack.
The evaluateExpression
method returns the final value of the expression represented by the parse tree.
We provide the usage example code at the bottom that tests both the methods.