-
Notifications
You must be signed in to change notification settings - Fork 210
v2 parser: change assignment's lhs to expression #936
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
Conversation
21 Jul 2023
moving data between mem and storage
notes on argotorg#907
notes on argotorg#907
Notes revised on argotorg#907
fixed notes on mem to storage, compare_bud and compare still need to be fixed
storage to memory copying
Merge pull request argotorg#907 from saifalkatout/master
create an eq trait and implement ir for the various primitive types
Changed Eq for signed integers
Change Eq for signed integers 2.
Finish buf refactor
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I missed another one.
crates/parser2/src/ast/stmt.rs
Outdated
| pub fn expr(&self) -> Option<super::Expr> { | ||
| support::child(self.syntax()) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect; please refer to ast::expr::BinExpr implementation as an example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just fixed it, I'll start working on AugAssignStmtScope, and then will squash the commits.
| } | ||
| pub fn parse_lhs_aug<S: TokenStream>(parser: &mut Parser<S>) -> bool { | ||
| //include Lp and Lb ? | ||
| parse_expr_with_min_bp(parser, 146, true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did some work on augassign; Is this bp acceptable ? (currently it takes dot, Lparen, Lbrack, and Lt)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bp seems fine.
However, I've started considering changing the specifications of the assignment. Clearly, it'd be better to define assign and augassign as expressions rather than statements.
Because:
- We can implement a trait
AddAssignor something like that. - Emitting a good error message in the parsing phase is difficult when they are defined as statements. e.g., please consider
x + 1 += 1.
This is my fault. I didn't fully consider it, even though I felt I should've when I implemented parser2. Sorry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, I think we could take two options.
- Merge this PR as is, then someone could change the specification, or
- You could work on the change in this PR if you are willing to help us
Anyways, I'm sorry that I didn't consider it fully and gave insufficient suggestions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sbillig Any thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries, I can start working on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No worries, I can start working on it.
Ok, thanks!
the rough sketch for implementing this would be
- Modify the parser so that it can parse them as expressions
- Change the HIR definition for
AssignandAugAssign - Rewrite the HIR lowering and remove the unnecessary desugaring related types(e.g.,
hir::span::DesugaredOrigin::AugAssign)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please feel free to let me know if you find any trouble.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the push_expr receives an origin so maybe we shouldn't delete the hir::span::DesugaredOrigin::AugAssign ??
We don't need desugar_aug_assign and DesugaredOrigin::AugAssign because we no longer need to desugar them. Instead, you need to modify hir::expr::Expr like the one below.
pub enum Expr {
Assign(ExprId, ExprId),
AugAssign(ExprId, ExprId, BinOp),
...
}Then you could lower ast::Assign and ast::AugAssign in lower_expr. Please refer to lower_expr to see how push_expr is used inside it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the aug_assign and assign are going to be parsed in the parse_atom_expr correct ??
I don't think you need to modify parse_expr_atom. Conceptually, what you need to do is the same that BinExprScope does. The one difficult thing is we don't have tokens to represent AugAssign operators(e.g., +=).
This would be solved in two different ways.
- Just modify
SyntaxKindand accept them as an atom kind (Please refer toSyntaxKind). - Modify
infix_binding_powerandbump_bin_opto handle them, then ifbump_bin_opindicates that the binary operator is assign or aug assign, callset_kindinBinExprScope::parsemethod. Please refer toParenScope::parseforset_kindmethod.
I think option 1. is easier to implement, but I prefer option 2. because it allows us to extract the span of the binary operation part in augassign op(e.g., span for + in +=)
crates/parser2/src/ast/stmt.rs
Outdated
| support::child(self.syntax()) | ||
| /// Returns the expression of the lhs and rhs of the assignment. | ||
| pub fn lhs_expr(&self) -> Option<super::Expr> { | ||
| support::children(self.syntax()).next() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can I convert this to support::child(...) ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The result is the same. Also, it seems the performance wouldn't be different. Is there any motivation to change to support::child?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just to keep the code concise. Also, with next, I feel it implies that it is pointing at the second item rather the first item, since it is not clear where the iter is starting (index -1 or 0).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel it implies that it is pointing at the second item rather the first item, since it is not clear where the iter is starting (index -1 or 0).
This is a well-known Rust idiom. Also, It'd be pretty strange if an iterator starts with the second element in any language, right?
When I wrote support::children(...).next(), I might feel it'd be slightly readable because lhs and rhs are symmetric(but I don't remember why, actually).
Anyways, this is clearly a bikeshedding discussion. I'm perfectly fine with either.
|
Hey Yoshi, this is a rough sketch of what im trying to do with |
|
Sorry for the late response.
For example, x += 1 + 1is AugAssignExpr@52..62
PathExpr@52..53
Path@52..53
PathSegment@52..53
Ident@52..53 "x"
WhiteSpace@53..54 " "
Plus@54..55 "+"
Eq@55..56 "="
WhiteSpace@56..57 " "
BinExpr@57..62
LitExpr@57..58
Lit@57..58
Int@57..58 "1"
WhiteSpace@58..59 " "
Plus@59..60 "+"
WhiteSpace@60..61 " "
LitExpr@61..62
Lit@61..62
Int@61..62 "1, and not the one below that this PR generates. BinExpr@52..62
AugAssignExpr@52..58
PathExpr@52..53
Path@52..53
PathSegment@52..53
Ident@52..53 "x"
WhiteSpace@53..54 " "
AugAssignExpr@54..58
Plus@54..55 "+"
Eq@55..56 "="
WhiteSpace@56..57 " "
LitExpr@57..58
Lit@57..58
Int@57..58 "1"
WhiteSpace@58..59 " "
Plus@59..60 "+"
WhiteSpace@60..61 " "
LitExpr@61..62
Lit@61..62
Int@61..62 "1" |
|
Hey Yoshi, this is some bare bones changes, I want to see what CSTs will be generated |
|
Hey Yoshi, regarding the latest push:
2)This is the CST created from
and I will have to write tests for our new
|
IIRC, we decided to make them exprs.
Almost all parts of MIR will be discarded, so I think you don't need to modify MIR.
You have two options.
|
|
Oh, regarding the answer about 3., I did not mean you should not try to find a better solution. I just described my thoughts and the current situation. Your research is always welcome, of course. |
|
Hey Yoshi, just some last comments from my end:
3)I would still need to squash changes into 1 commit, just need to know how to proceed with 1. |
Rust publishes a new version every six weeks, and Clippy as well. It often causes new lint errors that are detected by newly added lint pass of Clippy.
I fixed them in #931, so there is no need to fix them.
I vaguely thought that it might be nice if we could guarantee side effects only happen in
You can squash them into multiple commits that have reasonable granularity. I mean, you don't necessarily need to squash them to a single commit. But it's up to you. |
|
Hey @Y-Nak, do you think the granularity is acceptable like this ? GH doesn't allow squashing merge commits, I found a solution for this by reverting all the changes then merging to the latest commit and pushing without syncing with the main branch, please let me know if this is necessary. Other than this, I think we can merge ? |
Y-Nak
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left some comments. After those comments are addressed, I'll revisit this PR again for a detailed review.
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey Yoshi, I fixed all the comments, had to rewrite this test because we stopped desugaring.
| Star | Slash | Percent => { | ||
| if is_aug(parser) { | ||
| (38, 39) | ||
| } else { | ||
| (130, 131) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what I came up with, to give accurate BPs by peeking forward, is this approach concise/fast enough ? if so I'll do the same in the Plus | Minus and Lshift | Rshift arms.
|
hey @Y-Nak could u check my latest comments plz |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a suggestion about
- the assignment's binding power and its associativity
- wrong
infix_binding_powerusage
Also, would you add LazyAugAssignExprSpan and LazyAssignSpan in hir::span::expr? Please refer to the implementation for LazyBinExprSpan for the implementation details.
crates/parser2/src/parser/expr.rs
Outdated
| if is_aug(parser) { | ||
| (38, 39) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if is_aug(parser) { | |
| (38, 39) | |
| if is_aug(parser) { | |
| (11, 10) |
I think aug assign and assign operators should have a minimum binding power and right associativity.
e.g., a = b = c should be parsed as a = (b = c).
| Dot => (151, 150), | ||
| Eq => { | ||
| // `Assign` and `AugAssign` have the same binding power | ||
| (40, 41) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| (40, 41) | |
| (11, 10) |
ditto
| if is_aug(parser) { | ||
| self.set_kind(SyntaxKind::AugAssignExpr); | ||
| bump_aug_assign_op(parser); | ||
| let (_, rbp) = infix_binding_power(parser).unwrap(); | ||
| parser.bump_or_recover(SyntaxKind::Eq, "expected `=`", None); | ||
| parse_expr_with_min_bp(parser, rbp, true); | ||
| } else if is_asn(parser) { | ||
| self.set_kind(SyntaxKind::AssignExpr); | ||
| parser.set_newline_as_trivia(false); | ||
| let (_, rbp) = infix_binding_power(parser).unwrap(); | ||
| parser.bump_or_recover(SyntaxKind::Eq, "expected `=`", None); | ||
| parse_expr_with_min_bp(parser, rbp, true); | ||
| } else { | ||
| bump_bin_op(parser); | ||
| parse_expr_with_min_bp(parser, rbp, false); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| if is_aug(parser) { | |
| self.set_kind(SyntaxKind::AugAssignExpr); | |
| bump_aug_assign_op(parser); | |
| let (_, rbp) = infix_binding_power(parser).unwrap(); | |
| parser.bump_or_recover(SyntaxKind::Eq, "expected `=`", None); | |
| parse_expr_with_min_bp(parser, rbp, true); | |
| } else if is_asn(parser) { | |
| self.set_kind(SyntaxKind::AssignExpr); | |
| parser.set_newline_as_trivia(false); | |
| let (_, rbp) = infix_binding_power(parser).unwrap(); | |
| parser.bump_or_recover(SyntaxKind::Eq, "expected `=`", None); | |
| parse_expr_with_min_bp(parser, rbp, true); | |
| } else { | |
| bump_bin_op(parser); | |
| parse_expr_with_min_bp(parser, rbp, false); | |
| } | |
| if is_aug(parser) { | |
| self.set_kind(SyntaxKind::AugAssignExpr); | |
| bump_aug_assign_op(parser); | |
| parser.bump_expected(SyntaxKind::Eq); | |
| parse_expr_with_min_bp(parser, rbp, true); | |
| } else if is_asn(parser) { | |
| self.set_kind(SyntaxKind::AssignExpr); | |
| parser.set_newline_as_trivia(false); | |
| parser.bump_expected(SyntaxKind::Eq); | |
| parse_expr_with_min_bp(parser, rbp, true); | |
| } else { | |
| bump_bin_op(parser); | |
| parse_expr_with_min_bp(parser, rbp, false); | |
| } |
- It's not necessary to recalculate
rbp, also this would fix a bug that is introduced inif is_augblock(theinfix_binding_powerreturned a wrong binding power). - I think it'd be clearer to call
parser.bump_expected(SyntaxKind:Eq)instead ofparser.bump_or_recover(SyntaxKind::Eq, ...)since you already assert it inside the if conditions.



resolves #875
What was wrong?
The new parser (v2 branch) wrongly supposes the assignment destination is a pattern, but it should be an expression.
How was it fixed?
Parse lhs in assignment statements as an expression.