Skip to content

Commit acaa5df

Browse files
committed
vm: more tests for lists and DELETE_SUBSCR, UNPACK_EX, UNPACK_SEQUENCE etc
* Change interface to py.Next * Fill out List interface
1 parent bb6f44c commit acaa5df

File tree

6 files changed

+317
-60
lines changed

6 files changed

+317
-60
lines changed

builtin/builtin.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,15 @@ func builtin_next(self py.Object, args py.Tuple) (res py.Object) {
324324
}()
325325
}
326326

327-
return py.Next(it)
327+
res, finished := py.Next(it)
328+
if finished != nil {
329+
if def != nil {
330+
res = def
331+
} else {
332+
panic(finished)
333+
}
334+
}
335+
return res
328336
}
329337

330338
const import_doc = `__import__(name, globals=None, locals=None, fromlist=(), level=0) -> module

py/list.go

+86-13
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,21 @@ func (l *List) Append(item Object) {
6666
l.Items = append(l.Items, item)
6767
}
6868

69+
// Resize the list
70+
func (l *List) Resize(newSize int) {
71+
l.Items = l.Items[:newSize]
72+
}
73+
6974
// Extend the list with items
7075
func (l *List) Extend(items []Object) {
7176
l.Items = append(l.Items, items...)
7277
}
7378

79+
// Len of list
80+
func (l *List) Len() int {
81+
return len(l.Items)
82+
}
83+
7484
func (l *List) M__len__() Object {
7585
return Int(len(l.Items))
7686
}
@@ -99,25 +109,56 @@ func (l *List) M__getitem__(key Object) Object {
99109
func (l *List) M__setitem__(key, value Object) Object {
100110
if slice, ok := key.(*Slice); ok {
101111
start, stop, step, slicelength := slice.GetIndices(len(l.Items))
102-
if step != 1 {
103-
panic("Setting slices with step != 1 not implemented yet")
104-
}
105-
if stop == len(l.Items) {
106-
// tail of the list only
112+
if step == 1 {
113+
// Make a copy of the tail
114+
tailSlice := l.Items[stop:]
115+
tail := make([]Object, len(tailSlice))
116+
copy(tail, tailSlice)
107117
l.Items = l.Items[:start]
108118
Iterate(value, func(item Object) {
109119
l.Append(item)
110120
})
111-
return None
121+
l.Items = append(l.Items, tail...)
122+
} else {
123+
newItems := SequenceTuple(value)
124+
if len(newItems) != slicelength {
125+
panic(ExceptionNewf(ValueError, "attempt to assign sequence of size %d to extended slice of size %d", len(newItems), slicelength))
126+
}
127+
j := 0
128+
for i := start; i < stop; i += step {
129+
l.Items[i] = newItems[j]
130+
j++
131+
}
112132
}
113-
_ = slicelength
114-
_ = start
115-
_ = stop
116-
panic("Set slice not implemented fully yet")
117-
return None
133+
} else {
134+
i := IndexIntCheck(key, len(l.Items))
135+
l.Items[i] = value
136+
}
137+
return None
138+
}
139+
140+
// Removes the item at i
141+
func (a *List) DelItem(i int) {
142+
a.Items = append(a.Items[:i], a.Items[i+1:]...)
143+
}
144+
145+
// Removes items from a list
146+
func (a *List) M__delitem__(key Object) Object {
147+
if slice, ok := key.(*Slice); ok {
148+
start, stop, step, _ := slice.GetIndices(len(a.Items))
149+
if step == 1 {
150+
a.Items = append(a.Items[:start], a.Items[stop:]...)
151+
} else {
152+
j := 0
153+
for i := start; i < stop; i += step {
154+
a.DelItem(i - j)
155+
j++
156+
}
157+
}
158+
} else {
159+
i := IndexIntCheck(key, len(a.Items))
160+
a.DelItem(i)
118161
}
119-
i := IndexIntCheck(key, len(l.Items))
120-
l.Items[i] = value
121162
return None
122163
}
123164

@@ -177,3 +218,35 @@ var _ I__getitem__ = (*List)(nil)
177218
var _ I__setitem__ = (*List)(nil)
178219

179220
// var _ richComparison = (*List)(nil)
221+
222+
func (a *List) M__eq__(other Object) Object {
223+
b, ok := other.(*List)
224+
if !ok {
225+
return NotImplemented
226+
}
227+
if len(a.Items) != len(b.Items) {
228+
return False
229+
}
230+
for i := range a.Items {
231+
if Eq(a.Items[i], b.Items[i]) == False {
232+
return False
233+
}
234+
}
235+
return True
236+
}
237+
238+
func (a *List) M__ne__(other Object) Object {
239+
b, ok := other.(*List)
240+
if !ok {
241+
return NotImplemented
242+
}
243+
if len(a.Items) != len(b.Items) {
244+
return True
245+
}
246+
for i := range a.Items {
247+
if Eq(a.Items[i], b.Items[i]) == False {
248+
return True
249+
}
250+
}
251+
return False
252+
}

py/sequence.go

+23-14
Original file line numberDiff line numberDiff line change
@@ -35,28 +35,34 @@ func SequenceList(v Object) *List {
3535
}
3636

3737
// Call __next__ for the python object
38-
func Next(self Object) Object {
39-
if I, ok := self.(I__next__); ok {
40-
return I.M__next__()
41-
} else if res, ok := TypeCall0(self, "__next__"); ok {
42-
return res
43-
}
44-
45-
panic(ExceptionNewf(TypeError, "'%s' object is not iterable", self.Type().Name))
46-
}
47-
48-
// Create an iterator from obj and iterate the iterator until finished
49-
// calling the function passed in on each object
50-
func Iterate(obj Object, fn func(Object)) {
38+
//
39+
// Returns the next object
40+
//
41+
// finished == StopIteration or subclass when finished
42+
func Next(self Object) (obj Object, finished Object) {
5143
defer func() {
5244
if r := recover(); r != nil {
5345
if IsException(StopIteration, r) {
5446
// StopIteration or subclass raised
47+
finished = r.(Object)
5548
} else {
5649
panic(r)
5750
}
5851
}
5952
}()
53+
if I, ok := self.(I__next__); ok {
54+
obj = I.M__next__()
55+
return
56+
} else if obj, ok = TypeCall0(self, "__next__"); ok {
57+
return
58+
}
59+
60+
panic(ExceptionNewf(TypeError, "'%s' object is not iterable", self.Type().Name))
61+
}
62+
63+
// Create an iterator from obj and iterate the iterator until finished
64+
// calling the function passed in on each object
65+
func Iterate(obj Object, fn func(Object)) {
6066
// Some easy cases
6167
switch x := obj.(type) {
6268
case Tuple:
@@ -78,7 +84,10 @@ func Iterate(obj Object, fn func(Object)) {
7884
default:
7985
iterator := Iter(obj)
8086
for {
81-
item := Next(iterator)
87+
item, finished := Next(iterator)
88+
if finished != nil {
89+
break
90+
}
8291
fn(item)
8392
}
8493
}

vm/eval.go

+88-32
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ func (vm *Vm) EXTEND(items py.Tuple) {
8585
vm.frame.Stack = append(vm.frame.Stack, items...)
8686
}
8787

88+
// Push items to top of vm stack in reverse order
89+
func (vm *Vm) EXTEND_REVERSED(items py.Tuple) {
90+
start := len(vm.frame.Stack)
91+
vm.frame.Stack = append(vm.frame.Stack, items...)
92+
py.Tuple(vm.frame.Stack[start:]).Reverse()
93+
}
94+
8895
// Adds a traceback to the exc passed in for the current vm state
8996
func (vm *Vm) AddTraceback(exc *py.ExceptionInfo) {
9097
exc.Traceback = &py.Traceback{
@@ -473,7 +480,11 @@ func do_STORE_SUBSCR(vm *Vm, arg int32) {
473480
// Implements del TOS1[TOS].
474481
func do_DELETE_SUBSCR(vm *Vm, arg int32) {
475482
defer vm.CheckException()
476-
vm.NotImplemented("DELETE_SUBSCR", arg)
483+
sub := vm.TOP()
484+
container := vm.SECOND()
485+
vm.DROPN(2)
486+
/* del v[w] */
487+
py.DelItem(container, sub)
477488
}
478489

479490
// Miscellaneous opcodes.
@@ -513,6 +524,52 @@ func do_CONTINUE_LOOP(vm *Vm, target int32) {
513524
vm.frame.Lasti = vm.frame.Block.Handler
514525
}
515526

527+
// Iterate v argcnt times and store the results on the stack (via decreasing
528+
// sp). Return 1 for success, 0 if error.
529+
//
530+
// If argcntafter == -1, do a simple unpack. If it is >= 0, do an unpack
531+
// with a variable target.
532+
func unpack_iterable(vm *Vm, v py.Object, argcnt int, argcntafter int, sp int) {
533+
it := py.Iter(v)
534+
i := 0
535+
for i = 0; i < argcnt; i++ {
536+
w, finished := py.Next(it)
537+
if finished != nil {
538+
/* Iterator done, via error or exhaustion. */
539+
panic(py.ExceptionNewf(py.ValueError, "need more than %d value(s) to unpack", i))
540+
}
541+
sp--
542+
vm.frame.Stack[sp] = w
543+
}
544+
545+
if argcntafter == -1 {
546+
/* We better have exhausted the iterator now. */
547+
_, finished := py.Next(it)
548+
if finished != nil {
549+
return
550+
}
551+
panic(py.ExceptionNewf(py.ValueError, "too many values to unpack (expected %d)", argcnt))
552+
}
553+
554+
l := py.SequenceList(it)
555+
sp--
556+
vm.frame.Stack[sp] = l
557+
i++
558+
559+
ll := l.Len()
560+
if ll < argcntafter {
561+
panic(py.ExceptionNewf(py.ValueError, "need more than %d values to unpack", argcnt+ll))
562+
}
563+
564+
/* Pop the "after-variable" args off the list. */
565+
for j := argcntafter; j > 0; j-- {
566+
sp--
567+
vm.frame.Stack[sp] = l.M__getitem__(py.Int(ll - j))
568+
}
569+
/* Resize the list. */
570+
l.Resize(ll - argcntafter)
571+
}
572+
516573
// Implements assignment with a starred target: Unpacks an iterable in
517574
// TOS into individual values, where the total number of values can be
518575
// smaller than the number of items in the iterable: one the new
@@ -523,7 +580,13 @@ func do_CONTINUE_LOOP(vm *Vm, target int32) {
523580
// resulting values are put onto the stack right-to-left.
524581
func do_UNPACK_EX(vm *Vm, counts int32) {
525582
defer vm.CheckException()
526-
vm.NotImplemented("UNPACK_EX", counts)
583+
before := int(counts & 0xFF)
584+
after := int(counts >> 8)
585+
totalargs := 1 + before + after
586+
seq := vm.POP()
587+
sp := len(vm.frame.Stack)
588+
vm.EXTEND(make([]py.Object, totalargs))
589+
unpack_iterable(vm, seq, before, after, sp+totalargs)
527590
}
528591

529592
// Calls set.add(TOS1[-i], TOS). Used to implement set comprehensions.
@@ -571,23 +634,21 @@ func do_RETURN_VALUE(vm *Vm, arg int32) {
571634

572635
// Pops TOS and delegates to it as a subiterator from a generator.
573636
func do_YIELD_FROM(vm *Vm, arg int32) {
574-
defer func() {
575-
if r := recover(); r != nil {
576-
if vm.catchStopIteration(r) {
577-
// No extra action needed
578-
}
579-
}
580-
}()
637+
defer vm.CheckException()
581638

582639
var retval py.Object
640+
var finished py.Object
583641
u := vm.POP()
584642
x := vm.TOP()
585643
// send u to x
586644
if u == py.None {
587-
retval = py.Next(x)
645+
retval, finished = py.Next(x)
588646
} else {
589647
retval = py.Send(x, u)
590648
}
649+
if finished != nil {
650+
return
651+
}
591652
// x remains on stack, retval is value to be yielded
592653
// FIXME vm.frame.Stacktop = stack_pointer
593654
//why = exitYield
@@ -756,19 +817,16 @@ func do_DELETE_NAME(vm *Vm, namei int32) {
756817
func do_UNPACK_SEQUENCE(vm *Vm, count int32) {
757818
defer vm.CheckException()
758819
it := vm.POP()
759-
i := count
760-
items := make(py.Tuple, count)
761-
py.Iterate(it, func(item py.Object) {
762-
i--
763-
if i < 0 {
764-
panic(py.ExceptionNewf(py.ValueError, "Too many values to unpack (expected %d)", count))
765-
}
766-
items[i] = item
767-
})
768-
if i != 0 {
769-
panic(py.ExceptionNewf(py.ValueError, "Need more than %d values to unpack (expected %d)", count-i, count))
820+
args := int(count)
821+
if tuple, ok := it.(py.Tuple); ok && len(tuple) == args {
822+
vm.EXTEND_REVERSED(tuple)
823+
} else if list, ok := it.(*py.List); ok && list.Len() == args {
824+
vm.EXTEND_REVERSED(list.Items)
825+
} else {
826+
sp := len(vm.frame.Stack)
827+
vm.EXTEND(make([]py.Object, args))
828+
unpack_iterable(vm, it, args, -1, sp+args)
770829
}
771-
vm.EXTEND(items)
772830
}
773831

774832
// Implements TOS.name = TOS1, where namei is the index of name in
@@ -1018,16 +1076,14 @@ func do_JUMP_ABSOLUTE(vm *Vm, target int32) {
10181076
// iterator indicates it is exhausted TOS is popped, and the bytecode
10191077
// counter is incremented by delta.
10201078
func do_FOR_ITER(vm *Vm, delta int32) {
1021-
defer func() {
1022-
if r := recover(); r != nil {
1023-
if vm.catchStopIteration(r) {
1024-
vm.DROP()
1025-
vm.frame.Lasti += delta
1026-
}
1027-
}
1028-
}()
1029-
r := py.Next(vm.TOP())
1030-
vm.PUSH(r)
1079+
defer vm.CheckException()
1080+
r, finished := py.Next(vm.TOP())
1081+
if finished != nil {
1082+
vm.DROP()
1083+
vm.frame.Lasti += delta
1084+
} else {
1085+
vm.PUSH(r)
1086+
}
10311087
}
10321088

10331089
// Loads the global named co_names[namei] onto the stack.

vm/tests/exceptions.py

+8
Original file line numberDiff line numberDiff line change
@@ -144,5 +144,13 @@
144144
# assert False, "Expecting ValueError (outer)"
145145
# assert ok1 and ok2
146146

147+
# FIXME - exeption not being caught
148+
# ok = False
149+
# try:
150+
# print(1/0)
151+
# except ZeroDivisionError:
152+
# ok = True
153+
# assert ok
154+
147155
# End with this
148156
finished = True

0 commit comments

Comments
 (0)