规则完成后,野牛接受输入

时间:2018-05-15 18:07:57

标签: bison flex-lexer yacc lex rule

我想解析单个查询的文本。此查询将以分号结束。它会像sql一样。例如:CREATE TABLE 'somename'; 我的y文件是

%{
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <stdbool.h>
#include "ast.h"

extern int yylex(void);
extern void yyerror(char const *msg);

QueryNode *queryNode;

%}

%union {
int integer;
char *str;
char chr;
bool boolean;
int intval;
char *strval;
ObjectTypeNode *objectTypeNode;
CreateClauseNode *createClauseNode;
QueryNode *queryNode;
}

%token  NUMBER
%token  INTNUM

%token<str> CREATE_KEYWORD
%token<str> DATABASE_KEYWORD
%token<str> TABLE_KEYWORD
%token<str> LETTER
%token<str> STRING
%token<str> IDENTIFIER
%token<chr> LEFT_BRACKET RIGHT_BRACKET COMMA SEMICOLON EOL

%type<objectTypeNode> object_type
%type<createClauseNode> create_clause
%type<queryNode> query

%start input

%%
input:      SEMICOLON EOL                               { queryNode = NULL; }
        |   query   SEMICOLON EOL                       { queryNode = $1; }
        ;

query:  create_clause                                   { $$ = CreateQueryNode($1, CREATE_CLAUSE_TYPE); }
        ;

create_clause:  CREATE_KEYWORD  object_type STRING      { $$ = CreateCreateClauseNode($2, $3); }
                ;

object_type:    DATABASE_KEYWORD                        { $$ = CreateObjectTypeNode(DATABASE_OBJECT); }
            |   TABLE_KEYWORD                           { $$ = CreateObjectTypeNode(TABLE_OBJECT); }
            ;
%%
void yyerror(char const *msg) {
    printf("Error: %s\n", msg);
}

我的档案是

%{
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <stdarg.h>
#include "ast.h"
#include "y.tab.h"
%}

%option noyywrap nodefault yylineno case-insensitive

%%
CREATE                  { yylval.strval = "create"; return CREATE_KEYWORD; }
DATABASE                { return DATABASE_KEYWORD; }
TABLE                   { return TABLE_KEYWORD; }
"("                     { return LEFT_BRACKET; }
")"                     { return RIGHT_BRACKET; }
";"                     { return SEMICOLON; }

-?[0-9]+                { yylval.intval = atoi(yytext); return INTNUM; }

L?'(\\.|[^\\'])+'   |
L?\"(\\.|[^\\"])*\"     { yylval.strval = yytext;   return STRING; }

[a-zA-Z]+[0-9]*         { return IDENTIFIER; }
[a-zA-Z]+               { return LETTER; }
[\n]                    { printf("eol\n"); return EOL; }
[ \t\f\v]               { ; }

.                       { return *yytext; }
%%

我在其他主要功能中使用yyparse()功能。 main.c文件是

#include <stdio.h>
#include <stdlib.h>
#include "ast.h"
#include "y.tab.h"

extern QueryNode *queryNode;

int main(int argc, char *argv[]) {
    int result = yyparse();
    if(result == 0 && queryNode != NULL) {
        printf("AST created\n");
    } else {
        printf("Problem!\n");
    }
    return 0;
}

当我输入CREATE TABLE 'testo';时,yyparse不会在int result = yyparse();行终止并编程等待。我该如何解决?我使用flex和bison。我想用这个输入终止。

1 个答案:

答案 0 :(得分:1)

在这个问题的原始版本中,语法规范中的主要规则是:

输入:SEMICOLON {queryNode = NULL; YYACCEPT; }      |查询SEMICOLON {queryNode = $ 1; YYACCEPT; }      ;

正如我在这个答案的原始版本中所说,这些规则保证一旦遇到分号,yacc就会接受一个后跟分号的查询,因为YYACCEPT动作:< / p>

  

yacc&#34;接受&#34;因为你在动作中使用了YYACCEPTYYACCEPT表示&#34;一旦识别出该产品,即使没有完全消耗,也接受输入。&#34;所以它正在按照你的要求去做。

然后我建议删除YYACCEPT操作,以便解析器不会返回,直到词法分析器发出输入结束信号:

  

如果您只想在整个输入与语法匹配时接受输入,请不要拨打YYACCEPT。如果开始生产匹配且下一个标记是输入结束标记,Yacc将自动接受。

但当然,当遇到换行符时,这并不会导致阅读停止。它所做的就是确保如果整个输入是一个命令,它将被接受,否则它将被拒绝。但是由于它正在检查以确保命令没有任何内容,它将继续请求输入,直到它得到一些。

如果您希望词法分析器只读取必须是有效命令的单行,您可以通过从解析器操作中删除YYACCEPT并让扫描器返回文件结束指示来轻松完成此操作。它看到一个换行符:

\n    { return 0; }

(返回零是扫描仪发出输入结束信号的方式。)

如果您真正想要的是构建一个读取多行输入的程序,独立解析每一行并在每一行之后返回,那么上述解决方案将正常工作。

您也可以在解析器中玩游戏,就像您的新提案一样,但让扫描程序在看到换行符时返回换行符号。然后,您可以在收到换行符号时接受或拒绝输入,使用YYACCEPTYYABORTerror制作:

input: SEMICOLON EOL              { queryNode = NULL; YYACCEPT; }
     | query SEMICOLON EOL        { queryNode = $1;   YYACCEPT; }
     | query error EOL            { YYABORT; }
     ;

在遇到语法错误时,为了刷新行的其余部分,必须生成错误。否则,对解析器的下一次调用将在产生错误的行的中间开始,在一个稍微不可预测的点(因为它将取决于解析器在发出错误信号时是否持有前瞻标记。)

虽然这个解决方案确实有一些优点,但它比读取换行符时只返回0的解决方案要复杂一些。因此很难证明额外的复杂性。

无论如何,这些解决方案都不是真正理想的。在某些时候,您几乎肯定需要处理太长而无法方便地输入一行的输入。

现在您已经包含了完整的扫描程序,我可以看到您将遇到另一个严重问题,因为您不会在将令牌字符串存储到yylval之前复制它。保留令牌的地址(它是扫描仪的内部输入缓冲区的一部分)是不正确的;扫描仪会在没有警告的情况下更改缓冲区(例如,当需要更多输入时)。特别是,一旦扫描器开始处理下一个令牌,它将覆盖它之前用于终止令牌的NUL字节,这将明显影响令牌的字符串变为两个(或更多) )连续的代币。您可以在此站点上找到有关此问题的大量讨论。