Skip to content

Commit cd8c5fa

Browse files
committed
compile: implement starred assignment
1 parent e03f367 commit cd8c5fa

File tree

3 files changed

+140
-8
lines changed

3 files changed

+140
-8
lines changed

compile/compile.go

+40-7
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,36 @@ func (c *compiler) comprehension(expr ast.Expr, generators []ast.Comprehension)
13731373
c.OpArg(vm.CALL_FUNCTION, 1)
13741374
}
13751375

1376+
// Compile a tuple or a list
1377+
func (c *compiler) tupleOrList(op byte, ctx ast.ExprContext, elts []ast.Expr) {
1378+
const INT_MAX = 0x7FFFFFFF
1379+
n := len(elts)
1380+
if ctx == ast.Store {
1381+
seen_star := false
1382+
for i, elt := range elts {
1383+
starred, isStarred := elt.(*ast.Starred)
1384+
if isStarred && !seen_star {
1385+
if i >= (1<<8) || n-i-1 >= (INT_MAX>>8) {
1386+
panic(py.ExceptionNewf(py.SyntaxError, "too many expressions in star-unpacking assignment"))
1387+
}
1388+
c.OpArg(vm.UNPACK_EX, uint32((i + ((n - i - 1) << 8))))
1389+
seen_star = true
1390+
// FIXME Overwrite the starred element
1391+
elts[i] = starred.Value
1392+
} else if isStarred {
1393+
panic(py.ExceptionNewf(py.SyntaxError, "two starred expressions in assignment"))
1394+
}
1395+
}
1396+
if !seen_star {
1397+
c.OpArg(vm.UNPACK_SEQUENCE, uint32(n))
1398+
}
1399+
}
1400+
c.Exprs(elts)
1401+
if ctx == ast.Load {
1402+
c.OpArg(op, uint32(n))
1403+
}
1404+
}
1405+
13761406
// Compile expressions
13771407
func (c *compiler) Exprs(exprs []ast.Expr) {
13781408
for _, expr := range exprs {
@@ -1624,23 +1654,26 @@ func (c *compiler) Expr(expr ast.Expr) {
16241654
case *ast.Starred:
16251655
// Value Expr
16261656
// Ctx ExprContext
1627-
panic("FIXME compile: Starred not implemented")
1657+
switch node.Ctx {
1658+
case ast.Store:
1659+
// In all legitimate cases, the Starred node was already replaced
1660+
// by tupleOrList: is that okay?
1661+
panic(py.ExceptionNewf(py.SyntaxError, "starred assignment target must be in a list or tuple"))
1662+
default:
1663+
panic(py.ExceptionNewf(py.SyntaxError, "can use starred expression only as assignment target"))
1664+
}
16281665
case *ast.Name:
16291666
// Id Identifier
16301667
// Ctx ExprContext
16311668
c.NameOp(string(node.Id), node.Ctx)
16321669
case *ast.List:
16331670
// Elts []Expr
16341671
// Ctx ExprContext
1635-
// FIXME do something with Ctx
1636-
c.Exprs(node.Elts)
1637-
c.OpArg(vm.BUILD_LIST, uint32(len(node.Elts)))
1672+
c.tupleOrList(vm.BUILD_LIST, node.Ctx, node.Elts)
16381673
case *ast.Tuple:
16391674
// Elts []Expr
16401675
// Ctx ExprContext
1641-
// FIXME do something with Ctx
1642-
c.Exprs(node.Elts)
1643-
c.OpArg(vm.BUILD_TUPLE, uint32(len(node.Elts)))
1676+
c.tupleOrList(vm.BUILD_TUPLE, node.Ctx, node.Elts)
16441677
default:
16451678
panic(py.ExceptionNewf(py.SyntaxError, "Unknown ExprBase: %v", expr))
16461679
}

compile/compile_data_test.go

+89
Original file line numberDiff line numberDiff line change
@@ -3841,4 +3841,93 @@ var compileTestData = []struct {
38413841
Firstlineno: 1,
38423842
Lnotab: "",
38433843
}, nil, ""},
3844+
{"*a = t", "exec", nil, py.SyntaxError, "starred assignment target must be in a list or tuple"},
3845+
{"a, *b = t", "exec", &py.Code{
3846+
Argcount: 0,
3847+
Kwonlyargcount: 0,
3848+
Nlocals: 0,
3849+
Stacksize: 2,
3850+
Flags: 64,
3851+
Code: "\x65\x00\x00\x5e\x01\x00\x5a\x01\x00\x5a\x02\x00\x64\x00\x00\x53",
3852+
Consts: []py.Object{py.None},
3853+
Names: []string{"t", "a", "b"},
3854+
Varnames: []string{},
3855+
Freevars: []string{},
3856+
Cellvars: []string{},
3857+
Filename: "<string>",
3858+
Name: "<module>",
3859+
Firstlineno: 1,
3860+
Lnotab: "",
3861+
}, nil, ""},
3862+
{"(a, *b) = t", "exec", &py.Code{
3863+
Argcount: 0,
3864+
Kwonlyargcount: 0,
3865+
Nlocals: 0,
3866+
Stacksize: 2,
3867+
Flags: 64,
3868+
Code: "\x65\x00\x00\x5e\x01\x00\x5a\x01\x00\x5a\x02\x00\x64\x00\x00\x53",
3869+
Consts: []py.Object{py.None},
3870+
Names: []string{"t", "a", "b"},
3871+
Varnames: []string{},
3872+
Freevars: []string{},
3873+
Cellvars: []string{},
3874+
Filename: "<string>",
3875+
Name: "<module>",
3876+
Firstlineno: 1,
3877+
Lnotab: "",
3878+
}, nil, ""},
3879+
{"[a, *b] = t", "exec", &py.Code{
3880+
Argcount: 0,
3881+
Kwonlyargcount: 0,
3882+
Nlocals: 0,
3883+
Stacksize: 2,
3884+
Flags: 64,
3885+
Code: "\x65\x00\x00\x5e\x01\x00\x5a\x01\x00\x5a\x02\x00\x64\x00\x00\x53",
3886+
Consts: []py.Object{py.None},
3887+
Names: []string{"t", "a", "b"},
3888+
Varnames: []string{},
3889+
Freevars: []string{},
3890+
Cellvars: []string{},
3891+
Filename: "<string>",
3892+
Name: "<module>",
3893+
Firstlineno: 1,
3894+
Lnotab: "",
3895+
}, nil, ""},
3896+
{"a, *b, c = t", "exec", &py.Code{
3897+
Argcount: 0,
3898+
Kwonlyargcount: 0,
3899+
Nlocals: 0,
3900+
Stacksize: 3,
3901+
Flags: 64,
3902+
Code: "\x65\x00\x00\x5e\x01\x01\x5a\x01\x00\x5a\x02\x00\x5a\x03\x00\x64\x00\x00\x53",
3903+
Consts: []py.Object{py.None},
3904+
Names: []string{"t", "a", "b", "c"},
3905+
Varnames: []string{},
3906+
Freevars: []string{},
3907+
Cellvars: []string{},
3908+
Filename: "<string>",
3909+
Name: "<module>",
3910+
Firstlineno: 1,
3911+
Lnotab: "",
3912+
}, nil, ""},
3913+
{"a, *b, *c = t", "exec", nil, py.SyntaxError, "two starred expressions in assignment"},
3914+
{"a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,*a = t", "exec", nil, py.SyntaxError, "too many expressions in star-unpacking assignment"},
3915+
{"a, b, *c", "exec", nil, py.SyntaxError, "can use starred expression only as assignment target"},
3916+
{"a, (b, c), d = t", "exec", &py.Code{
3917+
Argcount: 0,
3918+
Kwonlyargcount: 0,
3919+
Nlocals: 0,
3920+
Stacksize: 3,
3921+
Flags: 64,
3922+
Code: "\x65\x00\x00\x5c\x03\x00\x5a\x01\x00\x5c\x02\x00\x5a\x02\x00\x5a\x03\x00\x5a\x04\x00\x64\x00\x00\x53",
3923+
Consts: []py.Object{py.None},
3924+
Names: []string{"t", "a", "b", "c", "d"},
3925+
Varnames: []string{},
3926+
Freevars: []string{},
3927+
Cellvars: []string{},
3928+
Filename: "<string>",
3929+
Name: "<module>",
3930+
Firstlineno: 1,
3931+
Lnotab: "",
3932+
}, nil, ""},
38443933
}

compile/make_compile_test.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,17 @@ def f():
363363
''', "exec"),
364364
# ellipsis
365365
('''...''', "exec"),
366-
]
366+
# starred...
367+
('''*a = t''', "exec", SyntaxError),
368+
('''a, *b = t''', "exec"),
369+
('''(a, *b) = t''', "exec"),
370+
('''[a, *b] = t''', "exec"),
371+
('''a, *b, c = t''', "exec"),
372+
('''a, *b, *c = t''', "exec", SyntaxError),
373+
('''a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,a,*a = t''', "exec", SyntaxError),
374+
('''a, b, *c''', "exec", SyntaxError),
375+
('''a, (b, c), d = t''', "exec"),
376+
]
367377

368378
def string(s):
369379
if isinstance(s, str):

0 commit comments

Comments
 (0)