I am resurrecting some ancient code written back before Proto was an official Boost library. Things have changed quite a bit since then so I need to port the code. The code was never complete in the first place but I think much of it still applies. In any case, my goal is to take a Proto expression tree and transform it into another kind of tree using custom classes. Essentially it's an IR translation problem. Let's take an add node for example. Here's what I have so far: struct Base { ... }; struct Add : Base { ... }; // Custom expression nodes. // Transform a two-operand node. template<typename NodeType, typename Dummy = boost::proto::callable> struct ConstructBinary : boost::proto::callable { typedef boost:shared_ptr<NodeType> result_type; result_type operator()(boost::shared_ptr<Base> left, boost::shared_ptr<Base> right) { return result_type(new NodeType(left, right)); } }; struct ConstructExpressionGrammar; struct AddRule : boost::proto::plus<ConstructExpressionGrammar, ConstructExpressionGrammar> {}; // Want to use switch because of the large expression space. struct ConstructExpressionGrammarCases { // The primary template matches nothing: template<typename Tag> struct case_ : proto::not_<_> {}; }; // Match and construct an Add tree. template<> struct ConstructExpressionGrammarCases::case_<proto::tag::plus> : boost::proto::when<AddRule, // ?? ConstructBinary<Add>( ConstructExpressionGrammmar( boost::proto::_left), ConstructExpressionGrammmar( boost::proto::_right))> {}; struct ConstructExpressionGrammar : boost::proto::switch_<ConstructExpressionGrammarCases> {}; That's the basic outline. I don't think this is correct, however. In the grammar rule when<>, I'm not sure I can make a nested function type like that. I'm trying to tell the grammar to match the children and transform them, passing them to the ConstructBinary transformation to build the Add subtree. Any help is greatly appreciated. Thanks! -Dave
In the grammar rule when<>, I'm not sure I can make a nested function type like that. I'm trying to tell the grammar to match the children and transform them, passing them to the ConstructBinary transformation to build the Add subtree.
Any help is greatly appreciated. Thanks!
Here is a compilable testcase. Surprisingly, the nested function type seems to compile. But how can I statically initialize a terminal that refers to a boost::shared_ptr? I can't statically initialize the shared_ptr. Is there a way to set a terminal's value after the fact? -Dave #include <boost/proto/proto.hpp> #include <boost/shared_ptr.hpp> #include <string> #include <iostream> // The toy IR struct Base { virtual void print(std::ostream &) = 0; }; class Reference : public Base { std::string name; public: Reference(const std::string &n) : name(n) {}; const std::string &getName(void) const { return name; } void print(std::ostream &out) { out << getName(); } }; class Add : public Base { typedef boost::shared_ptr<Base> child_ptr; child_ptr left; child_ptr right; public: Add(child_ptr l, child_ptr r) : left(l), right(r) {}; void print(std::ostream &out) { out << "+ "; left->print(out); out << " "; right->print(out); } }; // Define grammar struct ConstructGrammar; // Convenience typedefs typedef boost::proto::terminal<boost::shared_ptr<Reference> >::type Variable; typedef boost::proto::plus<ConstructGrammar, ConstructGrammar> AddRule; // Semantic Actions // Transform a one-operand node template<typename NodeType, typename Child = typename NodeType::child_ptr, typename Dummy = boost::proto::callable> struct ConstructUnary : boost::proto::callable { typedef boost::shared_ptr<NodeType> result_type; result_type operator()(Child child) { return result_type(new NodeType(child)); } }; // Transform a two-operand node template<typename NodeType, typename Child, typename Dummy = boost::proto::callable> struct ConstructBinary : boost::proto::callable { typedef boost::shared_ptr<NodeType> result_type; result_type operator()(Child left, Child right) { return result_type(new NodeType(left, right)); } }; // Grammar rules struct ConstructGrammarCases { // The primary template matches nothing: template<typename Tag> struct case_ : boost::proto::not_<boost::proto::_> {}; }; template<> struct ConstructGrammarCases::case_<boost::proto::tag::terminal> : boost::proto::or_< boost::proto::when< Variable, boost::proto::_value> > {}; template<> struct ConstructGrammarCases::case_<boost::proto::tag::plus> : boost::proto::when< AddRule, ConstructBinary< Add, boost::shared_ptr<Base> >(ConstructGrammar(boost::proto::_left), ConstructGrammar(boost::proto::_right))> {}; struct ConstructGrammar : boost::proto::switch_<ConstructGrammarCases> {}; template<typename Expr> boost::shared_ptr<Base> translate(const Expr &expr) { ConstructGrammar trans; return trans(expr); } int main(void) { boost::shared_ptr<Base> a(new Reference("a")); boost::shared_ptr<Base> b(new Reference("b")); boost::shared_ptr<Base> c(new Reference("c")); boost::shared_ptr<Base> expr1(new Add(b, c)); boost::shared_ptr<Base> expr2(new Add(a, expr1)); expr2->print(std::cout); std::cout << '\n'; // How to initialize? Variable av = {}; Variable bv = {}; Variable cv = {}; // These compile, but don't appear to do anything. av = new Reference("a"); bv = new Reference("b"); cv = new Reference("c"); boost::shared_ptr<Base> expr3 = translate(av + (bv + cv)); // This faults with a null shared_ptr. expr3->print(std::cout); std::cout << '\n'; return 0; }
// How to initialize? Variable av = {}; Variable bv = {}; Variable cv = {};
Hmm, surprisingly (to me), this works: Variable av = {boost::shared_ptr<Reference>(new Reference("a"))}; Variable bv = {boost::shared_ptr<Reference>(new Reference("b"))}; Variable cv = {boost::shared_ptr<Reference>(new Reference("c"))}; How can this be static initialization when it includes a dynamic allocation? With this, the testcase compiles and runs correctly. -Dave
participants (2)
-
David A. Greene
-
David Greene