23 июня 2009

Fast parsing

Чем вы парсите всякую "мелочёвку" типа целочисленных значений и прочее? Самописными парсерами с приправами вроде boost::tokenizer, boost::lexical_cast или даже atoi, etc.? Я обычно использовал boost::regex + lexical_cast. А тут недавно посмотрел презентацию Spirit-а с BoostCon-а, даже немного проникся. Раньше как-то я к Спириту более равнодушен был.

Описывать грамматику на C++ и потом давать компилятору [несколько минут] это компилировать - это, конечно, большой изврат. Но весьма забавно. И, говорят, работает потом очень быстро. Даже если нужно просто число распарсить - говорят быстрее всех парсит.
std::string buffer("1234");
int value = 0;

using namespace boost::spirit;
bool r = qi::parse(buffer.begin(), buffer.end(), int_, value);
assert(r && value == 1234);

10 комментариев:

Alex Ott комментирует...

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

Raider комментирует...

А компилируется быстро? ;)

Alex Ott комментирует...

мои грамматики для языков - до минут на gcc 4.3 и минут 10-15 на gcc 3.4

Raider комментирует...

Минута - в принципе более-менее приемлемо для цикла "подправил грамматику - скомпилировал - посмотрел результат - снова подправил ...".

Alex Ott комментирует...

в принципе, да, я так и делал, когда грамматики отлаживал...

Анонимный комментирует...

Использовал и boost::tokenizer, и boost::regex, и вручную писал, но остановился на spirit'е. Работает быстро даже на достаточно сложных выражениях, некоторое время потребовалось на изучение, но оно того стоит. Очень гибкая вещь. А вот lexical_cast не использую, действительно, не самая быстрая штука.

Анонимный комментирует...

Задам вопрос здесь. Уже сталкивались с проблемой использования неинициализированных range. Как боритесь? Избегаете или отключаете checked iterators ?

Raider комментирует...

> ... с проблемой использования неинициализированных range

Есть boost::optional если не хочется что-либо инициализировать. Или, если нужен пустой range - итераторы можно взять у пустого static контейнера, специально для этого объявленного прямо в том месте, где нужно возвращать пустой range.

Анонимный комментирует...

Spirit 2 пока использовать не советую - сыроват и документации почти нет. Лучше classic, хотя есть некоторые различия между этими версиями.

Анонимный комментирует...

я вот читал у Саттера про перевод int в строку, рассматриваются подходы, в том числе и lexical_cast (причем его производительность по тем тестам на порядок хуже sprintf, и даже проигрывает std-шному stringstream'у).
Но что то я засомневался, что текущее положение дел не поменялось.
Протестировал версию 1,38 на mingw, vs2008, intel 10, С++Builder 6 - во всех случаях производительность была выше чем у stringstream, а отставание от sprintf значительно сократилось.
Часто встречал плохие отзывы касаемо производительности lexical_cast - но видимо зачастую это связано именно с заметкой Саттера (которая как оказалось несколько устарела).
Для себя пришел к выводу что для одиночных вызовов - это одно из самых удобных средств.