Flex和Bison:词汇分析在输入结束前停止

时间:2015-11-25 21:00:19

标签: linux bison

我使用Flex和Bison来解析简化的SQL语法。我遇到了一个问题,Flex将在文件结束前停止标记化。当我通过仅包含的测试用例时:

  

创建数据库;

一切正常,创建了令牌CREATE,DATEABASE和ID。但是当我通过一个只包含的测试用例时:

  

create table this(integer qty);

它会标记化,包括"数量"但没有读过')'或者&#39 ;;'。相反,它将直接进入解析和打印" REJECT" (因为它应该与不正确的令牌)。如何修复我的代码以标记所有内容,以便我传递CREATE,TABLE,ID,(,INTEGER,ID,);作为Bison的代币?下面是我的.l .y和makefile。提前感谢您的帮助!

sql.l

%{
#include "sql.tab.h"
%}

Delimiter    [ \t]
WhiteSpace   {Delimiter}+
Letter       [A-Za-z]
Name         [A-Za-z][A-Za-z0-9_]*
Digit        [0-9]
Integer      {Digit}+
Float        {Digit}+"."{Digit}+
Date         (0[1-9]|1[012])"/"(0[1-9]|[12][0-9]|3[01])"/"[0-9][0-9][0-9][0-9]
Other        [-!@#$%&+:"~`]


%%
{WhiteSpace}                                     { ; }
[Cc][Rr][Ee][Aa][Tt][Ee]                 {printf("token:     CREATE\n");return(CREATE);}
[Dd][Rr][Oo][Pp]                         {printf("token: DROP\n");return(DROP);}
[Ll][Oo][Aa][Dd]                         {printf("token: LOAD\n");return(LOAD);}
[Ss][Aa][Vv][Ee]                         {printf("token: SAVE\n");return(SAVE);}
[Dd][Aa][Tt][Aa][Bb][Aa][Ss][Ee]         {printf("token: DATABASE\n");return(DATABASE);}
[Tt][Aa][Bb][Ll][Ee]                     {printf("token: TABLE\n");return(TABLE);}
[Ii][Nn][Ss][Ee][Rr][Tt]                 {printf("token: INSERT\n");return(INSERT);}
[Ii][Nn][Tt][Oo]                         {printf("token: INTO\n");return(INTO);}
[Ff][Rr][Oo][Mm]                         {printf("token: FROM\n");return(FROM);}
[Ww][Hh][Ee][Rr][Ee]                     {printf("token: WHERE\n");return(WHERE);}
[Ss][Ee][Tt]                               {printf("token: SET\n");return(SET);}
[Dd][Ee][Ll][Ee][Tt][Ee]                 {printf("token: DELETE\n");return(DELETE);}
[Uu][Pp][Dd][Aa][Tt][Ee]                 {printf("token: UPDATE\n");return(UPDATE);}
[Ss][Ee][Ll][Ee][Cc][Tt]                 {printf("token: SELECT\n");return(SELECT);}
[Ww][Ss][Ee][Ll][Ee][Cc][Tt]             {printf("token: WSELECT\n");return(WSELECT);}
[Vv][Aa][Ll][Uu][Ee][Ss]                 {printf("token: VALUES\n");return(VALUES);}
[Ii][Nn][Tt][Ee][Gg][Ee][Rr]             {printf("token: INTEGER\n");return(INTEGER);}
[Nn][Uu][Mm][Bb][Ee][Rr]                 {printf("token: NUMBER\n");return(NUMBER);}
[Cc][Hh][Aa][Rr][Aa][Cc][Tt][Ee][Rr]     {printf("token: CHARACTER\n");return(CHARACTER);}
[Dd][Aa][Tt][Ee]                               {printf("token: DATE\n");return(DATE);}
{Name}                       {printf("token: ID %s\n", yytext);return(ID);}
"*"                      {printf("token: *\n");return('*');}
"("                      {printf("token: (\n");return('(');}
")"                      {printf("token: )\n");return(')');}
","                      {printf("token: ,\n");return(',');}
"<"                      {printf("token: LT\n");return(LT);}
">"                      {printf("token: GT\n");return(GT);}
"<="                     {printf("token: LTEQ\n");return(LTEQ);}
">="                     {printf("token: GTEQ\n");return(GTEQ);}
"=="                           {printf("token: EQEQ\n");return(EQEQ);}
"!="                           {printf("token: NOTEQ\n");return(NOTEQ);}
";"                      {printf("token: ;\n");return(';');}
{Float}                  {printf("token: DEC\n");return(DEC);}
"-"{Float}               {printf("token: DEC\n");return(DEC);}
{Integer}                {printf("token: INT\n");return(INT);}
"-"{Integer}             {printf("token: INT\n");return(INT);}
"'"[ \ta-zA-Z0-9]+"'"    {printf("token: STR\n");return(STR);}
{Date}                     {printf("token: DATETYPE\n");return(DATETYPE);}
\n           { ; } 
.                    {printf("Character %s not tokenized\n", yytext);exit(0)
;}

sql.y

%{ 
#define YYSTYPE char*
#include <stdio.h>
%}

%start START
%token ID 
%token CREATE DROP LOAD SAVE DATABASE TABLE INSERT INTO FROM 
%token WHERE SET DELETE UPDATE SELECT WSELECT VALUES
%token DEC INT STR DATETYPE DATE NUMBER CHARACTER INTEGER
%token LT GT LTEQ GTEQ EQ EQEQ NOTEQ


%%
START          : COMMAND_LIST  

COMMAND_LIST   : COMMAND COMMAND_LIST2

COMMAND_LIST2  : COMMAND COMMAND_LIST2 
           | /* EMPTY */

COMMAND        : SYSTEM_COMMAND 
           | DDL_COMMAND 
           | DML_COMMAND

SYSTEM_COMMAND : CREATE_DATABASE_COMMAND
               | DROP_DATABASE_COMMAND
               | SAVE_COMMAND
               | LOAD_DATABASE_COMMAND


DDL_COMMAND    : CREATE_TABLE_COMMAND
               | DROP_TABLE_COMMAND

DDL_COMMAND    : CREATE_TABLE_COMMAND
               | DROP_TABLE_COMMAND

DML_COMMAND    : INSERT_INTO_COMMAND
               | DELETE_FROM_COMMAND
               | UPDATE_COMMAND
               | SELECT_COMMAND
               | W_SELECT_COMMAND

CREATE_DATABASE_COMMAND : CREATE DATABASE ID ';'

DROP_DATABASE_COMMAND   : DROP DATABASE ID ';'

SAVE_COMMAND            : SAVE ';'

LOAD_DATABASE_COMMAND   : LOAD DATABASE ID ';'

CREATE_TABLE_COMMAND    : CREATE TABLE ID '(' FIELD_DEF_LIST ')' ';'

DROP_TABLE_COMMAND      : DROP TABLE ID ';'

INSERT_INTO_COMMAND     : INSERT INTO ID INSERT_INTO_COMMAND2

INSERT_INTO_COMMAND2    : '(' FIELD_LIST ')' VALUES '(' LITERAL_LIST ')' ';'
                        | VALUES '(' LITERAL_LIST ')' ';'

DELETE_FROM_COMMAND     : DELETE FROM ID DELETE_FROM_COMMAND2

DELETE_FROM_COMMAND2    : WHERE CONDITION ';' | ';'

UPDATE_COMMAND          : UPDATE ID SET ID '=' LITERAL UPDATE_COMMAND2

UPDATE_COMMAND2         : ',' ID '=' LITERAL UPDATE_COMMAND2      
                        | UPDATE_COMMAND3

UPDATE_COMMAND3         : WHERE CONDITION ';' 
            | ';'

SELECT_COMMAND          : SELECT '*' FROM ID ';'

W_SELECT_COMMAND        : WSELECT W_SELECT_COMMAND2

W_SELECT_COMMAND2       : '*' FROM ID W_SELECT_COMMAND3
                        | '(' FIELD_LIST ')' FROM ID W_SELECT_COMMAND3

W_SELECT_COMMAND3       : WHERE CONDITION ';' 
                | ';'

FIELD_DEF_LIST      : FIELD_DEF FIELD_DEF_LIST2 

FIELD_DEF_LIST2     : ',' FIELD_DEF FIELD_DEF_LIST2 
                | /* EMPTY */ 

FIELD_DEF       : FIELD_TYPE ID 

FIELD_LIST      : ID FIELD_LIST2

FIELD_LIST2         : ',' ID FIELD_LIST2 
                | /* EMPTY */ 

FIELD_TYPE              : INTEGER '(' INT ')' 
                        | INT
                        | NUMBER '(' INT ')' 
                        | NUMBER '(' INT ',' INT ')'
                        | NUMBER
                        | CHARACTER '(' INT ')'
                        | DATE

LITERAL_LIST            : LITERAL LITERAL_LIST2

LITERAL_LIST2           : ',' LITERAL LITERAL_LIST2 
                | /* EMPTY */

LITERAL                 : INT 
                        | DEC 
                        | STR 
                        | DATETYPE

CONDITION               : ID COMP LITERAL 

COMP                    : LT 
            | GT 
            | EQEQ 
            | LTEQ 
            | GTEQ
            | NOTEQ 
%%

main()
{
   yyparse();
}
yyerror()
{
   printf("REJECT\n");
}
yywrap()
{
   printf("ACCEPT\n");
}

生成文件

trial: lex.yy.o sql.tab.o
    cc -o trial lex.yy.o sql.tab.o 

sql.tab.o: sql.tab.c
    cc -c sql.tab.c

sql.tab.c: sql.y
    bison -d sql.y

lex.yy.o: lex.yy.c 
    cc -c lex.yy.c

lex.yy.c: sql.l sql.tab.c
    flex sql.l

顺便说一下.l文件中有一些打印语句用于调试。喜欢的可执行文件称为“试用版”。

1 个答案:

答案 0 :(得分:0)

一旦bison解析器出现错误(无法解析的内容),它就会调用yyerror并出现&#34;语法错误&#34;消息,然后进入错误恢复模式。在错误恢复模式下,它会在右侧查找带有error的规则(错误恢复规则),并将使用其中一个尝试恢复。如果语法没有错误恢复规则(就像你的那样),那么它就不会找到任何错误,因此yyparse只会返回而不会尝试再读取任何令牌。

如果您想尝试从语法错误中恢复并继续解析备用命令(通常是在语法错误的部分之后填充),则需要添加一些错误恢复规则。编写好的错误恢复规则有点棘手,你可以做很多不同的事情。在你的情况下,你可以尝试的最简单的事情是只在顶层恢复,因此它将释放当前命令的其余部分(带有sytax错误)并尝试找到下一个命令:

START          : /* empty */
               | START COMMAND

COMMAND        : SYSTEM_COMMAND 
               | DDL_COMMAND 
               | DML_COMMAND
               | error ';'

请注意,我已经摆脱了无用的冗余COMMAND_LIST规则并将其更改为使用左递归而不是递归(因为bison通常更好的左递归 - 你应该只使用正确的递归,如果你真的需要它并知道你在做什么)。

每当您尝试解析命令时,此错误恢复规则都将处于活动状态,并且可以通过丢弃输入令牌直到&#39 ;;&#39;看到了。一旦到达&#39 ;;&#39;,它会将其视为命令的结尾(它将减少COMMAND : error ';'规则,然后是START : START COMMAND规则),并继续解析(寻找另一个COMMAND

请注意,一旦错误恢复规则从错误中恢复,解析器将继续解析,如果在没有进一步错误的情况下到达输入的末尾,yyparse将返回成功。