3

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:

Current Code On Coliru

#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?

Dev Dev
  • 105
  • 6
  • There is no AST. There's no generator API invocation (neither iterator nor stream based). There's no bound attributes. Exactly what do want to achieve? "wheee", "cool" and "childish" seem to have been achieved. _Hint:_ loose the noise, and show a SSCCE/MVCE – sehe Mar 18 '15 at 10:56
  • 1
    SSCCE as requested at [link](http://coliru.stacked-crooked.com/a/52a2e4ff2ed8ed84). – Dev Dev Mar 18 '15 at 20:21
  • As asked I would know if is possible to access/pass a given rule attribute from other rules and hereby be able to do the same as above. It should be noted that I have changed the naming of the rules in the source link. – Dev Dev Mar 18 '15 at 20:26
  • The SSCCE is looking promising. You should really include the minimalized version of that in the question, but lemme look at it. I might reduce it for you in the process of reading :/ – sehe Mar 18 '15 at 20:38
  • You don't specify the desired outcome. It's hard to "guess" what you mean by this "specification" in the form of dream code that (a) doesn't compile (b) doesn't show what you want it to actually look like (c) doesn't show what it would do, even if it were realistic code. I'll keep reading the linked sample just in case the penny drops there (but since it does compile, I have a suspicion, it won't help) – sehe Mar 18 '15 at 20:41
  • (a) The code in the link compiles just fine in VS12, parsing the storage string fine into the output "parent whee -> 4 -> 5 -> 6 ahhhh -> 5 -> 6". (b) Thought the karma grammar in it self was the answer to what the output should look like (c) Do not know what this means, as the source compiles and works just fine. As to support your confusion, then I would like to get output such as "parent parent whee -> parent 4 -> parent 5 -> parent 6 ahhhh -> parent 5 -> parent 6". As to my question: How to get an attribute value from a parent rule to some other child rule. – Dev Dev Mar 18 '15 at 20:53
  • (a) I know, that's what I _said_ in my previous comment (b) no the karma grammar shows a lot of frivolous prose inside a non-realistic karma grammar (c) the output is what you expect to be generated (duh). I'll see if I can wrap my head around this sample: "parent parent whee -> parent 4 -> parent 5 -> parent 6 ahhhh -> parent 5 -> parent 6". – sehe Mar 18 '15 at 20:59
  • I reverse engineered your question into proper form. I honestly think it'd be clearer if you remove the original "non-code" sample. I left it in for context now. – sehe Mar 18 '15 at 22:00

1 Answers1

3

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:

  • I've simplified both the Qi and Karma rules
  • favour delimiters in Karma rules
  • use lexeme where appropriate in the Qi rules (Boost spirit skipper issues)
  • use lit("x") instead of string("x") if you don't want to synthesize an attribute
  • drop lit() unless non-spirit domain expression template operator overloads would be selected
  • use BOOST_SPIRIT_DEBUG_NODES
  • Do not mix using namespaces. There's no need and it will lead to trouble.

Live On Coliru

//#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:

Live On Coliru

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
Community
  • 1
  • 1
sehe
  • 374,641
  • 47
  • 450
  • 633
  • Got some questions: **1:** "generate_delimited" adds delimiters to the output. Is there any other reason for this generator except for proper spacing in the output? Guess "generate" is as applicable if delimitation is not needed. **2:** Can it be assumed that use of "_a_type"/"_r1_type" and use of locals is a general solution to solve this problem? It seems as this implementation easily can be extended for more complex use. **3:** Why did you not use the default phoenix or spirit _a/_r1 variables? I might have missed something in the documentation. – Dev Dev Mar 19 '15 at 06:48
  • @DevDev **1:** Yes it's there for the same reason you were manually inserting the whitespace. And yes if it was not needed, it was not needed. **2:** Erm. Yes. I linked to the documentation **3:** I prefer readable code. – sehe Mar 19 '15 at 07:21
  • 1
    Anyone reading this: This is a very good and comprehensive feedback which solved my issue. Hats off for helping and showing how to properly ask a question. Thanks – Dev Dev Mar 19 '15 at 07:38
  • Hrm. Your comment was ~8 hours later than [that edit](http://stackoverflow.com/revisions/29133268/2) :) Thanks for the kind words – sehe Mar 19 '15 at 07:50