Boost.Spirit 2
Mar. 13th, 2010 09:15 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Довгими холодними зимовими вечорами займаюсь однією "шабашкою". І як я уже для себе завів, беру від шабашки не тільки гроші а і новий досвід. Цього разу вирішив зайняти себе вивченням Boost. А точніше Boost.Asio і Boost.Spirit.
Якщо з першою все зрозуміло, то з другою я маявся 2 тижні. Спочатку отримав загальні відомості вивчаючи tutorial, потім взяв готовий код ini-парсера осьо звідси і прикрутив його до проекту (як не педагогічно!). Все працює, все добре... тільки код для першої версії, а вивчаю я вже другу!
"Добре!", подумав я, "Візьму і перероблю код для Spirit 2. Як раз і навчусь чогось". І почалось! Мучав я його цілий тиждень. І так його крутив, і інакше. То з одного боку, то з іншого... Компілюється, зараза, по хвилині. Викидає полотна повідомлень про помилки, які навіть при детальному розборі нічого мені не кажуть. Намучився я з ним, плюнув, і написав парсер "з нуля". Треба було бачити мій подив коли він просто взяв... і запрацював! Без жодної помилки компіляції! Мабуть таки не дарма я стільки мучався :)
Враження від Spirit 2:
Якщо комусь знадобиться - наводжу код парсера (граматика, мабуть, жахлива):
namespace qi = boost::spirit::qi;
typedef std::map<std::string, std::string> PairsType;
typedef std::map<std::string, PairsType> SectionsType;
template <typename Iterator>
struct IniGrammar
: qi::grammar<Iterator, SectionsType()>
{
IniGrammar()
: IniGrammar::base_type(query)
{
query = +(section | (comment >> eol) | (space >> eol));
section = sectionHeader >> -sectionContent;
sectionHeader = '[' >> space >> key >> space >> ']' >> eol;
sectionContent = +((line | comment | space) >> eol);
comment = (qi::char_(';') | '#') >> *qi::print;
line = space >> key >> space >> -('=' >> space >> value >> space);
key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
value = +qi::print;
space = *qi::char_("\t ");
eol = qi::lit("\r\n") | '\r' | '\n';
}
qi::rule<Iterator, SectionsType()> query;
qi::rule<Iterator, std::pair<std::string, PairsType>()> section;
qi::rule<Iterator, PairsType()> sectionContent;
qi::rule<Iterator, std::pair<std::string, std::string>()> line;
qi::rule<Iterator, std::string()> key, value, sectionHeader;
qi::rule<Iterator> comment, space, eol;
};
Вживати для розбору ini-файлів, за смаком перевіривши розбір на повноту порівнянням ітераторів після розбору (ох і накаламбурив):
std::string::iterator begin = text.begin();
std::string::iterator end = text.end();
SectionsType data;
IniGrammar<std::string::iterator> parser;
bool result = qi::parse(begin, end, parser, data);
Якщо з першою все зрозуміло, то з другою я маявся 2 тижні. Спочатку отримав загальні відомості вивчаючи tutorial, потім взяв готовий код ini-парсера осьо звідси і прикрутив його до проекту (як не педагогічно!). Все працює, все добре... тільки код для першої версії, а вивчаю я вже другу!
"Добре!", подумав я, "Візьму і перероблю код для Spirit 2. Як раз і навчусь чогось". І почалось! Мучав я його цілий тиждень. І так його крутив, і інакше. То з одного боку, то з іншого... Компілюється, зараза, по хвилині. Викидає полотна повідомлень про помилки, які навіть при детальному розборі нічого мені не кажуть. Намучився я з ним, плюнув, і написав парсер "з нуля". Треба було бачити мій подив коли він просто взяв... і запрацював! Без жодної помилки компіляції! Мабуть таки не дарма я стільки мучався :)
Враження від Spirit 2:
- дуже-дуже-дуже повільно компілюється навіть найпростіший код;
- помилки важкувато виявити, але і допустити, маючи досвід, майже неможливо;
- для стороннього погляду замість коду - каша (правда потім починаєш бачити у ньому нормальну БНФ);
- вражаючий приклад потужності мови (особливо з Boost.Phoenix і Boost.Fusion);
- швидкість розробки і швидкість роботи;
Якщо комусь знадобиться - наводжу код парсера (граматика, мабуть, жахлива):
namespace qi = boost::spirit::qi;
typedef std::map<std::string, std::string> PairsType;
typedef std::map<std::string, PairsType> SectionsType;
template <typename Iterator>
struct IniGrammar
: qi::grammar<Iterator, SectionsType()>
{
IniGrammar()
: IniGrammar::base_type(query)
{
query = +(section | (comment >> eol) | (space >> eol));
section = sectionHeader >> -sectionContent;
sectionHeader = '[' >> space >> key >> space >> ']' >> eol;
sectionContent = +((line | comment | space) >> eol);
comment = (qi::char_(';') | '#') >> *qi::print;
line = space >> key >> space >> -('=' >> space >> value >> space);
key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
value = +qi::print;
space = *qi::char_("\t ");
eol = qi::lit("\r\n") | '\r' | '\n';
}
qi::rule<Iterator, SectionsType()> query;
qi::rule<Iterator, std::pair<std::string, PairsType>()> section;
qi::rule<Iterator, PairsType()> sectionContent;
qi::rule<Iterator, std::pair<std::string, std::string>()> line;
qi::rule<Iterator, std::string()> key, value, sectionHeader;
qi::rule<Iterator> comment, space, eol;
};
Вживати для розбору ini-файлів, за смаком перевіривши розбір на повноту порівнянням ітераторів після розбору (ох і накаламбурив):
std::string::iterator begin = text.begin();
std::string::iterator end = text.end();
SectionsType data;
IniGrammar<std::string::iterator> parser;
bool result = qi::parse(begin, end, parser, data);