Skip to content

Commit 99f54b3

Browse files
authored
Unpack sequences (#1153)
* Add nullable annotations * Unpack sequences
1 parent 4d5b85e commit 99f54b3

11 files changed

Lines changed: 207 additions & 88 deletions

File tree

Src/IronPython/Compiler/Ast/AstMethods.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ internal static class AstMethods {
8181

8282
// methods matching Python opcodes
8383
public static readonly MethodInfo DictUpdate = GetMethod((Action<CodeContext, PythonDictionary, object>)PythonOps.DictUpdate);
84+
public static readonly MethodInfo ListAppend = GetMethod((Action<PythonList, object>)PythonOps.ListAppend);
85+
public static readonly MethodInfo ListExtend = GetMethod((Action<PythonList, object>)PythonOps.ListExtend);
86+
public static readonly MethodInfo ListToTuple = GetMethod((Func<PythonList, PythonTuple>)PythonOps.ListToTuple);
87+
public static readonly MethodInfo SetAdd = GetMethod((Action<SetCollection, object>)PythonOps.SetAdd);
88+
public static readonly MethodInfo SetUpdate = GetMethod((Action<SetCollection, object>)PythonOps.SetUpdate);
8489

8590
private static MethodInfo GetMethod(Delegate x) {
8691
return x.Method;

Src/IronPython/Compiler/Ast/Expression.cs

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,39 @@
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 MSAst = System.Linq.Expressions;
5+
#nullable enable
66

77
using System;
8+
using System.Collections.Generic;
89
using System.Diagnostics;
10+
using System.Reflection;
11+
using System.Runtime.CompilerServices;
12+
13+
using IronPython.Runtime.Binding;
914

1015
using Microsoft.Scripting;
1116

12-
using IronPython.Runtime.Binding;
17+
using AstUtils = Microsoft.Scripting.Ast.Utils;
18+
using MSAst = System.Linq.Expressions;
1319

1420
namespace IronPython.Compiler.Ast {
1521
public abstract class Expression : Node {
16-
internal static Expression[] EmptyArray = new Expression[0];
22+
internal static readonly Expression[] EmptyArray = Array.Empty<Expression>();
23+
24+
protected internal static MSAst.BlockExpression UnpackSequenceHelper<T>(IList<Expression> items, MethodInfo makeEmpty, MethodInfo append, MethodInfo extend) {
25+
var expressions = new ReadOnlyCollectionBuilder<MSAst.Expression>(items.Count + 2);
26+
var varExpr = Expression.Variable(typeof(T), "$coll");
27+
expressions.Add(Expression.Assign(varExpr, Expression.Call(makeEmpty)));
28+
foreach (var item in items) {
29+
if (item is StarredExpression starredExpression) {
30+
expressions.Add(Expression.Call(extend, varExpr, AstUtils.Convert(starredExpression.Value, typeof(object))));
31+
} else {
32+
expressions.Add(Expression.Call(append, varExpr, AstUtils.Convert(item, typeof(object))));
33+
}
34+
}
35+
expressions.Add(varExpr);
36+
return Expression.Block(typeof(T), new MSAst.ParameterExpression[] { varExpr }, expressions);
37+
}
1738

1839
internal virtual MSAst.Expression TransformSet(SourceSpan span, MSAst.Expression right, PythonOperationKind op) {
1940
// unreachable, CheckAssign prevents us from calling this at parse time.
@@ -26,23 +47,15 @@ internal virtual MSAst.Expression TransformDelete() {
2647
throw new InvalidOperationException();
2748
}
2849

29-
internal virtual ConstantExpression ConstantFold() => null;
50+
internal virtual ConstantExpression? ConstantFold() => null;
3051

31-
internal virtual string CheckAssign() => "can't assign to " + NodeName;
52+
internal virtual string? CheckAssign() => "can't assign to " + NodeName;
3253

33-
internal virtual string CheckAugmentedAssign() => CheckAssign();
54+
internal virtual string? CheckAugmentedAssign() => CheckAssign();
3455

35-
internal virtual string CheckDelete() => "can't delete " + NodeName;
56+
internal virtual string? CheckDelete() => "can't delete " + NodeName;
3657

37-
internal virtual bool IsConstant {
38-
get {
39-
var folded = ConstantFold();
40-
if (folded != null) {
41-
return folded.IsConstant;
42-
}
43-
return false;
44-
}
45-
}
58+
internal virtual bool IsConstant => ConstantFold()?.IsConstant ?? false;
4659

4760
internal virtual object GetConstantValue() {
4861
var folded = ConstantFold();

Src/IronPython/Compiler/Ast/ListExpression.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,25 @@
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+
#nullable enable
6+
7+
using IronPython.Runtime;
8+
59
using MSAst = System.Linq.Expressions;
610

711
namespace IronPython.Compiler.Ast {
8-
using Ast = MSAst.Expression;
9-
1012
public class ListExpression : SequenceExpression {
1113
public ListExpression(params Expression[] items)
1214
: base(items) {
1315
}
1416

1517
public override MSAst.Expression Reduce() {
1618
if (Items.Count == 0) {
17-
return Ast.Call(
18-
AstMethods.MakeEmptyList
19-
);
19+
return Expression.Call(AstMethods.MakeEmptyList);
20+
}
21+
22+
if (HasStarredExpression) {
23+
return UnpackSequenceHelper<PythonList>(Items, AstMethods.MakeEmptyList, AstMethods.ListAppend, AstMethods.ListExtend);
2024
}
2125

2226
return Call(

Src/IronPython/Compiler/Ast/SequenceExpression.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,21 @@
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+
#nullable enable
6+
57
using System.Collections.Generic;
6-
using System.Collections.ObjectModel;
8+
using System.Linq;
79
using System.Runtime.CompilerServices;
810

9-
using Microsoft.Scripting;
10-
using Microsoft.Scripting.Runtime;
11-
1211
using IronPython.Runtime.Binding;
13-
using IronPython.Runtime.Operations;
1412

15-
using MSAst = System.Linq.Expressions;
13+
using Microsoft.Scripting;
14+
using Microsoft.Scripting.Runtime;
1615

1716
using AstUtils = Microsoft.Scripting.Ast.Utils;
17+
using MSAst = System.Linq.Expressions;
1818

1919
namespace IronPython.Compiler.Ast {
20-
using Ast = MSAst.Expression;
21-
2220
public abstract class SequenceExpression : Expression {
2321
private readonly Expression[] _items;
2422

@@ -28,6 +26,8 @@ protected SequenceExpression(Expression[] items) {
2826

2927
public IList<Expression> Items => _items;
3028

29+
protected bool HasStarredExpression => Items.OfType<StarredExpression>().Any();
30+
3131
internal override MSAst.Expression TransformSet(SourceSpan span, MSAst.Expression right, PythonOperationKind op) {
3232
// if we just have a simple named multi-assignment (e.g. a, b = 1,2)
3333
// then go ahead and step over the entire statement at once. If we have a
@@ -58,7 +58,7 @@ internal override MSAst.Expression TransformSet(SourceSpan span, MSAst.Expressio
5858
}
5959

6060
// 1. Evaluate the expression and assign the value to the temp.
61-
MSAst.ParameterExpression right_temp = Ast.Variable(typeof(object), "unpacking");
61+
MSAst.ParameterExpression right_temp = Expression.Variable(typeof(object), "unpacking");
6262

6363
// 2. Add the assignment "right_temp = right" into the suite/block
6464
MSAst.Expression assignStmt1 = MakeAssignment(right_temp, right);
@@ -92,7 +92,7 @@ internal override MSAst.Expression TransformSet(SourceSpan span, MSAst.Expressio
9292
), typeof(object[]));
9393

9494
// 4. Create temporary variable for the array
95-
MSAst.ParameterExpression array_temp = Ast.Variable(typeof(object[]), "array");
95+
MSAst.ParameterExpression array_temp = Expression.Variable(typeof(object[]), "array");
9696

9797
// 5. Assign the value of the method call (mce) into the array temp
9898
// And add the assignment "array_temp = Ops.GetEnumeratorValues(...)" into the block
@@ -112,7 +112,7 @@ internal override MSAst.Expression TransformSet(SourceSpan span, MSAst.Expressio
112112
}
113113

114114
// 6. array_temp[i]
115-
MSAst.Expression element = Ast.ArrayAccess(
115+
MSAst.Expression element = Expression.ArrayAccess(
116116
array_temp, // array expression
117117
AstUtils.Constant(i) // index
118118
);
@@ -129,13 +129,13 @@ internal override MSAst.Expression TransformSet(SourceSpan span, MSAst.Expressio
129129
}
130130
// 9. add the sets as their own block so they can be marked as a single span, if necessary.
131131
sets.Add(AstUtils.Empty());
132-
MSAst.Expression itemSet = GlobalParent.AddDebugInfo(Ast.Block(sets.ToReadOnlyCollection()), leftSpan);
132+
MSAst.Expression itemSet = GlobalParent.AddDebugInfo(Expression.Block(sets.ToReadOnlyCollection()), leftSpan);
133133

134134
// 10. Return the suite statement (block)
135-
return GlobalParent.AddDebugInfo(Ast.Block(new[] { array_temp, right_temp }, assignStmt1, assignStmt2, itemSet, AstUtils.Empty()), totalSpan);
135+
return GlobalParent.AddDebugInfo(Expression.Block(new[] { array_temp, right_temp }, assignStmt1, assignStmt2, itemSet, AstUtils.Empty()), totalSpan);
136136
}
137137

138-
internal override string CheckAssign() {
138+
internal override string? CheckAssign() {
139139
var starCount = 0;
140140
foreach (var item in Items) {
141141
if (item.CheckAssign() is { } checkAssign) {
@@ -157,7 +157,7 @@ internal override string CheckAssign() {
157157
return null;
158158
}
159159

160-
internal override string CheckDelete() => null;
160+
internal override string? CheckDelete() => null;
161161

162162
internal override string CheckAugmentedAssign()
163163
=> CheckAssign() ?? "illegal expression for augmented assignment";
@@ -170,7 +170,7 @@ internal override MSAst.Expression TransformDelete() {
170170
statements[i] = _items[i].TransformDelete();
171171
}
172172
statements[_items.Length] = AstUtils.Empty();
173-
return GlobalParent.AddDebugInfo(Ast.Block(statements), Span);
173+
return GlobalParent.AddDebugInfo(Expression.Block(statements), Span);
174174
}
175175

176176
internal override bool CanThrow {

Src/IronPython/Compiler/Ast/SetExpression.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
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 MSAst = System.Linq.Expressions;
5+
#nullable enable
66

77
using System.Collections.Generic;
8+
using System.Linq;
9+
10+
using IronPython.Runtime;
811

912
using Microsoft.Scripting.Utils;
1013

1114
using AstUtils = Microsoft.Scripting.Ast.Utils;
15+
using MSAst = System.Linq.Expressions;
1216

1317
namespace IronPython.Compiler.Ast {
14-
using Ast = MSAst.Expression;
15-
1618
public class SetExpression : Expression {
1719
private readonly Expression[] _items;
1820

@@ -24,7 +26,17 @@ public SetExpression(params Expression[] items) {
2426

2527
public IList<Expression> Items => _items;
2628

29+
protected bool HasStarredExpression => Items.OfType<StarredExpression>().Any();
30+
2731
public override MSAst.Expression Reduce() {
32+
if (Items.Count == 0) {
33+
return Expression.Call(AstMethods.MakeEmptySet);
34+
}
35+
36+
if (HasStarredExpression) {
37+
return UnpackSequenceHelper<SetCollection>(Items, AstMethods.MakeEmptySet, AstMethods.SetAdd, AstMethods.SetUpdate);
38+
}
39+
2840
return Expression.Call(
2941
AstMethods.MakeSet,
3042
NewArrayInit(

Src/IronPython/Compiler/Ast/StarredExpression.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ public StarredExpression(Expression value) {
2929
internal override MSAst.Expression TransformSet(SourceSpan span, MSAst.Expression right, PythonOperationKind op)
3030
=> Value.TransformSet(span, right, op);
3131

32-
internal override string CheckAssign() => Value.CheckAssign();
32+
internal override string? CheckAssign() => Value.CheckAssign();
3333

34-
internal override string CheckDelete() => "can use starred expression only as assignment target";
34+
internal override string CheckDelete() => "can't use starred expression here"; // TODO: change error message in 3.9
3535

3636
internal override MSAst.Expression TransformDelete() => Value.TransformDelete();
3737

@@ -76,10 +76,16 @@ public override bool Walk(ForStatement node) {
7676
}
7777

7878
public override bool Walk(StarredExpression node) {
79-
ReportSyntaxError("can use starred expression only as assignment target", node);
79+
ReportSyntaxError("can't use starred expression here", node);
8080
return base.Walk(node);
8181
}
8282

83+
public override bool Walk(ListExpression node) => WalkItems(node.Items);
84+
85+
public override bool Walk(SetExpression node) => WalkItems(node.Items);
86+
87+
public override bool Walk(TupleExpression node) => WalkItems(node.Items);
88+
8389
private void ReportSyntaxError(string message, Node node) {
8490
context.Errors.Add(context.SourceUnit, message, node.Span, ErrorCodes.SyntaxError, Severity.FatalError);
8591
}

Src/IronPython/Compiler/Ast/TupleExpression.cs

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,21 @@
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;
6-
7-
using Microsoft.Scripting;
8-
using Microsoft.Scripting.Runtime;
5+
#nullable enable
96

107
using IronPython.Runtime;
11-
using IronPython.Runtime.Binding;
128
using IronPython.Runtime.Operations;
139

1410
using MSAst = System.Linq.Expressions;
1511

16-
1712
namespace IronPython.Compiler.Ast {
18-
using Ast = MSAst.Expression;
19-
2013
public class TupleExpression : SequenceExpression {
2114
public TupleExpression(bool expandable, params Expression[] items)
2215
: base(items) {
2316
IsExpandable = expandable;
2417
}
2518

26-
internal override string CheckAssign() {
19+
internal override string? CheckAssign() {
2720
if (Items.Count == 0) {
2821
// TODO: remove this when we get to 3.6
2922
return "can't assign to ()";
@@ -32,30 +25,34 @@ internal override string CheckAssign() {
3225
return base.CheckAssign();
3326
}
3427

35-
internal override string CheckDelete() {
28+
internal override string? CheckDelete() {
3629
if (Items.Count == 0)
3730
return "can't delete ()"; // TODO: remove this when we get to 3.6
3831
return base.CheckDelete();
3932
}
4033

4134
public override MSAst.Expression Reduce() {
4235
if (IsExpandable) {
43-
return Ast.NewArrayInit(
36+
return Expression.NewArrayInit(
4437
typeof(object),
4538
ToObjectArray(Items)
4639
);
4740
}
4841

4942
if (Items.Count == 0) {
50-
return Ast.Field(
51-
null,
52-
typeof(PythonOps).GetField(nameof(PythonOps.EmptyTuple))
43+
return Expression.Field(
44+
null!,
45+
typeof(PythonOps).GetField(nameof(PythonOps.EmptyTuple))!
5346
);
5447
}
5548

56-
return Ast.Call(
49+
if (HasStarredExpression) {
50+
return Expression.Call(AstMethods.ListToTuple, UnpackSequenceHelper<PythonList>(Items, AstMethods.MakeEmptyList, AstMethods.ListAppend, AstMethods.ListExtend));
51+
}
52+
53+
return Expression.Call(
5754
AstMethods.MakeTuple,
58-
Ast.NewArrayInit(
55+
Expression.NewArrayInit(
5956
typeof(object),
6057
ToObjectArray(Items)
6158
)

0 commit comments

Comments
 (0)