boost::spirit and generating different nodes
gree开发者_StackOverflow中文版tings.
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.
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.
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
精彩评论