将节点放入不应该存在的解析树中

时间:2011-08-07 11:36:19

标签: c parsing programming-languages compiler-theory lalr

我正在为一种语言编写一个解析器,而扫描器的设计是

  1. 要么也返回不需要的终端(例如whitespacing)或
  2. 不这样做
  3. 基于布尔标志。

    现在,在解析器中,我不想用所有这些终端混淆语法,它们应该被我正在构建的解析树“自动地”吞噬。

    为了做到这个“神奇”,我想我会链接终端(简单地链接循环列表)所以我可以迭代它们并“填充空白”,因为减少发生(我使用LALR(1)解析器生成器)。

    这听起来像是一个理智的想法,虽然有一个问题。记得我说“要么退还......要不”?在场景(2)中,我会释放终端,因为谁知道接下来会发生什么?而且我不希望任何内存泄漏。

    但是在方案(1)中,我无法释放终端,因为基于它们,我将决定进一步减少哪些“填空”过程应该停止。

    我也无法有条件地释放它,因为同样的原因:我不知道接下来会发生什么。如果没有任何“填空”流程被触发怎么办?如果根本没有进一步减少怎么办?

    你有类似的问题吗?你是怎么解决的?

    注意:这一切都在我脑海中,我可能没有说清楚,请问我会编辑我的问题。这个场景实际上有点复杂,我不是从头开始写这个,我可以用我的想象力,我把它整合到其他东西中,所以很可能我会回答“我做不到因为环境限制“。


    附录

    我想到的唯一真正好主意是分叉和改进解析器生成器,我已经在这里和那里的一些小地方做了,以克服我上面提到的一些限制。

1 个答案:

答案 0 :(得分:3)

你的词汇有点奇怪。大多数解析器旨在识别语言的语法。通常,语言定义定义了终端的一些概念,并明确排除了“空白”,包括终端文本之间无趣的文本序列,通常包括空格,标签和各种独立评论。因此,解析中使用的“终端”一词通常意味着“那些不是空白的语言原子”。你已经隐含地定义它以包含空格,我认为这会导致你的悲伤。

从这个角度来看,避免使用空格来破坏解析器使用的语法定义的最简单方法是简单地让词法分析器不将空格传递给解析器。然后你的语法不需要指出它们是如何被处理的(是的,这样做的语法真的很乱),解析器不必担心它们而且它们不会出现在树中。

如果要构建编译器或解释器,则忽略空格是最简单的。

如果要构建重新设计解析器(请参阅我们的DMS Software Reengineering Toolkit,那么在AST中捕获注释(至少)非常重要,最终 一个人希望从构造的AST重新生成文本,如果重新生成的文本也包含注释,则会很有帮助。 [你可以通过其他方式做到这一点,但它们并不那么容易]。

DMS词法分析器在内部生成“微”标记,这是您的语言标记,空格和注释的概念。它抛弃了空白微代币,因为它们根本不添加任何内容(参见上面的讨论)。正如您所期望的那样,它将传统令牌传递给解析器。它将注释标记粘贴到前面或后面的语言标记,具体取决于标记类型和遇到的位置;对于C,a / * ... * /在标记附加到它之前看到,并且// ...注释附加到前面的标记(这里没有讨论更多细微的细节)。然后解析仍然只看到语言标记,因此语法不是不必要的复杂,如果附加到标记的所有信息都放在树中,则评论会继续进行。

现在,人们经常需要“抽象”语法树;他们想要遗漏“(”和“)”之类的东西。我上面描述的方案附带了对这些具体令牌的评论。现在有一个复杂的问题:如果你将(..)标记留在树外,附加的评论就会消失。哎呀。 因此,DMS解析器做了一件复杂的事情:附加到树中具有逻辑位置但实际上并不存在(“已消除的终端”)的标记的注释被提升到父树节点,并带有注释,表明它们属于丢失的子令牌。是的,实现这个确实是一个PITA。 好消息是我们只需要在DMS的一般解析机制中执行一次,它适用于许多语言。但这意味着你必须愿意建立一个不寻常的(“再造”)解析器,我们有这样的商业动机。

编辑:目前尚不清楚为什么OP想要这个,但他坚持要抓住树中的空白。由于他没有告诉我们原因,我猜测:他想要令牌/树节点的精确列信息。这并不难:教lexer跟踪位置(行/列),并用开始/结束位置标记每个标记(微评级,例如评论),并让解析器存储该信息那个树。这种方式也避免了在树中保留空白。 (DMS也是这样做的,因为在报告问题时,准确的信息很有用,并且在重新生成代码时,通常需要将代码放回原始位置(至少是同一列)。

EDIT2:如果OP坚持捕获空格,他可能会考虑探索scannerless GLR parsing。这会在输入流中保留每个字符,包括空格。