Skip to content

Commit 3f358a4

Browse files
authored
Move starred expression checks out of parser (#1158)
1 parent 25654bb commit 3f358a4

3 files changed

Lines changed: 74 additions & 37 deletions

File tree

Src/IronPython/Compiler/Ast/PythonAst.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ public void ParsingFinished(int[] lineLocations, Statement body, ModuleOptions l
149149
/// </summary>
150150
public void Bind() {
151151
PythonNameBinder.BindAst(this, _compilerContext);
152+
StarredExpressionChecker.Check(this, _compilerContext);
152153
}
153154

154155
public override string Name {

Src/IronPython/Compiler/Ast/StarredExpression.cs

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,29 @@
22
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
33
// See the LICENSE file in the project root for more information.
44

5-
using System;
5+
#nullable enable
66

7-
using Microsoft.Scripting;
7+
using System;
8+
using System.Collections.Generic;
89

10+
using IronPython.Runtime;
911
using IronPython.Runtime.Binding;
1012

13+
using Microsoft.Scripting;
14+
using Microsoft.Scripting.Runtime;
15+
1116
using MSAst = System.Linq.Expressions;
1217

1318
namespace IronPython.Compiler.Ast {
14-
1519
public class StarredExpression : Expression {
1620
public StarredExpression(Expression value) {
21+
if (value is null) throw new ArgumentNullException(nameof(value));
1722
Value = value;
1823
}
1924

2025
public Expression Value { get; }
2126

22-
public override MSAst.Expression Reduce() => Value;
27+
public override bool CanReduce => false;
2328

2429
internal override MSAst.Expression TransformSet(SourceSpan span, MSAst.Expression right, PythonOperationKind op)
2530
=> Value.TransformSet(span, right, op);
@@ -34,11 +39,74 @@ internal override MSAst.Expression TransformSet(SourceSpan span, MSAst.Expressio
3439

3540
public override void Walk(PythonWalker walker) {
3641
if (walker.Walk(this)) {
37-
Value?.Walk(walker);
42+
Value.Walk(walker);
3843
}
3944
walker.PostWalk(this);
4045
}
4146

4247
internal override bool CanThrow => Value.CanThrow;
4348
}
49+
50+
internal class StarredExpressionChecker : PythonWalker {
51+
private readonly CompilerContext context;
52+
53+
private StarredExpressionChecker(CompilerContext context) {
54+
this.context = context;
55+
}
56+
57+
public static void Check(PythonAst ast, CompilerContext context) {
58+
var finder = new StarredExpressionChecker(context);
59+
ast.Walk(finder);
60+
}
61+
62+
public override bool Walk(AssignmentStatement node) {
63+
foreach (var expr in node.Left) {
64+
WalkAssignmentTarget(expr);
65+
}
66+
node.Right?.Walk(this);
67+
return false;
68+
}
69+
70+
public override bool Walk(ForStatement node) {
71+
WalkAssignmentTarget(node.Left);
72+
node.List?.Walk(this);
73+
node.Body?.Walk(this);
74+
node.Else?.Walk(this);
75+
return false;
76+
}
77+
78+
public override bool Walk(StarredExpression node) {
79+
ReportSyntaxError("can use starred expression only as assignment target", node);
80+
return base.Walk(node);
81+
}
82+
83+
private void ReportSyntaxError(string message, Node node) {
84+
context.Errors.Add(context.SourceUnit, message, node.Span, ErrorCodes.SyntaxError, Severity.FatalError);
85+
}
86+
87+
private void WalkAssignmentTarget(Expression expr) {
88+
switch (expr) {
89+
case StarredExpression starred:
90+
ReportSyntaxError("starred assignment target must be in a list or tuple", starred);
91+
break;
92+
case SequenceExpression sequenceExpression:
93+
WalkItems(sequenceExpression.Items);
94+
break;
95+
default:
96+
expr?.Walk(this);
97+
break;
98+
}
99+
}
100+
101+
private bool WalkItems(IList<Expression> items) {
102+
foreach (var item in items) {
103+
if (item is StarredExpression starred) {
104+
starred.Value.Walk(this);
105+
} else {
106+
item.Walk(this);
107+
}
108+
}
109+
return false;
110+
}
111+
}
44112
}

Src/IronPython/Compiler/Parser.cs

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -627,10 +627,6 @@ private Statement FinishAssignments(Expression right) {
627627
ReportSyntaxError(right.StartIndex, right.EndIndex, assignError, ErrorCodes.SyntaxError | ErrorCodes.NoCaret);
628628
}
629629

630-
if (right is StarredExpression) {
631-
ReportSyntaxError(right.StartIndex, right.EndIndex, "starred assignment target must be in a list or tuple");
632-
}
633-
634630
if (singleLeft == null) {
635631
singleLeft = right;
636632
} else {
@@ -644,8 +640,6 @@ private Statement FinishAssignments(Expression right) {
644640
right = MaybeEat(TokenKind.KeywordYield) ? ParseYieldExpression() : ParseTestListStarExpr();
645641
}
646642

647-
CheckNotAssignmentTargetOnly(right);
648-
649643
var target = left?.ToArray() ?? new[] { singleLeft };
650644

651645
Debug.Assert(target.Length > 0);
@@ -689,37 +683,11 @@ private Statement ParseExprStmt() {
689683
return aug;
690684
}
691685

692-
CheckNotAssignmentTargetOnly(ret);
693-
694686
Statement stmt = new ExpressionStatement(ret);
695687
stmt.SetLoc(_globalParent, ret.IndexSpan);
696688
return stmt;
697689
}
698690

699-
private void CheckNotAssignmentTargetOnly(Expression expr) {
700-
switch (expr) {
701-
case SequenceExpression sequence: {
702-
foreach (var expression in sequence.Items) {
703-
if (expression is StarredExpression starred) {
704-
ReportSyntaxError(
705-
starred.StartIndex,
706-
starred.EndIndex,
707-
"can use starred expression only as assignment target");
708-
}
709-
}
710-
711-
break;
712-
}
713-
case StarredExpression starred:
714-
ReportSyntaxError(
715-
starred.StartIndex,
716-
starred.EndIndex,
717-
"can use starred expression only as assignment target");
718-
719-
break;
720-
}
721-
}
722-
723691
private PythonOperator GetAssignOperator(Token t) {
724692
switch (t.Kind) {
725693
case TokenKind.AddEqual: return PythonOperator.Add;

0 commit comments

Comments
 (0)