📜  门| GATE-CS-2005 |问题8(1)

📅  最后修改于: 2023-12-03 14:58:26.936000             🧑  作者: Mango

GATE-CS-2005 - Question 8

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.

Problem

Write a Java program to parse and evaluate arithmetic expressions. You need to implement two methods in your program:

  1. 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.
  2. 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
Solution

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.