Решил наконец изучить flex & bison. Изучил, понравилось. Начал прикручивать парсер к C++ проекту, полез глубже копаться (начал пока с голого лексера), и охренел.
Во-первых, код ни разу не exception-safe. Как вам это (сгенерённый код):
YY_BUFFER_STATE yyFlexLexer::yy_create_buffer( std::istream* file, int size )
{
YY_BUFFER_STATE b;
b = (YY_BUFFER_STATE) yyalloc(sizeof( struct yy_buffer_state ) );
if ( ! b )
YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
b->yy_buf_size = size;
/* yy_ch_buf has to be 2 characters longer than the size given because
* we need to put in 2 end-of-buffer characters.
*/
b->yy_ch_buf = (char *) yyalloc(b->yy_buf_size + 2 );
if ( ! b->yy_ch_buf )
YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
b->yy_is_our_buffer = 1;
yy_init_buffer( b, file );
return b;
}
Если лень читать - при фейле alloc вызывается YY_FATAL_ERROR. Что прикажете мне в ней делать? Если просто вывести ошибку и сделать вид что ничего не случилос (ну или установить флаг и выкинуть результаты кривого парсинга), то после фейла первого аллока при обращении к b->yy_ch_buf будет SEGV. А если кинуть исключение, при фейле второго alloc'а память, выделенная в первом весело утекает. Получается, что делать остаётся только abort, весело.
Во-вторых, C++'ный лексер наследуется от помоечного класса yyFlexParser, набитого кучей ненужной гадости типа работы с потоками, которые мне нахрен не сделись, вместо того чтобы наследоваться от виртуального интерфейса и дать мне возможность самому определить как работать с вводом/выводом, как выделять память и как обрабатывать ошибки. На самом деле, я пришёл к выводу что из C++ удобнее будет использовать сишный reentrant лексер, а не плюсовый, но невозможность обработки ошибок остаётся, и мусор там свой есть.
В bison глубоко не копал, но сдаётся мне там будут свои проблемы с памятью и исключениями.
В общем, хочется инструмента из этого века - удобного, гибкого и безопасного. В идеале - сочетающего лексер, парсер и кнопку «сделать заебись», но хотя бы по минимуму - нормальный лексер, который не генерит говнокод, позволяет управлять буферами как я хочу и не суёт в нос работу с FILE* или ++'ными потоками. В качестве лексера на ум приходит только ragel, но может есть лучшее и/или комплексное решение?