使用Flex(lex)和Bison(yacc)处理错误

使用Flex(lex)和Bison(yacc)处理错误,c,bison,yacc,lex,flex-lexer,C,Bison,Yacc,Lex,Flex Lexer,从野牛手册: 在一个简单的交互式命令解析器中 如果每个输入为一行,则可能 足以允许yyparse 错误时返回1,并让调用者 在输入时忽略输入行的其余部分 发生这种情况(然后调用yyparse) 再次) 这正是我想要的,但我上班有困难。基本上,我想在flex中检测并纠正错误,如果检测到错误,让Bison丢弃整行代码。我现在所做的工作不太正常,因为我的命令仍在执行: kbsh: ls '/home Error: Unterminated Single Quote admin kbrandt te

从野牛手册:

在一个简单的交互式命令解析器中 如果每个输入为一行,则可能 足以允许yyparse 错误时返回1,并让调用者 在输入时忽略输入行的其余部分 发生这种情况(然后调用yyparse) 再次)

这正是我想要的,但我上班有困难。基本上,我想在flex中检测并纠正错误,如果检测到错误,让Bison丢弃整行代码。我现在所做的工作不太正常,因为我的命令仍在执行:

kbsh: ls '/home
Error: Unterminated Single Quote
admin  kbrandt  tempuser
syntax error
kbsh: 
在我的野牛档案中:

commands:
     /*Empty*/ { prompt(); } |
     command { prompt(); }
    ;

command:
    error {return 1; } |
    chdir_command |
    pwd_command |
    exit_command |
    WORD arg_list {
        execute_command($1, $2);
        //printf("%s, %s\n", $1, $2); 
    } |
    WORD { execute_command($1, NULL); }
    ;
在我的Flex中:

'   {BEGIN inQuote; }

<inQuote>\n {printf("Error: Unterminated Single Quote\n"); BEGIN(0); return(ERROR);}
“{BEGIN inQuote;}
\n{printf(“错误:未终止的单引号\n”);开始(0);返回(错误);}

我认为您无法在lexer中找到处理这些类型解析错误的简单解决方案

我希望lexer(flex/lex)尽可能保持沉默,它应该只提供一个基本令牌流(标识符、关键字等),并让解析器(yacc/bison)进行错误检测。事实上,它是为你想要的东西而设置的,只是对你的方法进行了一些调整

在lexer(parser.l)中,保持简单(没有eol/换行处理),类似(不是完整的东西):

然后在parser.y文件中执行所有真正的处理(不是全部):

命令:
错误换行符
{yyclearin;yyerrorok;打印_next_命令_prompt()}
|chdir_命令字符串换行符
{do_the_chdir($2);print_the_next_command_prompt();}
| ... 等等
这里有两件事需要注意:

  • 将诸如换行符之类的内容转移到yacc端,以便您可以确定用户何时完成了命令,然后您可以清除并重新开始(假设您有“
    int yywrap(){return 1;}
    ”)。如果您试图在flex中过早地检测到它,您知道何时会引发错误

  • chdir不是一个命令(除非它是子规则,而您只是没有显示它),它现在有chdir_命令字符串(chdir的参数)。这使得解析器能够找出哪里出了问题,如果该目录不存在,您就可以发现错误,等等
  • 通过这种方式,您应该得到如下结果(猜测chdir可能是什么样子):

    cd'some_目录
    语法错误
    cd“某些目录”
    你在某个目录里,伙计

    所有这些都是由yacc语法器处理的,而不是由标记器处理的


    我发现让flex尽可能简单能让你获得最大的灵活性

    因为我写了一个过于复杂的lexer,所以我经常发现自己脚上有一颗子弹。非常感谢蒂姆,当我有机会的时候,我会尝试将这个应用到我的项目中。我刚刚开始学习这方面的知识,所以假设你的建议是保持flex简单,这是一个很好的建议,这个答案很好!“除非它是子规则的,而你只是没有显示”——是的,它是子规则的,我只是没有显示它……所以在这个例子中,copy_to_tmp_缓冲区会去掉引号字符吗?你可以肯定地去掉copy_to_tmp_缓冲区中的引号。或者编写一个类似于copy_to_tmp_buffer的包装(去掉_引号(yytext))。只要您能在lexer中正确地隔离标识符,您就可以想象将引号解析移到yacc端,就像“chdir\u命令'\''标识符'\''换行符”一样。
    }%
    
    /* I don't recall if the backslashify is required below */
    SINGLE_QUOTE_STRING \'.*\'
    DOUBLE_QUOTE_STRING \".*\"
    
    %%
    {SINGLE_QUOTE_STRING} {
        yylval.charstr = copy_to_tmp_buffer(yytext);  // implies a %union
        return STRING;
    }
    {DOUBLE_QUOTE_STRING} {
        yylval.charstr = copy_to_tmp_buffer(yytext);  // implies a %union
        return STRING;
    }
    \n   return NEWLINE;
    
    command:
        error NEWLINE
            { yyclearin; yyerrorok; print_the_next_command_prompt(); }
        | chdir_command STRING NEWLINE
            { do_the_chdir($<charstr>2); print_the_next_command_prompt(); }
        | ... and so on ...