Skip to content

Commit 732ebed

Browse files
committed
BUILD_SLICE, slice objects and slice indexing for lists and tuple implemented
1 parent 287a1df commit 732ebed

File tree

4 files changed

+189
-1
lines changed

4 files changed

+189
-1
lines changed

py/list.go

+27
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,38 @@ func (l *List) M__iter__() Object {
8484
}
8585

8686
func (l *List) M__getitem__(key Object) Object {
87+
if slice, ok := key.(*Slice); ok {
88+
start, _, step, slicelength := slice.GetIndices(len(l.Items))
89+
newList := NewListSized(slicelength)
90+
for i, j := start, 0; j < slicelength; i, j = i+step, j+1 {
91+
newList.Items[j] = l.Items[i]
92+
}
93+
return newList
94+
}
8795
i := IndexIntCheck(key, len(l.Items))
8896
return l.Items[i]
8997
}
9098

9199
func (l *List) M__setitem__(key, value Object) Object {
100+
if slice, ok := key.(*Slice); ok {
101+
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
107+
l.Items = l.Items[:start]
108+
Iterate(value, func(item Object) {
109+
l.Append(item)
110+
})
111+
return None
112+
}
113+
_ = slicelength
114+
_ = start
115+
_ = stop
116+
panic("Set slice not implemented fully yet")
117+
return None
118+
}
92119
i := IndexIntCheck(key, len(l.Items))
93120
l.Items[i] = value
94121
return None

py/slice.go

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Slice object
2+
3+
package py
4+
5+
// A python Slice object
6+
type Slice struct {
7+
Start Object
8+
Stop Object
9+
Step Object
10+
}
11+
12+
var SliceType = NewType("slice", `slice(stop) -> slice object
13+
"slice(stop)
14+
slice(start, stop[, step])
15+
16+
Create a slice object. This is used for extended slicing (e.g. a[0:10:2]).`)
17+
18+
// Type of this object
19+
func (o *Slice) Type() *Type {
20+
return SliceType
21+
}
22+
23+
// Make a new slice object
24+
func NewSlice(start, stop, step Object) *Slice {
25+
return &Slice{
26+
Start: start,
27+
Stop: stop,
28+
Step: step,
29+
}
30+
}
31+
32+
// SliceNew
33+
func SliceNew(metatype *Type, args Tuple, kwargs StringDict) Object {
34+
var start Object = None
35+
var stop Object = None
36+
var step Object = None
37+
UnpackTuple(args, kwargs, "slice", 1, 3, &start, &stop, &step)
38+
if len(args) == 1 {
39+
return NewSlice(None, start, None)
40+
}
41+
return NewSlice(start, stop, step)
42+
}
43+
44+
// GetIndices
45+
//
46+
// Retrieve the start, stop, and step indices from the slice object
47+
// slice assuming a sequence of length length, and store the length of
48+
// the slice in slicelength. Out of bounds indices are clipped in a
49+
// manner consistent with the handling of normal slices.
50+
func (r *Slice) GetIndices(length int) (start, stop, step, slicelength int) {
51+
var defstart, defstop int
52+
53+
if r.Step == None {
54+
step = 1
55+
} else {
56+
step = IndexInt(r.Step)
57+
if step == 0 {
58+
panic(ExceptionNewf(ValueError, "slice step cannot be zero"))
59+
}
60+
const PY_SSIZE_T_MAX = int(^uint(0) >> 1)
61+
/* Here *step might be -PY_SSIZE_T_MAX-1; in this case we replace it
62+
* with -PY_SSIZE_T_MAX. This doesn't affect the semantics, and it
63+
* guards against later undefined behaviour resulting from code that
64+
* does "step = -step" as part of a slice reversal.
65+
*/
66+
if step < -PY_SSIZE_T_MAX {
67+
step = -PY_SSIZE_T_MAX
68+
}
69+
}
70+
71+
if step < 0 {
72+
defstart = length - 1
73+
defstop = -1
74+
} else {
75+
defstart = 0
76+
defstop = length
77+
}
78+
79+
if r.Start == None {
80+
start = defstart
81+
} else {
82+
start = IndexInt(r.Start)
83+
if start < 0 {
84+
start += length
85+
}
86+
if start < 0 {
87+
if step < 0 {
88+
89+
start = -1
90+
} else {
91+
start = 0
92+
}
93+
}
94+
if start >= length {
95+
if step < 0 {
96+
start = length - 1
97+
} else {
98+
start = length
99+
}
100+
}
101+
}
102+
103+
if r.Stop == None {
104+
stop = defstop
105+
} else {
106+
stop = IndexInt(r.Stop)
107+
if stop < 0 {
108+
stop += length
109+
}
110+
if stop < 0 {
111+
if step < 0 {
112+
stop = -1
113+
} else {
114+
stop = 0
115+
}
116+
}
117+
if stop >= length {
118+
if step < 0 {
119+
stop = length - 1
120+
} else {
121+
stop = length
122+
}
123+
}
124+
}
125+
126+
if (step < 0 && stop >= start) || (step > 0 && start >= stop) {
127+
slicelength = 0
128+
} else if step < 0 {
129+
slicelength = (stop-start+1)/(step) + 1
130+
} else {
131+
slicelength = (stop-start-1)/(step) + 1
132+
}
133+
134+
return
135+
}
136+
137+
// Check interface is satisfied

py/tuple.go

+12
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ func (t Tuple) M__iter__() Object {
4040
}
4141

4242
func (t Tuple) M__getitem__(key Object) Object {
43+
if slice, ok := key.(*Slice); ok {
44+
start, stop, step, slicelength := slice.GetIndices(len(t))
45+
if step == 1 {
46+
// Return a subslice since tuples are immutable
47+
return t[start:stop]
48+
}
49+
newTuple := make(Tuple, slicelength)
50+
for i, j := start, 0; j < slicelength; i, j = i+step, j+1 {
51+
newTuple[j] = t[i]
52+
}
53+
return newTuple
54+
}
4355
i := IndexIntCheck(key, len(t))
4456
return t[i]
4557
}

vm/eval.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -1270,7 +1270,19 @@ func do_MAKE_CLOSURE(vm *Vm, argc int32) {
12701270
// is pushed. See the slice( ) built-in function for more information.
12711271
func do_BUILD_SLICE(vm *Vm, argc int32) {
12721272
defer vm.CheckException()
1273-
vm.NotImplemented("BUILD_SLICE", argc)
1273+
var step py.Object
1274+
switch argc {
1275+
case 2:
1276+
step = py.None
1277+
case 3:
1278+
step = vm.POP()
1279+
default:
1280+
panic("Bad value for argc in BUILD_SLICE")
1281+
}
1282+
stop := vm.POP()
1283+
start := vm.TOP()
1284+
x := py.NewSlice(start, stop, step)
1285+
vm.SET_TOP(x)
12741286
}
12751287

12761288
// Prefixes any opcode which has an argument too big to fit into the

0 commit comments

Comments
 (0)