From 56da6c91b716d1eb4a9f90145349b2014dc41db8 Mon Sep 17 00:00:00 2001 From: Anders Kaseorg Date: Sat, 24 Dec 2022 11:21:16 -0800 Subject: [PATCH] Fix parsing of tuple with named expression as context manager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because the ‘with’ item grammar disallows named expressions, CPython parses ‘with (a := 0, b := 1):’ as a tuple rather than two ‘with’ items. Signed-off-by: Anders Kaseorg --- parser/python.lalrpop | 74 ++++--- ...n_parser__with__tests__with_statement.snap | 183 ++++++++++-------- 2 files changed, 141 insertions(+), 116 deletions(-) diff --git a/parser/python.lalrpop b/parser/python.lalrpop index f586f0a..3979018 100644 --- a/parser/python.lalrpop +++ b/parser/python.lalrpop @@ -164,6 +164,11 @@ TestOrStarExpr: ast::Expr = { StarExpr, }; +NamedOrStarExpr: ast::Expr = { + NamedExpression, + StarExpr, +}; + TestOrStarNamedExpr: ast::Expr = { NamedExpressionTest, StarExpr, @@ -518,7 +523,7 @@ WithItems: Vec = { #[inline] WithItemsNoAs: Vec = { - > => { + >> => { <>.into_iter().map(|context_expr| ast::Withitem { context_expr, optional_vars: None }).collect() }, } @@ -746,27 +751,28 @@ Test: ast::Expr = { }; NamedExpressionTest: ast::Expr = { - > => { - if let Some(l) = left { - ast::Expr { - location, - end_location: Some(end_location), - custom: (), - node: ast::ExprKind::NamedExpr { - target: Box::new(ast::Expr::new( - location, - end_location, - ast::ExprKind::Name { id: l.0, ctx: ast::ExprContext::Store }, - )), - value: Box::new(right), - } - } - } else { - right - } - } + NamedExpression, + Test<"all">, } +NamedExpression: ast::Expr = { + ":=" > => { + ast::Expr { + location, + end_location: Some(end_location), + custom: (), + node: ast::ExprKind::NamedExpr { + target: Box::new(ast::Expr::new( + location, + end_location, + ast::ExprKind::Name { id, ctx: ast::ExprContext::Store }, + )), + value: Box::new(value), + } + } + }, +}; + LambdaDef: ast::Expr = { "lambda" ?> ":" > => { let p = p.unwrap_or_else(|| { @@ -1066,7 +1072,7 @@ Atom: ast::Expr = { node: ast::ExprKind::ListComp { elt: Box::new(elt), generators } } }, - "(" > ")" if Goal != "no-withitems" => { + "(" >> ")" if Goal != "no-withitems" => { if elts.len() == 1 && trailing_comma.is_none() { elts.into_iter().next().unwrap() } else { @@ -1077,19 +1083,23 @@ Atom: ast::Expr = { ) } }, - "(" > ",")?> )*> ")" =>? { + "(" >> ",")?> )*> ")" =>? { if left.is_none() && right.is_empty() && trailing_comma.is_none() { - Err(LexicalError{ - error: LexicalErrorType::OtherError("cannot use starred expression here".to_string()), - location: mid.location, - })? + if matches!(mid.node, ast::ExprKind::Starred { .. }) { + Err(LexicalError{ + error: LexicalErrorType::OtherError("cannot use starred expression here".to_string()), + location: mid.location, + })? + } + Ok(mid) + } else { + let elts = left.into_iter().flatten().chain([mid]).chain(right).collect(); + Ok(ast::Expr::new( + location, + end_location, + ast::ExprKind::Tuple { elts, ctx: ast::ExprContext::Load }, + )) } - let elts = left.into_iter().flatten().chain([mid]).chain(right).collect(); - Ok(ast::Expr::new( - location, - end_location, - ast::ExprKind::Tuple { elts, ctx: ast::ExprContext::Load }, - )) }, "(" ")" => ast::Expr::new( location, diff --git a/parser/src/snapshots/rustpython_parser__with__tests__with_statement.snap b/parser/src/snapshots/rustpython_parser__with__tests__with_statement.snap index 031d6d8..7e3da24 100644 --- a/parser/src/snapshots/rustpython_parser__with__tests__with_statement.snap +++ b/parser/src/snapshots/rustpython_parser__with__tests__with_statement.snap @@ -1792,106 +1792,121 @@ expression: "parse_program(source, \"\").unwrap()" context_expr: Located { location: Location { row: 21, - column: 6, + column: 5, }, end_location: Some( Location { row: 21, - column: 12, + column: 21, }, ), custom: (), - node: NamedExpr { - target: Located { - location: Location { - row: 21, - column: 6, - }, - end_location: Some( - Location { + node: Tuple { + elts: [ + Located { + location: Location { row: 21, - column: 12, + column: 6, }, - ), - custom: (), - node: Name { - id: "a", - ctx: Store, - }, - }, - value: Located { - location: Location { - row: 21, - column: 11, - }, - end_location: Some( - Location { - row: 21, - column: 12, - }, - ), - custom: (), - node: Constant { - value: Int( - 0, + end_location: Some( + Location { + row: 21, + column: 12, + }, ), - kind: None, - }, - }, - }, - }, - optional_vars: None, - }, - Withitem { - context_expr: Located { - location: Location { - row: 21, - column: 14, - }, - end_location: Some( - Location { - row: 21, - column: 20, - }, - ), - custom: (), - node: NamedExpr { - target: Located { - location: Location { - row: 21, - column: 14, - }, - end_location: Some( - Location { - row: 21, - column: 20, + custom: (), + node: NamedExpr { + target: Located { + location: Location { + row: 21, + column: 6, + }, + end_location: Some( + Location { + row: 21, + column: 12, + }, + ), + custom: (), + node: Name { + id: "a", + ctx: Store, + }, + }, + value: Located { + location: Location { + row: 21, + column: 11, + }, + end_location: Some( + Location { + row: 21, + column: 12, + }, + ), + custom: (), + node: Constant { + value: Int( + 0, + ), + kind: None, + }, + }, }, - ), - custom: (), - node: Name { - id: "b", - ctx: Store, }, - }, - value: Located { - location: Location { - row: 21, - column: 19, - }, - end_location: Some( - Location { + Located { + location: Location { row: 21, - column: 20, + column: 14, }, - ), - custom: (), - node: Constant { - value: Int( - 1, + end_location: Some( + Location { + row: 21, + column: 20, + }, ), - kind: None, + custom: (), + node: NamedExpr { + target: Located { + location: Location { + row: 21, + column: 14, + }, + end_location: Some( + Location { + row: 21, + column: 20, + }, + ), + custom: (), + node: Name { + id: "b", + ctx: Store, + }, + }, + value: Located { + location: Location { + row: 21, + column: 19, + }, + end_location: Some( + Location { + row: 21, + column: 20, + }, + ), + custom: (), + node: Constant { + value: Int( + 1, + ), + kind: None, + }, + }, + }, }, - }, + ], + ctx: Load, }, }, optional_vars: None,