I'm studying parsers for a college project. I found out about Boost.Spirit and decided to use it. After reading its documentation and implementing some basic examples, I tried to make a parser that parses a small INI section. The output of the program is:
Program error
<section>
<try>[Section]\nkey1 = val</try>
<key>
<try>Section]\nkey1 = valu</try>
<success>]\nkey1 = value1\nkey2</success>
<attributes>[]</attributes>
</key>
<fail/>
</section>
--------------------
Parsing failed
--------------------
Code
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
using namespace std;
namespace client
{
namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
template <typename Iterator>
struct ini_grammar : qi::grammar<Iterator, ascii::space_type>
{
ini_grammar() : ini_grammar::base_type(section)
{
using qi::char_;
key = +char_("a-zA-Z_0-9");
pair = key >> '=' >> key;
section =
'[' >> key >> ']'
>> '\n'
>> *(pair >> '\n')
;
BOOST_SPIRIT_DEBUG_NODES((key)(pair)(section))
}
qi::rule<Iterator, ascii::space_type> section, pair, key;
};
}
int main()
{
using boost::spirit::qi::phrase_parse;
using boost::spirit::ascii::space;
string ini_section =
"[Section]\n"
"key1 = value1\n"
"key2 = value2\n";
typedef client::ini_grammar<string::const_iterator> ini_grammar;
ini_grammar grammar;
string::const_iterator iter = ini_section.begin();
string::const_iterator end = ini_section.end();
bool r = phrase_parse(iter, end, grammar, space);
if (r == true)
{
cout << "-------------------------\n";
cout << "Parsing succeeded\n";
cout << "-------------------------\n";
}
else
{
cout << "-------------------------\n";
cout << "Parsing failed\n";
cout << "-------------------------\n";
}
return 0;
}
For some context, I read this SO thread. I used the code provided on the question as a reference and try to use some of the hints provided on the answers.
Key should not use a skipper. It is a "lexeme".
Next up,
'\n'can never be matched as your skipper already eats it.See Boost spirit skipper issues
Since you intend blank space to be insignificant in those rules, but NOT newlines, use
qi::blank, notqi::space. Also, matchqi::eolfor more portability (DOS/UNIX line ends). For finishing touches, accept multiple line ends if blank/empty lines are accepted.While you're at it, don't expose the choice of skipper as it's essential to your grammar.
I've also elected to use
qi::space/qi::blankfor consistency withqi::char_.Live On Coliru
Prints
BONUS
With actual attribute propagation into datastructure:
And a better value rule that doesn't disallow spaces and other special characters.
And using a custom skipper to also ignore comments.
Live On Coliru
Prints
Further examples
To have a flavor of INI file that expects quoted strings with optional escape characters instead of just accepting "bare strings" like the above: