forked from spylang/spy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path_slice.spy
More file actions
170 lines (129 loc) · 5.45 KB
/
Copy path_slice.spy
File metadata and controls
170 lines (129 loc) · 5.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
"""
SPy Slice module
A Slice similar to CPython's
"""
from operator import OpSpec, MetaArg
from types import NoneType
from __spy__ import interp_list, interp_tuple
# ===== ATTENTION =======
#
# The code in this module is very fragile/brittle: the problem is that we cannot use ANY
# tuple literal and/or tuple type here, because as soon as we do, we trigger an implicit
# import of _tuple.spy, which causes a circular dependency:
#
# _slice.spy --> _tuple.spy
# _tuple.spy --> _range.spy (because it calls range())
# _range.spy --> _list.spy (because it uses a list literal)
# _list.spy --> _slice.spy (because of explicit import)
#
# This results in this error:
# E ImportError: cannot import `_slice.Slice`
# E | .../spy/stdlib/_list.spy:11
# E | from _slice import Slice, tuple3
#
# The temporary workaround is to use interp_tuple instead of tuple, which we can do
# since it's used only in blue code.
#
# We probably need a better story to deal with this, though.
# =============================
@struct
class tuple3:
start: i32
stop: i32
step: i32
@struct
class Slice:
start: i32
start_is_none: i32 # bool
stop: i32
stop_is_none: i32 # bool
step: i32
step_is_none: i32 # bool
@blue.metafunc
def __new__(m_cls, m_start, m_stop, m_step) -> OpSpec:
TYPES = interp_tuple(
m_start.static_type, m_stop.static_type, m_step.static_type
)
# We can't use a normal list to hold the MetaArgs, since the _list module
# imports the _slice module (to use in list.__getitem__ and __setitem__)
# TO avoid a circular import, we use an interp_list
args_m = interp_list[MetaArg](m_start, m_stop, m_step)
# For each of the 8 possible combinations of three variables which
# can be either None and i32, create an mplementation with the
# correct parameter types. Each implementation calls Slice.__make__
# with the appropriate "flags" for None-ness set
if TYPES == interp_tuple(i32, i32, i32):
def impl1(_start: i32, _stop: i32, _step: i32) -> Slice:
return Slice.__make__(_start, 0, _stop, 0, _step, 0)
return OpSpec(impl1, interp_list[MetaArg](m_start, m_stop, m_step))
elif TYPES == interp_tuple(NoneType, i32, i32):
def impl2(_start: NoneType, _stop: i32, _step: i32) -> Slice:
return Slice.__make__(0, 1, _stop, 0, _step, 0)
return OpSpec(impl2, interp_list[MetaArg](m_start, m_stop, m_step))
elif TYPES == interp_tuple(i32, NoneType, i32):
def impl3(_start: i32, _stop: NoneType, _step: i32) -> Slice:
return Slice.__make__(_start, 0, 0, 1, _step, 0)
return OpSpec(impl3, interp_list[MetaArg](m_start, m_stop, m_step))
elif TYPES == interp_tuple(i32, i32, NoneType):
def impl4(_start: i32, _stop: i32, _step: NoneType) -> Slice:
return Slice.__make__(_start, 0, _stop, 0, 1, 1)
return OpSpec(impl4, interp_list[MetaArg](m_start, m_stop, m_step))
elif TYPES == interp_tuple(NoneType, NoneType, i32):
def impl5(_start: NoneType, _stop: NoneType, _step: i32) -> Slice:
return Slice.__make__(0, 1, 0, 1, _step, 0)
return OpSpec(impl5, interp_list[MetaArg](m_start, m_stop, m_step))
elif TYPES == interp_tuple(NoneType, i32, NoneType):
def impl6(_start: None, _stop: i32, _step: None) -> Slice:
return Slice.__make__(0, 1, _stop, 0, 1, 1)
return OpSpec(impl6, interp_list[MetaArg](m_start, m_stop, m_step))
elif TYPES == interp_tuple(i32, NoneType, NoneType):
def impl7(_start: i32, _stop: None, _step: None) -> Slice:
return Slice.__make__(_start, 0, 0, 1, 1, 1)
return OpSpec(impl7, interp_list[MetaArg](m_start, m_stop, m_step))
elif TYPES == interp_tuple(NoneType, NoneType, NoneType):
def impl8(_start: NoneType, _stop: NoneType, _step: NoneType) -> Slice:
return Slice.__make__(0, 1, 0, 1, 1, 1)
return OpSpec(impl8, interp_list[MetaArg](m_start, m_stop, m_step))
return OpSpec.NULL
def indices(self, length: i32) -> tuple3:
_step: i32 = 1
if not self.step_is_none:
_step = self.step
if _step == 0:
raise ValueError("slice step cannot be zero")
if length < 0:
raise ValueError("length should not be negative")
# Find lower and upper bounds for start and stop.
_lower: i32 = 0
if _step < 0:
_lower = -1
_upper: i32 = length
if _step < 0:
_upper = length - 1
# Compute start.
_start: i32 = 0
if self.start_is_none:
if _step < 0:
_start = _upper
else:
_start = _lower
else:
_start = self.start
if _start < 0:
_start = max(_start + length, _lower)
else:
_start = min(_start, _upper)
# Compute stop.
_stop: i32 = 0
if self.stop_is_none != 0:
if _step < 0:
_stop = _lower
else:
_stop = _upper
else:
_stop = self.stop
if _stop < 0:
_stop = max(_stop + length, _lower)
else:
_stop = min(_stop, _upper)
return tuple3(_start, _stop, _step)