Parsing 解析中的优先级

Parsing 解析中的优先级,parsing,ocaml,ocamlyacc,Parsing,Ocaml,Ocamlyacc,我重新表述了我以前问过的一个问题。目的是了解优先权在解析中是如何工作的 我想解析一个语句a(3)。value=100parser.mly如下在读取后停止,并返回错误 但是,如果我将专用于参数列表(由开始和结束)的部分移动到文件的末尾(因此在l\u表达式之后),解析工作会很好 在解析语句的情况下,它被简化为data\u-manipulation\u语句的let\u语句a(3).值减少为成员访问表达式a(3)被简化为index_表达式;而3则减少为参数列表 在无法解析语句的情况下,它似乎试图将语句的

我重新表述了我以前问过的一个问题。目的是了解优先权在解析中是如何工作的

我想解析一个语句
a(3)。value=100
parser.mly
如下在读取
后停止,并返回错误

但是,如果我将专用于
参数列表
(由
开始
结束
)的部分移动到文件的末尾(因此在
l\u表达式
之后),解析工作会很好

在解析语句的情况下,它被简化为
data\u-manipulation\u语句的
let\u语句
<代码>a(3).值
减少为
成员访问表达式
a(3)
被简化为
index_表达式
;而
3
则减少为
参数列表

在无法解析语句的情况下,它似乎试图将语句的开头缩减为
control\u语句
call\u语句
。。。它以一个错误结束

我一直认为,无论优先顺序是什么,解析总是拒绝不能以成功结束的缩减。但在那里,它似乎尝试过,但失败了,并拒绝尝试其他的可能性

有人能帮忙澄清一下吗

statement:
| control_statement { $1 }
| data_manipulation_statement  { BS_DMS $1 }

control_statement: | control_statement_except_multiline_if { BS_CSEMI $1 }
control_statement_except_multiline_if: | call_statement { $1 }
call_statement: | simple_name_expression argument_list { CSEMI_SNE_AL ($1, $2) }
data_manipulation_statement: | let_statement { $1 }
let_statement: | l_expression EQUAL expression { DMS_let (None, $1, $3) } 

simple_name_expression: | name { $1 } 
index_expression: | l_expression LPAREN argument_list RPAREN { IE_LE_AL ($1, $3) }
member_access_expression: | l_expression DOT unrestricted_name { MAE_LE_UN ($1, $3) }
literal_expression: | INTEGER { LIE_INT $1 }

unrestricted_name: | name { UN_N $1 }
name: | untyped_name { N_UN $1 }
untyped_name: | IDENTIFIER { UN_I $1 } 

expression: 
| l_expression { E_LE $1 }
| value_expression { E_VE $1 }

value_expression:
| literal_expression { VE_LIE $1 }
| parenthesized_expression { VE_PE $1 }

(***** begin argument_list *****)
argument_list: | positional_or_named_argument_list { AL_PONAL $1 }

positional_or_named_argument_list:
| positional_argument_COMMAs required_positional_argument { PONAL_PAs_RPA (List.rev $1, $2) }
positional_argument_COMMAs:
  /* empty */ { [] }
| positional_argument_COMMAs positional_argument COMMA { $2 :: $1 }

positional_argument: | argument_expression { $1 }
required_positional_argument: | argument_expression { $1 }
argument_expression: | expression { AE_expression (None, $1) }
(***** end argument_list *****)

l_expression:
| simple_name_expression { LE_SNE $1 } 
| index_expression { LE_IE $1 } 
| member_access_expression { LE_MAE $1 } 

关于解析如何进行,没有绝对的规则。这取决于解析器。可以说,一个正确的解析器应该“始终拒绝不能以成功结束的缩减”,但通常不能使用线性时间从左到右的解析器。GLR解析器(bison可以生成)可以做到这一点,可能需要多达立方时间,具体取决于语法。回溯解析器(如大多数解析组合器库)可以做到这一点,但估计算法的复杂性并不容易。但是一个*LR(1)解析器,我认为您正在使用的,基于您的标记,只能解析*LR(1)语法,而您的语法不是其中之一

LR(1)解析器在输入上从左到右(L),一次一个标记。读取每个标记后,它“减少”(即,匹配一个产品)在输入的最右侧标记(R)处结束的所有产品。为此,它使用尽可能多的关于解析(“解析堆栈”)和下一个(1)标记(“前瞻”)的进度信息。它们与有限状态下推自动机一起工作,有几种构造这些自动机的方法,它们提供了理想PDA的不同近似值,但对于语法而言,这并不重要。我相信ocamlyacc会生成LALR(1)解析器,就像原始的yacc一样,但您的语法甚至不是LR(1),因此它肯定不能被简化的LR(1)解析器解析

在上面的描述中,我没有使用“优先级”一词,因为它不适用于LR解析。有诸如优先级解析器之类的东西——它们通常不如LR解析器强大,但也更容易构造——但它们不再那么常见,除非是以手写解析器的形式出现。(我不确定是否有优先语法解析器生成器;一旦发现了LALR(1)解析,它就迅速占领了解析器生成器市场,因为它比优先或LL(1)解析更强大。)然而,“优先”在其开发的早期阶段被添加到
yacc
,以处理歧义语法;或者,通常是那些本可以写得毫不含糊但形式更加紧凑的语法

当LR解析器决定要做什么时,可能存在多个备选方案。这可能是因为存在不止一种可能的减少(“减少-减少”冲突),或者因为不清楚可用的减少是否正确(“减少-转移”冲突)。发生冲突的原因是语法不明确,或者无法仅使用指定的前瞻进行LR分析

在这两种情况下,迂腐的LR解析器生成器都会报告失败。但是,一个实用的解析器生成器可能会试图找出哪个选项是所需的,并相应地进行处理。一种简单的方法是,根据程序员提供的声明(“优先级声明”)或启发性声明(“优先语法文件中的优先项”),给一个备选优先级高于另一个。从某种意义上说,这种启发式是危险的,因为它们可能导致一个解析器,而该解析器实际上并不解析您希望解析的语言;IMHO任何解析器生成器都不应该在没有至少警告您它已经这样做的情况下应用启发式

现在,实际问题是:为什么你的语法不是LR(1)

让我们从以下摘录开始

call_statement: simple_name_expression argument_list
let_statement: l_expression EQUAL expression
l_expression: index_expression
l_expression: simple_name_expression
index_expression: l_expression LPAREN argument_list RPAREN

现在考虑输入:<代码> A(<代码> >。我们刚刚读了令牌<代码> A<代码>,先行令牌是代码>(.<代码> A<代码> >必须简化为<代码> SimeLyNAMEAY表达式< /代码>(通过多个单元制作,但细节不相关)。现在,解析器状态将包括:(

·
表示产品中的当前“点”):

也就是说,我们可以保持
simple\u name\u expression
不变,继续收集
参数列表
,或者我们可以将
simple\u name\u expression
简化为
l\u expression
,以便继续收集其余的
索引表达式
。它是哪一个

不幸的是,至少当我们到达匹配的
rpare
时,我们才知道答案。即使这样,我们也可能不知道答案,因为下一个标记可能是扩展
l_表达式的东西(例如
或另一个
LPAREN
),在这种情况下,我们需要继续扫描。不知道什么时候我们会找到答案,所以没有有限的前瞻就足够了,这个语法——尽管可能是unab
call_statement:   simple_name_expression · argument_list
index_expression: l_expression · LPAREN argument_list RPAREN
l_expression:     simple_name_expression ·
print a(3), value