如何解决LR(1)语法定义中的歧义?

时间:2014-09-22 14:21:30

标签: parsing go ocaml

我正在OCaml中编写一个Golang编译器,参数列表让我有点头疼。在Go中,您可以按以下方式对相同类型的连续参数名称进行分组:

func f(a, b, c int)  ===  func f(a int, b int, c int)

您还可以拥有一个类型列表,不带参数名称:

func g(int, string, int)

这两种风格不能混合搭配;要么所有参数都被命名,要么都没有。

我的问题是,当解析器看到逗号时,它不知道该怎么做。在第一个示例中,a是一个类型的名称,还是一个变量的名称,其中包含更多变量?逗号有双重角色,我不知道如何解决这个问题。

我正在为OCaml使用Menhir解析器生成器工具。

编辑:目前,我的Menhir语法完全遵循http://golang.org/ref/spec#Function_types

中指定的规则

2 个答案:

答案 0 :(得分:4)

如上所述,go语法不是LALR(1)。事实上,任何LR(k)都不是k。但是,它是明确的,所以你可以使用GLR解析器成功解析它,如果你能找到一个(我很确定有几个用于OCAML的GLR解析器生成器,但我没有&#39} ;对他们中的任何人都足够了解推荐一个。)

如果您不想(或者不能)使用GLR解析器,您可以像使用gccgo编译器时使用的那样执行此操作bison。 (bison可以生成GLR解析器,但Cox不使用该功能。)他的技术不依赖于扫描程序区分类型名称和非类型名称。

相反,它只接受其元素为name_or_typename name_or_type的参数列表(实际上,由于...语法,有更多可能性,但它没有&#39改变一般原则。)那简单,明确和LALR(1),但它过于接受 - 它会接受func foo(a, b int, c),例如 - 它不会产生正确的抽象语法树,因为它没有将类型附加到声明的参数列表。

这意味着一旦参数列表被完全解析并且即将被插入到附加到某个函数声明的AST中(例如),就会执行语义扫描来修复它,并在必要时生成一个错误信息。该扫描在声明元素列表中从右到左完成,因此指定的类型可以传播到左侧。

值得注意的是,参考手册中的语法也过于接受,因为它没有表达所有参数都被命名或者没有被"的约束。该约束可以以LR(1)语法表达 - 我将其作为读者的练习 - 但结果语法将更难以理解。

答案 1 :(得分:1)

你不会有歧义。标准Go解析器是LALR(1)这一事实证明了这一点。

  

是一个类型的名称或变量的名称,其中会有更多变量出现?

所以基本上你的语法和解析器作为一个整体应该完全与符号表断开连接;不是C - 您的语法不明确,因此您可以在AST中稍后检查类型名称。

这些是相关规则(来自http://golang.org/ref/spec);他们已经是正确的了。

Parameters     = "(" [ ParameterList [ "," ] ] ")" .
ParameterList  = ParameterDecl { "," ParameterDecl } .
ParameterDecl  = [ IdentifierList ] [ "..." ] Type .
IdentifierList = identifier { "," identifier } .

我会向你解释一下:

IdentifierList = identifier { "," identifier } .

花括号表示kleene-closure(在POSIX正则表达式表示法中,它是星号)。此规则表示"标识符名称,可选地后跟文字逗号和标识符,可选地后跟文字逗号和标识符等... ad infinitum"

ParameterDecl  = [ IdentifierList ] [ "..." ] Type .

方括号是可空性的;这意味着该部分可能存在也可能不存在。 (在POSIX正则表达式中,它是问号)。所以你有"可能是一个IdentifierList,后面跟一个省略号,后跟一个类型。

ParameterList  = ParameterDecl { "," ParameterDecl } .

您可以在列表中包含多个ParameterDecl,例如func x(a, b int, c, d string)

Parameters     = "(" [ ParameterList [ "," ] ] ")" .

这条规则定义了一个ParameterList是可选的,并且被括号括起来,并且可能包含一个可选的最终逗号文字,当你写下这样的内容时很有用:

func x(
    a, b int,
    c, d string, // <- note the final comma
)

Go语法是可移植的,任何自下而上的解析器都可以使用一个前瞻标记进行解析。


编辑关于&#34;不是C&#34; :我说这是因为C is context-sensitive以及他们解决这个问题的方式很多(全部?)编译器是将符号表连接到词法分析器和lexing标记,具体取决于它们是否被定义为类型名称或变量。这是一个黑客攻击,不应该用于明确的语法!