Flex / Bison:我如何拥有“SELECT 123 +”?

时间:2013-02-02 12:54:27

标签: c bison flex-lexer

我创建了一个非常简单的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 {
    }
;

%%

1 个答案:

答案 0 :(得分:3)

您需要了解LR(shift-reduce)解析,并且您需要使用语法中的错误规则来理解yacc如何从错误中恢复。前者是一个很大的问题,有很多书涵盖理论和实践的PDA和转移 - 减少解析(经典,Hopcroft & UllmanAho, Sethi & Ullman是完整的,如果相当密集)。

一旦理解了shift-reduce解析,yacc错误恢复就相当简单。基本上,每当它进入无法移动或减少当前令牌的状态时,需要一系列简单的步骤来尝试恢复:

  1. 它弹出状态,直到它到达可以移动特殊error标记的状态。如果当前状态可以转换error,则可能是零弹出。

  2. 它会移动错误令牌,然后执行目标状态的任何默认缩减。

  3. 它抛弃输入令牌,直到找到一个可以在当前状态下处理的令牌。与状态丢弃一样,如果转移error之后的状态可以处理下一个令牌,则可能为零丢弃。

  4. 就是这样。

    因此,如果我们看看您当前的语法和示例错误输入会发生什么,我们就是这样:

    1. SELECT令牌转移到州select_statement: K_SELECT ...
    2. 转移123令牌,将其缩减为value并转换为州*expr: value ...
    3. 转移+令牌,将其缩减为operator并转换为州binary_expression: value operator ...
    4. 看到令牌,并且无法在当前状态下移位或缩小,因此会发出语法错误。
    5. Pops州寻找可以处理error的州。前两个状态(从上面的3和2)不能被丢弃。下一个州可以,所以我们最终处于州select_statement: K_SELECT error
    6. 这是默认的缩减状态,因此缩小为select_statement,然后缩减为statement,转换为状态input: statement END
    7. 它开始丢弃输入令牌,直到它找到当前状态可以处理的值,这只是END。所以它抛弃了所有东西,直到它到达END或eof。
    8. 现在你的问题似乎是“我怎么做不同的事情?”

      如果您想要“二进制表达式未完成”恢复,您可以添加如下规则:

      binary_expression: value error
      

      这将最终成为上述*expr: value状态的一部分,因此错误恢复将停止弹出并转移错误令牌,最终处于可以移动,令牌的状态。

      每当你试图解释大语法中的状态并理解错误恢复将会做什么时,使用-v标志运行yacc / bison以产生具有所有状态的.output文件会非常有帮助在它。