我正在使用Happy解析器来处理具有以下类型的语言,还有更多。
type :: { ... }
type :
'void' { ... }
| type '*' { ... } {- pointer -}
| type '(' types ')' { ... } {- function -}
| ... {- many more! -}
types :: { ... }
{- empty -} { ... }
| types ',' type { ... }
该语言对于调用显然具有模糊的语法。
callable :: { ... }
callable :
type operand { ... } {- return type -}
| type '(' types ')' '*' operand { ... } {- return and argument types -}
当type
采用函数指针的类型时,第二条规则与第一条规则的含义不同。
可以通过为不是函数指针的类型添加特殊规则来消除歧义。除非这样做并复制所有类型定义以产生类似
的内容callable :: { ... }
callable :
typeThatIsNotAFunctionPointer operand { ... }
| type '(' types ')' '*' operand { ... }
如果type operand
替代方案失败,如何指定替代type '(' types ')' '*' operand
合法?
有很多关于堆栈溢出的问题,关于为什么语法有歧义(我发现至少7个),有些关于如何消除歧义,但没有关于如何指定如何解决歧义的问题。
我知道我可以将类型的语法重构为一个巨大的复杂混乱。
neverConstrainedType :: { ... }
neverConstrainedType :
'int' { ... }
| ... {- many more! -}
voidType :: { ... }
voidType :
'void'
pointerType :: { ... }
pointerType :
type '*' { ... } {- pointer -}
functionType :: { ... }
type '(' types ')' { ... } {- function -}
type :: { ... }
type :
neverConstrainedType { ... }
| voidType { ... }
| pointerType { ... }
| functionType { ... }
typeNonVoid :: { ... } {- this already exists -}
typeNonVoid :
neverConstrainedType { ... }
| pointerType { ... }
| functionType { ... }
typeNonPointer :: { ... }
typeNonPointer :
neverConstrainedType { ... }
| voidType { ... }
| functionType { ... }
typeNonFunction :: { ... }
typeNonFunction :
neverConstrainedType { ... }
| voidType { ... }
| functionType { ... }
typeNonFunctionPointer :: { ... }
typeNonFunctionPointer :
typeNonPointer { ... }
| typeNonFunction '*' { ... }
然后将callable
定义为
callable :: { ... }
callable :
typeNonFunctionPointer operand { ... }
| type '(' types ')' '*' operand { ... }
答案 0 :(得分:2)
基本上你有什么称为转移/减少冲突。你可以google"解决转移/减少冲突"了解更多信息和资源。
解决转移/减少冲突的基本思想是重构语法。例如,这个语法含糊不清:
%token id comma int
A : B comma int
B : id
| id comma B
通过将其重构为:
,可以消除转变/减少冲突A : B int
B : id comma
| id comma B
在你的情况下,你可以尝试这样的事情:
type : simple {0}
| func {0}
| funcptr {0}
simple : 'void' {0}
| simple '*' {0}
| funcptr '*' {0}
func : type '(' type ')' {0}
funcptr : func '*' {0}
这个想法是这样的:
尽管如此,我已经尝试在我发现的语法中做的许多事情都可以通过在创建解析树后进行分析来更好地完成。