views:

518

answers:

2

greetings.

i've been interesting in how to force boost::spirit to produce nodes of different classes when parsing the grammar and generating AST. say, i want to have different nodes such as VariableNode (which has name of variable as its member), ValueNode (which has value as its member), etc.

it would be very useful when dealing with tree-walker. in this case we would write a base abstract class for walking all the different nodes (applying "the visitor" pattern) and extend it when dealing with semantics checking phase, code generation phase and the such.

boost::spirit allows us to parameterize factory being used for trees, but i've been unable to find a proper way to tune its behavior.

any ideas, code? thanks in advance.

+2  A: 

I'm not sure I understand your question, do you mean something like this? :

typedef boost::variant<VariableNode, ValueNode> AbstractNode;

template <typename Iterator>
struct NodeGrammar: public boost::spirit::qi::grammar<Iterator, AbstractNode(), boost::spirit::ascii::space_type>
{
    NodeGrammar: NodeGrammar::base_type(start)
    {
     start %= variableNode | valueNode >> eps;

     variableNode %= /*something*/;
     valueNode %= /*something*/;
    }

    //start
    boost::spirit::qi::rule<Iterator, AbstractNode(), boost::spirit::ascii::space_type> start;

    boost::spirit::qi::rule<Iterator, VariableNode(), boost::spirit::ascii::space_type> variableNode;
    boost::spirit::qi::rule<Iterator, ValueNode(), boost::spirit::ascii::space_type> valueNode;
};

You can then use boost::apply_visitor (see boost::variant documentation) with a visitor class to do the behavior you want.

n1ck
n1ck, many thanks. yes, you've got my question right. hmm, i need time to have a look at the spirit 2.x version and at boost::variant docs. please stay in touch.
varnie
Well, nice to have been able to help. Here is the link for the most up to date doc for spirit 2.x, don't know if you had been able to get it but I found it hard to find and the one on the boost website was not the latest last time I checked : http://svn.boost.org/svn/boost/trunk/libs/spirit/doc/html/index.html. boost::apply_visitor should be relatively easy to work with if you look at the exemples but if you need help don't hesitate to tell me. Cheers.
n1ck
all this boost::spirit::qi stuff is completely new for me. i have old boost-spirit classic grammar with generating AST using old boost directives (gen_pt_node_d, gen_ast_node_d etc). it is a time for me to rewrite this code with the usage of boost::spirit::qi. i've found out it is possible to generate AST almost on-the-fly just describing it in the grammar (with the help of boost::fusion).
varnie
now i'm facing several issues: 1) how to distinguish between "identifiers" and "keywords" in boost-spirit-2? 2) suppose we have some class, say, ScriptNode which holds std::vector<boost::shared_ptr<UserFuncNode> > as its private member. having all that, how to create an instance of ScriptNode, being parsing an appropriate rule: script_start = *user_func >> eps? thank you for your comments!
varnie
A: 

To answer your comment (you might want to start a new question for this): identifiers should probably be stored in a qi::symbols-derived class and keywords would be in your other qi::rules.

As for 2) this would be something like this (not tested):

class ScriptNodes
{
   //this will  enable fusion_adapt_struct to access your private members
   template < typename, int>
   friend struct boost::fusion::extension::struct_member;

private:
   typdef std::vector<boost::shared_ptr<UserFuncNode> > Nodes
   Nodes nodes;
};

//when using fusion_adapt_struct, try to typedef any type that contain a ,
//since it will confuse the macro (ex std::pair<int, int>)
BOOST_FUSION_ADAPT_STRUCT(
    ScriptNode,
    (ScriptNodes::Nodes, nodes)
)

..

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

template <typename Iterator>
struct NodeGrammar: public grammar<Iterator, ScriptNodes(), space_type>
{
    NodeGrammar: NodeGrammar::base_type(start)
    {
        using namespace boost::spirit::arg_names;
        using boost::spirit::arg_names::_1;

        //the %= should automatically store the user_func nodes in start
        //for more complex rules you might need to do the push_back manually
        //using phoenix::push_back
        start %= *user_func >> eps;

        //this should parse a double and create a new UserFuncNode with the
        //parsed argument and the result will be assigned in the shared_ptr
        //variable stored in a user_func
        user_func = double_[_val = new UserFuncNode(_1)];
    }

    using boost::spirit::qi::rule;
    using boost::shared_ptr;

    //start
    rule<Iterator, ScriptNodes(), space_type> start;

    rule<Iterator, shared_ptr<UserFuncNode>(), space_type> user_func;
};

I could probably expend more if you need but you should probably start a new question if you got specific issues so other people can help too as I'm just a beginner user of boost::spirit and they might have better answers.

Cheers

n1ck
thanks for answer. well, i've started another thread for my question about keywords/idents problem.
varnie