Defining Statements with Proto
A few years ago, I asked how to define a grammar with Proto to match something like this: if_(expr) [ stmt, stmt, ... ].else [ stmt, stmt, ... ]; The answer was to do this: struct Grammar; struct ExpressionGrammar; struct StatementGrammar; struct Domain; template<typename Expr> struct Expression : proto::extends<Expr, Expression<Expr>, Domain> { typedef proto::extends<Expr, Expression<Expr>, Domain> base_type; Expression(Expr const & expr = Expr()) : base_type( expr ) {} }; struct Domain : proto::domain< proto::generator< Expression >, Grammar> {}; struct If {}; BOOST_PROTO_DEFINE_FUNCTION_TEMPLATE( 1 , if_ , Domain , (If) , BOOST_PP_SEQ_NIL ); // Here is the grammar for if_ statements // matches if_(e1)[e2] struct IfGrammar : boost::proto::subscript< boost::proto::arg< boost::proto::unary_expr< If, ExpressionGrammar >, >, StatementGrammar > {}; // An else_ "operator" template<typename Expr> struct Expression< Expr , typename enable_if<proto::matches<Expr, IfGrammar> >::type > : proto::extends<Expr, Expression<Expr>, Domain> { Expression() : else_(proto::make_expr<Else>(*this)) {}; proto::result_of::make_expr< Else, // else is now an "operator" Expression<Expr> const >::type const else_; }; // matches if_(e1)[e2].else_[e3] struct IfElseGrammar : boost:proto::subscript< boost::proto::arg< boost::proto::unary_expr< Else, IfGrammar >, >, StatementGrammar > {}; I didn't understand it then and I don't now. :) However, it seems to be obsolete since BOOST_PROTO_DEFINE_FUNCTION_TEMPLATE doesn't seem to exist anymore (it's not documented, anyway). So how would this be formulated in modern Boost.Proto? I'm guessing I will need an expression template wrapper (called Expression<> above) that uses some combination of BOOST_PROTO_BASIC_EXTENDS, BOOST_PROTO_EXTENDS_SUBSCRIPT and BOOST_PROTO_EXTENDS_FUNCTION. But of course not all statements have a valid operator() overload and no expressions do. I guess the grammar will take care of that, yes? I'm really puzzled about how to do the ".else_" part. I was hoping I could look to Phoenix to get a clue but it doesn't seem to use Proto. Any help? Thanks! -Dave
Apologies in advance for only the partial response. I'm pressed for time. On 1/1/2010 6:29 AM, David A. Greene wrote:
A few years ago, I asked how to define a grammar with Proto to match something like this:
if_(expr) [ stmt, stmt, ... ].else [ stmt, stmt, ... ]; <snip>
I didn't understand it then and I don't now. :) However, it seems to be obsolete since BOOST_PROTO_DEFINE_FUNCTION_TEMPLATE doesn't seem to exist anymore (it's not documented, anyway).
It exists, but it's deprecated. It's been replaced with BOOST_PROTO_REPEAT and BOOST_PROTO_LOCAL_ITERATE macros. See the docs for boost/proto/repeat.hpp.
So how would this be formulated in modern Boost.Proto? I'm guessing I will need an expression template wrapper (called Expression<> above) that uses some combination of BOOST_PROTO_BASIC_EXTENDS, BOOST_PROTO_EXTENDS_SUBSCRIPT and BOOST_PROTO_EXTENDS_FUNCTION. But of course not all statements have a valid operator() overload and no expressions do. I guess the grammar will take care of that, yes?
I'm really puzzled about how to do the ".else_" part. I was hoping I could look to Phoenix to get a clue but it doesn't seem to use Proto.
Any help? Thanks!
There is experimental and undocumented support in Proto for members like else_. See libs/proto/example/virtual_member.cpp for a working example. -- Eric Niebler BoostPro Computing http://www.boostpro.com
On Saturday 02 January 2010 05:30:33 Eric Niebler wrote:
There is experimental and undocumented support in Proto for members like else_. See libs/proto/example/virtual_member.cpp for a working example.
Ah, this is what I was looking for. What is the "experimental" and "undocumented" part? -Dave
On Saturday 02 January 2010 11:23:19 David A. Greene wrote:
On Saturday 02 January 2010 05:30:33 Eric Niebler wrote:
There is experimental and undocumented support in Proto for members like else_. See libs/proto/example/virtual_member.cpp for a working example.
Ah, this is what I was looking for. What is the "experimental" and "undocumented" part?
So following this example, I came up with the attached testcase, modified from the one I posted to the development list. I know the grammar is not strict enough (it should have a separation between "expressions" and "statements") but that's not important. With the modified testcase display_expr breaks with an incomprehensible compiler error. I assume I'm missing an operator<< definition somewhere but I don't know where it belongs. And I'm still getting the same compiler error on the #if 0'd code. Thanks for your help! -Dave
On 1/3/2010 6:40 AM, David A. Greene wrote:
On Saturday 02 January 2010 11:23:19 David A. Greene wrote:
On Saturday 02 January 2010 05:30:33 Eric Niebler wrote:
There is experimental and undocumented support in Proto for members like else_. See libs/proto/example/virtual_member.cpp for a working example.
Ah, this is what I was looking for. What is the "experimental" and "undocumented" part?
BOOST_PROTO_EXTENDS_MEMBERS
So following this example, I came up with the attached testcase, modified from the one I posted to the development list.
I know the grammar is not strict enough (it should have a separation between "expressions" and "statements") but that's not important.
With the modified testcase display_expr breaks with an incomprehensible compiler error. I assume I'm missing an operator<< definition somewhere but I don't know where it belongs.
That means the tag types you've defined in the keyword namespace are not Streamable. Define a stream insertion operator on keyword::if_. Using display_expr is documented here: http://www.boost.org/doc/libs/1_41_0/doc/html/proto/users_guide.html#boost_p...
And I'm still getting the same compiler error on the #if 0'd code.
Still on my radar ... -- Eric Niebler BoostPro Computing http://www.boostpro.com
On 1/3/2010 6:40 AM, David A. Greene wrote:
And I'm still getting the same compiler error on the #if 0'd code.
Thanks for your help!
The attached code compiles. You weren't handling the if_ case correctly. You had specialized ConstructGrammarCases on IfTag, but you weren't using IfTag anywhere, so that specialization wasn't getting picked up. There were a few other small problems, but on the whole, you were pretty close. -- Eric Niebler BoostPro Computing http://www.boostpro.com
On Saturday 02 January 2010 16:17:03 Eric Niebler wrote:
The attached code compiles. You weren't handling the if_ case correctly. You had specialized ConstructGrammarCases on IfTag, but you weren't using IfTag anywhere, so that specialization wasn't getting picked up. There were a few other small problems, but on the whole, you were pretty close.
If I wanted to keep the IfRule case as a specialization, what should it be specialized on? IfTag was a leftover remnant, so that was a coding error. What I intended to do is this: template<> struct ConstructGrammarCases::case_<keyword::if_> : boost::proto::when< IfRule, ConstructBinary<If>(ConstructGrammar(boost::proto::_right(boost::proto::_left)), ConstructGrammar(boost::proto::_right))> {}; But this leads to the compiler error seen earlier. Something like if_ is going to have to use a case_ specialization, so it's necessary to know how this is supposed to work. -Dave
On 1/4/2010 5:15 AM, David A. Greene wrote:
On Saturday 02 January 2010 16:17:03 Eric Niebler wrote:
The attached code compiles. You weren't handling the if_ case correctly. You had specialized ConstructGrammarCases on IfTag, but you weren't using IfTag anywhere, so that specialization wasn't getting picked up. There were a few other small problems, but on the whole, you were pretty close.
If I wanted to keep the IfRule case as a specialization, what should it be specialized on? IfTag was a leftover remnant, so that was a coding error. What I intended to do is this:
template<> struct ConstructGrammarCases::case_<keyword::if_> : boost::proto::when< IfRule, ConstructBinary<If>(ConstructGrammar(boost::proto::_right(boost::proto::_left)), ConstructGrammar(boost::proto::_right))> {};
But this leads to the compiler error seen earlier. Something like if_ is going to have to use a case_ specialization, so it's necessary to know how this is supposed to work.
You already know the answer, but you don't know you know it. ;-) You correctly defined IfRule as: typedef boost::proto::subscript< boost::proto::function< IfTerminal, ConstructGrammar >, ConstructGrammar
IfRule;
So you know that the topmost node of an if_ statement has a tag type of boost::proto::tag::subscript. So you should create a case_ specialization on that tag type, as follows: template<> struct ConstructGrammarCases::case_<boost::proto::tag::subscript> : boost::proto::when< IfRule, ConstructBinary< If, boost::shared_ptr<Base>
(ConstructGrammar(boost::proto::_right(boost::proto::_left)), ConstructGrammar(boost::proto::_right))> {};
Hope that helps. -- Eric Niebler BoostPro Computing http://www.boostpro.com
On Sunday 03 January 2010 21:00:31 Eric Niebler wrote:
So you know that the topmost node of an if_ statement has a tag type of boost::proto::tag::subscript. So you should create a case_ specialization on that tag type, as follows:
template<> struct ConstructGrammarCases::case_<boost::proto::tag::subscript>
So I tried that before and it didn't work. I tried again and it still fails (check attached testcase). I'm using boost 1.40 if it matters. -Dave
On 1/5/2010 3:43 PM, David A. Greene wrote:
On Sunday 03 January 2010 21:00:31 Eric Niebler wrote:
So you know that the topmost node of an if_ statement has a tag type of boost::proto::tag::subscript. So you should create a case_ specialization on that tag type, as follows:
template<> struct ConstructGrammarCases::case_<boost::proto::tag::subscript>
So I tried that before and it didn't work. I tried again and it still fails (check attached testcase). I'm using boost 1.40 if it matters.
In your code, you have: #ifdef NO_SPECIALIZATION template<> struct ConstructStatementGrammarCases::case_<boost::proto::tag::subscript> I'm sure if you change your ifdef to ifndef, or just remove the PP gunk, you'll see that it works. -- Eric Niebler BoostPro Computing http://www.boostpro.com
On Tuesday 05 January 2010 03:47:39 Eric Niebler wrote:
In your code, you have:
#ifdef NO_SPECIALIZATION template<> struct ConstructStatementGrammarCases::case_<boost::proto::tag::subscript>
I'm sure if you change your ifdef to ifndef, or just remove the PP gunk, you'll see that it works.
<brownpaperbag> Oh, bother. </brownpaperbag> It works now. And I think I grok proto! I've successfully added more statement-type rules and it works great. A couple of questions: Is there any advantage to doing: // An if_ "operator" template<typename Expr> typename boost::proto::result_of::make_expr< boost::proto::tag::function, Domain, keyword::if_, Expr>::type if_(Expr const &expr) { return boost::proto::make_expr<boost::proto::tag::function, Domain>( keyword::if_(), expr); } over: typedef Wrapper<boost::proto::terminal<keyword::if_>::type> IfTerminal; // An if_ "operator." namespace { const IfTerminal if_ = {{}}; } and letting proto take care of creating the function expression? Perhaps the former has one or two less template instantiations but the latter is certainly easier to maintain. Also, we can get something akin to synthesized attributes with the grammar semantics actions: struct case_ : boost::proto::when< IfRule, ConstructBinary<If>(ConstructExpressionGrammar(boost::proto::_right(boost::proto::_left)), ConstructStatementGrammar(boost::proto::_right))> {}; where Construct*Grammar is a transform grammar that returns some result (so it's a transform too, right?). Is there any way to get something akin to inherited attributes that one can pass down the expression tree? Finally, I think the wrapper metafunction problem I posted about is still an issue, though I seem to be getting by without the dummy template argument to do the ADL trick. -Dave
Is there any advantage to doing:
// An if_ "operator" template<typename Expr> typename boost::proto::result_of::make_expr< boost::proto::tag::function, Domain, keyword::if_, Expr>::type if_(Expr const &expr) { return boost::proto::make_expr<boost::proto::tag::function, Domain>( keyword::if_(), expr); }
over:
typedef Wrapper<boost::proto::terminal<keyword::if_>::type> IfTerminal;
// An if_ "operator." namespace { const IfTerminal if_ = {{}}; }
and letting proto take care of creating the function expression? Perhaps the former has one or two less template instantiations but the latter is certainly easier to maintain
I can't find lu right now but i made an extensive test for this and the function form id more scalable in terme of compile time and exe size
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
On 1/7/2010 12:54 PM, David A. Greene wrote:
On Tuesday 05 January 2010 03:47:39 Eric Niebler wrote:
In your code, you have:
#ifdef NO_SPECIALIZATION template<> struct ConstructStatementGrammarCases::case_<boost::proto::tag::subscript>
I'm sure if you change your ifdef to ifndef, or just remove the PP gunk, you'll see that it works.
<brownpaperbag> Oh, bother. </brownpaperbag>
Haha!
It works now. And I think I grok proto!
Welcome to the club. Your propeller hat should arrive in a few days. ;-)
I've successfully added more statement-type rules and it works great.
A couple of questions:
Is there any advantage to doing:
// An if_ "operator" template<typename Expr> typename boost::proto::result_of::make_expr< boost::proto::tag::function, Domain, keyword::if_, Expr>::type if_(Expr const&expr) { return boost::proto::make_expr<boost::proto::tag::function, Domain>( keyword::if_(), expr); }
over:
typedef Wrapper<boost::proto::terminal<keyword::if_>::type> IfTerminal;
// An if_ "operator." namespace { const IfTerminal if_ = {{}}; }
and letting proto take care of creating the function expression? Perhaps the former has one or two less template instantiations but the latter is certainly easier to maintain.
Joel F, already answered this one, and his answer is right. The two formulations are functionally equivalent. The function compiles faster, though.
Also, we can get something akin to synthesized attributes with the grammar semantics actions:
struct case_ : boost::proto::when< IfRule, ConstructBinary<If>(ConstructExpressionGrammar(boost::proto::_right(boost::proto::_left)), ConstructStatementGrammar(boost::proto::_right))> {};
where Construct*Grammar is a transform grammar that returns some result (so it's a transform too, right?).
Yes, all Proto _grammars_ are also _primitive transforms_ and can be used as you are using them above to compose _callable transforms_. (Check the glossary for the meaning of those terms.)
Is there any way to get something akin to inherited attributes that one can pass down the expression tree?
Not exactly sure what you are asking. You mean you want to pass stuff around to all the transforms? All _primitive transforms_ are automatically passed a state and a data parameter. If you don't specify them (you haven't), dummies get filled in for you. You can use one of those to pass around any extra information your transforms need.
Finally, I think the wrapper metafunction problem I posted about is still an issue, though I seem to be getting by without the dummy template argument to do the ADL trick.
Sorry, which issue was that? -- Eric Niebler BoostPro Computing http://www.boostpro.com
participants (3)
-
David A. Greene
-
Eric Niebler
-
Joel.Falcou@lri.fr