【问题标题】:LR(1) Parser: Why adding an epsilon production makes r/r or s/r conflictsLR(1) 解析器:为什么添加 epsilon 产生式会导致 r/r 或 s/r 冲突
【发布时间】:2020-04-20 15:27:05
【问题描述】:

我想做一个阅读器,它可以读取类似于 mswin 的 INI 文件的配置文件。 使用我制作的词法分析器/解析器生成器自学是为了锻炼。 语法是:

%lexer
HEADER ::= "\\[[0-9a-zA-Z]+\\]"
TRUE ::= "yes|true"
FALSE ::= "no|false"
ASSIGN ::= "="
OPTION_NAME ::= "[a-zA-Z][0-9a-zA-Z]*"
INT ::= "[0-9]+"
STRING ::= "\"(\\\"|[^\"])*\""
CODE ::= "<{(.*)}>"
BLANK ::= "[ \t\f]+" :ignore
COMMENT ::= "#[^\n\r]*(\r|\n)?" :ignore
NEWLINE ::= "\r|\n"
%parser
Options ::= OptionGroup Options | OptionGroup | @epsilon@
OptionGroup ::= HEADER NEWLINE OptionsList 
OptionsList ::= Option NEWLINE OptionsList | Option 
Option ::= OPTION_NAME ASSIGN OptionValue
OptionValue ::= TRUE | FALSE | INT | STRING | CODE 

问题在于@epsilon@ 生产。我添加它是因为我希望我的读者也接受空文件。但是当 'OptionsList' 或 'OptionGroup' 包含 epsilon 产生时,我会遇到冲突。我尝试重新排列作品中的元素,但我只会遇到冲突(r/r 或 s/r,取决于我所做的),除非我从语法中完全删除 epsilon。它解决了这个问题,但是......在我的逻辑中,“OptionsList”或“OptionGroup”之一应该包含一个 epsilon,否则我接受空文件的目标不会实现。

我的解析器生成器使用 LR(1) 方法,所以我认为我可以在我的语法中使用 epsilon 产生式。看来我擅长编写生成器,但不擅长构建无错误的语法:(。

我应该忘记ε吗?或者即使没有 epsilon 产生,我的语法是否也接受空输入?

【问题讨论】:

    标签: parsing shift-reduce-conflict reduce-reduce-conflict lr1


    【解决方案1】:

    您的Options 产生式允许OptionsOptionGroups 的序列,从一个空列表或由单个元素组成的列表开始。这显然是模棱两可的,因为只有一个 OptionGroup 的列表可能是:

    • 基础案例OptionGroup
    • 基本情况@epsilon@ 加上OptionGroup

    简而言之,而不是

    Options ::= OptionGroup Options | OptionGroup | @epsilon@
    

    你需要

    Options ::= OptionGroup Options | @epsilon@
    

    匹配完全相同的一组句子,但明确。

    一般来说,您通常最好为自下而上的解析器编写左递归规则。所以我会写

    Options ::= Options OptionGroup | @epsilon@
    

    【讨论】:

    • 有效!但是对于我的信息:我的生成器还不理解“使 X 成为 Y 的列表”的指令,所以我不得不“步行”编写我的产品,结果就是那条线。那么,我的另一个问题是:如何表示 NTerm 应该包含其他元素的列表?
    • @Felix:递归产生式 ListOfX ::= ListOfX X | X(用于非空列表)或 ListOfX ::= ListOfX X | @epsilon@ 正是您在 BNF 中编写“X 列表”的方式。但是你必须选择其中一个。列表可以为空,也可以不能。
    • 感谢您的回答,很抱歉编辑,我在阅读您的回答之前就这样做了
    • @Thomas:是的,我知道这种观点。与该帖子中的声明不同,bison 还在堆上分配解析器堆栈。然而,与 Menhir 不同的是,许多 yacc 实现(包括 bison 版本)对解析器堆栈的大小设置了任意限制。这是一个有趣的例子,说明了这个问题如何表现出来:unix.stackexchange.com/a/186446/24557。有左递归解决冲突的句法上下文,也有右递归解决冲突的上下文。甚至有两个列表需要相反递归的环境......
    • 但是,总的来说,对于初学者来说,通常最好使用野牛手册中的建议,并使用左递归编写列表。一方面,这将避免在 SO 上经常出现的问题:“为什么我的列表向后打印?”
    猜你喜欢
    • 1970-01-01
    • 2020-08-23
    • 1970-01-01
    • 2010-09-19
    • 1970-01-01
    • 1970-01-01
    • 2019-12-22
    • 2021-08-14
    • 1970-01-01
    相关资源
    最近更新 更多