什么是ANTLR中的树解析器,我被迫写一个?

Hip*_*ter 6 grammar parsing programming-languages antlr

我正在为ANTLR中的一小部分C编写一个词法分析器/解析器,它将在Java环境中运行.我是语言语法世界的新手,在许多ANTLR教程中,他们创建了一个AST - 抽象语法树,我被迫创建一个,为什么?

Rus*_*ett 7

使用ANTLR创建AST已纳入语法.您不必这样做,但它是一个非常好的工具,可以满足更复杂的要求.这是您可以使用的树构造教程.

基本上,使用ANTLR解析源时,您有几个选项.您可以使用语法中的重写规则生成代码或AST.一个AST基本上是一个在源的内存中表示.从那里,你可以做很多事情.

ANTLR有很多东西.如果你还没有,我会建议你拿到这本书.


Hip*_*ter 3

我在 jGuru 上找到了这个问题的答案,作者是 ANTLR 的创建者 Terence Parr。我从此处链接的网站复制了此解释:

解析器内的操作只能完成简单的、所谓的语法定向翻译。这些类型的翻译只能吐出作为解析中此时已经看到的信息的函数的结构。树解析器允许您遍历中间形式并操作该树,在几个翻译阶段逐渐将其变形为最终形式,可以轻松地作为新翻译打印出来。

想象一个简单的翻译问题,您想要打印一个标题为“There are n items”的 html 页面,其中 n 是您在输入流中找到的标识符的数量。id 必须打印在标题后面,如下所示:

<html>
<head>
<title>There are 3 items</title>
</head>
<body>
<ol>
<li>Dog</li>
<li>Cat</li>
<li>Velociraptor</li>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

从输入

Dog
Cat
Velociraptor
Run Code Online (Sandbox Code Playgroud)

那么,通过语法中的简单操作,如何计算标题呢?如果不阅读整个输入,您就无法做到这一点。好的,现在我们知道我们需要一个中间形式。最好的通常是我找到的 AST,因为它记录了输入结构。在本例中,它只是一个列表,但它证明了我的观点。

好吧,现在你知道树除了简单的翻译之外还有什么用处。给定一个 AST,如何从中获取输出?想象一下简单的表达式树。一种方法是将树中的节点指定为特定类,例如 PlusNode、IntegerNode 等。然后你只需要求每个节点将其自身打印出来即可。对于输入,3+4 你将有树:

+ | 3 - 4

和课程

class PlusNode extends CommonAST {
  public String toString() {
    AST left = getFirstChild();
    AST right = left.getNextSibling();
    return left + " + " + right;
  }
}

class IntNode extends CommonAST {
  public String toString() {
    return getText();
  }
}
Run Code Online (Sandbox Code Playgroud)

给定一个表达式树,您可以使用 t.toString() 将其转换回文本。那么,这有什么问题吗?看起来效果很好,对吧?在这种情况下它似乎工作得很好,因为它很简单,但我认为,即使对于这个简单的示例,树语法也更具可读性,并且是对 PlusNode.toString() 中编码内容的形式化描述。

expr returns [String r]
{
    String left=null, right=null;
}

: #("+" left=expr right=expr) {r=left + " + " + right;}
| i:INT                       {r=i.getText();}
;
Run Code Online (Sandbox Code Playgroud)

请注意,特定类(“异构 AST”)方法实际上在 toString() 中手动编码了 #(+ INT INT) 的完整递归下降解析器。作为解析器生成器的人们,这应该会让你感到畏缩。;)

异构 AST 方法的主要弱点是它无法方便地访问上下文信息。在递归下降解析器中,您的上下文很容易访问,因为它可以作为参数传入。通过查看语法,您还可以准确地知道哪个规则可以调用哪个其他规则(例如,该表达式是 WHILE 条件还是 IF 条件?)。上面的 PlusNode 类存在于一个独立的、孤立的世界中,它不知道谁将调用它的 toString() 方法。更糟糕的是,程序员无法通过读取它来判断将在哪个上下文中调用它。

总之,向输入解析器添加操作可以实现非常简单的翻译,其中:

  1. 输出结构的顺序与输入顺序相同
  2. 所有构造都可以从解析到需要吐出它们的信息中生成

除此之外,您还需要一个中间形式——AST 通常是最好的形式。使用语法来描述 AST 的结构类似于使用语法来解析输入文本。使用特定于领域的高级语言(如 ANTLR)进行形式化描述比手动编码的解析器更好。树语法中的操作具有非常清晰的上下文,并且可以方便地访问从调用规则传递的信息。使用树语法,操纵树进行多遍翻译的翻译也容易得多。