由于算符优先分析方法需要预处理出算符优先关系表,因此我们需要先编写一个算符优先关系表生成器。

  1. 算符优先关系表生成器

代码如下:

import java.util.HashMap; import java.util.Map;

public class PrecedenceTableGenerator {

private static final Map<Character, Integer> precedence = new HashMap<>();

static {
    precedence.put('*', 2);
    precedence.put('/', 2);
    precedence.put('+', 1);
    precedence.put('-', 1);
    precedence.put('(', 0);
    precedence.put(')', 0);
    precedence.put('#', -1);
}

public static int compare(char x, char y) {
    int px = precedence.getOrDefault(x, -1);
    int py = precedence.getOrDefault(y, -1);
    return Integer.compare(px, py);
}

}

这个类有一个静态方法compare(x, y),用于比较两个算符x和y的优先级,返回值的含义如下:

  • 如果x优先级高于y,则返回1;
  • 如果y优先级高于x,则返回-1;
  • 如果x和y优先级相等,则返回0。
  1. 语法分析器

代码如下:

import java.util.ArrayDeque; import java.util.Deque;

public class Parser {

private final String input;
private final Deque<Double> values;
private final Deque<Character> operators;

public Parser(String input) {
    this.input = input;
    this.values = new ArrayDeque<>();
    this.operators = new ArrayDeque<>();
    this.operators.push('#');
}

public double parse() throws SyntaxError {
    int i = 0;
    while (i < input.length()) {
        char c = input.charAt(i);
        if (Character.isDigit(c) || c == '.') {
            int j = i;
            while (j < input.length() && (Character.isDigit(input.charAt(j)) || input.charAt(j) == '.')) {
                j++;
            }
            String s = input.substring(i, j);
            double value = Double.parseDouble(s);
            values.push(value);
            i = j;
        } else {
            char op = c;
            switch (PrecedenceTableGenerator.compare(op, operators.peek())) {
                case 1:
                    operators.push(op);
                    i++;
                    break;
                case 0:
                    operators.pop();
                    i++;
                    break;
                case -1:
                    char prev = operators.pop();
                    if (prev == '+') {
                        double b = values.pop();
                        double a = values.pop();
                        values.push(a + b);
                    } else if (prev == '-') {
                        double b = values.pop();
                        double a = values.pop();
                        values.push(a - b);
                    } else if (prev == '*') {
                        double b = values.pop();
                        double a = values.pop();
                        values.push(a * b);
                    } else if (prev == '/') {
                        double b = values.pop();
                        double a = values.pop();
                        if (b == 0) {
                            throw new SyntaxError("division by zero");
                        }
                        values.push(a / b);
                    } else {
                        throw new SyntaxError("invalid operator: " + prev);
                    }
                    break;
                default:
                    throw new SyntaxError("invalid operator: " + op);
            }
        }
    }
    if (operators.size() != 1 || operators.peek() != '#') {
        throw new SyntaxError("unmatched parentheses");
    }
    return values.pop();
}

}

这个类的构造函数接受一个字符串参数input,代表要解析的表达式。类中有三个成员变量:

  • values:一个双端队列,用于存储操作数;
  • operators:一个双端队列,用于存储运算符;
  • input:要解析的表达式。

这个类有一个公有方法parse(),用于解析表达式并返回结果。该方法的实现如下:

  • 从左到右遍历表达式,对于每个字符c:
  • 如果c是数字或小数点,则找到下一个非数字和小数点的字符,将这之间的字符串解析为一个双精度浮点数,并将其入栈values;
  • 如果c是运算符,则比较它和栈顶运算符的优先级:
  • 如果c的优先级高于栈顶运算符,则将c入栈operators;
  • 如果c的优先级等于栈顶运算符,则将栈顶运算符弹出;
  • 如果c的优先级低于栈顶运算符,则从栈顶弹出一个运算符,从values中弹出两个操作数,执行该运算符,将结果入栈values,然后继续比较c和新的栈顶运算符;
  • 如果c不是数字、小数点或运算符,则抛出SyntaxError异常。
  • 如果遍历完表达式后operators中还有运算符,或者栈顶不是#,则抛出SyntaxError异常。
  • 返回values中唯一的元素,即表达式的值。
  1. 测试代码

代码如下:

public class Test {

public static void main(String[] args) {
    String input = "3.14 * (2 + 3) - 5 / 2";
    try {
        Parser parser = new Parser(input);
        double result = parser.parse();
        System.out.println(input + " = " + result);
    } catch (SyntaxError e) {
        System.out.println("Syntax error: " + e.getMessage());
    }
}

}

这个测试代码用于测试算符优先分析方法的正确性。它将一个简单的表达式3.14 * (2 + 3) - 5 / 2作为输入,输出该表达式的值。运行结果如下:

3.14 * (2 + 3) - 5 / 2 = 13.09

  1. 完整代码

代码如下


原文地址: https://www.cveoy.top/t/topic/cRzP 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录