diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..fd89ec5b --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-01-20 - Lifetime Refactoring in Parser to Eliminate Token Cloning +**Learning:** In the Rust parser (`compiler/parser/src/parser.rs`), methods like `advance()` and `previous()` originally returned a reference tied to `&mut self`. Because `Token` was still borrowing `self` mutably, the parser couldn't call methods like `self.parse_prefix` (which requires another `&mut self` borrow) without first calling `.clone()` on the token to drop the initial borrow. +**Action:** By explicitly defining the return lifetime as `&'a Token` (tied to the lifetime of the underlying token slice `&'a [Token]`, rather than the `Parser` instance), the mutable borrow of `self` ends immediately. This elegantly satisfies the borrow checker while removing the overhead of cloning tokens throughout `expressions.rs` and `statements.rs`. Look for similar lifetime constraints elsewhere in the compiler that force unnecessary copies. diff --git a/compiler/parser/src/expressions.rs b/compiler/parser/src/expressions.rs index 3350c0d1..fbb23782 100644 --- a/compiler/parser/src/expressions.rs +++ b/compiler/parser/src/expressions.rs @@ -14,8 +14,8 @@ impl<'a> Parser<'a> { precedence: Precedence, reporter: &mut DiagnosticReporter, ) -> ParseResult { - let token = self.advance().clone(); - let mut left = self.parse_prefix(&token, reporter)?; + let token = self.advance(); + let mut left = self.parse_prefix(token, reporter)?; while !self.is_at_end() { let next_prec = self.peek().kind.precedence(); @@ -41,8 +41,8 @@ impl<'a> Parser<'a> { break; } - let next_token = self.advance().clone(); - left = self.parse_infix(left, &next_token, reporter)?; + let next_token = self.advance(); + left = self.parse_infix(left, next_token, reporter)?; } Ok(left) diff --git a/compiler/parser/src/parser.rs b/compiler/parser/src/parser.rs index 55c67c16..496b3056 100644 --- a/compiler/parser/src/parser.rs +++ b/compiler/parser/src/parser.rs @@ -162,7 +162,7 @@ impl<'a> Parser<'a> { } /// Advances the cursor and returns the previous token. - pub(crate) fn advance(&mut self) -> &Token { + pub(crate) fn advance(&mut self) -> &'a Token { if !self.is_at_end() { self.pos += 1; } @@ -170,7 +170,7 @@ impl<'a> Parser<'a> { } /// Returns the previous token. - pub(crate) fn previous(&self) -> &Token { + pub(crate) fn previous(&self) -> &'a Token { &self.tokens[self.pos - 1] } diff --git a/compiler/parser/src/statements.rs b/compiler/parser/src/statements.rs index 0b728c01..fc844a40 100644 --- a/compiler/parser/src/statements.rs +++ b/compiler/parser/src/statements.rs @@ -67,7 +67,7 @@ impl<'a> Parser<'a> { || self.check(TokenKind::Send) { let start_pos = self.peek().span.start; - let kw_token = self.peek().clone(); + let kw_token = self.peek(); if kw_token.kind == TokenKind::Return { reporter.report(techscript_errors::Diagnostic::new( techscript_errors::DiagnosticLevel::Warning, diff --git a/patch_expressions.diff b/patch_expressions.diff new file mode 100644 index 00000000..f0802c57 --- /dev/null +++ b/patch_expressions.diff @@ -0,0 +1,17 @@ +--- compiler/parser/src/expressions.rs ++++ compiler/parser/src/expressions.rs +@@ -17,7 +17,7 @@ + let token = self.advance(); +- let mut left = self.parse_prefix(&token, reporter)?; ++ let mut left = self.parse_prefix(token, reporter)?; + + while !self.is_at_end() { + let next_prec = self.peek().kind.precedence(); +@@ -44,7 +44,7 @@ + + let next_token = self.advance(); +- left = self.parse_infix(left, &next_token, reporter)?; ++ left = self.parse_infix(left, next_token, reporter)?; + } + + Ok(left)