Python 解决开关块中默认标签的移位/减少冲突
我正在使用PLY为Unrealscript编写一个解析器,我(希望)遇到了我所设置的解析规则中最后的一个歧义 Unrealscript有一个关键字,Python 解决开关块中默认标签的移位/减少冲突,python,ply,lalr,unrealscript,Python,Ply,Lalr,Unrealscript,我正在使用PLY为Unrealscript编写一个解析器,我(希望)遇到了我所设置的解析规则中最后的一个歧义 Unrealscript有一个关键字,default,根据上下文的不同使用不同的关键字。在常规语句行中,您可以像这样使用default: default.SomeValue = 3; // sets the "default" class property to 3 当然,开关语句也有默认案例标签: switch (i) { case 0: break;
default
,根据上下文的不同使用不同的关键字。在常规语句行中,您可以像这样使用default
:
default.SomeValue = 3; // sets the "default" class property to 3
当然,开关
语句也有默认
案例标签:
switch (i) {
case 0:
break;
default:
break;
}
当它在它认为是case
的语句块中遇到default
标签时,解析过程中存在歧义。以下是遇到解析错误的示例文件:
输入
解析规则
以下是相互冲突的规则:
所有规则都可以完整地看到
默认值
开关
对于如何解决此冲突的任何帮助或指导,我们将不胜感激!提前感谢您。您这里有一个简单的移位/减少冲突(将标记
默认值
作为前瞻)作为移位解决
让我们把这一切简化为一个小得多的例子。以下是语法,部分基于OP中所指的Github存储库中的语法(但旨在自包含):
这里的关键是语句
可能以令牌默认值
开头,而案例
也可能以令牌默认值
开头。现在,假设我们在解析中达到了以下点:
switch ( <expression> ) { <cases> case <expression> : <statements>
项目2的先行设置为[r默认,默认,ID]
现在假设下一个标记是默认标记。如果缺省值后跟=,我们可以查看语句的开头。或者,如果默认值后跟:。但我们看不到未来的两个标志,只有一个;下一个标记是default,这就是我们所知道的
但我们需要做出决定:
- 如果默认值是语句的开头,我们可以将其移位(第5项)。然后,当我们看到=,我们将把默认值减少为
,并继续解析lvalue
assign
- 如果默认值是案例的开头,我们需要将
减少为案例表达式冒号语句
(第2项)。然后,我们将把案例
减少到cases-case
,然后再最终转换默认值。然后,我们将移动:并继续使用cases-case
默认冒号语句
|
、*
和+
(交替、可选重复和重复)的开关主体是:
或者,为了让它不那么麻烦:
case-header -> ("case" expression | "default") ":"
switch-body -> (case-header statement*)+
从接受字符串的角度来看,这与
switch-body -> case-header (case-header | statement)*
换言之,是一系列事物,它们要么是案例标题
s,要么是语句
s,其中第一个是案例标题
这种编写规则的方式不会生成正确的解析树;它将switch语句的结构简化为语句和大小写标签的组合。但它确实能识别完全相同的语言
另一方面,它的优点是不强制解析器决定案例原因何时终止。(该语法不再包含大小写子句)因此它是一个简单的LR(1)语法:
现在,我们可以证明结果解析树实际上是准确的。Unrealscript与C共享关于switch
语句的相同设计决策,其中case
子句实际上并不定义任何真正意义上的块。它只是一个可以跳转到的标签,以及一个条件跳转到下一个标签
但实际上,在我们进行操作时修复解析树并不特别复杂,因为对switch\u body
的每次缩减都清楚地表明了我们要添加的内容。如果我们添加一个case头,我们可以在case子句的累加列表中附加一个新列表;如果它是一个语句,我们将该语句附加到最后一个case子句的末尾
因此,我们可以将上述规则大致写为:
def p_switch_body_1(p):
''' switch_body : case_header '''
p[0] = [p[1]]
def p_switch_body_2(p):
''' switch_body : switch_body statement '''
# Append the statement to the list which is the last element of
# the tuple which is the last element of the list which is the
# semantic value of symbol 1, the switch_body.
p[1][-1][-1].append(p[2])
p[0] = p[1]
def p_switch_body_3(p):
''' switch_body : switch_body case_header '''
# Add the new case header (symbol 2), whose statement list
# is initially empty, to the list of switch clauses.
p[1].append(p[2])
p[0] = p[1]
def p_case_header_1(p):
''' case_header : CASE expr COLON '''
p[0] = ('switch_case', p[2], [])
def p_case_header_2(p):
''' case_header : DEFAULT COLON '''
p[0] = ('default_case', [])
您是否尝试将
p_案例
规则移动到p_默认
规则之前?它可能不会消除歧义,但它会导致解析器执行正确的操作。嗯,没有这样的运气。同样的结果。@Bakuriu:类似yacc的解析器生成器中的结果顺序是不相关的。
switch ( <expression> ) { <cases> case <expression> : <statements>
1. statements: statements · statement
2. case : CASE expression COLON statements ·
3. statement : · assign SEMICOLON
4. assign : · lvalue EQUALS expression
5. lvalue : · DEFAULT
switch-body -> (("case" expression | "default") ":" statement*)+
case-header -> ("case" expression | "default") ":"
switch-body -> (case-header statement*)+
switch-body -> case-header (case-header | statement)*
switch : SWITCH LPAREN expression RPAREN LCURLY switch_body RCURLY
switch_body : case_header
| switch_body statement
| switch_body case_header
case_header : CASE expr COLON
| DEFAULT COLON
def p_switch_body_1(p):
''' switch_body : case_header '''
p[0] = [p[1]]
def p_switch_body_2(p):
''' switch_body : switch_body statement '''
# Append the statement to the list which is the last element of
# the tuple which is the last element of the list which is the
# semantic value of symbol 1, the switch_body.
p[1][-1][-1].append(p[2])
p[0] = p[1]
def p_switch_body_3(p):
''' switch_body : switch_body case_header '''
# Add the new case header (symbol 2), whose statement list
# is initially empty, to the list of switch clauses.
p[1].append(p[2])
p[0] = p[1]
def p_case_header_1(p):
''' case_header : CASE expr COLON '''
p[0] = ('switch_case', p[2], [])
def p_case_header_2(p):
''' case_header : DEFAULT COLON '''
p[0] = ('default_case', [])