学习编写递归下降解析器。一旦你理解了这些概念,你就可以用任何语言来做:Java、C++、JavaScript、SystemVerilog,……随便什么。如果你可以处理字符串,那么你可以解析。
递归下降解析是一种基本的解析技术,可以很容易地手动编码。如果您无法访问(或不想欺骗)解析器生成器,这将很有用。
在递归下降解析器中,语法中的每条规则都被转换为解析规则的过程。如果您需要参考其他规则,那么您可以通过调用它们来实现 - 它们只是程序。
一个简单的例子:涉及数字、加法和乘法的表达式(这说明了运算符的优先级)。一、语法:
expr ::= 术语
| expr "+" 项
术语 ::= 因子
| 术语“*”因子
因子 ::= /[0-9/+ (我在这里使用正则表达式)
现在编写解析器(包括词法分析器;使用递归下降,您可以将两者放在一起)。我从未使用过 JavaScript,所以让我们在(我生锈的)Java 中尝试一下:
class Parser {
string str;
int idx; // index into string
Node parseExpr() throws ParseException
{
Node op1 = parseTerm();
Node op2;
while (idx < str.size() && str.charAt(idx) == '+') {
idx++;
op2 = parseTerm();
op1 = new AddNode(op1, op2);
}
return op1;
}
Node parseTerm() throws ParseException
{
Node op1 = parseFactor();
Node op2;
while (idx < str.size() && str.charAt(idx) == '*') {
idx++;
op2 = parseFactor();
op1 = new MultNode(op1, op2);
}
return op1;
}
Node parseFactor() throws ParseException
{
StringBuffer sb = new StringBuffer();
int old_idx = idx;
while (idx < str.size() && str.charAt(idx) >= '0' && str.charAt(idx) <= '9') {
sb.append(str.charAt(idx));
idx++;
}
if (idx == old_idx) {
throw new ParseException();
}
return new NumberNode(sb.toString());
}
}
您可以看到每个语法规则如何转换为过程。我没有测试过这个;这是给读者的练习。
您还需要担心错误检测。现实世界的编译器需要从解析错误中恢复,以尝试解析其输入的其余部分。像这样的单行表达式解析器根本不需要尝试恢复,但它确实需要确定存在解析错误并标记它。如果您的语言允许,最简单的方法是抛出异常,并在解析器的入口点捕获它。在上面的示例中,我没有检测到所有可能的解析错误。
有关更多信息,请在 Wikipedia 中查找“LL 解析器”和“递归下降解析器”。正如我在开头所说,如果你能理解这些概念(与 LALR(1) 状态机配置闭包背后的概念相比,它们很简单),那么你就可以用任何语言为小任务编写解析器,只要因为你有一些基本的字符串能力。享受权力。