This is a very abbreviated case of what I am trying to do in the best way. (Of course, the question is also that I'm trying to figure out how to best use the spirit.)
I need to analyze data in a multi-member structure. Members are simply listed as key-value pairs, so this is straightforward, however, if some of them are different from each other, then the values that I process may show other values later or some keys may be omitted. Nevertheless, the data structure that I ultimately analyze has a fixed form.
The sample code my_struct
is struct
as follows:
struct my_struct {
std::string a;
std::string b;
std::string c;
std::string d;
};
and grammar1
- this is a grammar that analyzes strings such as
"a: x b: y c: z d: w"
,
my_struct{ "x", "y", "z", "w" }
:
"a: x b: y d-no-c: w"
,
my_struct{ "x", "y", "", "w" }
, .
, , "a" "b", "c" . , , . std::pair<std::string, std::string>
, fusion::vector
, , -, qi
<<
. (grammar2
, .)
, , my_struct
, , . , - , , a
b
. , , , ? ( grammar3
).
grammar3
, , , , ( ), , a
b
, . my_struct
, grammar4
, , grammar2
, , std::pair
. , .
, :
grammar4
, - grammar2
, ?grammar3
?
:
#define SPIRIT_USE_PHOENIX_V3
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/adapted/struct/define_struct.hpp>
#include <boost/fusion/include/define_struct.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <iostream>
#include <string>
#include <utility>
namespace qi = boost::spirit::qi;
BOOST_FUSION_DEFINE_STRUCT(
,
my_struct,
(std::string, a)
(std::string, b)
(std::string, c)
(std::string, d))
template<typename Iterator>
class grammar1 : public qi::grammar<Iterator, my_struct()> {
public:
qi::rule<Iterator, std::string()> id;
qi::rule<Iterator, my_struct()> main;
grammar1() : grammar1::base_type(main) {
using qi::lit;
using qi::char_;
using qi::omit;
using qi::space;
id = omit[ *space ] >> *char_("A-Za-z_") >> omit [ *space ];
main = lit("a:") >> id >> lit("b:") >> id >> lit("c:") >> id >> lit("d:") >> id;
}
};
typedef boost::fusion::vector<std::string, std::string> second_part_type;
template<typename Iterator>
class grammar2 : public qi::grammar<Iterator, my_struct()> {
public:
qi::rule<Iterator, std::string()> id;
qi::rule<Iterator, second_part_type()> with_c;
qi::rule<Iterator, second_part_type()> without_c;
qi::rule<Iterator, my_struct()> main;
grammar2() : grammar2::base_type(main) {
using qi::lit;
using qi::char_;
using qi::omit;
using qi::space;
using qi::attr;
id = omit[ *space ] >> *char_("A-Za-z_") >> omit [ *space ];
with_c = lit("c:") >> id >> lit("d:") >> id;
without_c = attr("") >> lit("d-no-c:") >> id;
main = lit("a:") >> id >> lit("b:") >> id >> (with_c | without_c);
}
};
template<typename Iterator>
class grammar3 : public qi::grammar<Iterator, my_struct()> {
public:
qi::rule<Iterator, std::string()> id;
qi::rule<Iterator, my_struct()> with_c;
qi::rule<Iterator, my_struct()> without_c;
qi::rule<Iterator, my_struct()> main;
grammar3() : grammar3::base_type(main) {
using qi::lit;
using qi::char_;
using qi::omit;
using qi::space;
using qi::attr;
id = omit[ *space ] >> *char_("A-Za-z_") >> omit [ *space ];
with_c = lit("a:") >> id >> lit("b:") >> id >> lit("c:") >> id >> lit("d:") >> id;
without_c = lit("a:") >> id >> lit("b:") >> id >> attr("") >> lit("d-no-c:") >> id;
main = with_c | without_c;
}
};
typedef std::pair<std::string, std::string> spair;
BOOST_FUSION_DEFINE_STRUCT(
,
my_struct2,
(std::string, a)
(std::string, b)
(spair, cd))
template<typename Iterator>
class grammar4 : public qi::grammar<Iterator, my_struct2()> {
public:
qi::rule<Iterator, std::string()> id;
qi::rule<Iterator, spair()> with_c;
qi::rule<Iterator, spair()> without_c;
qi::rule<Iterator, my_struct2()> main;
grammar4() : grammar4::base_type(main) {
using qi::lit;
using qi::char_;
using qi::omit;
using qi::space;
using qi::attr;
id = omit[ *space ] >> *char_("A-Za-z_") >> omit [ *space ];
with_c = lit("c:") >> id >> lit("d:") >> id;
without_c = attr("") >> lit("d-no-c:") >> id;
main = lit("a:") >> id >> lit("b:") >> id >> (with_c | without_c);
}
};
my_struct convert_struct(my_struct2 && s) {
return { std::move(s.a), std::move(s.b), std::move(s.cd.first), std::move(s.cd.second) };
}
void check_strings_eq(const std::string & a, const std::string & b, const char * label, int line = 0) {
if (a != b) {
std::cerr << "Mismatch '" << label << "' ";
if (line) { std::cerr << "at line " << line << " "; }
std::cerr << "\"" << a << "\" != \"" << b << "\"\n";
}
}
void check_eq(const my_struct & s, const my_struct & t, int line = 0) {
check_strings_eq(s.a, t.a, "a", line);
check_strings_eq(s.b, t.b, "b", line);
check_strings_eq(s.c, t.c, "c", line);
check_strings_eq(s.d, t.d, "d", line);
}
template<template<typename> class Grammar>
void test_grammar(const std::string & input, const my_struct & expected, int line = 0) {
auto it = input.begin();
auto end = input.end();
Grammar<decltype(it)> grammar;
my_struct result;
if (!qi::parse(it, end, grammar, result)) {
std::cerr << "Failed to parse! ";
if (line) { std::cerr << "line = " << line; }
std::cerr << "\n";
std::cerr << "Stopped at:\n" << input << "\n";
for (auto temp = input.begin(); temp != it; ++temp) { std::cerr << " "; }
std::cerr << "^\n";
} else {
check_eq(result, expected, line);
}
}
int main() {
test_grammar<grammar1> ( "a: x b: y c: z d: w", my_struct{ "x", "y", "z", "w" }, __LINE__);
test_grammar<grammar1> ( "a: asdf b: jkl c: foo d: bar", my_struct{ "asdf", "jkl", "foo", "bar" }, __LINE__ );
test_grammar<grammar3> ( "a: asdf b: jkl c: foo d: bar", my_struct{ "asdf", "jkl", "foo", "bar" }, __LINE__);
test_grammar<grammar3> ( "a: asdf b: jkl d-no-c: bar", my_struct{ "asdf", "jkl", "", "bar" }, __LINE__ );
{
std::string input = "a: asdf b: jkl c: foo d: bar";
auto it = input.begin();
auto end = input.end();
grammar4<decltype(it)> grammar;
my_struct2 result;
if (!qi::parse(it, end, grammar, result)) {
std::cerr << "Failed to parse! Line = " << __LINE__ << std::endl;
} else {
check_eq(convert_struct(std::move(result)), my_struct{ "asdf", "jkl", "foo", "bar" }, __LINE__);
}
}
{
std::string input = "a: asdf b: jkl d-no-c: bar";
auto it = input.begin();
auto end = input.end();
grammar4<decltype(it)> grammar;
my_struct2 result;
if (!qi::parse(it, end, grammar, result)) {
std::cerr << "Failed to parse! Line = " << __LINE__ << std::endl;
} else {
check_eq(convert_struct(std::move(result)), my_struct{ "asdf", "jkl", "", "bar" }, __LINE__);
}
}
}