1

I am trying to write a spirit parser for arithmetic expression which fills an abstract syntax tree. The parser compiles if I am not trying to fill the AST, but fails (with one 24K error) in the current version. I am using clang++ 3.5.0 with -std=c++11, and running on Ubuntu 14.4.

#include <string>
#include <vector>
#include <utility>

#include <boost/spirit/include/qi.hpp>

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/adapted.hpp>

#include <boost/variant/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>

using std::string;
using std::vector;
using std::pair;

using boost::spirit::qi::grammar;
using boost::spirit::qi::space_type;
using boost::spirit::qi::rule;

struct Term; // forward dec
typedef boost::recursive_wrapper<Term> RWTerm;
typedef pair<char, RWTerm> OpAndRWTerm;
typedef pair<RWTerm, vector<OpAndRWTerm> > Expr;
typedef boost::variant<Expr, double> Factor;
typedef pair<char, Factor> OpAndFactor;
struct Term : public pair<Factor, vector<OpAndFactor> >{};

template<typename It>
struct formula_parser : grammar<It, Expr(), space_type> {
  formula_parser() : formula_parser::base_type(expr_rule) {
    using boost::spirit::qi::double_;
    using boost::spirit::ascii::char_;

    factor_rule %= double_ | parenthesis_rule;
    parenthesis_rule %= '(' >> expr_rule >> ')';
    op_and_factor_rule %= char_("/*") >> factor_rule;
    term_rule %= factor_rule >> *op_and_factor_rule;
    op_and_term_rule %= char_("+-") >> term_rule;
    expr_rule %= term_rule >> *op_and_term_rule;
  }
  rule<It, OpAndRWTerm(), space_type> op_and_term_rule;
  rule<It, Expr(), space_type> expr_rule;
  rule<It, OpAndFactor(), space_type> op_and_factor_rule;
  rule<It, RWTerm(), space_type> term_rule;
  rule<It, Expr(), space_type> parenthesis_rule;
  rule<It, Factor(), space_type> factor_rule;
};

int main() {
  formula_parser<string::const_iterator> grammar;
}

What I understand from the error message is that fusion mixes up the Types Factor and RWTerm in the rule term_rule.

what am I doing wrong ?

David Lehavi
  • 1,186
  • 7
  • 16

1 Answers1

3

It compiles for me if I change two things:

  1. Since Term inherits from std::pair, Term is a new type. For this reason you need to apply BOOST_FUSION_ADAPT_STRUCT to Term, regardless whether this has been already done for std::pair in <boost/fusion/adapted/std_pair.hpp>:

    BOOST_FUSION_ADAPT_STRUCT(
        Term,
        (Factor, first)
        (std::vector<OpAndFactor>, second)
    )
    

    Alternatively, you could make Term a standalone struct with two members and then apply BOOST_FUSION_ADAPT_STRUCT on that:

    struct Term { Factor f; std::vector<OpAndFactor>  o;};
    
    BOOST_FUSION_ADAPT_STRUCT(
        Term,
        (Factor, f)
        (std::vector<OpAndFactor>, o)
    )
    

    By the way: You have to fully qualify std::vector here, because the following will not compile:

    using std::vector;
    BOOST_FUSION_ADAPT_STRUCT(
        Term,
        (Factor, f)
        (vector<OpAndFactor>, o)
    )
    
  2. Use Term instead of RWTerm when declaring term_rule:

    rule<It, Term(), space_type> term_rule;
    

full code:

#include <string>
#include <vector>
#include <utility>

#include <boost/spirit/include/qi.hpp>

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/adapted.hpp>

#include <boost/variant/variant.hpp>
#include <boost/variant/recursive_wrapper.hpp>

using boost::spirit::qi::grammar;
using boost::spirit::qi::space_type;
using boost::spirit::qi::rule;

struct Term; // forward dec
typedef boost::recursive_wrapper<Term> RWTerm;
typedef std::pair<char, RWTerm> OpAndRWTerm;
typedef std::pair<RWTerm, std::vector<OpAndRWTerm> > Expr;
typedef boost::variant<Expr, double> Factor;
typedef std::pair<char, Factor> OpAndFactor;
struct Term : public std::pair<Factor, std::vector<OpAndFactor> >{};

BOOST_FUSION_ADAPT_STRUCT(
    Term,
    (Factor, first)
    (std::vector<OpAndFactor>, second)
)


template<typename It>
struct formula_parser : grammar<It, Expr(), space_type> {
  formula_parser() : formula_parser::base_type(expr_rule) {
    using boost::spirit::qi::double_;
    using boost::spirit::ascii::char_;

    factor_rule %= double_ | parenthesis_rule;
    parenthesis_rule %= '(' >> expr_rule >> ')';
    op_and_factor_rule %= char_("/*") >> factor_rule;
    term_rule %= factor_rule >> *op_and_factor_rule;
    op_and_term_rule %= char_("+-") >> term_rule;
    expr_rule %= term_rule >> *op_and_term_rule;
  }
  rule<It, OpAndRWTerm(), space_type> op_and_term_rule;
  rule<It, Expr(), space_type> expr_rule;
  rule<It, OpAndFactor(), space_type> op_and_factor_rule;
  rule<It, Term(), space_type> term_rule;
  rule<It, Expr(), space_type> parenthesis_rule;
  rule<It, Factor(), space_type> factor_rule;
};

int main() {
  formula_parser<std::string::const_iterator> grammar;
}

live example

m.s.
  • 16,063
  • 7
  • 53
  • 88
  • Thanks - it works ! what's wrong with using inheritance from pair ? – David Lehavi Oct 30 '15 at 12:28
  • 1
    @DavidLehavi I updated the answer with an explanation – m.s. Oct 30 '15 at 13:01
  • thanks for the details (only after comparing you code with my previous attempts I realized one has to fully qualify the vector) – David Lehavi Oct 30 '15 at 13:16
  • @DavidLehavi I try to avoid `using namespace...` or `using ns::type...` wherever possible, since it might get you into trouble with macros. In this case, I think `std::vector` is more readable since there are similarly named `vectors` such as `boost::mpl::vector`, `boost::fusion::vector` etc. which could distract the reader of that code. – m.s. Oct 30 '15 at 13:19
  • 2
    Recent boost can also just `BOOST_FUSION_ADAPT_STRUCT(Term, first, second)`: [further cleanups](http://coliru.stacked-crooked.com/a/9e9f936e506f0d93) – sehe Oct 30 '15 at 13:32
  • @sehe good to know! which boost version introduced this feature? – m.s. Oct 30 '15 at 13:34
  • I'm not sure. 57 or 58 I think – sehe Oct 30 '15 at 13:35
  • 1
    @sehe I found it in the [changelog of 1.58](http://www.boost.org/users/history/version_1_58_0.html): *" New ADAPT_STRUCT, ADAPT_ADT, ADAPT_ASSOC_ that deduce the members types (#9516)"* – m.s. Oct 30 '15 at 13:37