我创建了一个非常简单的SQL解析器,但在模糊测试期间我遇到过这种情况:
SELECT 123 + ,
K_SELECT INTEGER T_PLUS T_COMMA
当然这是一个语法错误,但我不知道如何“抓住”它。
它是如何决定“next_column_expression来得太早”和“binary_expression没有完成”。我和ANTLR3在Java项目上做了很多工作。但这完全不同。
这是骨架解析器规则:
/* be more versbose about error messages */
%error-verbose
/* keywords */
%token K_CREATE
%token K_FROM
%token K_INTEGER
%token K_SELECT
%token K_TABLE
%token K_TEXT
%token K_WHERE
%token K_VALUES
%token K_INSERT
%token K_INTO
/* variable tokens */
%token IDENTIFIER
%token INTEGER
/* fixed tokens */
%token T_ASTERISK
%token T_PLUS
%token T_EQUALS
%token T_END ";"
%token T_COMMA
%token T_BRACKET_OPEN
%token T_BRACKET_CLOSE
%token END 0 "end of file"
%%
input:
statement {
}
END
;
statement:
select_statement {
}
|
create_table_statement {
}
|
insert_statement {
}
;
keyword:
K_CREATE | K_FROM | K_INTEGER | K_SELECT | K_TABLE | K_TEXT | K_WHERE | K_VALUES | K_INSERT | K_INTO
;
table_name:
error {
// "Expected table name"
}
|
keyword {
// "You cannot use a keyword for a table name."
}
|
IDENTIFIER {
}
;
select_statement:
K_SELECT column_expression_list {
// "Expected FROM after column list."
}
error
|
K_SELECT error {
// "Expected column list after SELECT."
}
|
K_SELECT column_expression_list {
}
K_FROM table_name {
}
;
column_expression_list:
column_expression {
}
next_column_expression
;
column_expression:
T_ASTERISK {
}
|
expression {
}
;
next_column_expression:
|
T_COMMA column_expression {
}
next_column_expression
;
binary_expression:
value {
}
operator {
}
value {
}
;
expression:
value
|
binary_expression
;
operator:
T_PLUS {
}
|
T_EQUALS {
}
;
value:
INTEGER {
}
|
IDENTIFIER {
}
;
%%
答案 0 :(得分:3)
您需要了解LR(shift-reduce)解析,并且您需要使用语法中的错误规则来理解yacc如何从错误中恢复。前者是一个很大的问题,有很多书涵盖理论和实践的PDA和转移 - 减少解析(经典,Hopcroft & Ullman和Aho, Sethi & Ullman是完整的,如果相当密集)。
一旦理解了shift-reduce解析,yacc错误恢复就相当简单。基本上,每当它进入无法移动或减少当前令牌的状态时,需要一系列简单的步骤来尝试恢复:
它弹出状态,直到它到达可以移动特殊error
标记的状态。如果当前状态可以转换error
,则可能是零弹出。
它会移动错误令牌,然后执行目标状态的任何默认缩减。
它抛弃输入令牌,直到找到一个可以在当前状态下处理的令牌。与状态丢弃一样,如果转移error
之后的状态可以处理下一个令牌,则可能为零丢弃。
就是这样。
因此,如果我们看看您当前的语法和示例错误输入会发生什么,我们就是这样:
SELECT
令牌转移到州select_statement: K_SELECT ...
123
令牌,将其缩减为value
并转换为州*expr: value ...
+
令牌,将其缩减为operator
并转换为州binary_expression: value operator ...
,
并且无法在当前状态下移位或缩小,因此会发出语法错误。error
的州。前两个状态(从上面的3和2)不能被丢弃。下一个州可以,所以我们最终处于州select_statement: K_SELECT error
select_statement
,然后缩减为statement
,转换为状态input: statement END
END
。所以它抛弃了所有东西,直到它到达END
或eof。现在你的问题似乎是“我怎么做不同的事情?”
如果您想要“二进制表达式未完成”恢复,您可以添加如下规则:
binary_expression: value error
这将最终成为上述*expr: value
状态的一部分,因此错误恢复将停止弹出并转移错误令牌,最终处于可以移动,
令牌的状态。
每当你试图解释大语法中的状态并理解错误恢复将会做什么时,使用-v标志运行yacc / bison以产生具有所有状态的.output
文件会非常有帮助在它。