我正在使用一些解析器和词法分析器生成工具(类似于 Lex 和 Bison,但用于 C#)来生成将字符串解析为抽象语法树的程序,以便以后进行评估。
我想做错误恢复(即在生成的抽象句子树中报告缺少标记等)。我有两种方法来构建生成的语法,我想知道哪种方法更好/更灵活/不会发生冲突(.y 和 .lex 文件是根据计算器的描述生成的)。
计算器描述允许用户指定终端/正则表达式及其对运算符和关联性的位置。所以像:
grammar.AddTerminal("Plus", "\\+").
AddNonTerminal(new NonTerminal("Add", Associativity.LeftToRight).
AddTerminal("Expression").
AddTerminal("Plus").
AddTerminal("Expression"));
Terminal
(通过添加's 和NonTerminal
's的顺序指定优先级。"Add"
是通过反射发现的方法的名称。基本上它告诉 NonTerminal 在抽象语法树中调用运算符的内容。)
方法1:(允许任何表达式的空规则)
S -> E
E -> E + T
E -> T
T -> T * P
T -> P
P -> (E)
P -> (E [error]
P -> a
P -> @ [error]
a
是一个终端。
@
是空的。
方法2:(只允许空规则作为开始规则)
S -> E
S -> @ [error]
E -> + [error]
E -> T + [error]
E -> + T [error]
E -> E + T
E -> T
T -> * [error]
T -> * P [error]
T -> P * [error]
T -> T * P
T -> P
P -> (E)
P -> (E [error]
P -> a
这是一个示例,显示使用每种方法的错误输入的最左推导。
输入: (a +
方法1:
S
E
T
P
(E
(E + T
(T + T
(P + T
(a + T
(a + P
(a +
方法2:
S
E
T
P
(E
(T +
(P +
(a +
方法 2 更难编码(考虑减法/一元负运算符。你不能只看减法A -> A - B
,先取出它A
并报告错误,A -> - B
因为这对一元运算符有效。)我今天早上为方法 2 编码只是发现我认为它有语法问题,并且方法 1 中的空规则使事情变得更简单,代码方面,但我主要担心的是,当程序员创建计算器描述时,哪种方法会产生最少的语法问题如上所述。