如何用基于语法的解析器替换宏?

时间:2011-09-12 03:10:12

标签: parsing compiler-construction grammar interpreter yacc

我需要一个异域编程语言的解析器。我为它编写了一个语法,并使用解析器生成器(PEGjs)生成解析器。这完全有效......除了一件事:宏(用预定义的文本替换占位符)。我不知道如何将其整合到语法中。让我来说明问题:

要解析的示例程序通常如下所示:

instructionA parameter1, parameter2
instructionB parameter1
instructionC parameter1, parameter2, parameter3

到目前为止没问题。但该语言也支持宏:

Define MacroX { foo, bar }
instructionD parameter1, MacroX, parameter4

Define MacroY(macroParameter1, macroParameter2) {
  instructionE parameter1, macroParameter1
  instructionF macroParameter2, MacroX
}

instructionG parameter1, MacroX
MacroY

当然,我可以定义一个语法来识别宏和对宏的引用。但在这种情况下,我不知道如何解析宏的内容,因为它不清楚宏包含什么。它可能只是一个参数(这是最简单的),但它也可能是一个宏中的几个参数(例如我的示例中的MacroX,它代表两个参数)或整个指令块(如MacroY)。宏甚至可以包含其他宏。如果不清楚宏在语义上是什么,我怎么把它放到语法中呢?

最简单的方法似乎是首先运行预处理器来替换所有宏,然后再运行解析器。但在这种情况下,行数会搞砸。如果存在解析错误,我希望解析器生成包含行号的错误消息。如果我预处理输入,则行号不再对应。

非常感谢。

3 个答案:

答案 0 :(得分:3)

宏处理器往往不尊重语言元素的界限;实质上,他们(通常)可以对指定的输入字符串进行任意更改。

如果是这种情况,你别无选择:你需要建立一个可以保留行号的宏处理器。

如果宏总是包含结构良好的语言元素,并且它们总是出现在代码中的结构化位置,那么您可以添加宏定义的概念并调用语法。这可能会使你的解析模糊不清; C代码中的foo(x)可能是宏调用,也可能是函数调用。你必须以某种方式解决这种歧义。 C解析器用于通过在解析时收集符号表信息来解决这种模糊问题;如果在解析时收集is-foo-a-macro,则可以确定foo(x)是否为宏调用。

答案 1 :(得分:1)

使用PEG,您必须手动定义可以检查宏扩展的位置。您可以将宏添加到哈希并在PEG规则中检查它,它允许宏(中缀expr,后缀expr,unop,binop,函数调用......)。它不像在lisp中那么容易,但比YACC及其运算符优先黑客更容易:)

其他已知的允许宏的PEG框架,如parrot,perl6,katahdin或PFront使用该技巧在运行时执行解析,从而与性能进行交易。 或者您可以同时执行这两项操作并允许预编译和解释的PEG解析。有几个项目考虑过这个,但你需要一个快速的虚拟机,如luajit,java,clr或朋友。

我使用特殊的语法块关键字来加载外部共享库和外部预编译的PEG解析器。例如。将SQL或FFI声明解析为AST。 但是您也可以要求C编译器并在运行时为所有宏编译解析。

答案 2 :(得分:0)

使用PEG比使用其他任何东西都容易得多。首先,基于Packrat的解析器和类似的解析器都是可扩展的。您的宏定义可以修改语法,因此下次使用它时,它将自然地进行解析。请参阅herehere此方法的一些极端示例。

另一种可能性是链解析器,这对于基于PEG的方法也是微不足道的。