sonolus.py 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sonolus.py might be problematic. Click here for more details.
- sonolus/backend/blocks.py +756 -756
- sonolus/backend/excepthook.py +37 -37
- sonolus/backend/finalize.py +77 -69
- sonolus/backend/interpret.py +7 -7
- sonolus/backend/ir.py +29 -3
- sonolus/backend/mode.py +24 -24
- sonolus/backend/node.py +40 -40
- sonolus/backend/ops.py +197 -197
- sonolus/backend/optimize/__init__.py +0 -0
- sonolus/backend/optimize/allocate.py +126 -0
- sonolus/backend/optimize/constant_evaluation.py +374 -0
- sonolus/backend/optimize/copy_coalesce.py +85 -0
- sonolus/backend/optimize/dead_code.py +185 -0
- sonolus/backend/optimize/dominance.py +96 -0
- sonolus/backend/{flow.py → optimize/flow.py} +122 -92
- sonolus/backend/optimize/inlining.py +137 -0
- sonolus/backend/optimize/liveness.py +177 -0
- sonolus/backend/optimize/optimize.py +44 -0
- sonolus/backend/optimize/passes.py +52 -0
- sonolus/backend/optimize/simplify.py +191 -0
- sonolus/backend/optimize/ssa.py +200 -0
- sonolus/backend/place.py +17 -25
- sonolus/backend/utils.py +58 -48
- sonolus/backend/visitor.py +1151 -882
- sonolus/build/cli.py +7 -1
- sonolus/build/compile.py +88 -90
- sonolus/build/engine.py +10 -5
- sonolus/build/level.py +24 -23
- sonolus/build/node.py +43 -43
- sonolus/script/archetype.py +438 -139
- sonolus/script/array.py +27 -10
- sonolus/script/array_like.py +297 -0
- sonolus/script/bucket.py +253 -191
- sonolus/script/containers.py +257 -51
- sonolus/script/debug.py +26 -10
- sonolus/script/easing.py +365 -0
- sonolus/script/effect.py +191 -131
- sonolus/script/engine.py +71 -4
- sonolus/script/globals.py +303 -269
- sonolus/script/instruction.py +205 -151
- sonolus/script/internal/__init__.py +5 -5
- sonolus/script/internal/builtin_impls.py +255 -144
- sonolus/script/{callbacks.py → internal/callbacks.py} +127 -127
- sonolus/script/internal/constant.py +139 -0
- sonolus/script/internal/context.py +26 -9
- sonolus/script/internal/descriptor.py +17 -17
- sonolus/script/internal/dict_impl.py +65 -0
- sonolus/script/internal/generic.py +6 -9
- sonolus/script/internal/impl.py +38 -13
- sonolus/script/internal/introspection.py +17 -14
- sonolus/script/internal/math_impls.py +121 -0
- sonolus/script/internal/native.py +40 -38
- sonolus/script/internal/random.py +67 -0
- sonolus/script/internal/range.py +81 -0
- sonolus/script/internal/transient.py +51 -0
- sonolus/script/internal/tuple_impl.py +113 -0
- sonolus/script/internal/value.py +3 -3
- sonolus/script/interval.py +338 -112
- sonolus/script/iterator.py +167 -214
- sonolus/script/level.py +24 -0
- sonolus/script/num.py +80 -48
- sonolus/script/options.py +257 -191
- sonolus/script/particle.py +190 -157
- sonolus/script/pointer.py +30 -30
- sonolus/script/print.py +102 -81
- sonolus/script/project.py +8 -0
- sonolus/script/quad.py +263 -0
- sonolus/script/record.py +47 -16
- sonolus/script/runtime.py +52 -1
- sonolus/script/sprite.py +418 -333
- sonolus/script/text.py +409 -407
- sonolus/script/timing.py +114 -42
- sonolus/script/transform.py +332 -48
- sonolus/script/ui.py +216 -160
- sonolus/script/values.py +6 -13
- sonolus/script/vec.py +196 -78
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/METADATA +1 -1
- sonolus_py-0.1.5.dist-info/RECORD +89 -0
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/WHEEL +1 -1
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/licenses/LICENSE +21 -21
- sonolus/backend/allocate.py +0 -51
- sonolus/backend/optimize.py +0 -9
- sonolus/backend/passes.py +0 -6
- sonolus/backend/simplify.py +0 -30
- sonolus/script/comptime.py +0 -160
- sonolus/script/graphics.py +0 -150
- sonolus/script/math.py +0 -92
- sonolus/script/range.py +0 -58
- sonolus_py-0.1.3.dist-info/RECORD +0 -75
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/entry_points.txt +0 -0
sonolus/script/array.py
CHANGED
|
@@ -5,29 +5,43 @@ from collections.abc import Iterable
|
|
|
5
5
|
from typing import Any, Self, final
|
|
6
6
|
|
|
7
7
|
from sonolus.backend.place import BlockPlace
|
|
8
|
+
from sonolus.script.array_like import ArrayLike
|
|
8
9
|
from sonolus.script.debug import assert_unreachable
|
|
9
10
|
from sonolus.script.internal.context import ctx
|
|
10
11
|
from sonolus.script.internal.error import InternalError
|
|
11
12
|
from sonolus.script.internal.generic import GenericValue
|
|
12
13
|
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
13
14
|
from sonolus.script.internal.value import Value
|
|
14
|
-
from sonolus.script.iterator import ArrayLike
|
|
15
15
|
from sonolus.script.num import Num
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
@final
|
|
19
19
|
class Array[T, Size](GenericValue, ArrayLike[T]):
|
|
20
|
+
"""A fixed size array of values.
|
|
21
|
+
|
|
22
|
+
Usage:
|
|
23
|
+
```python
|
|
24
|
+
array_1 = Array(1, 2, 3)
|
|
25
|
+
array_2 = Array[int, 0]()
|
|
26
|
+
```
|
|
27
|
+
"""
|
|
28
|
+
|
|
20
29
|
_value: list[T] | BlockPlace
|
|
21
30
|
|
|
22
31
|
@classmethod
|
|
23
32
|
@meta_fn
|
|
24
33
|
def element_type(cls) -> type[T] | type[Value]:
|
|
25
|
-
|
|
34
|
+
"""Return the type of elements in this array type."""
|
|
35
|
+
return cls.type_var_value(T)
|
|
26
36
|
|
|
27
37
|
@classmethod
|
|
28
38
|
@meta_fn
|
|
29
39
|
def size(cls) -> int:
|
|
30
|
-
|
|
40
|
+
"""Return the size of this array type.
|
|
41
|
+
|
|
42
|
+
On instances, use `len(array)` instead.
|
|
43
|
+
"""
|
|
44
|
+
return cls.type_var_value(Size)
|
|
31
45
|
|
|
32
46
|
def __new__(cls, *args: T) -> Array[T, Any]:
|
|
33
47
|
if cls._type_args_ is None:
|
|
@@ -97,10 +111,10 @@ class Array[T, Size](GenericValue, ArrayLike[T]):
|
|
|
97
111
|
iterator = iter(values)
|
|
98
112
|
return cls(*(cls.element_type()._from_list_(iterator) for _ in range(cls.size())))
|
|
99
113
|
|
|
100
|
-
def _to_list_(self) -> list[float | BlockPlace]:
|
|
114
|
+
def _to_list_(self, level_refs: dict[Any, int] | None = None) -> list[float | BlockPlace]:
|
|
101
115
|
match self._value:
|
|
102
116
|
case list():
|
|
103
|
-
return [entry for value in self._value for entry in value._to_list_()]
|
|
117
|
+
return [entry for value in self._value for entry in value._to_list_(level_refs)]
|
|
104
118
|
case BlockPlace():
|
|
105
119
|
return [
|
|
106
120
|
entry
|
|
@@ -130,7 +144,7 @@ class Array[T, Size](GenericValue, ArrayLike[T]):
|
|
|
130
144
|
|
|
131
145
|
def _copy_(self) -> Self:
|
|
132
146
|
if ctx():
|
|
133
|
-
place = ctx().alloc(size=self.
|
|
147
|
+
place = ctx().alloc(size=self._size_())
|
|
134
148
|
result: Self = self._from_place_(place)
|
|
135
149
|
result._copy_from_(self)
|
|
136
150
|
return result
|
|
@@ -145,16 +159,17 @@ class Array[T, Size](GenericValue, ArrayLike[T]):
|
|
|
145
159
|
else:
|
|
146
160
|
return cls._with_value([cls.element_type()._alloc_() for _ in range(cls.size())])
|
|
147
161
|
|
|
162
|
+
def __len__(self):
|
|
163
|
+
return self.size()
|
|
164
|
+
|
|
148
165
|
@meta_fn
|
|
149
166
|
def __getitem__(self, index: Num) -> T:
|
|
150
167
|
index: Num = Num._accept_(index)
|
|
151
|
-
if index._is_py_():
|
|
168
|
+
if index._is_py_() and 0 <= index._as_py_() < self.size():
|
|
152
169
|
const_index = index._as_py_()
|
|
153
170
|
if isinstance(const_index, float) and not const_index.is_integer():
|
|
154
171
|
raise ValueError("Array index must be an integer")
|
|
155
172
|
const_index = int(const_index)
|
|
156
|
-
if not 0 <= const_index < self.size():
|
|
157
|
-
raise IndexError("Array index out of range")
|
|
158
173
|
if isinstance(self._value, list):
|
|
159
174
|
if ctx():
|
|
160
175
|
return self._value[const_index]._get_()
|
|
@@ -215,7 +230,9 @@ class Array[T, Size](GenericValue, ArrayLike[T]):
|
|
|
215
230
|
dst._copy_from_(value)
|
|
216
231
|
|
|
217
232
|
def __eq__(self, other):
|
|
218
|
-
if
|
|
233
|
+
if not isinstance(other, ArrayLike):
|
|
234
|
+
return False
|
|
235
|
+
if len(self) != len(other):
|
|
219
236
|
return False
|
|
220
237
|
i = 0
|
|
221
238
|
while i < self.size():
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
from abc import ABC, abstractmethod
|
|
5
|
+
from collections.abc import Callable, Sequence
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from sonolus.script.iterator import SonolusIterator
|
|
9
|
+
from sonolus.script.num import Num
|
|
10
|
+
from sonolus.script.record import Record
|
|
11
|
+
from sonolus.script.values import copy
|
|
12
|
+
|
|
13
|
+
# Note: we don't use Range in this file because Range itself inherits from ArrayLike
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ArrayLike[T](Sequence, ABC):
|
|
17
|
+
"""Mixin for array-like objects.
|
|
18
|
+
|
|
19
|
+
Inheritors must implement `__len__`, `__getitem__`, and `__setitem__`.
|
|
20
|
+
|
|
21
|
+
Usage:
|
|
22
|
+
```python
|
|
23
|
+
class MyArrayLike[T](Record, ArrayLike[T]):
|
|
24
|
+
def __len__(self) -> int:
|
|
25
|
+
...
|
|
26
|
+
|
|
27
|
+
def __getitem__(self, index: Num) -> T:
|
|
28
|
+
...
|
|
29
|
+
|
|
30
|
+
def __setitem__(self, index: Num, value: T):
|
|
31
|
+
...
|
|
32
|
+
```
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def __len__(self) -> int:
|
|
37
|
+
"""Return the length of the array."""
|
|
38
|
+
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def __getitem__(self, index: Num) -> T:
|
|
41
|
+
"""Return the item at the given index.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
index: The index of the item. Must be an integer between 0 and `len(self) - 1`.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def __setitem__(self, index: Num, value: T):
|
|
49
|
+
"""Set the value of the item at the given index.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
index: The index of the item. Must be an integer between 0 and `len(self) - 1`.
|
|
53
|
+
value: The value to set.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __iter__(self) -> SonolusIterator[T]:
|
|
57
|
+
"""Return an iterator over the array."""
|
|
58
|
+
return _ArrayIterator(0, self)
|
|
59
|
+
|
|
60
|
+
def __contains__(self, value: Any) -> bool:
|
|
61
|
+
"""Return whether any element in the array is equal to the given value.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
value: The value to check for.
|
|
65
|
+
"""
|
|
66
|
+
i = 0
|
|
67
|
+
while i < len(self):
|
|
68
|
+
if self[i] == value:
|
|
69
|
+
return True
|
|
70
|
+
i += 1
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
def __reversed__(self):
|
|
74
|
+
"""Return a reversed view of the array."""
|
|
75
|
+
return _ArrayReverser(self)
|
|
76
|
+
|
|
77
|
+
def _enumerate_(self, start: Num = 0) -> SonolusIterator[T]:
|
|
78
|
+
return _ArrayEnumerator(0, start, self)
|
|
79
|
+
|
|
80
|
+
def index(self, value: T, start: Num = 0, stop: Num | None = None) -> Num:
|
|
81
|
+
"""Return the index of the value in the array equal to the given value.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
value: The value to search for.
|
|
85
|
+
start: The index to start searching from.
|
|
86
|
+
stop: The index to stop searching at. If `None`, search to the end of the array.
|
|
87
|
+
"""
|
|
88
|
+
if stop is None:
|
|
89
|
+
stop = len(self)
|
|
90
|
+
i = start
|
|
91
|
+
while i < stop:
|
|
92
|
+
if self[i] == value:
|
|
93
|
+
return i
|
|
94
|
+
i += 1
|
|
95
|
+
return -1
|
|
96
|
+
|
|
97
|
+
def count(self, value: T) -> Num:
|
|
98
|
+
"""Return the number of elements in the array equal to the given value.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
value: The value to count.
|
|
102
|
+
"""
|
|
103
|
+
count = 0
|
|
104
|
+
i = 0
|
|
105
|
+
while i < len(self):
|
|
106
|
+
if self[i] == value:
|
|
107
|
+
count += 1
|
|
108
|
+
i += 1
|
|
109
|
+
return count
|
|
110
|
+
|
|
111
|
+
def last_index(self, value: T) -> Num:
|
|
112
|
+
"""Return the last index of the value in the array equal to the given value.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
value: The value to search for.
|
|
116
|
+
"""
|
|
117
|
+
i = len(self) - 1
|
|
118
|
+
while i >= 0:
|
|
119
|
+
if self[i] == value:
|
|
120
|
+
return i
|
|
121
|
+
i -= 1
|
|
122
|
+
return -1
|
|
123
|
+
|
|
124
|
+
def index_of_max(self, *, key: Callable[T, Any] | None = None) -> Num:
|
|
125
|
+
"""Return the index of the maximum value in the array.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
key: A one-argument ordering function to use for comparison like the one used in `max()`.
|
|
129
|
+
"""
|
|
130
|
+
if len(self) == 0:
|
|
131
|
+
return -1
|
|
132
|
+
if key is None:
|
|
133
|
+
key = _identity
|
|
134
|
+
max_index = 0
|
|
135
|
+
i = 1
|
|
136
|
+
while i < len(self):
|
|
137
|
+
if key(self[i]) > key(self[max_index]):
|
|
138
|
+
max_index = i
|
|
139
|
+
i += 1
|
|
140
|
+
return max_index
|
|
141
|
+
|
|
142
|
+
def index_of_min(self, *, key: Callable[T, Any] | None = None) -> Num:
|
|
143
|
+
"""Return the index of the minimum value in the array.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
key: A one-argument ordering function to use for comparison like the one used in `min()`.
|
|
147
|
+
"""
|
|
148
|
+
if len(self) == 0:
|
|
149
|
+
return -1
|
|
150
|
+
if key is None:
|
|
151
|
+
key = _identity
|
|
152
|
+
min_index = 0
|
|
153
|
+
i = 1
|
|
154
|
+
while i < len(self):
|
|
155
|
+
if key(self[i]) < key(self[min_index]):
|
|
156
|
+
min_index = i
|
|
157
|
+
i += 1
|
|
158
|
+
return min_index
|
|
159
|
+
|
|
160
|
+
def _max_(self, key: Callable[T, Any] | None = None) -> T:
|
|
161
|
+
return self[self.index_of_max(key=key)]
|
|
162
|
+
|
|
163
|
+
def _min_(self, key: Callable[T, Any] | None = None) -> T:
|
|
164
|
+
return self[self.index_of_min(key=key)]
|
|
165
|
+
|
|
166
|
+
def swap(self, i: Num, j: Num, /):
|
|
167
|
+
"""Swap the values at the given indices.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
i: The first index.
|
|
171
|
+
j: The second index.
|
|
172
|
+
"""
|
|
173
|
+
temp = copy(self[i])
|
|
174
|
+
self[i] = self[j]
|
|
175
|
+
self[j] = temp
|
|
176
|
+
|
|
177
|
+
def sort(self, *, key: Callable[T, Any] | None = None, reverse: bool = False):
|
|
178
|
+
"""Sort the values in the array in place.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
key: A one-argument ordering function to use for comparison.
|
|
182
|
+
reverse: If `True`, sort in descending order, otherwise sort in ascending order.
|
|
183
|
+
"""
|
|
184
|
+
if len(self) < 15 or key is not None:
|
|
185
|
+
if key is None:
|
|
186
|
+
key = _identity
|
|
187
|
+
_insertion_sort(self, 0, len(self), key, reverse)
|
|
188
|
+
else:
|
|
189
|
+
# Heap sort is unstable, so if there's a key, we can't rely on it
|
|
190
|
+
_heap_sort(self, 0, len(self), reverse)
|
|
191
|
+
|
|
192
|
+
def shuffle(self):
|
|
193
|
+
"""Shuffle the values in the array in place."""
|
|
194
|
+
random.shuffle(self) # type: ignore
|
|
195
|
+
|
|
196
|
+
def reverse(self):
|
|
197
|
+
"""Reverse the values in the array in place."""
|
|
198
|
+
i = 0
|
|
199
|
+
j = len(self) - 1
|
|
200
|
+
while i < j:
|
|
201
|
+
self.swap(i, j)
|
|
202
|
+
i += 1
|
|
203
|
+
j -= 1
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _identity[T](value: T) -> T:
|
|
207
|
+
return value
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _insertion_sort[T](array: ArrayLike[T], start: Num, end: Num, key: Callable[T, Any], reverse: bool):
|
|
211
|
+
i = start + 1
|
|
212
|
+
if reverse:
|
|
213
|
+
while i < end:
|
|
214
|
+
j = i
|
|
215
|
+
while j > start and key(array[j - 1]) < key(array[j]):
|
|
216
|
+
array.swap(j - 1, j)
|
|
217
|
+
j -= 1
|
|
218
|
+
i += 1
|
|
219
|
+
else:
|
|
220
|
+
while i < end:
|
|
221
|
+
j = i
|
|
222
|
+
while j > start and key(array[j - 1]) > key(array[j]):
|
|
223
|
+
array.swap(j - 1, j)
|
|
224
|
+
j -= 1
|
|
225
|
+
i += 1
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _heapify[T](array: ArrayLike[T], end: Num, index: Num, reverse: bool):
|
|
229
|
+
while True:
|
|
230
|
+
left = index * 2 + 1
|
|
231
|
+
right = left + 1
|
|
232
|
+
largest = index
|
|
233
|
+
if left < end and (array[left] > array[largest]) != reverse:
|
|
234
|
+
largest = left
|
|
235
|
+
if right < end and (array[right] > array[largest]) != reverse:
|
|
236
|
+
largest = right
|
|
237
|
+
if largest == index:
|
|
238
|
+
break
|
|
239
|
+
array.swap(index, largest)
|
|
240
|
+
index = largest
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def _heap_sort[T](array: ArrayLike[T], start: Num, end: Num, reverse: bool):
|
|
244
|
+
i = end // 2 - 1
|
|
245
|
+
while i >= start:
|
|
246
|
+
_heapify(array, end, i, reverse)
|
|
247
|
+
i -= 1
|
|
248
|
+
i = end - 1
|
|
249
|
+
while i > start:
|
|
250
|
+
array.swap(start, i)
|
|
251
|
+
_heapify(array, i, start, reverse)
|
|
252
|
+
i -= 1
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class _ArrayIterator[V: ArrayLike](Record, SonolusIterator):
|
|
256
|
+
i: int
|
|
257
|
+
array: V
|
|
258
|
+
|
|
259
|
+
def has_next(self) -> bool:
|
|
260
|
+
return self.i < len(self.array)
|
|
261
|
+
|
|
262
|
+
def get(self) -> V:
|
|
263
|
+
return self.array[self.i]
|
|
264
|
+
|
|
265
|
+
def advance(self):
|
|
266
|
+
self.i += 1
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class _ArrayReverser[V: ArrayLike](Record, ArrayLike):
|
|
270
|
+
array: V
|
|
271
|
+
|
|
272
|
+
def __len__(self) -> int:
|
|
273
|
+
return len(self.array)
|
|
274
|
+
|
|
275
|
+
def __getitem__(self, index: Num) -> V:
|
|
276
|
+
return self.array[len(self) - 1 - index]
|
|
277
|
+
|
|
278
|
+
def __setitem__(self, index: Num, value: V):
|
|
279
|
+
self.array[len(self) - 1 - index] = value
|
|
280
|
+
|
|
281
|
+
def reversed(self) -> ArrayLike[V]:
|
|
282
|
+
return self.array
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class _ArrayEnumerator[V: ArrayLike](Record, SonolusIterator):
|
|
286
|
+
i: int
|
|
287
|
+
offset: int
|
|
288
|
+
array: V
|
|
289
|
+
|
|
290
|
+
def has_next(self) -> bool:
|
|
291
|
+
return self.i < len(self.array)
|
|
292
|
+
|
|
293
|
+
def get(self) -> tuple[int, Any]:
|
|
294
|
+
return self.i + self.offset, self.array[self.i]
|
|
295
|
+
|
|
296
|
+
def advance(self):
|
|
297
|
+
self.i += 1
|