我正在为我设计的语言构建解析器,其中类型名称以大写字母开头,变量名称以小写字母开头,这样词法分析器可以区分并提供不同的标记。此外,字符串'this'由词法分析器识别(它是一种OOP语言)并作为单独的标记传递。最后,数据成员只能在'this'对象上访问,所以我建立了语法:
%token TYPENAME
%token VARNAME
%token THIS
%%
start:
Expression
;
Expression:
THIS
| THIS '.' VARNAME
| Expression '.' TYPENAME
;
%%
Expression的第一条规则允许用户将'this'作为值传递(例如,从方法返回或传递给方法调用)。第二个是访问'this'的数据。第三个规则是调用方法,但是我删除了括号和参数,因为它们与问题无关。原来的语法显然比这大得多,但是这是产生相同错误的最小部分(1个Shift / Reduce冲突) - 我把它隔离到自己的解析器文件中并验证了这个,所以错误与任何无关其他符号。
据我所知,这里给出的语法是明确的,所以不应该产生任何错误。如果删除三个规则中的任何一个或将第二个规则更改为
Expression '.' VARNAME
没有冲突。在任何情况下,我可能都需要有人说明为什么会发生这种冲突以及如何解决冲突。
答案 0 :(得分:4)
问题是语法只能提前一个。因此,当您看到THIS
然后.
时,您是否在第2行(Expression: THIS '.' VARNAME
)或第3行(Expression: Expression '.' TYPENAME
,通过根据第1行的缩减)。< / p>
语法可以将THIS.
缩减为Expression.
,然后查找TYPENAME
或将其转移到THIS.
并查找VARNAME
,但它有决定何时到达.
。
答案 1 :(得分:3)
我尽量避免y.output,但有时它会有所帮助。我查看了它制作的文件并看到了。
state 1
2 Expression: THIS. [$end, '.']
3 | THIS . '.' VARNAME
'.' shift, and go to state 4
'.' [reduce using rule 2 (Expression)]
$default reduce using rule 2 (Expression)
基本上它是说它看到'。'并且可以减少或者可以改变。减少使我有时因为很难被罚款。转变是规则3并且是显而易见的(但输出没有提到规则#)。减少它所看到的''。在这种情况下是行
| Expression '.' TYPENAME
当它转到Expression时,它会查看下一个字母('。')并进入。现在它看到THIS |
所以当它到达该语句的末尾时它会预期'。'。当它离开或错误。但它看到了这个'。'而它介于此和'。'之间(因此out文件中的点)它可以减少规则,因此存在路径冲突。我相信你可以使用%glr-parser
来允许它尝试两者,但是你遇到的冲突越多,你就越有可能获得意外输出或模糊错误。我过去有歧义错误。他们很烦人,特别是如果你不记得什么规则造成或影响他们。建议避免冲突。
在尝试使用野牛之前,我强烈推荐this book。
我无法想到一个“伟大的”解决方案,但这不会产生冲突
start:
ExpressionLoop
;
ExpressionLoop:
Expression
| ExpressionLoop ';' Expression
;
Expression:
rval
| rval '.' TYPENAME
| THIS //trick is moving this AWAY so it doesnt reduce
rval:
THIS '.' VARNAME
替代方案,您可以通过向规则添加更多内容来使其减少,因此它不会很快减少,或者通过在之前或之后添加令牌来明确哪条路径采取或失败(请记住,它必须知道减少任何路径之前) )
start:
ExpressionLoop
;
ExpressionLoop:
Expression
| ExpressionLoop ';' Expression
;
Expression:
rval
| rval '.' TYPENAME
rval:
THIS '@'
| THIS '.' VARNAME
%%
-edit-注意如果我想做func param
和type varname
我不能因为根据词法分析器func的类型是Var(它是A-Za-z09_)以及类型。 param和varname也都是var,所以这会导致我减少/减少冲突。你不能把它写成它们是什么,只是它们看起来像什么。所以在写作时请记住这一点。你必须编写一个令牌来区分这两个,或者把它写成两个中的一个但是在代码中编写额外的逻辑(在规则右侧的{}中的部分)来检查它是否是一个函数名或一个类型并处理这两种情况。