Skip to content

Commit 85430a9

Browse files
Share postfix parsing and reduce expression copies (#29480)
## Summary We skip prefix-operator dispatch for identifiers and parse atoms together with their postfix chains. Keeping this work in `parse_primary_expression` makes the grouping-parenthesis flag local and avoids passing an intermediate `ParsedExpr` between atom and postfix parsing.
1 parent 0a977a8 commit 85430a9

1 file changed

Lines changed: 84 additions & 92 deletions

File tree

‎crates/ruff_python_parser/src/parser/expression.rs‎

Lines changed: 84 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -335,94 +335,93 @@ impl<'src> Parser<'src> {
335335
context: ExpressionContext,
336336
) -> ParsedExpr {
337337
let token = self.current_token_kind();
338-
let start = self.node_start();
339338

340-
if let Some(unary_op) = token.as_unary_operator() {
341-
let expr = self.parse_unary_expression(unary_op, context);
339+
if token != TokenKind::Identifier {
340+
if let Some(unary_op) = token.as_unary_operator() {
341+
let expr = self.parse_unary_expression(unary_op, context);
342342

343-
if matches!(unary_op, UnaryOp::Not) {
344-
if left_precedence > OperatorPrecedence::Not {
345-
self.add_error(
346-
ParseErrorType::OtherError(
347-
"Boolean 'not' expression cannot be used here".to_string(),
348-
),
349-
&expr,
350-
);
351-
}
352-
} else {
353-
// > The power operator `**` binds less tightly than an arithmetic
354-
// > or bitwise unary operator on its right, that is, 2**-1 is 0.5.
355-
//
356-
// Reference: https://docs.python.org/3/reference/expressions.html#id21
357-
if left_precedence > OperatorPrecedence::PosNegBitNot
358-
&& left_precedence != OperatorPrecedence::Exponent
359-
{
360-
self.add_error(
361-
ParseErrorType::OtherError(format!(
362-
"Unary '{unary_op}' expression cannot be used here",
363-
)),
364-
&expr,
365-
);
343+
if matches!(unary_op, UnaryOp::Not) {
344+
if left_precedence > OperatorPrecedence::Not {
345+
self.add_error(
346+
ParseErrorType::OtherError(
347+
"Boolean 'not' expression cannot be used here".to_string(),
348+
),
349+
&expr,
350+
);
351+
}
352+
} else {
353+
// > The power operator `**` binds less tightly than an arithmetic
354+
// > or bitwise unary operator on its right, that is, 2**-1 is 0.5.
355+
//
356+
// Reference: https://docs.python.org/3/reference/expressions.html#id21
357+
if left_precedence > OperatorPrecedence::PosNegBitNot
358+
&& left_precedence != OperatorPrecedence::Exponent
359+
{
360+
self.add_error(
361+
ParseErrorType::OtherError(format!(
362+
"Unary '{unary_op}' expression cannot be used here",
363+
)),
364+
&expr,
365+
);
366+
}
366367
}
368+
369+
return Expr::UnaryOp(expr).into();
367370
}
368371

369-
return Expr::UnaryOp(expr).into();
370-
}
372+
match token {
373+
TokenKind::Star => {
374+
let starred_expr = self.parse_starred_expression(context);
371375

372-
match token {
373-
TokenKind::Star => {
374-
let starred_expr = self.parse_starred_expression(context);
376+
if left_precedence > OperatorPrecedence::None
377+
|| !context.is_starred_expression_allowed()
378+
{
379+
self.add_error(
380+
ParseErrorType::InvalidStarredExpressionUsage,
381+
&starred_expr,
382+
);
383+
}
375384

376-
if left_precedence > OperatorPrecedence::None
377-
|| !context.is_starred_expression_allowed()
378-
{
379-
self.add_error(ParseErrorType::InvalidStarredExpressionUsage, &starred_expr);
385+
return Expr::Starred(starred_expr).into();
380386
}
387+
TokenKind::Await => {
388+
let await_expr = self.parse_await_expression();
381389

382-
return Expr::Starred(starred_expr).into();
383-
}
384-
TokenKind::Await => {
385-
let await_expr = self.parse_await_expression();
390+
// `await` expressions cannot be nested
391+
if left_precedence >= OperatorPrecedence::Await {
392+
self.add_error(
393+
ParseErrorType::OtherError(
394+
"Await expression cannot be used here".to_string(),
395+
),
396+
&await_expr,
397+
);
398+
}
386399

387-
// `await` expressions cannot be nested
388-
if left_precedence >= OperatorPrecedence::Await {
389-
self.add_error(
390-
ParseErrorType::OtherError(
391-
"Await expression cannot be used here".to_string(),
392-
),
393-
&await_expr,
394-
);
400+
return Expr::Await(await_expr).into();
401+
}
402+
TokenKind::Lambda => {
403+
// Lambda expression isn't allowed in this context but we'll still parse it and
404+
// report an error for better recovery.
405+
let lambda_expr = self.parse_lambda_expr();
406+
self.add_error(ParseErrorType::InvalidLambdaExpressionUsage, &lambda_expr);
407+
return Expr::Lambda(lambda_expr).into();
395408
}
409+
TokenKind::Yield => {
410+
let expr = self.parse_yield_expression();
396411

397-
return Expr::Await(await_expr).into();
398-
}
399-
TokenKind::Lambda => {
400-
// Lambda expression isn't allowed in this context but we'll still parse it and
401-
// report an error for better recovery.
402-
let lambda_expr = self.parse_lambda_expr();
403-
self.add_error(ParseErrorType::InvalidLambdaExpressionUsage, &lambda_expr);
404-
return Expr::Lambda(lambda_expr).into();
405-
}
406-
TokenKind::Yield => {
407-
let expr = self.parse_yield_expression();
412+
if left_precedence > OperatorPrecedence::None
413+
|| !context.is_yield_expression_allowed()
414+
{
415+
self.add_error(ParseErrorType::InvalidYieldExpressionUsage, &expr);
416+
}
408417

409-
if left_precedence > OperatorPrecedence::None
410-
|| !context.is_yield_expression_allowed()
411-
{
412-
self.add_error(ParseErrorType::InvalidYieldExpressionUsage, &expr);
418+
return expr.into();
413419
}
414-
415-
return expr.into();
420+
_ => {}
416421
}
417-
_ => {}
418422
}
419423

420-
let lhs = self.parse_atom(context);
421-
422-
ParsedExpr {
423-
expr: self.parse_postfix_expression(lhs.expr, start, context),
424-
is_parenthesized: lhs.is_parenthesized,
425-
}
424+
self.parse_primary_expression(context)
426425
}
427426

428427
/// Parses an expression with a minimum precedence of bitwise `or`.
@@ -577,13 +576,14 @@ impl<'src> Parser<'src> {
577576
}
578577
}
579578

580-
/// Parses an atom.
579+
/// Parses an atom and any following calls, subscripts, or attributes.
581580
///
582-
/// See: <https://docs.python.org/3/reference/expressions.html#atoms>
583-
fn parse_atom(&mut self, context: ExpressionContext) -> ParsedExpr {
581+
/// See: <https://docs.python.org/3/reference/expressions.html#primaries>
582+
fn parse_primary_expression(&mut self, context: ExpressionContext) -> ParsedExpr {
583+
let mut is_parenthesized = false;
584584
let start = self.node_start();
585585

586-
let lhs = match self.current_token_kind() {
586+
let mut lhs = match self.current_token_kind() {
587587
TokenKind::Float => {
588588
let value = self.bump_float();
589589

@@ -647,7 +647,9 @@ impl<'src> Parser<'src> {
647647
self.parse_strings()
648648
}
649649
TokenKind::Lpar => {
650-
return self.parse_parenthesized_expression();
650+
let parsed = self.parse_parenthesized_expression();
651+
is_parenthesized = parsed.is_parenthesized;
652+
parsed.expr
651653
}
652654
TokenKind::Lsqb => self.parse_list_like_expression(),
653655
TokenKind::Lbrace => self.parse_set_or_dict_like_expression(),
@@ -670,31 +672,21 @@ impl<'src> Parser<'src> {
670672
}
671673
};
672674

673-
lhs.into()
674-
}
675-
676-
/// Parses a postfix expression in a loop until there are no postfix expressions left to parse.
677-
///
678-
/// For a given left-hand side, a postfix expression can begin with either `(` for a call
679-
/// expression, `[` for a subscript expression, or `.` for an attribute expression.
680-
///
681-
/// This method does nothing if the current token is not a candidate for a postfix expression.
682-
fn parse_postfix_expression(
683-
&mut self,
684-
mut lhs: Expr,
685-
start: TextSize,
686-
context: ExpressionContext,
687-
) -> Expr {
688675
loop {
689676
lhs = match self.current_token_kind() {
690677
TokenKind::Lpar => Expr::Call(self.parse_call_expression(lhs, start)),
691678
TokenKind::Lsqb => Expr::Subscript(self.parse_subscript_expression(lhs, start)),
692679
TokenKind::Dot => {
693680
Expr::Attribute(self.parse_attribute_expression(lhs, start, context))
694681
}
695-
_ => break lhs,
682+
_ => break,
696683
};
697684
}
685+
686+
ParsedExpr {
687+
expr: lhs,
688+
is_parenthesized,
689+
}
698690
}
699691

700692
/// Parse a call expression.

0 commit comments

Comments
 (0)