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/containers.py
CHANGED
|
@@ -1,59 +1,150 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from sonolus.script.array import Array
|
|
4
|
+
from sonolus.script.array_like import ArrayLike
|
|
4
5
|
from sonolus.script.debug import error
|
|
5
|
-
from sonolus.script.iterator import
|
|
6
|
-
from sonolus.script.range import Range
|
|
6
|
+
from sonolus.script.iterator import SonolusIterator
|
|
7
7
|
from sonolus.script.record import Record
|
|
8
8
|
from sonolus.script.values import alloc, copy
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class Pair[T, U](Record):
|
|
12
|
+
"""A generic pair of values.
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
```python
|
|
16
|
+
Pair[T, U](first: T, second: U)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
```python
|
|
21
|
+
pair = Pair(1, 2)
|
|
22
|
+
pair = Pair[int, Pair[int, int]](1, Pair(2, 3))
|
|
23
|
+
```
|
|
24
|
+
"""
|
|
25
|
+
|
|
12
26
|
first: T
|
|
27
|
+
"""The first value."""
|
|
28
|
+
|
|
13
29
|
second: U
|
|
30
|
+
"""The second value."""
|
|
31
|
+
|
|
32
|
+
def __lt__(self, other):
|
|
33
|
+
if self.first == other.first:
|
|
34
|
+
return self.second < other.second
|
|
35
|
+
return self.first < other.first
|
|
36
|
+
|
|
37
|
+
def __le__(self, other):
|
|
38
|
+
if self.first == other.first:
|
|
39
|
+
return self.second <= other.second
|
|
40
|
+
return self.first <= other.first
|
|
41
|
+
|
|
42
|
+
def __gt__(self, other):
|
|
43
|
+
if self.first == other.first:
|
|
44
|
+
return self.second > other.second
|
|
45
|
+
return self.first > other.first
|
|
46
|
+
|
|
47
|
+
def __ge__(self, other):
|
|
48
|
+
if self.first == other.first:
|
|
49
|
+
return self.second >= other.second
|
|
50
|
+
return self.first >= other.first
|
|
14
51
|
|
|
15
52
|
|
|
16
53
|
class VarArray[T, Capacity](Record, ArrayLike[T]):
|
|
54
|
+
"""An array with a variable size and fixed maximum capacity.
|
|
55
|
+
|
|
56
|
+
Usage:
|
|
57
|
+
```python
|
|
58
|
+
VarArray[T, Capacity].new() # Create a new empty array
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Examples:
|
|
62
|
+
```python
|
|
63
|
+
array = VarArray[int, 10].new()
|
|
64
|
+
array.append(1)
|
|
65
|
+
```
|
|
66
|
+
"""
|
|
67
|
+
|
|
17
68
|
_size: int
|
|
18
69
|
_array: Array[T, Capacity]
|
|
19
70
|
|
|
20
71
|
@classmethod
|
|
21
72
|
def new(cls):
|
|
22
|
-
|
|
23
|
-
|
|
73
|
+
"""Create a new empty array."""
|
|
74
|
+
element_type = cls.type_var_value(T)
|
|
75
|
+
capacity = cls.type_var_value(Capacity)
|
|
24
76
|
return cls(0, alloc(Array[element_type, capacity]))
|
|
25
77
|
|
|
26
|
-
def
|
|
78
|
+
def __len__(self) -> int:
|
|
79
|
+
"""Return the number of elements in the array."""
|
|
27
80
|
return self._size
|
|
28
81
|
|
|
29
82
|
@classmethod
|
|
30
83
|
def capacity(cls) -> int:
|
|
31
|
-
|
|
84
|
+
"""Return the maximum number of elements the array can hold."""
|
|
85
|
+
return cls.type_var_value(Capacity)
|
|
32
86
|
|
|
33
87
|
def is_full(self) -> bool:
|
|
88
|
+
"""Return whether the array is full."""
|
|
34
89
|
return self._size == self.capacity()
|
|
35
90
|
|
|
36
91
|
def __getitem__(self, item) -> T:
|
|
92
|
+
"""Return the element at the given index.
|
|
93
|
+
|
|
94
|
+
The returned value continues to be part of the array.
|
|
95
|
+
Future modifications to the array will affect the returned value.
|
|
96
|
+
|
|
97
|
+
Note:
|
|
98
|
+
Future modifications to the array may cause unexpected changes to the returned value.
|
|
99
|
+
If the array may be modified in the future, it's recommended to make a copy of the value.
|
|
100
|
+
|
|
101
|
+
For example:
|
|
102
|
+
```python
|
|
103
|
+
a = VarArray[Pair, 10].new()
|
|
104
|
+
a.append(Pair(1, 2))
|
|
105
|
+
a.append(Pair(3, 4))
|
|
106
|
+
a.append(Pair(5, 6))
|
|
107
|
+
p = a[1]
|
|
108
|
+
a.pop(0) # Elements are shifted back
|
|
109
|
+
assert p == Pair(5, 6) # The value of p has changed
|
|
110
|
+
```
|
|
111
|
+
"""
|
|
37
112
|
return self._array[item]
|
|
38
113
|
|
|
39
114
|
def __setitem__(self, key: int, value: T):
|
|
115
|
+
"""Update the element at the given index."""
|
|
40
116
|
self._array[key] = value
|
|
41
117
|
|
|
118
|
+
def __delitem__(self, key: int):
|
|
119
|
+
"""Remove the element at the given index."""
|
|
120
|
+
self.pop(key)
|
|
121
|
+
|
|
42
122
|
def append(self, value: T):
|
|
43
|
-
"""
|
|
44
|
-
|
|
123
|
+
"""Append a copy of the given value to the end of the array.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
value: The value to append.
|
|
127
|
+
"""
|
|
128
|
+
assert self._size < len(self._array)
|
|
45
129
|
self._array[self._size] = value
|
|
46
130
|
self._size += 1
|
|
47
131
|
|
|
48
132
|
def extend(self, values: ArrayLike[T]):
|
|
49
|
-
"""Appends copies of the values in the given array to the end of the array.
|
|
133
|
+
"""Appends copies of the values in the given array to the end of the array.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
values: The values to append.
|
|
137
|
+
"""
|
|
50
138
|
for value in values:
|
|
51
139
|
self.append(value)
|
|
52
140
|
|
|
53
141
|
def pop(self, index: int | None = None) -> T:
|
|
54
|
-
"""
|
|
142
|
+
"""Remove and return a copy of the value at the given index.
|
|
55
143
|
|
|
56
144
|
Preserves the relative order of the elements.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
index: The index of the value to remove. If None, the last element is removed.
|
|
57
148
|
"""
|
|
58
149
|
if index is None:
|
|
59
150
|
index = self._size - 1
|
|
@@ -61,35 +152,48 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
|
|
|
61
152
|
value = copy(self._array[index])
|
|
62
153
|
self._size -= 1
|
|
63
154
|
if index < self._size:
|
|
64
|
-
for i in
|
|
155
|
+
for i in range(index, self._size):
|
|
65
156
|
self._array[i] = self._array[i + 1]
|
|
66
157
|
return value
|
|
67
158
|
|
|
68
159
|
def insert(self, index: int, value: T):
|
|
69
|
-
"""
|
|
160
|
+
"""Insert a copy of the given value at the given index.
|
|
70
161
|
|
|
71
162
|
Preserves the relative order of the elements.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
index: The index at which to insert the value. Must be in the range [0, size].
|
|
166
|
+
value: The value to insert.
|
|
72
167
|
"""
|
|
73
168
|
assert 0 <= index <= self._size
|
|
74
|
-
assert self._size < self._array
|
|
169
|
+
assert self._size < len(self._array)
|
|
75
170
|
self._size += 1
|
|
76
|
-
for i in
|
|
171
|
+
for i in range(self._size - 1, index, -1):
|
|
77
172
|
self._array[i] = self._array[i - 1]
|
|
78
173
|
self._array[index] = value
|
|
79
174
|
|
|
80
175
|
def remove(self, value: T) -> bool:
|
|
81
|
-
"""
|
|
176
|
+
"""Remove the first occurrence of the given value, returning whether the value was removed.
|
|
82
177
|
|
|
83
178
|
Preserves the relative order of the elements.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
value: The value to remove
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
True if the value was removed, False otherwise.
|
|
84
185
|
"""
|
|
85
|
-
index = self.
|
|
186
|
+
index = self.index(value)
|
|
86
187
|
if index < 0:
|
|
87
188
|
return False
|
|
88
189
|
self.pop(index)
|
|
89
190
|
return True
|
|
90
191
|
|
|
91
192
|
def clear(self):
|
|
92
|
-
"""
|
|
193
|
+
"""Clear the array, removing all elements.
|
|
194
|
+
|
|
195
|
+
References to elements are not immediately changed, but future insertions may overwrite them.
|
|
196
|
+
"""
|
|
93
197
|
self._size = 0
|
|
94
198
|
|
|
95
199
|
def set_add(self, value: T) -> bool:
|
|
@@ -97,8 +201,14 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
|
|
|
97
201
|
|
|
98
202
|
If the value is already present, the array is not modified.
|
|
99
203
|
If the array is full, the value is not added.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
value: The value to add
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
True if the value was added, False otherwise.
|
|
100
210
|
"""
|
|
101
|
-
if self._size >= self._array
|
|
211
|
+
if self._size >= len(self._array):
|
|
102
212
|
return False
|
|
103
213
|
if value in self:
|
|
104
214
|
return False
|
|
@@ -109,8 +219,14 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
|
|
|
109
219
|
"""Removes the first occurrence of the given value, returning whether the value was removed.
|
|
110
220
|
|
|
111
221
|
Does not preserve the relative order of the elements.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
value: The value to remove
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
True if the value was removed, False otherwise.
|
|
112
228
|
"""
|
|
113
|
-
index = self.
|
|
229
|
+
index = self.index(value)
|
|
114
230
|
if index < 0:
|
|
115
231
|
return False
|
|
116
232
|
if index < self._size - 1:
|
|
@@ -118,11 +234,18 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
|
|
|
118
234
|
self._size -= 1
|
|
119
235
|
return True
|
|
120
236
|
|
|
237
|
+
def __iadd__(self, other):
|
|
238
|
+
"""Appends copies of the values in the given array to the end of the array."""
|
|
239
|
+
self.extend(other)
|
|
240
|
+
return self
|
|
241
|
+
|
|
121
242
|
def __eq__(self, other):
|
|
122
|
-
if
|
|
243
|
+
if not isinstance(other, ArrayLike):
|
|
244
|
+
return False
|
|
245
|
+
if len(self) != len(other):
|
|
123
246
|
return False
|
|
124
247
|
i = 0
|
|
125
|
-
while i < self
|
|
248
|
+
while i < len(self):
|
|
126
249
|
if self[i] != other[i]:
|
|
127
250
|
return False
|
|
128
251
|
i += 1
|
|
@@ -135,66 +258,144 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
|
|
|
135
258
|
raise TypeError("unhashable type: 'VarArray'")
|
|
136
259
|
|
|
137
260
|
|
|
138
|
-
class
|
|
261
|
+
class _ArrayMapEntry[K, V](Record):
|
|
139
262
|
key: K
|
|
140
263
|
value: V
|
|
141
264
|
|
|
142
265
|
|
|
143
266
|
class ArrayMap[K, V, Capacity](Record):
|
|
267
|
+
"""A map implemented as an array of key-value pairs with a fixed maximum capacity.
|
|
268
|
+
|
|
269
|
+
Usage:
|
|
270
|
+
```python
|
|
271
|
+
ArrayMap[K, V, Capacity].new() # Create a new empty map
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Examples:
|
|
275
|
+
```python
|
|
276
|
+
map = ArrayMap[int, int, 10].new()
|
|
277
|
+
map[1] = 2
|
|
278
|
+
map[3] = 4
|
|
279
|
+
assert 1 in map
|
|
280
|
+
assert 2 not in map
|
|
281
|
+
assert map[3] == 4
|
|
282
|
+
```
|
|
283
|
+
"""
|
|
284
|
+
|
|
144
285
|
_size: int
|
|
145
|
-
_array: Array[
|
|
286
|
+
_array: Array[_ArrayMapEntry[K, V], Capacity]
|
|
146
287
|
|
|
147
288
|
@classmethod
|
|
148
289
|
def new(cls):
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
290
|
+
"""Create a new empty map."""
|
|
291
|
+
key_type = cls.type_var_value(K)
|
|
292
|
+
value_type = cls.type_var_value(V)
|
|
293
|
+
capacity = cls.type_var_value(Capacity)
|
|
294
|
+
return cls(0, alloc(Array[_ArrayMapEntry[key_type, value_type], capacity]))
|
|
295
|
+
|
|
296
|
+
def __len__(self) -> int:
|
|
297
|
+
"""Return the number of key-value pairs in the map."""
|
|
155
298
|
return self._size
|
|
156
299
|
|
|
157
300
|
@classmethod
|
|
158
301
|
def capacity(cls) -> int:
|
|
159
|
-
|
|
302
|
+
"""Return the maximum number of key-value pairs the map can hold."""
|
|
303
|
+
return cls.type_var_value(Capacity)
|
|
160
304
|
|
|
161
305
|
def is_full(self) -> bool:
|
|
306
|
+
"""Return whether the map is full."""
|
|
162
307
|
return self._size == self.capacity()
|
|
163
308
|
|
|
164
309
|
def keys(self) -> SonolusIterator[K]:
|
|
310
|
+
"""Return an iterator over the keys in the map."""
|
|
165
311
|
return _ArrayMapKeyIterator(self, 0)
|
|
166
312
|
|
|
167
313
|
def values(self) -> SonolusIterator[V]:
|
|
168
|
-
|
|
314
|
+
"""Return an iterator over the values in the map."""
|
|
315
|
+
return _ArrayMapValueIterator(self, 0)
|
|
169
316
|
|
|
170
317
|
def items(self) -> SonolusIterator[tuple[K, V]]:
|
|
171
|
-
|
|
318
|
+
"""Return an iterator over the key-value pairs in the map."""
|
|
319
|
+
return _ArrayMapEntryIterator(self, 0)
|
|
320
|
+
|
|
321
|
+
def __iter__(self):
|
|
322
|
+
"""Return an iterator over the keys in the map."""
|
|
323
|
+
return self.keys()
|
|
172
324
|
|
|
173
325
|
def __getitem__(self, key: K) -> V:
|
|
174
|
-
|
|
326
|
+
"""Return the value associated with the given key.
|
|
327
|
+
|
|
328
|
+
Must be called with a key that is present in the map.
|
|
329
|
+
|
|
330
|
+
The returned value continues to be part of the map.
|
|
331
|
+
Future modifications to the map will affect the returned value.
|
|
332
|
+
|
|
333
|
+
Notes:
|
|
334
|
+
Future modifications to the map may cause unexpected changes to the returned value.
|
|
335
|
+
If the map may be modified in the future, it's recommended to make a copy of the value.
|
|
336
|
+
|
|
337
|
+
For example:
|
|
338
|
+
```python
|
|
339
|
+
map = ArrayMap[int, Pair[int, int], 10].new()
|
|
340
|
+
map[1] = Pair(2, 3)
|
|
341
|
+
map[3] = Pair(4, 5)
|
|
342
|
+
map[5] = Pair(6, 7)
|
|
343
|
+
p = map[3]
|
|
344
|
+
map.pop(1)
|
|
345
|
+
# The value of `p` may now be different
|
|
346
|
+
```
|
|
347
|
+
"""
|
|
348
|
+
for i in range(self._size):
|
|
175
349
|
entry = self._array[i]
|
|
176
350
|
if entry.key == key:
|
|
177
351
|
return entry.value
|
|
178
352
|
error()
|
|
179
353
|
|
|
180
354
|
def __setitem__(self, key: K, value: V):
|
|
181
|
-
|
|
355
|
+
"""Associate the given key with the given value.
|
|
356
|
+
|
|
357
|
+
If the key is already present in the map, the value is updated.
|
|
358
|
+
Must not be called if the map is full.
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
key: The key to associate with the value.
|
|
362
|
+
value: The value to associate with the key
|
|
363
|
+
"""
|
|
364
|
+
for i in range(self._size):
|
|
182
365
|
entry = self._array[i]
|
|
183
366
|
if entry.key == key:
|
|
184
367
|
entry.value = value
|
|
185
368
|
return
|
|
186
|
-
|
|
187
|
-
self._array[self._size] =
|
|
369
|
+
assert self._size < self.capacity()
|
|
370
|
+
self._array[self._size] = _ArrayMapEntry(key, value)
|
|
188
371
|
self._size += 1
|
|
189
372
|
|
|
190
373
|
def __contains__(self, key: K) -> bool:
|
|
191
|
-
|
|
374
|
+
"""Return whether the given key is present in the map.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
key: The key to check for
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
True if the key is present, False otherwise.
|
|
381
|
+
"""
|
|
382
|
+
for i in range(self._size): # noqa: SIM110
|
|
192
383
|
if self._array[i].key == key:
|
|
193
384
|
return True
|
|
194
385
|
return False
|
|
195
386
|
|
|
196
387
|
def pop(self, key: K) -> V:
|
|
197
|
-
|
|
388
|
+
"""Remove and return a copy of the value associated with the given key.
|
|
389
|
+
|
|
390
|
+
Must be called with a key that is present in the map.
|
|
391
|
+
|
|
392
|
+
Args:
|
|
393
|
+
key: The key to remove
|
|
394
|
+
|
|
395
|
+
Returns:
|
|
396
|
+
The value associated with the key
|
|
397
|
+
"""
|
|
398
|
+
for i in range(self._size):
|
|
198
399
|
entry = self._array[i]
|
|
199
400
|
if entry.key == key:
|
|
200
401
|
value = copy(entry.value)
|
|
@@ -205,6 +406,7 @@ class ArrayMap[K, V, Capacity](Record):
|
|
|
205
406
|
error()
|
|
206
407
|
|
|
207
408
|
def clear(self):
|
|
409
|
+
"""Clear the map, removing all key-value pairs."""
|
|
208
410
|
self._size = 0
|
|
209
411
|
|
|
210
412
|
|
|
@@ -213,35 +415,39 @@ class _ArrayMapKeyIterator[K, V, Capacity](Record, SonolusIterator):
|
|
|
213
415
|
_index: int
|
|
214
416
|
|
|
215
417
|
def has_next(self) -> bool:
|
|
216
|
-
return self._index < self._map
|
|
418
|
+
return self._index < len(self._map)
|
|
217
419
|
|
|
218
|
-
def
|
|
219
|
-
|
|
420
|
+
def get(self) -> K:
|
|
421
|
+
return self._map._array[self._index].key
|
|
422
|
+
|
|
423
|
+
def advance(self):
|
|
220
424
|
self._index += 1
|
|
221
|
-
return key
|
|
222
425
|
|
|
223
426
|
|
|
224
|
-
class
|
|
427
|
+
class _ArrayMapValueIterator[K, V, Capacity](Record, SonolusIterator):
|
|
225
428
|
_map: ArrayMap[K, V, Capacity]
|
|
226
429
|
_index: int
|
|
227
430
|
|
|
228
431
|
def has_next(self) -> bool:
|
|
229
|
-
return self._index < self._map
|
|
432
|
+
return self._index < len(self._map)
|
|
433
|
+
|
|
434
|
+
def get(self) -> V:
|
|
435
|
+
return self._map._array[self._index].value
|
|
230
436
|
|
|
231
|
-
def
|
|
232
|
-
value = self._map._array[self._index].value
|
|
437
|
+
def advance(self):
|
|
233
438
|
self._index += 1
|
|
234
|
-
return value
|
|
235
439
|
|
|
236
440
|
|
|
237
|
-
class
|
|
441
|
+
class _ArrayMapEntryIterator[K, V, Capacity](Record, SonolusIterator):
|
|
238
442
|
_map: ArrayMap[K, V, Capacity]
|
|
239
443
|
_index: int
|
|
240
444
|
|
|
241
445
|
def has_next(self) -> bool:
|
|
242
|
-
return self._index < self._map
|
|
446
|
+
return self._index < len(self._map)
|
|
243
447
|
|
|
244
|
-
def
|
|
448
|
+
def get(self) -> tuple[K, V]:
|
|
245
449
|
entry = self._map._array[self._index]
|
|
246
|
-
self._index += 1
|
|
247
450
|
return entry.key, entry.value
|
|
451
|
+
|
|
452
|
+
def advance(self):
|
|
453
|
+
self._index += 1
|
sonolus/script/debug.py
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
|
+
from contextvars import ContextVar
|
|
2
3
|
from typing import Any, Never
|
|
3
4
|
|
|
4
|
-
from sonolus.backend.flow import cfg_to_mermaid
|
|
5
5
|
from sonolus.backend.mode import Mode
|
|
6
6
|
from sonolus.backend.ops import Op
|
|
7
|
-
from sonolus.backend.
|
|
8
|
-
from sonolus.
|
|
7
|
+
from sonolus.backend.optimize.flow import cfg_to_mermaid
|
|
8
|
+
from sonolus.backend.optimize.passes import CompilerPass, run_passes
|
|
9
|
+
from sonolus.backend.optimize.simplify import CoalesceFlow
|
|
9
10
|
from sonolus.script.internal.context import GlobalContextState, ctx, set_ctx
|
|
10
11
|
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
11
12
|
from sonolus.script.internal.native import native_function
|
|
12
13
|
from sonolus.script.num import Num
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
debug_log_callback = ContextVar[Callable[[Num], None]]("debug_log_callback")
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
@meta_fn
|
|
17
19
|
def error(message: str | None = None) -> None:
|
|
18
|
-
message =
|
|
20
|
+
message = message._as_py_() if message is not None else "Error"
|
|
19
21
|
if not isinstance(message, str):
|
|
20
22
|
raise ValueError("Expected a string")
|
|
21
23
|
if ctx():
|
|
@@ -26,24 +28,35 @@ def error(message: str | None = None) -> None:
|
|
|
26
28
|
raise RuntimeError(message)
|
|
27
29
|
|
|
28
30
|
|
|
29
|
-
@
|
|
31
|
+
@meta_fn
|
|
30
32
|
def debug_log(value: Num):
|
|
33
|
+
"""Log a value in debug mode."""
|
|
34
|
+
if debug_log_callback.get(None):
|
|
35
|
+
return debug_log_callback.get()(value)
|
|
36
|
+
else:
|
|
37
|
+
return _debug_log(value)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@native_function(Op.DebugLog)
|
|
41
|
+
def _debug_log(value: Num):
|
|
31
42
|
print(f"[DEBUG] {value}")
|
|
43
|
+
return 0
|
|
32
44
|
|
|
33
45
|
|
|
34
46
|
@native_function(Op.DebugPause)
|
|
35
47
|
def debug_pause():
|
|
48
|
+
"""Pause the game if in debug mode."""
|
|
36
49
|
input("[DEBUG] Paused")
|
|
37
50
|
|
|
38
51
|
|
|
39
52
|
def assert_true(value: Num, message: str | None = None):
|
|
40
|
-
message =
|
|
53
|
+
message = message if message is not None else "Assertion failed"
|
|
41
54
|
if not value:
|
|
42
55
|
error(message)
|
|
43
56
|
|
|
44
57
|
|
|
45
58
|
def assert_false(value: Num, message: str | None = None):
|
|
46
|
-
message =
|
|
59
|
+
message = message if message is not None else "Assertion failed"
|
|
47
60
|
if value:
|
|
48
61
|
error(message)
|
|
49
62
|
|
|
@@ -62,9 +75,12 @@ def terminate():
|
|
|
62
75
|
raise RuntimeError("Terminated")
|
|
63
76
|
|
|
64
77
|
|
|
65
|
-
def visualize_cfg(fn: Callable[[], Any]) -> str:
|
|
78
|
+
def visualize_cfg(fn: Callable[[], Any], passes: list[CompilerPass] | None = None) -> str:
|
|
66
79
|
from sonolus.build.compile import callback_to_cfg
|
|
67
80
|
|
|
81
|
+
if passes is None:
|
|
82
|
+
passes = [CoalesceFlow()]
|
|
83
|
+
|
|
68
84
|
cfg = callback_to_cfg(GlobalContextState(Mode.PLAY), fn, "")
|
|
69
|
-
cfg =
|
|
85
|
+
cfg = run_passes(cfg, passes)
|
|
70
86
|
return cfg_to_mermaid(cfg)
|