1

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

sehe
  • 374,641
  • 47
  • 450
  • 633
CppChris
  • 1,226
  • 9
  • 14
  • It is unclear whether CreateVarNode is some kind of (static) member function or something else. Also google "CreateVarNode fabric" results in a single post: this question... Perhaps you should show a bit more context. – sehe Dec 05 '12 at 14:09
  • Unrelated: you'd probably want to do `qi::lexeme['%' >> +(qi::alpha) >> '%']` instead of `( '%' >> +(qi::alpha) >> '%')` (to avoid matching e.g. `"% this type of input%"` – sehe Dec 05 '12 at 14:32
  • Sorry for not providing enough information. Doesn't the Skipper avoid these constructs of "% this type of input%"? – CppChris Dec 05 '12 at 15:00
  • That depends on the actual skipper. If your skipper allows whitespace (which is the usual) or even comments, they will be allowed in between *any* parser character. qi::no_skip, qi::lexeme can prevent this – sehe Dec 05 '12 at 16:17

1 Answers1

2

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));
}
sehe
  • 374,641
  • 47
  • 450
  • 633