sonolus.py 0.10.0__py3-none-any.whl → 0.10.1__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/optimize/constant_evaluation.py +4 -2
- sonolus/backend/optimize/optimize.py +2 -0
- sonolus/backend/optimize/simplify.py +15 -0
- sonolus/build/dev_server.py +5 -5
- sonolus/script/array.py +33 -4
- sonolus/script/array_like.py +141 -26
- sonolus/script/containers.py +40 -38
- sonolus/script/debug.py +42 -7
- sonolus/script/effect.py +5 -2
- sonolus/script/internal/builtin_impls.py +4 -4
- sonolus/script/internal/context.py +3 -0
- sonolus/script/internal/math_impls.py +19 -19
- sonolus/script/internal/native.py +7 -1
- sonolus/script/internal/range.py +3 -0
- sonolus/script/interval.py +6 -4
- sonolus/script/particle.py +5 -2
- sonolus/script/sprite.py +6 -2
- sonolus/script/transform.py +1 -0
- sonolus/script/vec.py +1 -0
- {sonolus_py-0.10.0.dist-info → sonolus_py-0.10.1.dist-info}/METADATA +1 -1
- {sonolus_py-0.10.0.dist-info → sonolus_py-0.10.1.dist-info}/RECORD +24 -24
- {sonolus_py-0.10.0.dist-info → sonolus_py-0.10.1.dist-info}/WHEEL +0 -0
- {sonolus_py-0.10.0.dist-info → sonolus_py-0.10.1.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.10.0.dist-info → sonolus_py-0.10.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -158,7 +158,8 @@ class SparseConditionalConstantPropagation(CompilerPass):
|
|
|
158
158
|
test_value = values[block]
|
|
159
159
|
new_test_value = self.evaluate_stmt(block.test, values)
|
|
160
160
|
if new_test_value != test_value:
|
|
161
|
-
|
|
161
|
+
if new_test_value is UNDEF:
|
|
162
|
+
continue
|
|
162
163
|
values[block] = new_test_value
|
|
163
164
|
if new_test_value is NAC:
|
|
164
165
|
flow_worklist.update(block.outgoing)
|
|
@@ -195,7 +196,8 @@ class SparseConditionalConstantPropagation(CompilerPass):
|
|
|
195
196
|
test_value = values[p]
|
|
196
197
|
new_test_value = self.evaluate_stmt(defn, values)
|
|
197
198
|
if new_test_value != test_value:
|
|
198
|
-
|
|
199
|
+
if new_test_value is UNDEF:
|
|
200
|
+
continue
|
|
199
201
|
values[p] = new_test_value
|
|
200
202
|
if new_test_value is NAC:
|
|
201
203
|
flow_worklist.update(p.outgoing)
|
|
@@ -10,6 +10,7 @@ from sonolus.backend.optimize.inlining import InlineVars
|
|
|
10
10
|
from sonolus.backend.optimize.simplify import (
|
|
11
11
|
CoalesceFlow,
|
|
12
12
|
CoalesceSmallConditionalBlocks,
|
|
13
|
+
CombineExitBlocks,
|
|
13
14
|
NormalizeSwitch,
|
|
14
15
|
RemoveRedundantArguments,
|
|
15
16
|
RewriteToSwitch,
|
|
@@ -55,5 +56,6 @@ STANDARD_PASSES = (
|
|
|
55
56
|
AdvancedDeadCodeElimination(),
|
|
56
57
|
CoalesceFlow(),
|
|
57
58
|
NormalizeSwitch(),
|
|
59
|
+
CombineExitBlocks(),
|
|
58
60
|
Allocate(),
|
|
59
61
|
)
|
|
@@ -77,6 +77,21 @@ class CoalesceFlow(CompilerPass):
|
|
|
77
77
|
return entry
|
|
78
78
|
|
|
79
79
|
|
|
80
|
+
class CombineExitBlocks(CompilerPass):
|
|
81
|
+
def run(self, entry: BasicBlock, config: OptimizerConfig) -> BasicBlock:
|
|
82
|
+
first_exit_block = None
|
|
83
|
+
for block in traverse_cfg_preorder(entry):
|
|
84
|
+
if not block.outgoing and not block.phis and not block.statements:
|
|
85
|
+
if first_exit_block is None:
|
|
86
|
+
first_exit_block = block
|
|
87
|
+
else:
|
|
88
|
+
for edge in [*block.incoming]:
|
|
89
|
+
edge.dst = first_exit_block
|
|
90
|
+
first_exit_block.incoming.add(edge)
|
|
91
|
+
block.incoming.clear()
|
|
92
|
+
return entry
|
|
93
|
+
|
|
94
|
+
|
|
80
95
|
class CoalesceSmallConditionalBlocks(CompilerPass):
|
|
81
96
|
def run(self, entry: BasicBlock, config: OptimizerConfig) -> BasicBlock:
|
|
82
97
|
queue = [entry]
|
sonolus/build/dev_server.py
CHANGED
|
@@ -117,7 +117,7 @@ class RebuildCommand:
|
|
|
117
117
|
print("Rebuilding...")
|
|
118
118
|
try:
|
|
119
119
|
start_time = perf_counter()
|
|
120
|
-
server_state.project_state = ProjectContextState()
|
|
120
|
+
server_state.project_state = ProjectContextState(dev=True)
|
|
121
121
|
server_state.project = project_module.project
|
|
122
122
|
build_collection(
|
|
123
123
|
server_state.project,
|
|
@@ -156,11 +156,11 @@ class HelpCommand:
|
|
|
156
156
|
print("Available Commands:\n")
|
|
157
157
|
|
|
158
158
|
for entry in DETAILED_HELP_TEXT:
|
|
159
|
-
print(f"
|
|
159
|
+
print(f"[{entry.alias}] {entry.command}")
|
|
160
160
|
|
|
161
161
|
for paragraph in entry.description:
|
|
162
|
-
initial_indent = "
|
|
163
|
-
subsequent_indent = "
|
|
162
|
+
initial_indent = " "
|
|
163
|
+
subsequent_indent = " "
|
|
164
164
|
wrapped = textwrap.fill(
|
|
165
165
|
paragraph,
|
|
166
166
|
width=max_width - len(initial_indent),
|
|
@@ -260,7 +260,7 @@ def run_server(
|
|
|
260
260
|
from sonolus.build.cli import build_collection
|
|
261
261
|
|
|
262
262
|
cache = CompileCache()
|
|
263
|
-
project_state = ProjectContextState()
|
|
263
|
+
project_state = ProjectContextState(dev=True)
|
|
264
264
|
|
|
265
265
|
start_time = perf_counter()
|
|
266
266
|
build_collection(project, build_dir, config, cache=cache, project_state=project_state)
|
sonolus/script/array.py
CHANGED
|
@@ -194,7 +194,22 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
194
194
|
|
|
195
195
|
@meta_fn
|
|
196
196
|
def __getitem__(self, index: int) -> T:
|
|
197
|
-
|
|
197
|
+
return self.get_unchecked(get_positive_index(index, self.size()))
|
|
198
|
+
|
|
199
|
+
@meta_fn
|
|
200
|
+
def get_unchecked(self, index: Num) -> T:
|
|
201
|
+
"""Get the element at the given index possibly without bounds checking.
|
|
202
|
+
|
|
203
|
+
The compiler may still determine that the index is out of bounds and throw an error, but it may skip these
|
|
204
|
+
checks at runtime.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
index: The index to get.
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
The element at the given index.
|
|
211
|
+
"""
|
|
212
|
+
index = Num._accept_(index)
|
|
198
213
|
if index._is_py_() and 0 <= index._as_py_() < self.size():
|
|
199
214
|
const_index = index._as_py_()
|
|
200
215
|
if isinstance(const_index, float) and not const_index.is_integer():
|
|
@@ -213,7 +228,8 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
213
228
|
)
|
|
214
229
|
elif callable(self._value):
|
|
215
230
|
return self.element_type()._from_backing_source_(
|
|
216
|
-
lambda offset: self._value((Num(offset) + Num(const_index * self.element_type()._size_())).ir())
|
|
231
|
+
lambda offset: self._value((Num(offset) + Num(const_index * self.element_type()._size_())).ir())
|
|
232
|
+
# type: ignore
|
|
217
233
|
)
|
|
218
234
|
else:
|
|
219
235
|
raise InternalError("Unexpected array value")
|
|
@@ -238,7 +254,20 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
238
254
|
|
|
239
255
|
@meta_fn
|
|
240
256
|
def __setitem__(self, index: int, value: T):
|
|
241
|
-
|
|
257
|
+
self.set_unchecked(get_positive_index(index, self.size()), value)
|
|
258
|
+
|
|
259
|
+
@meta_fn
|
|
260
|
+
def set_unchecked(self, index: Num, value: T):
|
|
261
|
+
"""Set the element at the given index possibly without bounds checking.
|
|
262
|
+
|
|
263
|
+
The compiler may still determine that the index is out of bounds and throw an error, but it may skip these
|
|
264
|
+
checks at runtime.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
index: The index to set.
|
|
268
|
+
value: The value to set.
|
|
269
|
+
"""
|
|
270
|
+
index = Num._accept_(index)
|
|
242
271
|
value = self.element_type()._accept_(value)
|
|
243
272
|
if ctx():
|
|
244
273
|
if isinstance(self._value, list):
|
|
@@ -288,7 +317,7 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
288
317
|
return False
|
|
289
318
|
i = 0
|
|
290
319
|
while i < self.size():
|
|
291
|
-
if self
|
|
320
|
+
if self.get_unchecked(i) != other.get_unchecked(i):
|
|
292
321
|
return False
|
|
293
322
|
i += 1
|
|
294
323
|
return True
|
sonolus/script/array_like.py
CHANGED
|
@@ -5,8 +5,10 @@ from abc import abstractmethod
|
|
|
5
5
|
from collections.abc import Callable, Sequence
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
+
from sonolus.script.debug import assert_true
|
|
8
9
|
from sonolus.script.internal.context import ctx
|
|
9
10
|
from sonolus.script.internal.impl import meta_fn
|
|
11
|
+
from sonolus.script.internal.math_impls import _trunc
|
|
10
12
|
from sonolus.script.iterator import SonolusIterator
|
|
11
13
|
from sonolus.script.maybe import Maybe, Nothing, Some
|
|
12
14
|
from sonolus.script.num import Num
|
|
@@ -58,9 +60,37 @@ class ArrayLike[T](Sequence[T]):
|
|
|
58
60
|
value: The value to set.
|
|
59
61
|
"""
|
|
60
62
|
|
|
63
|
+
@meta_fn
|
|
64
|
+
def get_unchecked(self, index: Num) -> T:
|
|
65
|
+
"""Get the element at the given index possibly without bounds checking or conversion of negative indexes.
|
|
66
|
+
|
|
67
|
+
The compiler may still determine that the index is out of bounds and throw an error, but it may skip these
|
|
68
|
+
checks at runtime.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
index: The index to get.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
The element at the given index.
|
|
75
|
+
"""
|
|
76
|
+
return self[index]
|
|
77
|
+
|
|
78
|
+
@meta_fn
|
|
79
|
+
def set_unchecked(self, index: Num, value: T):
|
|
80
|
+
"""Set the element at the given index possibly without bounds checking or conversion of negative indexes.
|
|
81
|
+
|
|
82
|
+
The compiler may still determine that the index is out of bounds and throw an error, but it may skip these
|
|
83
|
+
checks at runtime.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
index: The index to set.
|
|
87
|
+
value: The value to set.
|
|
88
|
+
"""
|
|
89
|
+
self[index] = value
|
|
90
|
+
|
|
61
91
|
def __iter__(self) -> SonolusIterator[T]:
|
|
62
92
|
"""Return an iterator over the array."""
|
|
63
|
-
return _ArrayIterator(0, self)
|
|
93
|
+
return _ArrayIterator(0, self.unchecked())
|
|
64
94
|
|
|
65
95
|
def __contains__(self, value: Any) -> bool:
|
|
66
96
|
"""Return whether any element in the array is equal to the given value.
|
|
@@ -70,7 +100,7 @@ class ArrayLike[T](Sequence[T]):
|
|
|
70
100
|
"""
|
|
71
101
|
i = 0
|
|
72
102
|
while i < len(self):
|
|
73
|
-
if self
|
|
103
|
+
if self.get_unchecked(i) == value:
|
|
74
104
|
return True
|
|
75
105
|
i += 1
|
|
76
106
|
return False
|
|
@@ -92,9 +122,13 @@ class ArrayLike[T](Sequence[T]):
|
|
|
92
122
|
"""
|
|
93
123
|
if stop is None:
|
|
94
124
|
stop = len(self)
|
|
95
|
-
|
|
125
|
+
else:
|
|
126
|
+
stop = get_positive_index(stop, len(self), check=False)
|
|
127
|
+
stop = min(stop, len(self))
|
|
128
|
+
start = get_positive_index(start, len(self), check=False)
|
|
129
|
+
i = max(start, 0)
|
|
96
130
|
while i < stop:
|
|
97
|
-
if self
|
|
131
|
+
if self.get_unchecked(i) == value:
|
|
98
132
|
return i
|
|
99
133
|
i += 1
|
|
100
134
|
return -1
|
|
@@ -108,7 +142,7 @@ class ArrayLike[T](Sequence[T]):
|
|
|
108
142
|
count = 0
|
|
109
143
|
i = 0
|
|
110
144
|
while i < len(self):
|
|
111
|
-
if self
|
|
145
|
+
if self.get_unchecked(i) == value:
|
|
112
146
|
count += 1
|
|
113
147
|
i += 1
|
|
114
148
|
return count
|
|
@@ -121,7 +155,7 @@ class ArrayLike[T](Sequence[T]):
|
|
|
121
155
|
"""
|
|
122
156
|
i = len(self) - 1
|
|
123
157
|
while i >= 0:
|
|
124
|
-
if self
|
|
158
|
+
if self.get_unchecked(i) == value:
|
|
125
159
|
return i
|
|
126
160
|
i -= 1
|
|
127
161
|
return -1
|
|
@@ -139,7 +173,7 @@ class ArrayLike[T](Sequence[T]):
|
|
|
139
173
|
max_index = 0
|
|
140
174
|
i = 1
|
|
141
175
|
while i < len(self):
|
|
142
|
-
if key(self
|
|
176
|
+
if key(self.get_unchecked(i)) > key(self.get_unchecked(max_index)): # type: ignore
|
|
143
177
|
max_index = i
|
|
144
178
|
i += 1
|
|
145
179
|
return max_index
|
|
@@ -157,7 +191,7 @@ class ArrayLike[T](Sequence[T]):
|
|
|
157
191
|
min_index = 0
|
|
158
192
|
i = 1
|
|
159
193
|
while i < len(self):
|
|
160
|
-
if key(self
|
|
194
|
+
if key(self.get_unchecked(i)) < key(self.get_unchecked(min_index)): # type: ignore
|
|
161
195
|
min_index = i
|
|
162
196
|
i += 1
|
|
163
197
|
return min_index
|
|
@@ -165,23 +199,25 @@ class ArrayLike[T](Sequence[T]):
|
|
|
165
199
|
def _max_(self, key: Callable[[T], Any] | None = None) -> T:
|
|
166
200
|
index = self.index_of_max(key=key)
|
|
167
201
|
assert index != -1
|
|
168
|
-
return self
|
|
202
|
+
return self.get_unchecked(index)
|
|
169
203
|
|
|
170
204
|
def _min_(self, key: Callable[[T], Any] | None = None) -> T:
|
|
171
205
|
index = self.index_of_min(key=key)
|
|
172
206
|
assert index != -1
|
|
173
|
-
return self
|
|
207
|
+
return self.get_unchecked(index)
|
|
174
208
|
|
|
175
209
|
def swap(self, i: int, j: int, /):
|
|
176
|
-
"""Swap the values at the given indices.
|
|
210
|
+
"""Swap the values at the given positive indices.
|
|
177
211
|
|
|
178
212
|
Args:
|
|
179
213
|
i: The first index.
|
|
180
214
|
j: The second index.
|
|
181
215
|
"""
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
216
|
+
check_positive_index(i, len(self))
|
|
217
|
+
check_positive_index(j, len(self))
|
|
218
|
+
temp = copy(self.get_unchecked(i))
|
|
219
|
+
self.set_unchecked(i, self.get_unchecked(j))
|
|
220
|
+
self.set_unchecked(j, temp)
|
|
185
221
|
|
|
186
222
|
def sort(self, *, key: Callable[[T], Any] | None = None, reverse: bool = False):
|
|
187
223
|
"""Sort the values in the array in place.
|
|
@@ -194,10 +230,10 @@ class ArrayLike[T](Sequence[T]):
|
|
|
194
230
|
if key is None:
|
|
195
231
|
key = _identity # type: ignore
|
|
196
232
|
# May be worth adding a block sort variant for better performance on large arrays in the future
|
|
197
|
-
_insertion_sort(self, 0, len(self), key, reverse) # type: ignore
|
|
233
|
+
_insertion_sort(self.unchecked(), 0, len(self), key, reverse) # type: ignore
|
|
198
234
|
else:
|
|
199
235
|
# Heap sort is unstable, so if there's a key, we can't rely on it
|
|
200
|
-
_heap_sort(self, 0, len(self), reverse) # type: ignore
|
|
236
|
+
_heap_sort(self.unchecked(), 0, len(self), reverse) # type: ignore
|
|
201
237
|
|
|
202
238
|
def shuffle(self):
|
|
203
239
|
"""Shuffle the values in the array in place."""
|
|
@@ -212,6 +248,10 @@ class ArrayLike[T](Sequence[T]):
|
|
|
212
248
|
i += 1
|
|
213
249
|
j -= 1
|
|
214
250
|
|
|
251
|
+
def unchecked(self) -> ArrayLike[T]:
|
|
252
|
+
"""Return a proxy object that may skip bounds checking and may not support negative indexes."""
|
|
253
|
+
return UncheckedArrayProxy(self)
|
|
254
|
+
|
|
215
255
|
|
|
216
256
|
def _identity[T](value: T) -> T:
|
|
217
257
|
return value
|
|
@@ -268,7 +308,7 @@ class _ArrayIterator[V: ArrayLike](Record, SonolusIterator):
|
|
|
268
308
|
|
|
269
309
|
def next(self) -> Maybe[V]:
|
|
270
310
|
if self.i < len(self.array):
|
|
271
|
-
value = self.array
|
|
311
|
+
value = self.array.get_unchecked(self.i)
|
|
272
312
|
self.i += 1
|
|
273
313
|
return Some(value)
|
|
274
314
|
return Nothing
|
|
@@ -286,6 +326,12 @@ class _ArrayReverser[V: ArrayLike](Record, ArrayLike):
|
|
|
286
326
|
def __setitem__(self, index: int, value: V):
|
|
287
327
|
self.array[len(self) - 1 - index] = value
|
|
288
328
|
|
|
329
|
+
def get_unchecked(self, index: Num) -> V:
|
|
330
|
+
return self.array.get_unchecked(len(self) - 1 - index)
|
|
331
|
+
|
|
332
|
+
def set_unchecked(self, index: Num, value: V):
|
|
333
|
+
self.array.set_unchecked(len(self) - 1 - index, value)
|
|
334
|
+
|
|
289
335
|
def reversed(self) -> ArrayLike[V]:
|
|
290
336
|
return self.array
|
|
291
337
|
|
|
@@ -297,30 +343,99 @@ class _ArrayEnumerator[V: ArrayLike](Record, SonolusIterator):
|
|
|
297
343
|
|
|
298
344
|
def next(self) -> Maybe[tuple[int, Any]]:
|
|
299
345
|
if self.i < len(self.array):
|
|
300
|
-
result = (self.i + self.offset, self.array
|
|
346
|
+
result = (self.i + self.offset, self.array.get_unchecked(self.i))
|
|
301
347
|
self.i += 1
|
|
302
348
|
return Some(result)
|
|
303
349
|
return Nothing
|
|
304
350
|
|
|
305
351
|
|
|
306
352
|
@meta_fn
|
|
307
|
-
def get_positive_index(
|
|
308
|
-
|
|
353
|
+
def get_positive_index(
|
|
354
|
+
index: int | float, length: int | float, *, include_end: bool = False, check: bool = True
|
|
355
|
+
) -> int:
|
|
356
|
+
"""Get the positive index for the given index in the array of the given length, and also perform bounds checking.
|
|
309
357
|
|
|
310
|
-
This is used to convert negative
|
|
358
|
+
This is used to convert negative indices relative to the end of the array to positive indices.
|
|
311
359
|
|
|
312
360
|
Args:
|
|
313
361
|
index: The index to convert.
|
|
314
362
|
length: The length of the array.
|
|
363
|
+
include_end: Whether to allow the index to be equal to the length of the array (i.e., one past the end).
|
|
364
|
+
check: Whether to perform bounds checking. Must be a compile-time constant.
|
|
315
365
|
|
|
316
366
|
Returns:
|
|
317
|
-
The positive index.
|
|
367
|
+
The positive integer index.
|
|
318
368
|
"""
|
|
319
369
|
if not ctx():
|
|
320
|
-
|
|
370
|
+
if check:
|
|
371
|
+
if (include_end and not -length <= index <= length) or (not include_end and not -length <= index < length):
|
|
372
|
+
raise IndexError("Index out of range")
|
|
373
|
+
if int(index) != index:
|
|
374
|
+
raise ValueError("Index must be an integer")
|
|
375
|
+
if int(length) != length:
|
|
376
|
+
raise ValueError("Length must be an integer")
|
|
377
|
+
if length < 0:
|
|
378
|
+
raise ValueError("Length must be non-negative")
|
|
379
|
+
return int(index + (index < 0) * length)
|
|
321
380
|
index = Num._accept_(index)
|
|
322
381
|
length = Num._accept_(length)
|
|
323
|
-
if
|
|
324
|
-
|
|
382
|
+
if Num._accept_(check)._as_py_():
|
|
383
|
+
include_end = Num._accept_(include_end)
|
|
384
|
+
if not include_end._is_py_():
|
|
385
|
+
is_in_bounds = Num.and_(index >= -length, index < (length + include_end))
|
|
386
|
+
elif include_end._as_py_():
|
|
387
|
+
is_in_bounds = Num.and_(index >= -length, index <= length)
|
|
388
|
+
else:
|
|
389
|
+
is_in_bounds = Num.and_(index >= -length, index < length)
|
|
390
|
+
assert_true(Num.and_(is_in_bounds, _trunc(index) == index), "Invalid index")
|
|
391
|
+
# Skipping length check since typically these are managed by the library and unlikely to be wrong
|
|
392
|
+
return index + (index < 0) * length
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
@meta_fn
|
|
396
|
+
def check_positive_index(index: int, length: int, include_end: bool = False) -> int | float:
|
|
397
|
+
"""Check that the given index is a valid index for the array of the given length and convert it to an integer.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
index: The index to check.
|
|
401
|
+
length: The length of the array.
|
|
402
|
+
include_end: Whether to allow the index to be equal to the length of the array (i.e., one past the end).
|
|
403
|
+
|
|
404
|
+
Returns:
|
|
405
|
+
The index as an integer.
|
|
406
|
+
"""
|
|
407
|
+
if not ctx():
|
|
408
|
+
if (include_end and not 0 <= index <= length) or (not include_end and not 0 <= index < length):
|
|
409
|
+
raise IndexError("Index out of range")
|
|
410
|
+
if int(index) != index:
|
|
411
|
+
raise ValueError("Index must be an integer")
|
|
412
|
+
if int(length) != length:
|
|
413
|
+
raise ValueError("Length must be an integer")
|
|
414
|
+
if length < 0:
|
|
415
|
+
raise ValueError("Length must be non-negative")
|
|
416
|
+
return int(index)
|
|
417
|
+
index = Num._accept_(index)
|
|
418
|
+
length = Num._accept_(length)
|
|
419
|
+
include_end = Num._accept_(include_end)
|
|
420
|
+
if not include_end._is_py_():
|
|
421
|
+
is_in_bounds = Num.and_(index >= 0, index < (length + include_end))
|
|
422
|
+
elif include_end._as_py_():
|
|
423
|
+
is_in_bounds = Num.and_(index >= 0, index <= length)
|
|
325
424
|
else:
|
|
326
|
-
|
|
425
|
+
is_in_bounds = Num.and_(index >= 0, index < length)
|
|
426
|
+
assert_true(Num.and_(is_in_bounds, _trunc(index) == index), "Invalid index")
|
|
427
|
+
# Skipping length check since typically these are managed by the library and unlikely to be wrong
|
|
428
|
+
return index
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
class UncheckedArrayProxy[T](Record, ArrayLike):
|
|
432
|
+
array: T
|
|
433
|
+
|
|
434
|
+
def __len__(self) -> int:
|
|
435
|
+
return len(self.array)
|
|
436
|
+
|
|
437
|
+
def __getitem__(self, index: int) -> Any:
|
|
438
|
+
return self.array.get_unchecked(index)
|
|
439
|
+
|
|
440
|
+
def __setitem__(self, index: int, value: Any):
|
|
441
|
+
self.array.set_unchecked(index, value)
|
sonolus/script/containers.py
CHANGED
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Self
|
|
4
4
|
|
|
5
|
-
from sonolus.backend.visitor import compile_and_call
|
|
6
5
|
from sonolus.script.array import Array
|
|
7
6
|
from sonolus.script.array_like import ArrayLike, get_positive_index
|
|
8
7
|
from sonolus.script.debug import error
|
|
@@ -144,11 +143,11 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
|
|
|
144
143
|
assert p == Pair(5, 6) # The value of p has changed
|
|
145
144
|
```
|
|
146
145
|
"""
|
|
147
|
-
return self._array
|
|
146
|
+
return self._array.get_unchecked(get_positive_index(item, self._size))
|
|
148
147
|
|
|
149
148
|
def __setitem__(self, key: int, value: T):
|
|
150
149
|
"""Update the element at the given index."""
|
|
151
|
-
self._array
|
|
150
|
+
self._array.set_unchecked(get_positive_index(key, self._size), value)
|
|
152
151
|
|
|
153
152
|
def __delitem__(self, key: int):
|
|
154
153
|
"""Remove the element at the given index."""
|
|
@@ -160,8 +159,8 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
|
|
|
160
159
|
Args:
|
|
161
160
|
value: The value to append.
|
|
162
161
|
"""
|
|
163
|
-
assert self._size < len(self._array)
|
|
164
|
-
self._array
|
|
162
|
+
assert self._size < len(self._array), "Array is full"
|
|
163
|
+
self._array.set_unchecked(self._size, value)
|
|
165
164
|
self._size += 1
|
|
166
165
|
|
|
167
166
|
def append_unchecked(self, value: T):
|
|
@@ -172,7 +171,7 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
|
|
|
172
171
|
Args:
|
|
173
172
|
value: The value to append.
|
|
174
173
|
"""
|
|
175
|
-
self._array
|
|
174
|
+
self._array.set_unchecked(self._size, value)
|
|
176
175
|
self._size += 1
|
|
177
176
|
|
|
178
177
|
def extend(self, values: ArrayLike[T]):
|
|
@@ -181,8 +180,12 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
|
|
|
181
180
|
Args:
|
|
182
181
|
values: The values to append.
|
|
183
182
|
"""
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
assert self._size + len(values) <= len(self._array), "Array is full"
|
|
184
|
+
i = 0
|
|
185
|
+
while i < len(values):
|
|
186
|
+
self._array.set_unchecked(self._size + i, values[i])
|
|
187
|
+
i += 1
|
|
188
|
+
self._size += len(values)
|
|
186
189
|
|
|
187
190
|
def pop(self, index: int | None = None) -> T:
|
|
188
191
|
"""Remove and return a copy of the value at the given index.
|
|
@@ -194,13 +197,12 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
|
|
|
194
197
|
"""
|
|
195
198
|
if index is None:
|
|
196
199
|
index = self._size - 1
|
|
197
|
-
index = get_positive_index(index,
|
|
198
|
-
|
|
199
|
-
value = copy(self._array[index])
|
|
200
|
+
index = get_positive_index(index, self._size)
|
|
201
|
+
value = copy(self._array.get_unchecked(index))
|
|
200
202
|
self._size -= 1
|
|
201
203
|
if index < self._size:
|
|
202
204
|
for i in range(index, self._size):
|
|
203
|
-
self._array
|
|
205
|
+
self._array.set_unchecked(i, self._array.get_unchecked(i + 1))
|
|
204
206
|
return value
|
|
205
207
|
|
|
206
208
|
def insert(self, index: int, value: T):
|
|
@@ -212,12 +214,12 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
|
|
|
212
214
|
index: The index at which to insert the value. Must be in the range [0, size].
|
|
213
215
|
value: The value to insert.
|
|
214
216
|
"""
|
|
215
|
-
index = clamp(get_positive_index(index,
|
|
216
|
-
assert self._size < len(self._array)
|
|
217
|
+
index = clamp(get_positive_index(index, self._size, include_end=True), 0, self._size)
|
|
218
|
+
assert self._size < len(self._array), "Array is full"
|
|
217
219
|
self._size += 1
|
|
218
220
|
for i in range(self._size - 1, index, -1):
|
|
219
|
-
self._array
|
|
220
|
-
self._array
|
|
221
|
+
self._array.set_unchecked(i, self._array.get_unchecked(i - 1))
|
|
222
|
+
self._array.set_unchecked(index, value)
|
|
221
223
|
|
|
222
224
|
def remove(self, value: T) -> bool:
|
|
223
225
|
"""Remove the first occurrence of the given value, returning whether the value was removed.
|
|
@@ -277,7 +279,7 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
|
|
|
277
279
|
if index < 0:
|
|
278
280
|
return False
|
|
279
281
|
if index < self._size - 1:
|
|
280
|
-
self._array
|
|
282
|
+
self._array.set_unchecked(index, self._array.get_unchecked(self._size - 1))
|
|
281
283
|
self._size -= 1
|
|
282
284
|
return True
|
|
283
285
|
|
|
@@ -293,7 +295,7 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
|
|
|
293
295
|
return False
|
|
294
296
|
i = 0
|
|
295
297
|
while i < len(self):
|
|
296
|
-
if self
|
|
298
|
+
if self.get_unchecked(i) != other.get_unchecked(i):
|
|
297
299
|
return False
|
|
298
300
|
i += 1
|
|
299
301
|
return True
|
|
@@ -329,12 +331,8 @@ class ArrayPointer[T](Record, ArrayLike[T]):
|
|
|
329
331
|
"""Return the type of the elements in the array."""
|
|
330
332
|
return cls.type_var_value(T)
|
|
331
333
|
|
|
332
|
-
def _check_index(self, index: int):
|
|
333
|
-
assert 0 <= index < self.size
|
|
334
|
-
|
|
335
334
|
@meta_fn
|
|
336
335
|
def _get_item(self, item: int) -> T:
|
|
337
|
-
item = get_positive_index(item, self.size)
|
|
338
336
|
if not ctx():
|
|
339
337
|
raise TypeError("ArrayPointer values cannot be accessed outside of a context")
|
|
340
338
|
return _deref(
|
|
@@ -344,14 +342,18 @@ class ArrayPointer[T](Record, ArrayLike[T]):
|
|
|
344
342
|
self.element_type(),
|
|
345
343
|
)
|
|
346
344
|
|
|
345
|
+
def __getitem__(self, item) -> T:
|
|
346
|
+
return self.get_unchecked(get_positive_index(item, self.size))
|
|
347
|
+
|
|
348
|
+
def __setitem__(self, key: int, value: T):
|
|
349
|
+
self.set_unchecked(get_positive_index(key, self.size), value)
|
|
350
|
+
|
|
347
351
|
@meta_fn
|
|
348
|
-
def
|
|
349
|
-
compile_and_call(self._check_index, item)
|
|
352
|
+
def get_unchecked(self, item: int) -> T:
|
|
350
353
|
return self._get_item(item)._get_()
|
|
351
354
|
|
|
352
355
|
@meta_fn
|
|
353
|
-
def
|
|
354
|
-
compile_and_call(self._check_index, key)
|
|
356
|
+
def set_unchecked(self, key: int, value: T):
|
|
355
357
|
dst = self._get_item(key)
|
|
356
358
|
if self.element_type()._is_value_type_():
|
|
357
359
|
dst._set_(value)
|
|
@@ -519,7 +521,7 @@ class ArrayMap[K, V, Capacity](Record):
|
|
|
519
521
|
```
|
|
520
522
|
"""
|
|
521
523
|
for i in range(self._size):
|
|
522
|
-
entry = self._array
|
|
524
|
+
entry = self._array.get_unchecked(i)
|
|
523
525
|
if entry.key == key:
|
|
524
526
|
return entry.value
|
|
525
527
|
error()
|
|
@@ -535,12 +537,12 @@ class ArrayMap[K, V, Capacity](Record):
|
|
|
535
537
|
value: The value to associate with the key
|
|
536
538
|
"""
|
|
537
539
|
for i in range(self._size):
|
|
538
|
-
entry = self._array
|
|
540
|
+
entry = self._array.get_unchecked(i)
|
|
539
541
|
if entry.key == key:
|
|
540
542
|
entry.value = value
|
|
541
543
|
return
|
|
542
|
-
assert self._size < self.capacity()
|
|
543
|
-
self._array
|
|
544
|
+
assert self._size < self.capacity(), "Map is full"
|
|
545
|
+
self._array.set_unchecked(self._size, _ArrayMapEntry(key, value))
|
|
544
546
|
self._size += 1
|
|
545
547
|
|
|
546
548
|
def __delitem__(self, key: K):
|
|
@@ -552,11 +554,11 @@ class ArrayMap[K, V, Capacity](Record):
|
|
|
552
554
|
key: The key to remove
|
|
553
555
|
"""
|
|
554
556
|
for i in range(self._size):
|
|
555
|
-
entry = self._array
|
|
557
|
+
entry = self._array.get_unchecked(i)
|
|
556
558
|
if entry.key == key:
|
|
557
559
|
self._size -= 1
|
|
558
560
|
if i < self._size:
|
|
559
|
-
self._array
|
|
561
|
+
self._array.set_unchecked(i, self._array.get_unchecked(self._size))
|
|
560
562
|
return
|
|
561
563
|
error()
|
|
562
564
|
|
|
@@ -570,7 +572,7 @@ class ArrayMap[K, V, Capacity](Record):
|
|
|
570
572
|
True if the key is present, False otherwise.
|
|
571
573
|
"""
|
|
572
574
|
for i in range(self._size): # noqa: SIM110
|
|
573
|
-
if self._array
|
|
575
|
+
if self._array.get_unchecked(i).key == key:
|
|
574
576
|
return True
|
|
575
577
|
return False
|
|
576
578
|
|
|
@@ -586,12 +588,12 @@ class ArrayMap[K, V, Capacity](Record):
|
|
|
586
588
|
The value associated with the key
|
|
587
589
|
"""
|
|
588
590
|
for i in range(self._size):
|
|
589
|
-
entry = self._array
|
|
591
|
+
entry = self._array.get_unchecked(i)
|
|
590
592
|
if entry.key == key:
|
|
591
593
|
value = copy(entry.value)
|
|
592
594
|
self._size -= 1
|
|
593
595
|
if i < self._size:
|
|
594
|
-
self._array
|
|
596
|
+
self._array.set_unchecked(i, self._array.get_unchecked(self._size))
|
|
595
597
|
return value
|
|
596
598
|
error()
|
|
597
599
|
|
|
@@ -606,7 +608,7 @@ class _ArrayMapKeyIterator[K, V, Capacity](Record, SonolusIterator):
|
|
|
606
608
|
|
|
607
609
|
def next(self) -> Maybe[K]:
|
|
608
610
|
if self._index < len(self._map):
|
|
609
|
-
key = self._map._array
|
|
611
|
+
key = self._map._array.get_unchecked(self._index).key
|
|
610
612
|
self._index += 1
|
|
611
613
|
return Some(key)
|
|
612
614
|
return Nothing
|
|
@@ -618,7 +620,7 @@ class _ArrayMapValueIterator[K, V, Capacity](Record, SonolusIterator):
|
|
|
618
620
|
|
|
619
621
|
def next(self) -> Maybe[V]:
|
|
620
622
|
if self._index < len(self._map):
|
|
621
|
-
value = self._map._array
|
|
623
|
+
value = self._map._array.get_unchecked(self._index).value
|
|
622
624
|
self._index += 1
|
|
623
625
|
return Some(value)
|
|
624
626
|
return Nothing
|
|
@@ -630,7 +632,7 @@ class _ArrayMapEntryIterator[K, V, Capacity](Record, SonolusIterator):
|
|
|
630
632
|
|
|
631
633
|
def next(self) -> Maybe[tuple[K, V]]:
|
|
632
634
|
if self._index < len(self._map):
|
|
633
|
-
entry = self._map._array
|
|
635
|
+
entry = self._map._array.get_unchecked(self._index)
|
|
634
636
|
result = (entry.key, entry.value)
|
|
635
637
|
self._index += 1
|
|
636
638
|
return Some(result)
|
sonolus/script/debug.py
CHANGED
|
@@ -18,17 +18,23 @@ debug_log_callback = ContextVar[Callable[[Num], None]]("debug_log_callback")
|
|
|
18
18
|
|
|
19
19
|
@meta_fn
|
|
20
20
|
def error(message: str | None = None) -> Never: # type: ignore
|
|
21
|
-
"""Raise an error.
|
|
21
|
+
"""Raise an error, and if in a dev build, log a message and pause the game.
|
|
22
22
|
|
|
23
23
|
This function is used to raise an error during runtime.
|
|
24
24
|
When this happens, the game will pause in debug mode. The current callback will also immediately return 0.
|
|
25
|
+
|
|
26
|
+
In non-dev builds, this function will terminate the current callback silently.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
message: The message to log.
|
|
25
30
|
"""
|
|
26
31
|
message = validate_value(message)._as_py_() or "Error" # type: ignore
|
|
27
32
|
if not isinstance(message, str):
|
|
28
33
|
raise ValueError("Expected a string")
|
|
29
34
|
if ctx():
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
if ctx().project_state.dev:
|
|
36
|
+
debug_log(ctx().map_debug_message(message))
|
|
37
|
+
debug_pause()
|
|
32
38
|
terminate()
|
|
33
39
|
else:
|
|
34
40
|
raise RuntimeError(message)
|
|
@@ -40,6 +46,9 @@ def static_error(message: str | None = None) -> Never:
|
|
|
40
46
|
|
|
41
47
|
This function is used to raise an error during compile-time if the compiler cannot guarantee that
|
|
42
48
|
this function will not be called during runtime.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
message: The message to log.
|
|
43
52
|
"""
|
|
44
53
|
message = validate_value(message)._as_py_() or "Error" # type: ignore
|
|
45
54
|
if not isinstance(message, str):
|
|
@@ -70,19 +79,38 @@ def debug_pause():
|
|
|
70
79
|
|
|
71
80
|
@meta_fn
|
|
72
81
|
def notify(message: str):
|
|
73
|
-
"""Log a code that can be decoded by the dev server and pause the game if in debug mode.
|
|
82
|
+
"""Log a code that can be decoded by the dev server and pause the game if in debug mode and in a dev build.
|
|
83
|
+
|
|
84
|
+
Does nothing if not a dev build.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
message: The message to log.
|
|
88
|
+
"""
|
|
74
89
|
message = validate_value(message)._as_py_() # type: ignore
|
|
75
90
|
if not isinstance(message, str):
|
|
76
91
|
raise ValueError("Expected a string")
|
|
77
92
|
if ctx():
|
|
78
|
-
|
|
79
|
-
|
|
93
|
+
if ctx().project_state.dev:
|
|
94
|
+
debug_log(ctx().map_debug_message(message))
|
|
95
|
+
debug_pause()
|
|
80
96
|
else:
|
|
81
97
|
print(f"[NOTIFY] {message}")
|
|
82
98
|
|
|
83
99
|
|
|
84
100
|
@meta_fn
|
|
85
|
-
def
|
|
101
|
+
def require(value: int | float | bool, message: str | None = None):
|
|
102
|
+
"""Require a condition to be true, or raise an error.
|
|
103
|
+
|
|
104
|
+
Similar to assert, but does not get stripped in non-dev builds.
|
|
105
|
+
|
|
106
|
+
If in a dev build, this function will log a message and pause the game if the condition is false.
|
|
107
|
+
|
|
108
|
+
In non-dev builds, this function will terminate the current callback silently if the condition is false.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
value: The condition to check.
|
|
112
|
+
message: The message to log if the condition is false.
|
|
113
|
+
"""
|
|
86
114
|
if not ctx():
|
|
87
115
|
if not value:
|
|
88
116
|
raise AssertionError(message if message is not None else "Assertion failed")
|
|
@@ -104,6 +132,13 @@ def assert_true(value: int | float | bool, message: str | None = None):
|
|
|
104
132
|
set_ctx(t_branch)
|
|
105
133
|
|
|
106
134
|
|
|
135
|
+
@meta_fn
|
|
136
|
+
def assert_true(value: int | float | bool, message: str | None = None):
|
|
137
|
+
if ctx() and not ctx().project_state.dev:
|
|
138
|
+
return
|
|
139
|
+
require(value, message)
|
|
140
|
+
|
|
141
|
+
|
|
107
142
|
def assert_false(value: int | float | bool, message: str | None = None):
|
|
108
143
|
assert_true(not value, message)
|
|
109
144
|
|
sonolus/script/effect.py
CHANGED
|
@@ -5,7 +5,7 @@ from dataclasses import dataclass
|
|
|
5
5
|
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
6
6
|
|
|
7
7
|
from sonolus.backend.ops import Op
|
|
8
|
-
from sonolus.script.array_like import ArrayLike
|
|
8
|
+
from sonolus.script.array_like import ArrayLike, check_positive_index
|
|
9
9
|
from sonolus.script.debug import static_error
|
|
10
10
|
from sonolus.script.internal.introspection import get_field_specifiers
|
|
11
11
|
from sonolus.script.internal.native import native_function
|
|
@@ -111,7 +111,10 @@ class EffectGroup(Record, ArrayLike[Effect]):
|
|
|
111
111
|
return self.size
|
|
112
112
|
|
|
113
113
|
def __getitem__(self, index: int) -> Effect:
|
|
114
|
-
|
|
114
|
+
check_positive_index(index, self.size)
|
|
115
|
+
return Effect(self.start_id + index)
|
|
116
|
+
|
|
117
|
+
def get_unchecked(self, index: int) -> Effect:
|
|
115
118
|
return Effect(self.start_id + index)
|
|
116
119
|
|
|
117
120
|
def __setitem__(self, index: int, value: Effect) -> None:
|
|
@@ -3,7 +3,7 @@ from typing import Never, assert_never
|
|
|
3
3
|
from sonolus.backend.ops import Op
|
|
4
4
|
from sonolus.script.array import Array
|
|
5
5
|
from sonolus.script.array_like import ArrayLike
|
|
6
|
-
from sonolus.script.debug import error
|
|
6
|
+
from sonolus.script.debug import error, require
|
|
7
7
|
from sonolus.script.internal.context import ctx
|
|
8
8
|
from sonolus.script.internal.dict_impl import DictImpl
|
|
9
9
|
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
@@ -192,7 +192,7 @@ def _max_num_iterator(iterable, default, key):
|
|
|
192
192
|
iterator = iterable.__iter__() # noqa: PLC2801
|
|
193
193
|
initial = iterator.next()
|
|
194
194
|
if initial.is_nothing:
|
|
195
|
-
|
|
195
|
+
require(default is not None, "default must be provided if the iterator is empty")
|
|
196
196
|
return default
|
|
197
197
|
if key is not None:
|
|
198
198
|
result = initial.get_unsafe()
|
|
@@ -285,7 +285,7 @@ def _min_num_iterator(iterable, default, key):
|
|
|
285
285
|
iterator = iterable.__iter__() # noqa: PLC2801
|
|
286
286
|
initial = iterator.next()
|
|
287
287
|
if initial.is_nothing:
|
|
288
|
-
|
|
288
|
+
require(default is not None, "default must be provided if the iterator is empty")
|
|
289
289
|
return default
|
|
290
290
|
if key is not None:
|
|
291
291
|
result = initial.get_unsafe()
|
|
@@ -371,7 +371,7 @@ def _sum(iterable, /, start=0):
|
|
|
371
371
|
|
|
372
372
|
|
|
373
373
|
def _next(iterator):
|
|
374
|
-
|
|
374
|
+
require(isinstance(iterator, SonolusIterator), "Only subclasses of SonolusIterator are supported as iterators")
|
|
375
375
|
value = iterator.next()
|
|
376
376
|
if value.is_some:
|
|
377
377
|
return value.get_unsafe()
|
|
@@ -44,17 +44,20 @@ class ProjectContextState:
|
|
|
44
44
|
const_mappings: dict[Any, int]
|
|
45
45
|
debug_str_mappings: dict[str, int]
|
|
46
46
|
lock: Lock
|
|
47
|
+
dev: bool
|
|
47
48
|
|
|
48
49
|
def __init__(
|
|
49
50
|
self,
|
|
50
51
|
rom: ReadOnlyMemory | None = None,
|
|
51
52
|
const_mappings: dict[Any, int] | None = None,
|
|
52
53
|
debug_str_mappings: dict[str, int] | None = None,
|
|
54
|
+
dev: bool = False,
|
|
53
55
|
):
|
|
54
56
|
self.rom = ReadOnlyMemory() if rom is None else rom
|
|
55
57
|
self.const_mappings = {} if const_mappings is None else const_mappings
|
|
56
58
|
self.debug_str_mappings = {} if debug_str_mappings is None else debug_str_mappings
|
|
57
59
|
self.lock = Lock()
|
|
60
|
+
self.dev = dev
|
|
58
61
|
|
|
59
62
|
|
|
60
63
|
class ModeContextState:
|
|
@@ -4,72 +4,72 @@ from sonolus.backend.ops import Op
|
|
|
4
4
|
from sonolus.script.internal.native import native_function
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
@native_function(Op.Sin)
|
|
7
|
+
@native_function(Op.Sin, const_eval=True)
|
|
8
8
|
def _sin(x: float) -> float:
|
|
9
9
|
return math.sin(x)
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
@native_function(Op.Cos)
|
|
12
|
+
@native_function(Op.Cos, const_eval=True)
|
|
13
13
|
def _cos(x: float) -> float:
|
|
14
14
|
return math.cos(x)
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
@native_function(Op.Tan)
|
|
17
|
+
@native_function(Op.Tan, const_eval=True)
|
|
18
18
|
def _tan(x: float) -> float:
|
|
19
19
|
return math.tan(x)
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
@native_function(Op.Arcsin)
|
|
22
|
+
@native_function(Op.Arcsin, const_eval=True)
|
|
23
23
|
def _asin(x: float) -> float:
|
|
24
24
|
return math.asin(x)
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
@native_function(Op.Arccos)
|
|
27
|
+
@native_function(Op.Arccos, const_eval=True)
|
|
28
28
|
def _acos(x: float) -> float:
|
|
29
29
|
return math.acos(x)
|
|
30
30
|
|
|
31
31
|
|
|
32
|
-
@native_function(Op.Arctan)
|
|
32
|
+
@native_function(Op.Arctan, const_eval=True)
|
|
33
33
|
def _atan(x: float) -> float:
|
|
34
34
|
return math.atan(x)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
@native_function(Op.Arctan2)
|
|
37
|
+
@native_function(Op.Arctan2, const_eval=True)
|
|
38
38
|
def _atan2(y: float, x: float) -> float:
|
|
39
39
|
return math.atan2(y, x)
|
|
40
40
|
|
|
41
41
|
|
|
42
|
-
@native_function(Op.Sinh)
|
|
42
|
+
@native_function(Op.Sinh, const_eval=True)
|
|
43
43
|
def _sinh(x: float) -> float:
|
|
44
44
|
return math.sinh(x)
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
@native_function(Op.Cosh)
|
|
47
|
+
@native_function(Op.Cosh, const_eval=True)
|
|
48
48
|
def _cosh(x: float) -> float:
|
|
49
49
|
return math.cosh(x)
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
@native_function(Op.Tanh)
|
|
52
|
+
@native_function(Op.Tanh, const_eval=True)
|
|
53
53
|
def _tanh(x: float) -> float:
|
|
54
54
|
return math.tanh(x)
|
|
55
55
|
|
|
56
56
|
|
|
57
|
-
@native_function(Op.Floor)
|
|
57
|
+
@native_function(Op.Floor, const_eval=True)
|
|
58
58
|
def _floor(x: float) -> float:
|
|
59
59
|
return math.floor(x)
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
@native_function(Op.Ceil)
|
|
62
|
+
@native_function(Op.Ceil, const_eval=True)
|
|
63
63
|
def _ceil(x: float) -> float:
|
|
64
64
|
return math.ceil(x)
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
@native_function(Op.Trunc)
|
|
67
|
+
@native_function(Op.Trunc, const_eval=True)
|
|
68
68
|
def _trunc(x: float) -> float:
|
|
69
69
|
return math.trunc(x)
|
|
70
70
|
|
|
71
71
|
|
|
72
|
-
@native_function(Op.Round)
|
|
72
|
+
@native_function(Op.Round, const_eval=True)
|
|
73
73
|
def __round(x: float) -> float:
|
|
74
74
|
return round(x)
|
|
75
75
|
|
|
@@ -80,12 +80,12 @@ def _round(x: float, n: int = 0) -> float:
|
|
|
80
80
|
return __round(x * 10**n) / 10**n
|
|
81
81
|
|
|
82
82
|
|
|
83
|
-
@native_function(Op.Frac)
|
|
83
|
+
@native_function(Op.Frac, const_eval=True)
|
|
84
84
|
def frac(x: float) -> float:
|
|
85
85
|
return x % 1
|
|
86
86
|
|
|
87
87
|
|
|
88
|
-
@native_function(Op.Log)
|
|
88
|
+
@native_function(Op.Log, const_eval=True)
|
|
89
89
|
def _ln(x: float) -> float:
|
|
90
90
|
return math.log(x)
|
|
91
91
|
|
|
@@ -101,19 +101,19 @@ def _sqrt(x: float) -> float:
|
|
|
101
101
|
return x**0.5
|
|
102
102
|
|
|
103
103
|
|
|
104
|
-
@native_function(Op.Degree)
|
|
104
|
+
@native_function(Op.Degree, const_eval=True)
|
|
105
105
|
def _degrees(x: float) -> float:
|
|
106
106
|
"""Convert radians to degrees."""
|
|
107
107
|
return math.degrees(x)
|
|
108
108
|
|
|
109
109
|
|
|
110
|
-
@native_function(Op.Radian)
|
|
110
|
+
@native_function(Op.Radian, const_eval=True)
|
|
111
111
|
def _radians(x: float) -> float:
|
|
112
112
|
"""Convert degrees to radians."""
|
|
113
113
|
return math.radians(x)
|
|
114
114
|
|
|
115
115
|
|
|
116
|
-
@native_function(Op.Rem)
|
|
116
|
+
@native_function(Op.Rem, const_eval=True)
|
|
117
117
|
def _remainder(x: float, y: float) -> float:
|
|
118
118
|
# This is different from math.remainder in Python's math package, which could be confusing
|
|
119
119
|
return math.copysign(abs(x) % abs(y), x)
|
|
@@ -20,7 +20,7 @@ def native_call(op: Op, *args: int | float | bool) -> Num:
|
|
|
20
20
|
return Num._from_place_(result)
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
def native_function[**P, R](op: Op) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
|
23
|
+
def native_function[**P, R](op: Op, const_eval: bool = False) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
|
24
24
|
def decorator(fn: Callable[P, int | float | bool]) -> Callable[P, Num]:
|
|
25
25
|
signature = inspect.signature(fn)
|
|
26
26
|
|
|
@@ -30,6 +30,12 @@ def native_function[**P, R](op: Op) -> Callable[[Callable[P, R]], Callable[P, R]
|
|
|
30
30
|
if len(args) < sum(1 for p in signature.parameters.values() if p.default == inspect.Parameter.empty):
|
|
31
31
|
raise TypeError(f"Expected {len(signature.parameters)} arguments, got {len(args)}")
|
|
32
32
|
if ctx():
|
|
33
|
+
if const_eval:
|
|
34
|
+
args = tuple(validate_value(arg) for arg in args)
|
|
35
|
+
if not all(_is_num(arg) for arg in args):
|
|
36
|
+
raise RuntimeError("All arguments must be of type Num")
|
|
37
|
+
if all(arg._is_py_() for arg in args):
|
|
38
|
+
return Num._accept_(fn(*[arg._as_py_() for arg in args]))
|
|
33
39
|
bound_args = signature.bind(*args)
|
|
34
40
|
bound_args.apply_defaults()
|
|
35
41
|
return native_call(op, *bound_args.args)
|
sonolus/script/internal/range.py
CHANGED
|
@@ -41,6 +41,9 @@ class Range(Record, ArrayLike[int]):
|
|
|
41
41
|
def __getitem__(self, index: int) -> int:
|
|
42
42
|
return self.start + get_positive_index(index, len(self)) * self.step
|
|
43
43
|
|
|
44
|
+
def get_unchecked(self, index: Num) -> int:
|
|
45
|
+
return self.start + index * self.step
|
|
46
|
+
|
|
44
47
|
def __setitem__(self, index: int, value: int):
|
|
45
48
|
raise TypeError("Range does not support item assignment")
|
|
46
49
|
|
sonolus/script/interval.py
CHANGED
|
@@ -374,9 +374,10 @@ def interp(
|
|
|
374
374
|
Returns:
|
|
375
375
|
The interpolated value.
|
|
376
376
|
"""
|
|
377
|
-
assert len(xp) == len(fp)
|
|
378
|
-
assert len(xp) >= 2
|
|
377
|
+
assert len(xp) == len(fp), "xp and fp must have the same length"
|
|
378
|
+
assert len(xp) >= 2, "xp and fp must have at least 2 elements"
|
|
379
379
|
for i in range_or_tuple(1, len(xp) - 1):
|
|
380
|
+
assert xp[i] > xp[i - 1], "xp must be in increasing order"
|
|
380
381
|
# At i == 1, x may be less than x[0], but since we're extrapolating, we use the first segment regardless.
|
|
381
382
|
if x <= xp[i]:
|
|
382
383
|
return remap(xp[i - 1], xp[i], fp[i - 1], fp[i], x)
|
|
@@ -402,11 +403,12 @@ def interp_clamped(
|
|
|
402
403
|
Returns:
|
|
403
404
|
The interpolated value.
|
|
404
405
|
"""
|
|
405
|
-
assert len(xp) == len(fp)
|
|
406
|
-
assert len(xp) >= 2
|
|
406
|
+
assert len(xp) == len(fp), "xp and fp must have the same length"
|
|
407
|
+
assert len(xp) >= 2, "xp and fp must have at least 2 elements"
|
|
407
408
|
if x <= xp[0]:
|
|
408
409
|
return fp[0]
|
|
409
410
|
for i in range_or_tuple(1, len(xp)):
|
|
411
|
+
assert xp[i] > xp[i - 1], "xp must be in increasing order"
|
|
410
412
|
if x <= xp[i]:
|
|
411
413
|
return remap(xp[i - 1], xp[i], fp[i - 1], fp[i], x)
|
|
412
414
|
return fp[-1]
|
sonolus/script/particle.py
CHANGED
|
@@ -5,7 +5,7 @@ from dataclasses import dataclass
|
|
|
5
5
|
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
6
6
|
|
|
7
7
|
from sonolus.backend.ops import Op
|
|
8
|
-
from sonolus.script.array_like import ArrayLike
|
|
8
|
+
from sonolus.script.array_like import ArrayLike, check_positive_index
|
|
9
9
|
from sonolus.script.debug import static_error
|
|
10
10
|
from sonolus.script.internal.introspection import get_field_specifiers
|
|
11
11
|
from sonolus.script.internal.native import native_function
|
|
@@ -71,7 +71,10 @@ class ParticleGroup(Record, ArrayLike[Particle]):
|
|
|
71
71
|
return self.size
|
|
72
72
|
|
|
73
73
|
def __getitem__(self, index: int) -> Particle:
|
|
74
|
-
|
|
74
|
+
check_positive_index(index, self.size)
|
|
75
|
+
return Particle(self.start_id + index)
|
|
76
|
+
|
|
77
|
+
def get_unchecked(self, index: int) -> Particle:
|
|
75
78
|
return Particle(self.start_id + index)
|
|
76
79
|
|
|
77
80
|
def __setitem__(self, index: int, value: Particle) -> None:
|
sonolus/script/sprite.py
CHANGED
|
@@ -4,11 +4,12 @@ from enum import StrEnum
|
|
|
4
4
|
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
5
5
|
|
|
6
6
|
from sonolus.backend.ops import Op
|
|
7
|
-
from sonolus.script.array_like import ArrayLike
|
|
7
|
+
from sonolus.script.array_like import ArrayLike, check_positive_index
|
|
8
8
|
from sonolus.script.debug import static_error
|
|
9
9
|
from sonolus.script.internal.impl import perf_meta_fn
|
|
10
10
|
from sonolus.script.internal.introspection import get_field_specifiers
|
|
11
11
|
from sonolus.script.internal.native import native_function
|
|
12
|
+
from sonolus.script.num import Num
|
|
12
13
|
from sonolus.script.quad import QuadLike, flatten_quad
|
|
13
14
|
from sonolus.script.record import Record
|
|
14
15
|
from sonolus.script.vec import Vec2
|
|
@@ -138,7 +139,10 @@ class SpriteGroup(Record, ArrayLike[Sprite]):
|
|
|
138
139
|
return self.size
|
|
139
140
|
|
|
140
141
|
def __getitem__(self, index: int) -> Sprite:
|
|
141
|
-
|
|
142
|
+
check_positive_index(index, self.size)
|
|
143
|
+
return Sprite(self.start_id + index)
|
|
144
|
+
|
|
145
|
+
def get_unchecked(self, index: Num) -> Sprite:
|
|
142
146
|
return Sprite(self.start_id + index)
|
|
143
147
|
|
|
144
148
|
def __setitem__(self, index: int, value: Sprite) -> None:
|
sonolus/script/transform.py
CHANGED
sonolus/script/vec.py
CHANGED
|
@@ -158,6 +158,7 @@ class Vec2(Record):
|
|
|
158
158
|
A new vector with magnitude 1.
|
|
159
159
|
"""
|
|
160
160
|
magnitude = (self.x**2 + self.y**2) ** 0.5
|
|
161
|
+
assert magnitude != 0, "Cannot normalize a zero vector"
|
|
161
162
|
return Vec2._quick_construct(x=self.x / magnitude, y=self.y / magnitude)
|
|
162
163
|
|
|
163
164
|
@perf_meta_fn
|
|
@@ -14,81 +14,81 @@ sonolus/backend/utils.py,sha256=OwD1EPh8j-hsfkLzeKNzPQojT_3kklpJou0WTJNoCbc,2337
|
|
|
14
14
|
sonolus/backend/visitor.py,sha256=PxxIvhP3AzGS3hZMulaF5CO2aswiBMyZc_Hwo040hTk,64421
|
|
15
15
|
sonolus/backend/optimize/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
sonolus/backend/optimize/allocate.py,sha256=CuumoMphkpQlGRNeKLHT4FBGE0XVj5pwhfNdrqiLFSs,7535
|
|
17
|
-
sonolus/backend/optimize/constant_evaluation.py,sha256=
|
|
17
|
+
sonolus/backend/optimize/constant_evaluation.py,sha256=_u_VfLmd4Rlq9aKyRSeKb47352CXuf8uNgNhNTK1qe0,21510
|
|
18
18
|
sonolus/backend/optimize/copy_coalesce.py,sha256=AS-TmOoVG2oHxU8fejMxkOdRqfcaWdN53H4POL3G-O4,4797
|
|
19
19
|
sonolus/backend/optimize/dead_code.py,sha256=ZRJ95zJ49R-wZTzJtcSSbl5LYKHWI-byHM3n6jOyAqc,8307
|
|
20
20
|
sonolus/backend/optimize/dominance.py,sha256=3jAgXqXTbuYLpXvIm8UB06NkIOLtaoVp7pBVPcLb5vY,3259
|
|
21
21
|
sonolus/backend/optimize/flow.py,sha256=xUoBpWIYi-NjqXahA6obAZaPvLj_HaDNNv7cO13e2ps,7192
|
|
22
22
|
sonolus/backend/optimize/inlining.py,sha256=BEXjPbJMGTJbgA4ydC38TbEuYEFqb6oxDS0roZTmuds,10417
|
|
23
23
|
sonolus/backend/optimize/liveness.py,sha256=KYQlXdKuwnRvY9JeAjwm1bzPbFwshcUxtYs7ycMRS-M,7279
|
|
24
|
-
sonolus/backend/optimize/optimize.py,sha256=
|
|
24
|
+
sonolus/backend/optimize/optimize.py,sha256=2gW0n1AIlwgVjY6teQlt9YP-GsFUxU-mr1ZqAZamnUo,1672
|
|
25
25
|
sonolus/backend/optimize/passes.py,sha256=YyFKy6qCwcR_Ua2_SXpcBODfvBbm_ygVYcqloOlfDZI,1911
|
|
26
|
-
sonolus/backend/optimize/simplify.py,sha256=
|
|
26
|
+
sonolus/backend/optimize/simplify.py,sha256=wvhixe0SfditrGMh0nX0Wt0JR00JqAmz4BKBzMoBAVI,14701
|
|
27
27
|
sonolus/backend/optimize/ssa.py,sha256=raQO0furQQRPYb8iIBKfNrJlj-_5wqtI4EWNfLZ8QFo,10834
|
|
28
28
|
sonolus/build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
29
|
sonolus/build/cli.py,sha256=GQHzXGj55pqz84AnOKa21t74Nrbt7gUKfQe88gXIKtI,9357
|
|
30
30
|
sonolus/build/collection.py,sha256=6hniAzriPWBKUeGDkXabNXpbdHiHnqiK9shs6U1OExM,12748
|
|
31
31
|
sonolus/build/compile.py,sha256=KOmncDKmGfgzC_FWB_LTxAl0s9w4wnaDe-luACMlCVs,8397
|
|
32
|
-
sonolus/build/dev_server.py,sha256=
|
|
32
|
+
sonolus/build/dev_server.py,sha256=EOnIgAkFZAKiCwhFqDyKMLkJfYtGdjlacqYWZ2F2mAU,9891
|
|
33
33
|
sonolus/build/engine.py,sha256=No_q4O6PRMwxEPHvSlbaBKVe5CXJswWFZdR3xgfIuI8,14672
|
|
34
34
|
sonolus/build/level.py,sha256=KLqUAtxIuIqrzeFURJA97rdqjA5pcvYSmwNZQhElaMQ,702
|
|
35
35
|
sonolus/build/node.py,sha256=gnX71RYDUOK_gYMpinQi-bLWO4csqcfiG5gFmhxzSec,1330
|
|
36
36
|
sonolus/build/project.py,sha256=Uuz82QtTNFdklrVJ_i7EPp8hSjyOxLU1xAeOloa6G00,8579
|
|
37
37
|
sonolus/script/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
38
|
sonolus/script/archetype.py,sha256=ck_LR8z0ipVq3T9b735VwvQI2mxVUyjHylr4BFagXT8,49631
|
|
39
|
-
sonolus/script/array.py,sha256=
|
|
40
|
-
sonolus/script/array_like.py,sha256=
|
|
39
|
+
sonolus/script/array.py,sha256=0ZUI0alrwKztpQOpZodZPSPafu5cGwaiffBGQbY19LQ,13316
|
|
40
|
+
sonolus/script/array_like.py,sha256=E6S4TW2muXgcyVkhUASQVt7JSYUkpvdJPgHz6YiSHNo,14708
|
|
41
41
|
sonolus/script/bucket.py,sha256=yIod3DgX7Hv7RLe-4Cn81FcydvbkbdMt26FzpRj7oUI,7794
|
|
42
|
-
sonolus/script/containers.py,sha256=
|
|
43
|
-
sonolus/script/debug.py,sha256=
|
|
42
|
+
sonolus/script/containers.py,sha256=qKIyTs5Q_UpQggR6s0AfgWQAvPv-IM2DW9s951koKSc,19303
|
|
43
|
+
sonolus/script/debug.py,sha256=YOq06q5ahWI-uEG5naonOBWF62Qn_Af7JSV_Ra8Zr6E,7415
|
|
44
44
|
sonolus/script/easing.py,sha256=2FUJI_nfp990P_armCcRqHm2329O985glJAhSC6tnxs,11379
|
|
45
|
-
sonolus/script/effect.py,sha256=
|
|
45
|
+
sonolus/script/effect.py,sha256=aOxhBmX6I8vUS-bE53YFNBEN3wwdmpraqZjLeCqbgIY,7920
|
|
46
46
|
sonolus/script/engine.py,sha256=etI9dJsQ7V9YZICVNZg54WqpLijPxG8eTPHiV-_EiG8,10687
|
|
47
47
|
sonolus/script/globals.py,sha256=nlXSNS4NRXsgQU2AJImVIs752h1WqsMnShSKgU011c4,10270
|
|
48
48
|
sonolus/script/instruction.py,sha256=Dd-14D5Amo8nhPBr6DNyg2lpYw_rqZkT8Kix3HkfE7k,6793
|
|
49
|
-
sonolus/script/interval.py,sha256=
|
|
49
|
+
sonolus/script/interval.py,sha256=vnwm3CiB0rurRolA53wRzb_jTPguvOKnie4glE1MwsE,11986
|
|
50
50
|
sonolus/script/iterator.py,sha256=_ICY_yX7FG0Zbgs3NhVnaIBdVDpAeXjxJ_CQtq30l7Y,3774
|
|
51
51
|
sonolus/script/level.py,sha256=X3-V99ihruYYCcPdch66dHi_ydCWXXn7epviLLjxW8w,8288
|
|
52
52
|
sonolus/script/maybe.py,sha256=VYvTWgEfPzoXqI3i3zXhc4dz0pWBVoHmW8FtWH0GQvM,8194
|
|
53
53
|
sonolus/script/metadata.py,sha256=ttRK27eojHf3So50KQJ-8yj3udZoN1bli5iD-knaeLw,753
|
|
54
54
|
sonolus/script/num.py,sha256=9pahERQcIh16ytoDjJB3u3L6fH1Xh11Y99l8SYxkjMA,15927
|
|
55
55
|
sonolus/script/options.py,sha256=05y_4j2kr8fzct5FLqmSp5ZAjnq6-slmNgtsh4fVEpg,9451
|
|
56
|
-
sonolus/script/particle.py,sha256=
|
|
56
|
+
sonolus/script/particle.py,sha256=BuBM7fvLAj79upLf9yI4FyZhVUK7-H2dFj0D7UiYS7I,10458
|
|
57
57
|
sonolus/script/pointer.py,sha256=FoOfyD93r0G5d_2BaKfeOT9SqkOP3hq6sqtOs_Rb0c8,1511
|
|
58
58
|
sonolus/script/printing.py,sha256=mNYu9QWiacBBGZrnePZQMVwbbguoelUps9GiOK_aVRU,2096
|
|
59
59
|
sonolus/script/project.py,sha256=4svmMWYihF7olmYSMS5uoSjlnzbd7Ip2zTRY86oL1L8,4629
|
|
60
60
|
sonolus/script/quad.py,sha256=8lZ_5-eWeqePldNGBkNZTuOgS_IRb41URgGwSW4h2T0,14445
|
|
61
61
|
sonolus/script/record.py,sha256=BrQ8k-O4WX9FT_EfoRmNnKC1BZM9gWydZ4R4swh3chc,13051
|
|
62
62
|
sonolus/script/runtime.py,sha256=TjxcfIIPRH6oxlEjWfLHDj1oo7fPfwTBdwETfnhN7h4,33331
|
|
63
|
-
sonolus/script/sprite.py,sha256=
|
|
63
|
+
sonolus/script/sprite.py,sha256=d_wqUn7oMbkLZMdvnyDZVBykycTtiwegcarWXMcZMUI,18408
|
|
64
64
|
sonolus/script/stream.py,sha256=Fu02SNjH8j1FQ9_7ncacR9uRIhoWtAZR-sTi8qBT7rA,24707
|
|
65
65
|
sonolus/script/text.py,sha256=wxujIgKYcCfl2AD2_Im8g3vh0lDEHYwTSRZg9wsBPEU,13402
|
|
66
66
|
sonolus/script/timing.py,sha256=DklMvuxcFg3MzXsecUo6Yhdk7pScOJ7STwXvAiTvLKM,3067
|
|
67
|
-
sonolus/script/transform.py,sha256=
|
|
67
|
+
sonolus/script/transform.py,sha256=4aS7-NNzX0v9KMXZ4gIGOaU1Cd-ok7DO_OvIBca0mGU,21418
|
|
68
68
|
sonolus/script/ui.py,sha256=DYPGWIjHj1IFPxW1zaEuIUQx0b32FJPXtiwCvrtJ6oo,7528
|
|
69
69
|
sonolus/script/values.py,sha256=6iJG6h4IDlbcK8FH4GENSHOQc7C_7fCGa34wM80qToA,1629
|
|
70
|
-
sonolus/script/vec.py,sha256
|
|
70
|
+
sonolus/script/vec.py,sha256=UIgXRNum420VrXXbkHjZuQx2gFsvgV8tydi9_zOqwuc,8111
|
|
71
71
|
sonolus/script/internal/__init__.py,sha256=T6rzLoiOUaiSQtaHMZ88SNO-ijSjSSv33TKtUwu-Ms8,136
|
|
72
|
-
sonolus/script/internal/builtin_impls.py,sha256=
|
|
72
|
+
sonolus/script/internal/builtin_impls.py,sha256=tpNbaH6fLICd8TYj9Hf_wrPSWk3RkhmSPVN9nqOuqj4,13372
|
|
73
73
|
sonolus/script/internal/callbacks.py,sha256=vWzJG8uiJoEtsNnbeZPqOHogCwoLpz2D1MnHY2wVV8s,2801
|
|
74
74
|
sonolus/script/internal/constant.py,sha256=3ycbGkDJVUwcrCZ96vLjAoAARgsvaqDM8rJ_YCrLrvo,4289
|
|
75
|
-
sonolus/script/internal/context.py,sha256=
|
|
75
|
+
sonolus/script/internal/context.py,sha256=C0VMHBRppsnwPDVPc03Lpz7tO9djQMB5ELdtpFPMdsk,18779
|
|
76
76
|
sonolus/script/internal/descriptor.py,sha256=XRFey-EjiAm_--KsNl-8N0Mi_iyQwlPh68gDp0pKf3E,392
|
|
77
77
|
sonolus/script/internal/dict_impl.py,sha256=alu_wKGSk1kZajNf64qbe7t71shEzD4N5xNIATH8Swo,1885
|
|
78
78
|
sonolus/script/internal/error.py,sha256=ZNnsvQVQAnFKzcvsm6-sste2lo-tP5pPI8sD7XlAZWc,490
|
|
79
79
|
sonolus/script/internal/generic.py,sha256=_3d5Rn_tn214-77fPE67vdbdqt1PQF8-2WB_XDu5YRg,7551
|
|
80
80
|
sonolus/script/internal/impl.py,sha256=U9D8A207yBbA-R9Qa9xE9zK4f0uDvGbHuFhwaIO81Ew,3364
|
|
81
81
|
sonolus/script/internal/introspection.py,sha256=guL9_NR2D3OJAnNpeFdyYkO_vVXk-3KQr2-y4YielM0,1133
|
|
82
|
-
sonolus/script/internal/math_impls.py,sha256=
|
|
83
|
-
sonolus/script/internal/native.py,sha256=
|
|
82
|
+
sonolus/script/internal/math_impls.py,sha256=ox2pBJ6ELRO0LdLn_RZxgHHs_PCgQOHIhmDkwmLxJaU,2975
|
|
83
|
+
sonolus/script/internal/native.py,sha256=zOuRtgI3XJ_ExyR_ZkvbDABVc_JIWaKl62lFEL_bMaw,2007
|
|
84
84
|
sonolus/script/internal/random.py,sha256=6Ku5edRcDUh7rtqEEYCJz0BQavw69RALsVHS25z50pI,1695
|
|
85
|
-
sonolus/script/internal/range.py,sha256=
|
|
85
|
+
sonolus/script/internal/range.py,sha256=j94uV1NTZoCdZ8mOw3v51vD8L7h8l5vZpOAp6breD9I,3521
|
|
86
86
|
sonolus/script/internal/simulation_context.py,sha256=LGxLTvxbqBIhoe1R-SfwGajNIDwIJMVsHle0kvzd500,4818
|
|
87
87
|
sonolus/script/internal/transient.py,sha256=y2AWABqF1aoaP6H4_2u4MMpNioC4OsZQCtPyNI0txqo,1634
|
|
88
88
|
sonolus/script/internal/tuple_impl.py,sha256=DPNdmmRmupU8Ah4_XKq6-PdT336l4nt15_uCJKQGkkk,3587
|
|
89
89
|
sonolus/script/internal/value.py,sha256=OngrCdmY_h6mV2Zgwqhuo4eYFad0kTk6263UAxctZcY,6963
|
|
90
|
-
sonolus_py-0.10.
|
|
91
|
-
sonolus_py-0.10.
|
|
92
|
-
sonolus_py-0.10.
|
|
93
|
-
sonolus_py-0.10.
|
|
94
|
-
sonolus_py-0.10.
|
|
90
|
+
sonolus_py-0.10.1.dist-info/METADATA,sha256=hWRJb86bOgMAB7N1x5H_hdY8zK77ZwIia41--jjZTdI,554
|
|
91
|
+
sonolus_py-0.10.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
92
|
+
sonolus_py-0.10.1.dist-info/entry_points.txt,sha256=oTYspY_b7SA8TptEMTDxh4-Aj-ZVPnYC9f1lqH6s9G4,54
|
|
93
|
+
sonolus_py-0.10.1.dist-info/licenses/LICENSE,sha256=JEKpqVhQYfEc7zg3Mj462sKbKYmO1K7WmvX1qvg9IJk,1067
|
|
94
|
+
sonolus_py-0.10.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|