Обработка ошибок с Flex (lex) и Bison (yacc) - PullRequest
2 голосов
/ 16 сентября 2009

Из Руководства Бизона:

В простом интерактивном парсере команд где каждый вход - одна строка, это может быть достаточным, чтобы позволить yyparse верните 1 при ошибке и вызовите игнорировать остальную часть строки ввода, когда что происходит (а затем вызвать yyparse опять же).

Это в значительной степени то, что я хочу, но у меня проблемы с получением работы. По сути, я хочу обнаружить ошибку в flex, и если обнаружена ошибка, пусть Bison отбрасывает всю строку. То, что у меня есть сейчас, работает не совсем правильно, потому что мои команды все еще выполняются:

kbsh: ls '/home
Error: Unterminated Single Quote
admin  kbrandt  tempuser
syntax error
kbsh: 

В моем файле Bison:

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);}

1 Ответ

6 голосов
/ 17 сентября 2009

Я не думаю, что вы найдете простое решение для обработки этих типов ошибок синтаксического анализа в лексере.

Я бы хотел, чтобы лексер (flex / lex) был настолько тупым, насколько это возможно, он должен просто предоставлять поток базовых токенов (идентификаторы, ключевые слова и т. Д.) И иметь анализатор (yacc / bison) для обнаружения ошибок. , На самом деле это именно то, что вы хотите, с небольшой перестройкой вашего подхода ...

В лексере (parser.l) сделайте его простым (без обработки eol / newline), что-то вроде (не полная вещь):

}%

/* 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;

Затем в вашем файле parser.y выполните всю настоящую обработку (не полная вещь):

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 ...

Здесь следует отметить две вещи:

  1. Смещение таких вещей, как NEWLINE, в сторону yacc, чтобы вы могли определить, когда пользователь завершил работу с командой, затем вы можете очистить вещи и начать все сначала (при условии, что у вас где-то есть «int yywrap() {return 1;}»). Если вы попытаетесь обнаружить его слишком рано во флексе, когда вы узнаете, чтобы вызвать ошибку?
  2. chdir - это не одна команда (если она не была подчинена правилам, а вы ее просто не показывали), теперь она имеет chdir_command STRING (аргумент для chdir). Это делает так, что парсер может выяснить, что пошло не так, вы можете затем yyerror, если этот каталог не существует, и т. Д ...

Таким образом, вы должны получить что-то вроде (угадать, как может выглядеть chdir):

cd 'some_directory
синтаксическая ошибка
cd 'some_directory'
ты в чуваке some_directory!

И все это обрабатывается грамматикой yacc, а не токенизатором.

Я обнаружил, что максимально простое изгибание дает вам наибольшую гибкость. :)

...