
This one took me a long time to understand, and now that I think I understand it, I'm pretty sure I don't like Apparently (at least in the situation shown below, with user defined domain and expressions) unary_expr matches terminal< >. This was surprising to me because I was working under the assumption that unary_expr matched only bona-fide unary C++ operators. Ordering should have solved the problem in my real code, so I still have investigations to do. In my case I need to match both things, but threat the case separately (e.g. a terminal<foo> will be transfromed differently from a terminal<bar> and even more differently from ++a). Now, I can certainly (I think, not tested) wrap unary_expr in a proto::and_ that tests the tag, but it seems such a common occurrence that I'd like it to be the default proto behaviour. Unless I'm doing something stupid. Regards, Maurizio ===File ~/dev/proto_pdl/pe4.cpp============================= #define BOOST_PROTO_MAX_ARITY 10 #define BOOST_MPL_LIMIT_METAFUNCTION_ARITY 10 #include <iostream> #include <boost/xpressive/proto/proto.hpp> #include <boost/xpressive/proto/debug.hpp> namespace proto=boost::proto; template<typename> struct meta_expression; struct meta_domain : proto::domain<proto::generator<meta_expression> > {}; template <typename Expr> struct meta_expression : proto::extends<Expr, meta_expression<Expr>, meta_domain> { typedef proto::extends<Expr, meta_expression<Expr>, meta_domain> base_type; meta_expression (Expr const& expr = Expr()) : base_type (expr) {} using base_type::operator =; }; #define META_TERMINAL(NAME) \ struct NAME##_tag {}; \ inline char const* proto_tag_name (NAME##_tag) { return #NAME; } \ std::ostream& operator << (std::ostream& os, const NAME##_tag) { os << "[" #NAME "]" ; return os; } \ meta_expression<proto::terminal<NAME##_tag>::type> NAME struct _5_tag {}; inline const char* proto_tag_name (_5_tag) {return "_5"; } meta_expression<proto::terminal<_5_tag>::type> _5; // std::ostream& operator << (std::ostream& os, const _5_tag) { os << "[_5]" ; return os; } \ META_TERMINAL (_1); META_TERMINAL (_2); META_TERMINAL (_3); struct meta_grammar : proto::or_ < proto::terminal<_1_tag>, // proto::terminal<_2_tag>, proto::terminal<_3_tag>, proto::unary_expr<proto::_, proto::_>
{}; template<typename Expr> void is_meta_expression (const Expr& expr) { std::cout << "Expression:\n"; proto::display_expr (expr); if (proto::matches<Expr, meta_grammar>::value) std::cout << "matches meta grammar\n\n"; else std::cout << "doesn't matches meta grammar\n\n"; } int main (int,char**) { is_meta_expression (_2); } /// Local Variables: /// mode:c++ /// comment-column:60 /// fill-column:150 /// compilation-read-command:nil /// compile-command:"g++ -I. -ope4 pe4.cpp" /// End: ============================================================

Maurizio Vitale wrote:
This one took me a long time to understand, and now that I think I understand it, I'm pretty sure I don't like
Apparently (at least in the situation shown below, with user defined domain and expressions) unary_expr matches terminal< >. This was surprising to me because I was working under the assumption that unary_expr matched only bona-fide unary C++ operators.
Your understanding of the issue is correct. The implementation is dead simple, and the behavior you're seeing naturally falls out of the decision to make terminals behave like degenerate unary expressions. They are unary expressions because they store one datum inside, just like a "real" unary expression does. I agree that letting terminals match unary_expr<_,_> is a little surprising, though. I can easily patch up proto::matches<> such that tag::terminal does not match _, so that a terminal will not match unary_expr<_,_>. It *will* still match unary_expr<tag::terminal,_> though. Would that be ok? -- Eric Niebler Boost Consulting www.boost-consulting.com

On Jun 14, 2007, at 12:09 PM, Eric Niebler wrote:
Maurizio Vitale wrote:
This one took me a long time to understand, and now that I think I understand it, I'm pretty sure I don't like
Apparently (at least in the situation shown below, with user defined domain and expressions) unary_expr matches terminal< >. This was surprising to me because I was working under the assumption that unary_expr matched only bona-fide unary C++ operators.
Your understanding of the issue is correct. The implementation is dead simple, and the behavior you're seeing naturally falls out of the decision to make terminals behave like degenerate unary expressions. They are unary expressions because they store one datum inside, just like a "real" unary expression does.
I agree that letting terminals match unary_expr<_,_> is a little surprising, though. I can easily patch up proto::matches<> such that tag::terminal does not match _, so that a terminal will not match unary_expr<_,_>. It *will* still match unary_expr<tag::terminal,_> though. Would that be ok?
Would be helpful (and would solve my specific case completely), but if you could find a way to completely hide the fact that a terminal is a unary_expr from the user I think it would be much cleaner for a boost::proto release. Regards, Maurizio

Maurizio Vitale wrote:
if you could find a way to completely hide the fact that a terminal is a unary_expr from the user I think it would be much cleaner for a boost::proto release.
I'll give the issue some thought. One possibility is shoehorn into proto the concept of a nullary expression, which is what terminals should rightly be. Of course, what is there already works, so IMO there are bigger fish to fry first. -- Eric Niebler Boost Consulting www.boost-consulting.com

On 06/14/2007 11:57 AM, Eric Niebler wrote:
Maurizio Vitale wrote:
if you could find a way to completely hide the fact that a terminal is a unary_expr from the user I think it would be much cleaner for a boost::proto release.
I'll give the issue some thought. One possibility is shoehorn into proto the concept of a nullary expression, which is what terminals should rightly be. Of course, what is there already works, so IMO there are bigger fish to fry first.
I think the nullary expression concept is good. I've been trying to see how proto relates to algebra's and their transforms (or homo morphisms), and algebra's have nullary expressions (true and false are nullaries in bool algebra) and the transform from one algebra to another map the nullaries of the source algebra to nullaries of target algebra. Now one particular algebra relevant to proto and spirit and deterministic parsing is what I'll call the "derives_empty" algebra. Expressions in this target algebra are created by a transform from a source algebra which is the algebra of grammar expressions. In that transform (call it gram_to_empty), the transforms are: gram_to_empty(terminal) :-> empty_not gram_to_empty(epsilon) :-> empty_yes gram_to_empty(non_terminal) :-> empty_unknown gram_to_empty(E0 | E1 | ... | En) :-> gram_to_empty(E0) + gram_to_empty(E1) + ... + gram_to_empty(En) gram_to_empty(E0 >> E1 >> ... >> En) :-> gram_to_empty(E0) * gram_to_empty(E1) * ... * gram_to_empty(En) where + and * is a binary operators in the target algebra. Their definition are defined for empty_not and empty_yes in the obvious way. e.g. empty_not | X == X empty_yes | X == empty_yes empty_not * X == empty_not empty_yes * X == X and a grammar's productions can be transformed into a set of equations in the derives_empty algebra whose solution define the empty attribute for each grammar expression. This is, in essence, what the statement: this->attr_fixed_point<attr_empty >(maxit); on line 1348 of: http://svn.boost.org/trac/boost/browser/sandbox/boost/grammar_pipeline/eff/p... does. I've been struggling with doing the same in proto, but haven't been successful yet. Maybe transforming proto to a more "algebraic orientation" would ease the solution. However, without actually doing this transform (which would be a lot of work, I guess), that's just guessing; however, it is something to consider. -regards, Larry

Larry Evans wrote:
I think the nullary expression concept is good. I've been trying to see how proto relates to algebra's and their transforms (or homo morphisms), and algebra's have nullary expressions (true and false are nullaries in bool algebra) and the transform from one algebra to another map the nullaries of the source algebra to nullaries of target algebra. Now one particular algebra relevant to proto and spirit and deterministic parsing is what I'll call the "derives_empty" algebra. Expressions in this target algebra are created by a transform from a source algebra which is the algebra of grammar expressions. In that transform (call it gram_to_empty), the transforms are:
gram_to_empty(terminal) :-> empty_not gram_to_empty(epsilon) :-> empty_yes gram_to_empty(non_terminal) :-> empty_unknown gram_to_empty(E0 | E1 | ... | En) :-> gram_to_empty(E0) + gram_to_empty(E1) + ... + gram_to_empty(En) gram_to_empty(E0 >> E1 >> ... >> En) :-> gram_to_empty(E0) * gram_to_empty(E1) * ... * gram_to_empty(En)
<snip> The presence or absence of terminals as nullary expressions in Proto doesn't affect your ability to implement this transform in Proto. The transform would be pretty straightforward, I think. I think it would make a terrific example, actually. I've been working on the docs for Proto's transforms. You can find the latest here: http://boost-sandbox.sourceforge.net/libs/proto/doc/html/boost_proto/user_s_... Check out the examples for fold<>, fold_tree<> and applyN<>. I think they would be the most useful ones for this problem. -- Eric Niebler Boost Consulting www.boost-consulting.com

On Jun 14, 2007, at 12:57 PM, Eric Niebler wrote:
Maurizio Vitale wrote:
if you could find a way to completely hide the fact that a terminal is a unary_expr from the user I think it would be much cleaner for a boost::proto release.
I'll give the issue some thought. One possibility is shoehorn into proto the concept of a nullary expression, which is what terminals should rightly be. Of course, what is there already works, so IMO there are bigger fish to fry first.
I think "usability" considerations play a big role in a library acceptance. In this case, for instance, if one's code is correct everything works fine: just have terminals first and the unary_expr pattern last. But when writing the code in the first instance, the patterns for terminals may be wrong . It is not so easy (at least for me) to find what's going wrong in a complex mesh of transforms when some other pattern matches unexpectedly. You just get the wrong result. With some luck this results in a type error somewhere else, but otherwise finding the problem in absence of a meta debugger is, mmm, challenging. Btw, for now I've wrapped my unary_expr pattern in the following way: proto::and_< proto::not_<proto::terminal<proto::_> >, proto::unary_expr<proto::_, proto::_> > so it is not terrible, but it might bite others in future. Best regards, Maurizio

Maurizio Vitale wrote:
On Jun 14, 2007, at 12:57 PM, Eric Niebler wrote:
if you could find a way to completely hide the fact that a terminal is a unary_expr from the user I think it would be much cleaner for a boost::proto release. I'll give the issue some thought. One possibility is shoehorn into
Maurizio Vitale wrote: proto the concept of a nullary expression, which is what terminals should rightly be. Of course, what is there already works, so IMO there are bigger fish to fry first.
I think "usability" considerations play a big role in a library acceptance.
I'm willing to bet that you have been taking advantage of the usability that terminals-as-unary-expressions brings, without even noticing it. Q: How do you get the argument of a unary expression? A: proto::arg(). Q: How do you get the value out of a terminal? A: proto::arg(). This works because terminals have one "argument", just as unary expressions do. I like this. I don't want to have to use a separate function to extract the argument from a terminal. But if I draw a crisp line between terminals and unary expressions, I might have to. Otherwise, the line stays blurry. I really don't think it's as cut-and-dry as you make it. I'll think about it. -- Eric Niebler Boost Consulting www.boost-consulting.com

On Jun 14, 2007, at 7:00 PM, Eric Niebler wrote:
Maurizio Vitale wrote:
On Jun 14, 2007, at 12:57 PM, Eric Niebler wrote:
if you could find a way to completely hide the fact that a terminal is a unary_expr from the user I think it would be much cleaner for a boost::proto release. I'll give the issue some thought. One possibility is shoehorn into
Maurizio Vitale wrote: proto the concept of a nullary expression, which is what terminals should rightly be. Of course, what is there already works, so IMO there are bigger fish to fry first.
I think "usability" considerations play a big role in a library acceptance.
I'm willing to bet that you have been taking advantage of the usability that terminals-as-unary-expressions brings, without even noticing it.
Q: How do you get the argument of a unary expression? A: proto::arg().
Q: How do you get the value out of a terminal? A: proto::arg().
This works because terminals have one "argument", just as unary expressions do. I like this. I don't want to have to use a separate function to extract the argument from a terminal. But if I draw a crisp line between terminals and unary expressions, I might have to.
Maybe. I don't have the familiarity you have with the implementation. I don't immediately see why between overloading (for run-time calls) and specialization (for compile-time dispatching) it shouldn't be possible to have the same name for proto::arg() for terminals and unary_expr (with different implementations where needed). But if it comes to that, I'd personally prefer different names for ::arg than unary_function matching a terminal: the former is easy to learn and getting it wrong would result in compiler errors, the latter can result in hard to track errors.
Otherwise, the line stays blurry.
I really don't think it's as cut-and-dry as you make it. I'll think about it.

Maurizio Vitale wrote:
On Jun 14, 2007, at 12:09 PM, Eric Niebler wrote:
I can easily patch up proto::matches<> such that tag::terminal does not match _, so that a terminal will not match unary_expr<_,_>. It *will* still match unary_expr<tag::terminal,_> though. Would that be ok?
Would be helpful (and would solve my specific case completely), but
This behavior is now implemented.
if you could find a way to completely hide the fact that a terminal is a unary_expr from the user I think it would be much cleaner for a boost::proto release.
I now have some ideas about how to add nullary expressions to proto. I'll investigate. -- Eric Niebler Boost Consulting www.boost-consulting.com The Astoria Seminar ==> http://www.astoriaseminar.com

Eric Niebler wrote:
Maurizio Vitale wrote:
if you could find a way to completely hide the fact that a terminal is a unary_expr from the user I think it would be much cleaner for a boost::proto release.
I now have some ideas about how to add nullary expressions to proto. I'll investigate.
Proto terminals are now nullary expressions. The change is that terminal<int>::type used to be: expr<tag::terminal, args1<int>, 1> and now it is: expr<tag::terminal, args0<int>, 0> Everything else stays pretty much the same. This is a breaking change, so watch out. -- Eric Niebler Boost Consulting www.boost-consulting.com The Astoria Seminar ==> http://www.astoriaseminar.com

On Jun 15, 2007, at 2:34 PM, Eric Niebler wrote:
Eric Niebler wrote:
Maurizio Vitale wrote:
if you could find a way to completely hide the fact that a terminal is a unary_expr from the user I think it would be much cleaner for a boost::proto release.
I now have some ideas about how to add nullary expressions to proto. I'll investigate.
Proto terminals are now nullary expressions. The change is that terminal<int>::type used to be:
expr<tag::terminal, args1<int>, 1>
and now it is:
expr<tag::terminal, args0<int>, 0>
Everything else stays pretty much the same. This is a breaking change, so watch out.
Thanks Eric. My code and the testcases I have work fine, what should I be on the lookout for? I didn't change anything in my code, patterns are what they were and access to the terminal data is through unmodified proto::arg{,_c} Regards, Maurizio

Maurizio Vitale wrote:
On Jun 15, 2007, at 2:34 PM, Eric Niebler wrote:
Proto terminals are now nullary expressions. The change is that terminal<int>::type used to be:
expr<tag::terminal, args1<int>, 1>
and now it is:
expr<tag::terminal, args0<int>, 0>
Everything else stays pretty much the same. This is a breaking change, so watch out.
Thanks Eric.
My code and the testcases I have work fine, what should I be on the lookout for? I didn't change anything in my code, patterns are what they were and access to the terminal data is through unmodified proto::arg{,_c}
Yes, those interfaces are unchanged. If you code still compiles, you're safe. (You're using the code from CVS, right? I haven't updated the .zip in the file vault yet.) -- Eric Niebler Boost Consulting www.boost-consulting.com The Astoria Seminar ==> http://www.astoriaseminar.com

On Jun 15, 2007, at 4:47 PM, Eric Niebler wrote:
Maurizio Vitale wrote:
On Jun 15, 2007, at 2:34 PM, Eric Niebler wrote:
Proto terminals are now nullary expressions. The change is that terminal<int>::type used to be:
expr<tag::terminal, args1<int>, 1>
and now it is:
expr<tag::terminal, args0<int>, 0>
Everything else stays pretty much the same. This is a breaking change, so watch out.
Thanks Eric.
My code and the testcases I have work fine, what should I be on the lookout for? I didn't change anything in my code, patterns are what they were and access to the terminal data is through unmodified proto::arg{,_c}
Yes, those interfaces are unchanged. If you code still compiles, you're safe. (You're using the code from CVS, right? I haven't updated the .zip in the file vault yet.)
Yes CVS and when my code unexpectedly compiled I checked and I indeed have the new type for terminals. Great job Eric.
participants (4)
-
Eric Niebler
-
Larry Evans
-
Maurizio Vitale
-
Maurizio Vitale