Search code examples
c++boostboost-spiritboost-spirit-qi

Boost::spirit with fabric methods in a rule


I am using boost::spirit for constructing a parser for a simple script language. In a rule, I want to use a call to a fabric method to create a new object which is the return value of this rule.

The rule is

qi::rule<Iterator, NodeHandle(), Skipper>      value;

and defined as

value = ( '%' >> +(qi::alpha) >> '%')
        [phoenix::bind(&CreateVarNode, qi::_val, qi::_1)];

Actually, these "NodeHandles" are constructed via these kind of methods

NodeHandle CreateVarNode(std::wstring var)
{
    Node::ptr node(new VarTerminal(var));
    return NodeHandle(node);        
}

The problem is, that spirit does not like these kind of constructs for rules. Can you help me on how to implement a rule which uses this fabric method to return this object?

Thank you!

Cheers, Chris


Solution

  • Assuming the factory functions are actually namespace-level functions:

    namespace Factories
    {
        NodeHandle CreateVarNode(std::wstring var)
        {
            Node::ptr node(new VarTerminal(var));
            return NodeHandle(node);        
        }
    }
    

    This should work nicely:

    value = ( '%' >> +(qi::alpha) >> '%')
            [qi::_val = phoenix::bind(&Factories::CreateVarNode, qi::_1)];
    

    This should work unaltered if it was actually a static member of a class named Factories. You could also implement a bit of convenience and write it like:

    value = ( '%' >> +(qi::alpha) >> '%') [ createvar ];
    

    This requires a bit of plumbing to use Phoenix functions. Full sample:

    #define BOOST_SPIRIT_USE_PHOENIX_V3
    
    #include <boost/spirit/include/qi.hpp>
    namespace qi  = boost::spirit::qi;
    namespace phx = boost::phoenix;
    
    struct VarNode    { VarNode    (std::string        ){ /*todo*/ }  } ;
    struct LitNode    { LitNode    (double             ){ /*todo*/ }  } ;
    struct Assignment { Assignment (std::string, double){ /*todo*/ }  } ;
    
    struct Node { typedef Node* ptr; };
    struct NodeHandle { /*todo*/ };
    
    template <typename ProductNode>
    struct NodeFactory
    {
        template<typename... T> struct result { typedef NodeHandle type; };
    
        template<typename... T>
            NodeHandle operator()(T&&... a) const
            {
                Node::ptr node(new ProductNode(std::forward<T>(a)...));
                return NodeHandle(node);
            }
    };
    
    int main ()
    {
        static const phx::function<NodeFactory<VarNode> >    createvar;
        static const phx::function<NodeFactory<LitNode> >    createliteral;
        static const phx::function<NodeFactory<Assignment> > createassign;
    
        qi::rule<std::string::const_iterator, NodeHandle()> value 
            = ( '%' >> +(qi::alpha) >> '%') [createvar];
    
        const std::string input("%a%");
        auto f=begin(input), l=end(input);
        assert(qi::parse(f, l, value));
    }