I am parsing text into an AST via qi and generates text again via karma. This is working as expected, but wants some method to pass on an attribute from one rule to another.
Ported from the comments:
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/adapt_adt.hpp>
#include <fstream>
struct c_struct
{
int value1;
};
struct b_struct
{
std::string value1;
std::vector< c_struct > value2;
};
struct a_struct
{
std::string value1;
std::vector< b_struct > value2;
};
BOOST_FUSION_ADAPT_STRUCT( c_struct,
(int, value1)
)
BOOST_FUSION_ADAPT_STRUCT( b_struct,
(std::string, value1)
(std::vector< c_struct >, value2)
)
BOOST_FUSION_ADAPT_STRUCT( a_struct,
(std::string, value1)
(std::vector< b_struct >, value2)
)
using namespace boost::spirit;
using namespace boost::spirit::qi;
using namespace boost::spirit::karma;
using namespace boost::spirit::ascii;
template <typename Iterator>
struct grammarB : karma::grammar<Iterator, a_struct()>
{
grammarB() : grammarB::base_type(ruleA)
{
ruleA %= karma::string << karma::lit(' ') << +ruleB << eps;
ruleB %= karma::string << +ruleC << karma::lit(' ') << eps;
ruleC %= lit(" -> ") << karma::int_;
}
karma::rule<Iterator, a_struct()> ruleA;
karma::rule<Iterator, b_struct()> ruleB;
karma::rule<Iterator, c_struct()> ruleC;
};
template <typename Iterator>
struct grammarA : qi::grammar<Iterator, a_struct(), boost::spirit::ascii::space_type>
{
grammarA() : grammarA::base_type(ruleA)
{
ruleA %= ruleString >> omit[+qi::char_('.')] >> +ruleB;
ruleB %= ruleString >> omit[qi::char_(',')] >> (ruleC % qi::char_(',')) >> omit[qi::char_(';')];
ruleC %= qi::int_;
ruleString %= +qi::char_("a-z");
}
qi::rule<Iterator, a_struct(), boost::spirit::ascii::space_type> ruleA;
qi::rule<Iterator, b_struct(), boost::spirit::ascii::space_type> ruleB;
qi::rule<Iterator, c_struct(), boost::spirit::ascii::space_type> ruleC;
qi::rule<Iterator, std::string(), boost::spirit::ascii::space_type> ruleString;
};
int main(int argc, char **argv)
{
std::string storage("parent ... whee,4,5,6;ahhhh,5,6;"); // We will read the contents here.
typedef grammarA<std::string::const_iterator> grammarA_t;
grammarA_t grammar;
a_struct ast;
std::string::const_iterator iter = storage.begin();
std::string::const_iterator end = storage.end();
bool r = phrase_parse(iter, end, grammar, boost::spirit::ascii::space, ast);
if (r && iter == end)
{
std::cout << "Parsing succeeded" << std::endl;
typedef std::back_insert_iterator<std::string> output_iterator_type;
std::string generated;
output_iterator_type sink(generated);
typedef grammarB<output_iterator_type> grammarB_t;
grammarB_t generator;
if ( generate(sink, generator, ast) )
std::cout << generated << std::endl;
else
std::cout << "fail" << std::endl;
}
return 0;
}
It prints
Parsing succeeded
parent whee -> 4 -> 5 -> 6 ahhhh -> 5 -> 6
But I'd prefer it to print
Parsing succeeded
parent parent whee -> parent 4 -> parent 5 -> parent 6 ahhhh -> parent 5 -> parent 6
Is this possible, and how?
Okay. The sample of output "parent parent whee -> parent 4 -> parent 5 -> parent 6 ahhhh -> parent 5 -> parent 6"
actually made things /almost/ clear.
I think there's an inconsistency, since either you would expect
parent parent whee -> parent 4 -> parent 5 -> parent 6 parent ahhhh -> parent 5 -> parent 6
or you would expect
parent whee -> parent 4 -> parent 5 -> parent 6 ahhhh -> parent 5 -> parent 6
I'll show you both.
ruleA %= string [ _pass_along = _1 ] << +ruleB(_pass_along);
ruleB = string << +ruleC(_inherited);
//ruleB = lit(_inherited) << string << +ruleC(_inherited); // alternative interpretation
ruleC = "->" << lit(_inherited) << karma::int_;
This uses both karma::locals<>
and inherited attributes.
Notes:
lexeme
where appropriate in the Qi rules (Boost spirit skipper issues)lit("x")
instead of string("x")
if you don't want to synthesize an attributelit()
unless non-spirit domain expression template operator overloads would be selectedBOOST_SPIRIT_DEBUG_NODES
using namespace
s. There's no need and it will lead to trouble.//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted.hpp>
#include <iostream>
#include <fstream>
struct c_struct {
int value1;
};
struct b_struct {
std::string value1;
std::vector<c_struct> value2;
};
struct a_struct {
std::string value1;
std::vector<b_struct> value2;
};
BOOST_FUSION_ADAPT_STRUCT(c_struct, (int, value1))
BOOST_FUSION_ADAPT_STRUCT(b_struct, (std::string, value1)(std::vector<c_struct>, value2))
BOOST_FUSION_ADAPT_STRUCT(a_struct, (std::string, value1)(std::vector<b_struct>, value2))
namespace qi = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace ascii = boost::spirit::ascii;
template <typename Iterator> struct generator : karma::grammar<Iterator, a_struct(), karma::space_type> {
generator() : generator::base_type(start) {
using namespace karma;
_a_type _pass_along;
_r1_type _inherited;
start = ruleA;
ruleA %= string [ _pass_along = _1 ] << +ruleB(_pass_along);
ruleB = string << +ruleC(_inherited);
//ruleB = lit(_inherited) << string << +ruleC(_inherited); // alternative interpretation
ruleC = "->" << lit(_inherited) << karma::int_;
BOOST_SPIRIT_DEBUG_NODES((start)(ruleA)(ruleB)(ruleC))
}
karma::rule<Iterator, a_struct(), karma::space_type> start;
karma::rule<Iterator, a_struct(), qi::locals<std::string>, karma::space_type> ruleA;
karma::rule<Iterator, b_struct(std::string const&), karma::space_type> ruleB;
karma::rule<Iterator, c_struct(std::string const&), karma::space_type> ruleC;
};
template <typename Iterator> struct grammar : qi::grammar<Iterator, a_struct(), boost::spirit::ascii::space_type> {
grammar() : grammar::base_type(ruleA) {
using namespace qi;
ruleA = ruleString >> lexeme[+qi::lit('.')] >> +ruleB;
ruleB = ruleString >> ',' >> (ruleC % ',') >> ';';
ruleC = qi::int_;
ruleString = +qi::char_("a-z");
BOOST_SPIRIT_DEBUG_NODES((ruleA)(ruleB)(ruleC)(ruleString))
}
qi::rule<Iterator, a_struct(), boost::spirit::ascii::space_type> ruleA;
qi::rule<Iterator, b_struct(), boost::spirit::ascii::space_type> ruleB;
qi::rule<Iterator, c_struct(), boost::spirit::ascii::space_type> ruleC;
qi::rule<Iterator, std::string()/*, boost::spirit::ascii::space_type*/> ruleString;
};
int main() {
typedef std::string::const_iterator It;
std::string const storage("parent ... whee,4,5,6;ahhhh,5,6;"); // We will read the contents here.
grammar<It> grammar;
a_struct ast;
It iter = storage.begin(), end = storage.end();
bool r = phrase_parse(iter, end, grammar, ascii::space, ast);
if (r && iter == end) {
std::cout << "Parsing succeeded" << std::endl;
generator<boost::spirit::ostream_iterator> generator;
std::cout << "'parent whee -> 4 -> 5 -> 6 ahhhh -> 5 -> 6 ' // ORIGINAL\n";
std::cout << "'parent parent whee -> parent 4 -> parent 5 -> parent 6 ahhhh -> parent 5 -> parent 6 ' // DESIRED/EXPECTED\n";
std::cout << "'" << karma::format_delimited(generator, karma::space, ast) << "' // ACTUAL\n";
}
}
Output:
Parsing succeeded
'parent whee -> 4 -> 5 -> 6 ahhhh -> 5 -> 6 ' // ORIGINAL
'parent parent whee -> parent 4 -> parent 5 -> parent 6 ahhhh -> parent 5 -> parent 6 ' // DESIRED/EXPECTED
'parent whee -> parent 4 -> parent 5 -> parent 6 ahhhh -> parent 5 -> parent 6 ' // ACTUAL
If you uncomment the alternative ruleB
generator:
Output:
Parsing succeeded
'parent whee -> 4 -> 5 -> 6 ahhhh -> 5 -> 6 ' // ORIGINAL
'parent parent whee -> parent 4 -> parent 5 -> parent 6 ahhhh -> parent 5 -> parent 6 ' // DESIRED/EXPECTED
'parent parent whee -> parent 4 -> parent 5 -> parent 6 parent ahhhh -> parent 5 -> parent 6 ' // ACTUAL