I'm using spirit::qi grammars that construct and return nontrivial objects as their synthesized attributes. The problem is that I want the grammars to recursively depend on each other. It is straightforward to use recursive rules, but I want recursive grammars.
Here's some example code. Notice the comments that say CIRCULAR REFERENCE. Obviously this cannot compile if I uncomment those lines, because the FooGrammar and BarGrammar objects include each other.
I can think of two ways to achieve the desired effect, but I can't seem to make them work with qi:
Make the BarGrammar hold a (smart) pointer to a FooGrammar. It would need to construct the FooGrammar as a semantic action on the lit("start_bar"). But I don't know the qi internals to know if this is safe -- would I need to maintain my own explicit stack of FooGrammars? When can they be safely destroyed?
BarGrammar could hold a FooGrammar as one of its qi::locals. But for some reason I can't get it to compile if I try to pass the grammar in qi::locals an inherited attribute.
Is there any canonical way to make recursive grammars?
.
#include <boost/fusion/include/std_pair.hpp>
#include <boost/optional.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant.hpp>
namespace phoenix = boost::phoenix;
namespace spirit = boost::spirit;
namespace ascii = spirit::ascii;
namespace qi = spirit::qi;
using ascii::space_type;
using phoenix::ref;
using qi::grammar;
using qi::lit;
using qi::_r1;
using qi::_val;
using qi::lit;
using qi::omit;
using qi::rule;
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
typedef int Reffy;
struct Foo {
int foo;
};
struct Bar {
int bar;
};
template <typename Iterator>
struct BarGrammar;
template <typename Iterator>
struct FooGrammar : grammar<Iterator, Foo(Reffy&), space_type> {
FooGrammar() : FooGrammar::base_type(foo_) {
foo_ = lit("start_foo")[_val = phoenix::construct<Foo>()]
> -bar_(_r1)
> lit("end_foo");
;
}
BarGrammar<Iterator> bar_;
rule<Iterator, Foo(Reffy&), space_type> foo_;
};
template <typename Iterator>
struct BarGrammar : grammar<Iterator, Bar(Reffy&), space_type> {
BarGrammar() : BarGrammar::base_type(bar_) {
bar_ = lit("start_bar")[_val = phoenix::construct<Bar>()]
//> -foo_(_r1) // CIRCULAR REFERENCE
> lit("end_bar");
}
//FooGrammar<Iterator> foo_; // CIRCULAR REFERENCE
rule<Iterator, Bar(Reffy&), space_type> bar_;
};
int main(int argc, char *argv[]) {
Reffy reffy(0);
FooGrammar<string::iterator> foog;
rule<string::iterator, space_type> top_rule = omit[ foog(ref(reffy)) ];
string input("start_foo start_bar end_bar end_foo");
string::iterator begin = input.begin();
string::iterator end = input.end();
if(qi::phrase_parse(begin, end, top_rule, ascii::space)) {
cout << "passed" << endl;
} else {
cout << "failed" << endl;
}
return 0;
}
Thanks in advance!!