Skip to content

Commit a703954

Browse files
committed
Add namedexpr (aka walrus operator)
Main commit adding it in cpython: 8f59ee01be3d83d5513a9a3f654a237d77d80d9a But also this one: d4fceaafb8e3f8700d9ec6ab37a51e903392f74f because it's easier to implement both at the same time. No support for := in arguments yet.
1 parent 0750d1f commit a703954

File tree

4 files changed

+68
-5
lines changed

4 files changed

+68
-5
lines changed

Diff for: src/ast.rs

+2
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,8 @@ pub enum Expression {
260260
YieldFrom(Box<Expression>),
261261
Star(Box<Expression>),
262262
Lambdef(UntypedArgsList, Box<Expression>),
263+
/// Walrus operator: 1 := 2
264+
Named(Box<Expression>, Box<Expression>),
263265
}
264266

265267
/// An import statement.

Diff for: src/expressions.rs

+51-3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,24 @@ impl<ANS: AreNewlinesSpaces> ExpressionParser<ANS> {
2323
* Decorators
2424
*********************************************************************/
2525

26+
// namedexpr_test: test [':=' test]
27+
named!(pub namedexpr_test<StrSpan, Box<Expression>>,
28+
do_parse!(
29+
left: call!(Self::test) >>
30+
right: opt!(
31+
preceded!(
32+
ws_auto!(keyword!(":=")),
33+
call!(Self::test)
34+
)
35+
) >> (
36+
match right {
37+
None => left,
38+
Some(right) => Box::new(Expression::Named(left, right)),
39+
}
40+
)
41+
)
42+
);
43+
2644
// test: or_test ['if' or_test 'else' test] | lambdef
2745
named!(pub test<StrSpan, Box<Expression>>,
2846
alt!(
@@ -325,11 +343,11 @@ impl<ANS: AreNewlinesSpaces> ExpressionParser<ANS> {
325343
), |e| Box::new(e))
326344
);
327345

328-
// testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
346+
// testlist_comp: (namedexpr_test|star_expr) ( comp_for | (',' (namedexpr_test|star_expr))* [','] )
329347
named!(testlist_comp<StrSpan, TestlistCompReturn>,
330348
do_parse!(
331349
first: ws_auto!(alt!(
332-
call!(Self::test) => { |e: Box<_>| SetItem::Unique(*e) }
350+
call!(Self::namedexpr_test) => { |e: Box<_>| SetItem::Unique(*e) }
333351
| preceded!(char!('*'), call!(Self::expr)) => { |e: Box<_>| SetItem::Star(*e) }
334352
)) >>
335353
r: alt!(
@@ -338,7 +356,7 @@ impl<ANS: AreNewlinesSpaces> ExpressionParser<ANS> {
338356
ws_auto!(char!(',')),
339357
separated_list!(ws_auto!(char!(',')),
340358
alt!(
341-
call!(Self::test) => { |e: Box<_>| SetItem::Unique(*e) }
359+
call!(Self::namedexpr_test) => { |e: Box<_>| SetItem::Unique(*e) }
342360
| preceded!(char!('*'), call!(Self::expr)) => { |e: Box<_>| SetItem::Star(*e) }
343361
)
344362
),
@@ -1913,6 +1931,36 @@ mod tests {
19131931
);
19141932
}
19151933

1934+
#[test]
1935+
fn test_namedexpr() {
1936+
let namedexpr_test = ExpressionParser::<NewlinesAreNotSpaces>::namedexpr_test;
1937+
1938+
assert_parse_eq(
1939+
namedexpr_test(make_strspan("foo := bar")),
1940+
Ok((
1941+
make_strspan(""),
1942+
Box::new(Expression::Named(
1943+
Box::new(Expression::Name("foo".to_string())),
1944+
Box::new(Expression::Name("bar".to_string())),
1945+
)),
1946+
)),
1947+
);
1948+
1949+
assert_parse_eq(
1950+
namedexpr_test(make_strspan("foo := (bar := baz)")),
1951+
Ok((
1952+
make_strspan(""),
1953+
Box::new(Expression::Named(
1954+
Box::new(Expression::Name("foo".to_string())),
1955+
Box::new(Expression::Named(
1956+
Box::new(Expression::Name("bar".to_string())),
1957+
Box::new(Expression::Name("baz".to_string())),
1958+
)),
1959+
)),
1960+
)),
1961+
);
1962+
}
1963+
19161964
#[test]
19171965
fn test_multibop() {
19181966
let test = ExpressionParser::<NewlinesAreNotSpaces>::test;

Diff for: src/statements.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ named_args!(pub block(indent: usize) <StrSpan, Vec<Statement>>,
341341
| call!(simple_stmt)
342342
)
343343
);
344+
// used to parse `namedexpr_test ':' suite` in if_stmt and while_stmt
344345
named_args!(cond_and_block(indent: usize) <StrSpan, (Expression, Vec<Statement>)>,
345346
return_error!(do_parse!(
346347
spaces_nonl >>
@@ -384,7 +385,7 @@ named_args!(else_block(indent: usize) <StrSpan, Option<Vec<Statement>>>,
384385
)
385386
);
386387

387-
// if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
388+
// if_stmt: 'if' namedexpr_test ':' suite ('elif' namedexpr_test ':' suite)* ['else' ':' suite]
388389
named_args!(if_stmt(indent: usize) <StrSpan, CompoundStatement>,
389390
do_parse!(
390391
indent!(indent) >>
@@ -404,7 +405,7 @@ named_args!(if_stmt(indent: usize) <StrSpan, CompoundStatement>,
404405
)
405406
);
406407

407-
// while_stmt: 'while' test ':' suite ['else' ':' suite]
408+
// while_stmt: 'while' namedexpr_test ':' suite ['else' ':' suite]
408409
named_args!(while_stmt(indent: usize) <StrSpan, CompoundStatement>,
409410
do_parse!(
410411
indent!(indent) >>

Diff for: src/visitors/printer.rs

+12
Original file line numberDiff line numberDiff line change
@@ -720,6 +720,9 @@ fn format_expr(e: &Expression) -> String {
720720
format_untyped_params(params),
721721
format_expr(body)
722722
),
723+
Expression::Named(ref name, ref expr) => {
724+
format!("{} := ({})", format_expr(name), format_expr(expr),)
725+
}
723726
}
724727
}
725728

@@ -819,4 +822,13 @@ mod tests {
819822
"[a for a in L if ((f(a)) if (a) else (None))]"
820823
);
821824
}
825+
826+
#[test]
827+
fn test_namedexpr() {
828+
let e = Expression::Named(
829+
Box::new(Expression::Name("foo".to_string())),
830+
Box::new(Expression::Name("bar".to_string())),
831+
);
832+
assert_eq!(&format_expr(&e), "foo := (bar)");
833+
}
822834
}

0 commit comments

Comments
 (0)