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

Parsing 如何解决LR(1)语法定义中的歧义?,parsing,go,ocaml,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编写一个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语法完全遵循在

中指定的规则,您没有歧义。标准Go解析器是LALR(1)的事实证明了这一点

a是一个类型的名称还是一个变量的名称,会出现更多的变量

因此,基本上你的语法和语法分析器作为一个整体应该与符号表完全断开连接;不要是C–您的语法没有歧义,因此您可以稍后在AST中检查类型名称

这些是相关规则(来自);他们已经是正确的了

Parameters     = "(" [ ParameterList [ "," ] ] ")" .
ParameterList  = ParameterDecl { "," ParameterDecl } .
ParameterDecl  = [ IdentifierList ] [ "..." ] Type .
IdentifierList = identifier { "," identifier } .
我会向你解释:

IdentifierList = identifier { "," identifier } .
大括号表示kleene闭包(在POSIX正则表达式表示法中,它是星号)。此规则表示“标识符名称,可选后跟文字逗号和标识符,可选后跟文字逗号和标识符,等等…”

方括号为空;这意味着该部分可能存在,也可能不存在。(在POSIX正则表达式表示法中,它是问号)。所以你有“可能是一个识别列表,后面可能是一个省略号,后面是一个类型

ParameterList  = ParameterDecl { "," ParameterDecl } .
列表中可以有几个参数decl,例如
func x(a、b int、c、d字符串)

此规则定义ParameterList是可选的,并由括号包围,可能包含可选的最终逗号文字,在编写以下内容时非常有用:

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

c、 d字符串,//正如所写,go语法不是
LALR(1)
。事实上,对于任何
k
,它都不是
LR(k)
。但是,它是明确的,因此如果您能找到一个
GLR
解析器,您可以成功地解析它(我非常确定有几个用于OCAML的GLR解析器生成器,但我对其中任何一个都不太了解,因此不推荐一个)

如果您不想(或不能)使用
GLR
解析器,您可以像Russ Cox在
gccgo
编译器中所做的那样,使用
bison
bison
可以生成GLR解析器,但Cox不使用该功能。)他的技术不依赖于扫描仪区分类型名和非类型名

相反,它只接受其元素为
name\u或\u type
name name\u或\u type
的参数列表(实际上,由于
语法,有更多的可能性,但它不改变一般原则)。这很简单、明确且
LALR(1)
,但它太容易接受了——例如,它会接受
func foo(a,b int,c)
,而且它不会生成正确的抽象语法树,因为它不会将类型附加到正在声明的参数列表中

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


值得注意的是,参考手册中的语法也过于容易接受,因为它没有表达“要么所有参数都命名,要么没有命名”的约束。该约束可以用LR(1)表示语法——我将把它留给读者作为练习——但生成的语法会更难理解。

你的语法是什么样子的?@Tomwide:我用适当的信息编辑了这篇文章。代码只是遵循与规范相同的结构。我认为最简单的方法是处理任何空格(不带逗号)作为预类型标记。空格后的标记(并且只有一个空格不带逗号)必须是类型。如果空格前的标记与当前作用域中的任何类型或任何内置类型不匹配,则它必须是命名变量。这可能太幼稚了。
如果空格前的标记与任何类型不匹配,则表示您可以访问当前作用域及其内定义的类型名称。这是一个错误的idea.C做到了。看我的答案。>这些是相关的规则(来自);它们已经是正确的。它们不是,否则将它们输入LR(1)解析器生成器不会产生移位/减少冲突。当解析器看到一个标识符时,它是在查看变量名还是类型名?它不知道也不能决定是移位还是减少。现在我的想法是只进行更愚蠢的解析,并在后处理步骤中选择变量和类型。语法是明确的,但我t不是如所写的LR(1)。假设我们已经阅读了
func g(a
,前瞻标记是
标识符
a
可以简化为
类型
标识符列表
一旦到达a
,就会得到解决。)
或后跟
的标识符,或可以启动
类型的东西(就像另一个标识符),但解析可以是解析之前的任意数量的标记,这意味着语法对于任何
k
都不是
LR(k)
Parameters     = "(" [ ParameterList [ "," ] ] ")" .
func x(
    a, b int,
    c, d string, // <- note the final comma
)