Потреблять переменное количество гибкого ввода, основанного на грамматике - PullRequest
0 голосов
/ 06 ноября 2018

Я пытаюсь работать с протоколом SMTP (плюс некоторые расширения) с помощью flex / bison. Расширение BDAT представляет особый интерес. Вот как это работает:

Client: BDAT 100 <CRLF>
Client: <100 bytes of binary data>
Client: BDAT 256 <CRLF>
Client: <256 bytes of binary data>
Server: 250 OK
Server: 250 OK

Проблема, с которой я сталкиваюсь, заключается в том, что flex управляет потреблением входного потока в токены, но bison контролирует грамматику и поэтому знает, когда была введена действительная команда BDAT.

В flex:

(?i:BDAT[ ]) { return BDAT;   }
[0-9]+[ ]    { yylval->number = ...;
               return NUMBER: }
"\r\n"       { return CRLF;   }

В зубре:

bdat_command: BDAT NUMBER CRLF
            | BDAT NUMBER LAST CRLF

То, что я думал, я бы сделал, чтобы в Bison было действие, которое потребляло дополнительный ввод на основе указанного размера буфера:

bdat_command: BDAT NUMBER CRLF      { uint8_t * data = input($2); }
            | BDAT NUMBER LAST CRLF { uint8_t * data = input($2); }

Где input будет некоторой гипотетической функцией, потребляющей N байтов ввода из буфера flex. У Flex есть и input функция , но она не экспортируется в заголовок Flex (а также читает по одному байту за раз).

Я мог бы использовать дополнительный ввод целиком внутри лексера, но меня беспокоит, что будут последовательности ввода, для которых лексер будет неправильно потреблять байты, потому что он не понимает грамматику. Например, со следующими правилами лексера:

(?i:BDAT[ ])       { BEGIN SC_BDAT;
                     return BDAT;   }
<SC_BDAT>[0-9]+[ ] { yylval->number = ...;
                     return NUMBER: }
<SC_BDAT>"\r\n"    { yylval->data = ...;
                     BEGIN INITIAL;
                     return CRLF;   }

Лексер некорректно вводит последовательность ввода, как показано ниже:

Client: BDAT 100 100 <CRLF>
Client: RSET <CRLF>
Server: 501 Bad command syntax
Server: 250 Ok

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

Конечно, есть способ решить эту проблему - добавив дополнительные параметры в yylex и yypush_parse, чтобы открыть обратный канал между лексером и анализатором через вызывающую программу. Но, похоже, такой тупой инструмент для решения сценария использования, который выглядит так, как будто бы почти попадает в сферу компетенции flex / bison, предназначен для этого.

...