我正在尝试扩展SQLite的SQL语言(file parse.y)。我有一个解析冲突,但是柠檬解析器除了随机的"1 解析冲突"错误消息外没有显示任何内容。
问题在于create_table可以简化为"创建"或"创建或替换",后跟 temp,也可以减少到空令牌。
cmd ::= create_table create_table_args table_properties_args.
create_table ::= createorreplace(C) temp(T) TABLE ifnotexists(E) nm(X) dbnm(Y). {
// ...
}
%type createorreplace {int}
createorreplace(A) ::= CREATE. {disableLookaside(pParse); A = 0;}
createorreplace(A) ::= CREATE OR REPLACE. {disableLookaside(pParse); A = 1;}
%type temp {int}
temp(A) ::= TEMP. {A = pParse->db->init.busy==0;}
temp(A) ::= . {A = 0;}
如何使"或替换"有选择地减少,同时保留它可能跟在 TEMP 后面?
由于我只能猜测您可能如何以及在何处更改了SQLite的SQL语法,因此这个答案必然是试探性的。但无论如何,它可能有用。
原始 SQL 语法包含以下产品(我省略了这些操作,因为它们与诊断冲突无关):
cmd ::= create_table create_table_args.
create_table ::= createkw temp(T) TABLE
ifnotexists(E) nm(Y) dbnm(Z).
createkw(A) ::= CREATE(A).
temp(A) ::= TEMP.
temp(A) ::= .
cmd ::= createkw(X) temp(T) VIEW
ifnotexists(E) nm(Y) dbnm(Z) eidlist_opt(C) AS select(S).
您似乎已将create_table
修改为:
create_table ::= createorreplace(C) temp(T) TABLE
ifnotexists(E) nm(X) dbnm(Y).
createorreplace(A) ::= CREATE.
createorreplace(A) ::= CREATE OR REPLACE.
这种变化确实造成了冲突,但它与temp
为空无关。事实上,它与非终端temp
根本没有关系。您可以将temp
替换为TEMP
(从而使它成为强制性的而不是可选的),并且您仍然会遇到班次-减少冲突。
以CREATE TEMP
开头的输入发生冲突。该输入可能是
CREATE TEMP TABLE ...
CREATE TEMP VIEW ...
这些显然是不同的语法,没有歧义
但是,当刚刚读取终端CREATE
并且终端TEMP
是前瞻令牌时,这两种可能性仍然可用。这不一定是问题;自下而上的解析器在生产结束之前不需要解析将使用哪种可能的生产。所以原始语法工作正常,没有冲突。
但请注意,原始语法没有以终端CREATE
开头的cmd
生产。它拥有的是几部从非终端createkw
开始的cmd
作品。但那里也没有混淆的可能性。在cmd
作品(以及我没有列出的其他cmd
作品,也以createkw
开头)中,终端CREATE
减少到createkw
)。
但是,在您修改后的语法中,这两个作品并不都以createkw
开头。其中一个更改为以createorreplace
开头。
不包含可选关键字的输入仍然可以毫无问题地解析TEMP
。如果不存在TEMP
,将在create_table
命令中TABLE
前瞻令牌,在创建视图命令中VIEW
前瞻令牌。由于前瞻令牌不同,因此解析器可以轻松决定是减少到createkw
还是减少到createorreplace
。类似地,如果输入实际上是CREATE OR REPLACE ...
的,则前瞻令牌将是OR
的,这明确地强制将减少到createorreplace
。
但是有问题的输入,如上所示,开始CREATE TEMP
。现在,解析器必须在没有看到终端TEMP
后面的任何内容的情况下决定是将CREATE
减少到createkw
还是将其减少到createorreplace
。由于无法作出这一决定,因此报告了冲突。(您可以通过查看 Lemon 报告文件找到有关该冲突的更多信息,parse.out
。
解决方案(如果我对你的语法修改的猜测是正确的)是避免强迫解析器做出不必要的决定。这需要一点语法重复:
cmd ::= create_table create_table_args.
create_table ::= createkw temp(T) TABLE ifnotexists(E) nm(Y) dbnm(Z).
create_table ::= createorreplace temp(T) TABLE ifnotexists(E) nm(Y) dbnm(Z).
createkw(A) ::= CREATE(A).
createorreplace(A) ::= CREATE OR REPLACE.
temp(A) ::= TEMP.
temp(A) ::= .
cmd ::= createkw(X) temp(T) VIEW ifnotexists(E) nm(Y) dbnm(Z)
eidlist_opt(C) AS select(S).
现在,不跟OR REPLACE
的终端CREATE
总是减少到createkw
,而序列CREATE OR REPLACE
总是减少到createorreplace
。这是有效的,因为除了CREATE OR REPLACE
之外,没有可能的cmd
开始CREATE OR
的解析。