Tratamento de erros com Flex (lex) e Bison (yacc)
-
07-07-2019 - |
Pergunta
A partir do Bison Manual:
Em um analisador de comando interativo simples onde cada entrada é uma linha, que pode ser suficiente para permitir a yyparse retornar 1 em erro e que o chamador ignorar o resto da linha de entrada quando que acontece (e, em seguida, yyparse chamada de novo).
Isso é muito bonito o que eu quero, mas estou tendo problemas para chegar ao trabalho. Basicamente, eu quero detectar e erro em flex, e se for detectado um erro, tem Bison descartar a linha inteira. O que eu tenho agora, não está funcionando muito bem porque os meus comandos ainda são executadas:
kbsh: ls '/home
Error: Unterminated Single Quote
admin kbrandt tempuser
syntax error
kbsh:
No meu arquivo Vega:
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); }
;
E no meu Flex:
' {BEGIN inQuote; }
<inQuote>\n {printf("Error: Unterminated Single Quote\n"); BEGIN(0); return(ERROR);}
Solução
Eu não acho que você vai encontrar uma solução simples para lidar com esses tipos de erros de análise no lexer.
Gostaria de manter o lexer (flex / lex) tão idiota quanto possível, ele deve apenas fornecer um fluxo de símbolos básicos (identificadores, palavras-chave, etc ...) e têm o analisador (yacc / bison) fazer a detecção de erros . Na verdade, é a configuração para exatamente o que você quer, com um pouco de reestruturação da sua abordagem ...
No lexer (parser.l), mantê-lo simples (sem eol / manipulação de nova linha), algo como (não é coisa completa):
}%
/* 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;
Em seguida, em seu arquivo parser.y fazer todo o tratamento real (não é coisa completa):
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 ...
Há duas coisas a nota aqui:
- A mudança de coisas como NEWLINE para o lado do yacc para que você possa determinar quando o usuário é feito com o comando, em seguida, você pode limpar as coisas e começar de novo (supondo que você tem "
int yywrap() {return 1;}
" em algum lugar). Se você tentar detectá-lo cedo demais em flex, quando você sabe para gerar um erro? - chdir não é um comando (a menos que foi sub governado e você simplesmente não mostrá-lo), ele agora tem STRING chdir_command (o argumento para o chdir). Isso faz com que o analisador pode descobrir o que deu errado, você pode então yyerror se esse diretório não existe, etc ...
Desta forma, você deve obter algo como (supondo que chdir pode parecer):
cd 'some_directory
erro de sintaxe
cd 'some_directory'
você está na cara some_directory!
E tudo é manipulado pela gramática yacc, não pelo tokenizer.
Eu descobri que manter flexível o mais simples possível dá-lhe a mais *** *** Flex lidade. :)