-
Notifications
You must be signed in to change notification settings - Fork 299
Precedence doesn't seem to work with macros #951
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
It seems that #[inline]
UnaryExpr: Expr = {
"-" <e:Expr> => ...
} and use this one in pub Expr = {
...
#[precedence(level="2")] #[assoc(side="right")]
UnaryExpr,
#[precedence(level="3")] #[assoc(side="left")]
Binary<"*"> => ...
...
} I get a similar shift-reduce conflict. |
I wonder whether the last comment in #667 about macro substitution in relation with precedence relates to the same issue mentioned here. |
Argh, I entirely forgot about #667 to be honest. I couldn't invest enough time into it then. IIRC, it's very likely that precedence annotations are substituted before macro expansion, which would fit your observations. One possibility might be to perform elaboration after macro expansion, maybe this is the simplest approach (no need to handle issues with name capture as in #667). Or the #667 approach is another direction. I can't promise anything, but I'll try to give it a look when I can. |
Thank you for your answer! Indeed we certainly do not need the full of #667 to fix this issue. |
@stephanemagnenat Sorry I took so long, but for some reason this issue came back to my mind as I'm working on a grammar that makes use of macro + precedence. I think they can both work together quite well, but needs a tweak:
ExplanationBefore precedence annotation existedTo understand why, you'll have to understand how precedence and annotation rewriting works. Before precedence annotations were introduced, you would have to manually define some levels and carefully make them reference each other in the right order. Taking a slightly modified version of the calculator example from the manual:
NowThis can be more compactly rewritten to the following using annotations:
The gist is that LALRPOP actually rewrites the second example to the first example, prior to macro expansion. In particular, you can't just take one rule outside of
Because after rewriting, external references to
This works better: the The trick is thus to have two independent occurrences of
Now, the precedence transformation will see two occurrences in SummaryPrecedence and associativity works through a rewriting process. This process rewrites the self referential occurrences of a rule
This is why some seemingly valid refactoring don't work. Once you aren't in |
Thanks for the detailed write-up! Would switching the order of precedence and macro resolution fix this? Swapping their order does pass the current test suite, although I'm not sure how comprehensive our test coverage of precedence and macro interaction is. In a fairly short amount of time thinking about it, I can't think of any new issues that the switch would introduce, but that's not something I'm super confident in. |
Hmm, this is a good question. I don't know how Changing the order would solve the artificial need of having macros that are "linear" (in the sense of substructural logics: each argument is used exactly once), and would be backward compatible with the previous practice (that wouldn't mess up such linear macros, as the result would be the same). If you used a non-linear macro that duplicates its argument in a block with precedence annotations on purpose (albeit I'm not sure why anyone would want to do that) to avoid multiple substitution, I think you can always recover the old behavior by setting Hence, my first impression is that swapping the order of macro resolution and precedence would be ok. |
Also, we could mention #667, which wants precedence annotations to work within macros. Your proposal would probably be incompatible with that (as first expanding a macro and then substituting would be quite different that first substituting and then doing macro expansion, if the macro is used in a block with precedence annotations). That being said, I'm not sure we want to do #667, and I'm not sure about its original motivation either. |
Inlining is its own step in normalizing, after both precedence and macro expansion
This is a good point. In the abstract, letting users do precedence inside macros seems desirable, from a "least surprise" principle (ie Is there a reason users should expect precedence to not work inside a macro def?). I guess your hesitation is the practical points about how the two different rewrites interact? (And particular the associativity point raise in #667) Ideally, I'd think we'd like to ultimately get to a place where the original code here works and precedence works inside macros. I don't really have my head fully enough around the precedence rewriting at this point to reason too deeply about that though. BTW, poking around at the normalize code, I did discover that we do have a test for the sort of macro/precedence interaction with the extra args that you mentioned above. If we end up making changes in this area, we should probably expand our test coverage some more (eg a self-contained version of @stephanemagnenat's example in this issue). |
If we want that, then playing with the order of the two phases won't be enough. We would need to:
However, I find both approaches (making this issue work and precedence annotation works in macro) to be somehow opposed, at least on a philosophical level. To make the present issue work, the solution is perform macro expansion first then precedence elaboration. The philosophy could thus be summed up as macro are substituted verbatim before precedence elaboration takes place. Following this simple semantics, we might expect that the last example could be rewritten as:
Indeed, if macro expansions take place before, then we could expect that the precedence annotation is inline into Supporting the two feels a bit like an unnatural mix, which reflects in the fact that you can't express the transformation pipeline with two nicely separated and non-interfering phases, but need to interleave them somehow. We can still do it. And if we want to choose one or the other, I don't have a strong opinion either way - but in general when you need to perform case analysis to explain the semantics or the interaction of some constructs, it's a sign that said semantics might be a bit too ad-hoc IMHO. Or at least, it needs to be strongly motivated by practical examples both ways. |
Yeah, I think that way of thinking about it makes a ton of sense. I guess in that case, at least a helpful error message on the case(s) we don't support would be helpful. That leaves the question open of which (if any) of these two use cases we do want to support. I'm not immediately sure on that, and I haven't looked at #667 much. I'll try to give it some thought and develop a personal opinion. |
Hello, I have a grammar for an expression-based language like this (simplified):
and precedence works.
If now I use macros for unary and binary operations like this:
and I modify my
Expr
to use them:I get a shift-reduce conflict:
I tried with
#[inline]
on the macros but it didn't help. So it seems to me that using macros breaks the precedence, which is a bit surprising as I would assume that given their names macros would just expand and respect the precedence annotations.So my questions are:
The text was updated successfully, but these errors were encountered: