sonolus.py 0.3.4__py3-none-any.whl → 0.4.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/excepthook.py +30 -0
- sonolus/backend/finalize.py +15 -1
- sonolus/backend/ops.py +4 -0
- sonolus/backend/optimize/allocate.py +5 -5
- sonolus/backend/optimize/constant_evaluation.py +124 -19
- sonolus/backend/optimize/copy_coalesce.py +15 -12
- sonolus/backend/optimize/dead_code.py +7 -6
- sonolus/backend/optimize/dominance.py +2 -2
- sonolus/backend/optimize/flow.py +54 -8
- sonolus/backend/optimize/inlining.py +137 -30
- sonolus/backend/optimize/liveness.py +2 -2
- sonolus/backend/optimize/optimize.py +15 -1
- sonolus/backend/optimize/passes.py +11 -3
- sonolus/backend/optimize/simplify.py +137 -8
- sonolus/backend/optimize/ssa.py +47 -13
- sonolus/backend/place.py +5 -4
- sonolus/backend/utils.py +44 -16
- sonolus/backend/visitor.py +288 -17
- sonolus/build/cli.py +47 -19
- sonolus/build/compile.py +12 -5
- sonolus/build/engine.py +70 -1
- sonolus/build/level.py +3 -3
- sonolus/build/project.py +2 -2
- sonolus/script/archetype.py +12 -9
- sonolus/script/array.py +23 -18
- sonolus/script/array_like.py +26 -29
- sonolus/script/bucket.py +1 -1
- sonolus/script/containers.py +22 -26
- sonolus/script/debug.py +20 -43
- sonolus/script/effect.py +1 -1
- sonolus/script/globals.py +3 -3
- sonolus/script/instruction.py +2 -2
- sonolus/script/internal/builtin_impls.py +155 -28
- sonolus/script/internal/constant.py +13 -3
- sonolus/script/internal/context.py +46 -15
- sonolus/script/internal/impl.py +9 -3
- sonolus/script/internal/introspection.py +8 -1
- sonolus/script/internal/native.py +2 -2
- sonolus/script/internal/range.py +8 -11
- sonolus/script/internal/simulation_context.py +1 -1
- sonolus/script/internal/transient.py +2 -2
- sonolus/script/internal/value.py +41 -3
- sonolus/script/interval.py +13 -13
- sonolus/script/iterator.py +53 -107
- sonolus/script/level.py +2 -2
- sonolus/script/maybe.py +241 -0
- sonolus/script/num.py +29 -14
- sonolus/script/options.py +1 -1
- sonolus/script/particle.py +1 -1
- sonolus/script/project.py +24 -5
- sonolus/script/quad.py +15 -15
- sonolus/script/record.py +48 -44
- sonolus/script/runtime.py +22 -18
- sonolus/script/sprite.py +1 -1
- sonolus/script/stream.py +66 -82
- sonolus/script/transform.py +35 -34
- sonolus/script/values.py +10 -10
- sonolus/script/vec.py +21 -18
- {sonolus_py-0.3.4.dist-info → sonolus_py-0.4.1.dist-info}/METADATA +1 -1
- sonolus_py-0.4.1.dist-info/RECORD +93 -0
- sonolus_py-0.3.4.dist-info/RECORD +0 -92
- {sonolus_py-0.3.4.dist-info → sonolus_py-0.4.1.dist-info}/WHEEL +0 -0
- {sonolus_py-0.3.4.dist-info → sonolus_py-0.4.1.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.3.4.dist-info → sonolus_py-0.4.1.dist-info}/licenses/LICENSE +0 -0
sonolus/build/engine.py
CHANGED
|
@@ -6,7 +6,6 @@ from collections.abc import Callable
|
|
|
6
6
|
from concurrent.futures import Executor
|
|
7
7
|
from concurrent.futures.thread import ThreadPoolExecutor
|
|
8
8
|
from dataclasses import dataclass
|
|
9
|
-
from os import process_cpu_count
|
|
10
9
|
from pathlib import Path
|
|
11
10
|
|
|
12
11
|
from sonolus.backend.mode import Mode
|
|
@@ -74,6 +73,9 @@ def package_engine(
|
|
|
74
73
|
rom = ReadOnlyMemory()
|
|
75
74
|
configuration = build_engine_configuration(engine.options, engine.ui)
|
|
76
75
|
if no_gil():
|
|
76
|
+
# process_cpu_count is available in Python 3.13+
|
|
77
|
+
from os import process_cpu_count
|
|
78
|
+
|
|
77
79
|
thread_pool = ThreadPoolExecutor(process_cpu_count() or 1)
|
|
78
80
|
else:
|
|
79
81
|
thread_pool = None
|
|
@@ -189,6 +191,65 @@ def package_engine(
|
|
|
189
191
|
)
|
|
190
192
|
|
|
191
193
|
|
|
194
|
+
def validate_engine(
|
|
195
|
+
engine: EngineData,
|
|
196
|
+
config: BuildConfig | None = None,
|
|
197
|
+
):
|
|
198
|
+
config = config or BuildConfig()
|
|
199
|
+
rom = ReadOnlyMemory()
|
|
200
|
+
|
|
201
|
+
play_mode = engine.play if config.build_play else empty_play_mode()
|
|
202
|
+
watch_mode = engine.watch if config.build_watch else empty_watch_mode()
|
|
203
|
+
preview_mode = engine.preview if config.build_preview else empty_preview_mode()
|
|
204
|
+
tutorial_mode = engine.tutorial if config.build_tutorial else empty_tutorial_mode()
|
|
205
|
+
|
|
206
|
+
build_play_mode(
|
|
207
|
+
archetypes=play_mode.archetypes,
|
|
208
|
+
skin=play_mode.skin,
|
|
209
|
+
effects=play_mode.effects,
|
|
210
|
+
particles=play_mode.particles,
|
|
211
|
+
buckets=play_mode.buckets,
|
|
212
|
+
rom=rom,
|
|
213
|
+
config=config,
|
|
214
|
+
thread_pool=None,
|
|
215
|
+
validate_only=True,
|
|
216
|
+
)
|
|
217
|
+
build_watch_mode(
|
|
218
|
+
archetypes=watch_mode.archetypes,
|
|
219
|
+
skin=watch_mode.skin,
|
|
220
|
+
effects=watch_mode.effects,
|
|
221
|
+
particles=watch_mode.particles,
|
|
222
|
+
buckets=watch_mode.buckets,
|
|
223
|
+
rom=rom,
|
|
224
|
+
update_spawn=watch_mode.update_spawn,
|
|
225
|
+
config=config,
|
|
226
|
+
thread_pool=None,
|
|
227
|
+
validate_only=True,
|
|
228
|
+
)
|
|
229
|
+
build_preview_mode(
|
|
230
|
+
archetypes=preview_mode.archetypes,
|
|
231
|
+
skin=preview_mode.skin,
|
|
232
|
+
rom=rom,
|
|
233
|
+
config=config,
|
|
234
|
+
thread_pool=None,
|
|
235
|
+
validate_only=True,
|
|
236
|
+
)
|
|
237
|
+
build_tutorial_mode(
|
|
238
|
+
skin=tutorial_mode.skin,
|
|
239
|
+
effects=tutorial_mode.effects,
|
|
240
|
+
particles=tutorial_mode.particles,
|
|
241
|
+
instructions=tutorial_mode.instructions,
|
|
242
|
+
instruction_icons=tutorial_mode.instruction_icons,
|
|
243
|
+
preprocess=tutorial_mode.preprocess,
|
|
244
|
+
navigate=tutorial_mode.navigate,
|
|
245
|
+
update=tutorial_mode.update,
|
|
246
|
+
rom=rom,
|
|
247
|
+
config=config,
|
|
248
|
+
thread_pool=None,
|
|
249
|
+
validate_only=True,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
|
|
192
253
|
def build_engine_configuration(
|
|
193
254
|
options: Options,
|
|
194
255
|
ui: UiConfig,
|
|
@@ -208,6 +269,7 @@ def build_play_mode(
|
|
|
208
269
|
rom: ReadOnlyMemory,
|
|
209
270
|
config: BuildConfig,
|
|
210
271
|
thread_pool: Executor | None = None,
|
|
272
|
+
validate_only: bool = False,
|
|
211
273
|
):
|
|
212
274
|
return {
|
|
213
275
|
**compile_mode(
|
|
@@ -217,6 +279,7 @@ def build_play_mode(
|
|
|
217
279
|
global_callbacks=None,
|
|
218
280
|
passes=config.passes,
|
|
219
281
|
thread_pool=thread_pool,
|
|
282
|
+
validate_only=validate_only,
|
|
220
283
|
),
|
|
221
284
|
"skin": build_skin(skin),
|
|
222
285
|
"effect": build_effects(effects),
|
|
@@ -235,6 +298,7 @@ def build_watch_mode(
|
|
|
235
298
|
update_spawn: Callable[[], float],
|
|
236
299
|
config: BuildConfig,
|
|
237
300
|
thread_pool: Executor | None = None,
|
|
301
|
+
validate_only: bool = False,
|
|
238
302
|
):
|
|
239
303
|
return {
|
|
240
304
|
**compile_mode(
|
|
@@ -244,6 +308,7 @@ def build_watch_mode(
|
|
|
244
308
|
global_callbacks=[(update_spawn_callback, update_spawn)],
|
|
245
309
|
passes=config.passes,
|
|
246
310
|
thread_pool=thread_pool,
|
|
311
|
+
validate_only=validate_only,
|
|
247
312
|
),
|
|
248
313
|
"skin": build_skin(skin),
|
|
249
314
|
"effect": build_effects(effects),
|
|
@@ -258,6 +323,7 @@ def build_preview_mode(
|
|
|
258
323
|
rom: ReadOnlyMemory,
|
|
259
324
|
config: BuildConfig,
|
|
260
325
|
thread_pool: Executor | None = None,
|
|
326
|
+
validate_only: bool = False,
|
|
261
327
|
):
|
|
262
328
|
return {
|
|
263
329
|
**compile_mode(
|
|
@@ -267,6 +333,7 @@ def build_preview_mode(
|
|
|
267
333
|
global_callbacks=None,
|
|
268
334
|
passes=config.passes,
|
|
269
335
|
thread_pool=thread_pool,
|
|
336
|
+
validate_only=validate_only,
|
|
270
337
|
),
|
|
271
338
|
"skin": build_skin(skin),
|
|
272
339
|
}
|
|
@@ -284,6 +351,7 @@ def build_tutorial_mode(
|
|
|
284
351
|
rom: ReadOnlyMemory,
|
|
285
352
|
config: BuildConfig,
|
|
286
353
|
thread_pool: Executor | None = None,
|
|
354
|
+
validate_only: bool = False,
|
|
287
355
|
):
|
|
288
356
|
return {
|
|
289
357
|
**compile_mode(
|
|
@@ -297,6 +365,7 @@ def build_tutorial_mode(
|
|
|
297
365
|
],
|
|
298
366
|
passes=config.passes,
|
|
299
367
|
thread_pool=thread_pool,
|
|
368
|
+
validate_only=validate_only,
|
|
300
369
|
),
|
|
301
370
|
"skin": build_skin(skin),
|
|
302
371
|
"effect": build_effects(effects),
|
sonolus/build/level.py
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
from sonolus.build.engine import package_output
|
|
1
|
+
from sonolus.build.engine import JsonValue, package_output
|
|
2
2
|
from sonolus.script.level import LevelData
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
def package_level_data(
|
|
6
6
|
level_data: LevelData,
|
|
7
|
-
):
|
|
7
|
+
) -> bytes:
|
|
8
8
|
return package_output(build_level_data(level_data))
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def build_level_data(
|
|
12
12
|
level_data: LevelData,
|
|
13
|
-
):
|
|
13
|
+
) -> JsonValue:
|
|
14
14
|
level_refs = {entity: f"{i}_{entity.name}" for i, entity in enumerate(level_data.entities)}
|
|
15
15
|
return {
|
|
16
16
|
"bgmOffset": level_data.bgm_offset,
|
sonolus/build/project.py
CHANGED
|
@@ -17,7 +17,7 @@ BLANK_AUDIO = (
|
|
|
17
17
|
)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
def build_project_to_collection(project: Project, config: BuildConfig):
|
|
20
|
+
def build_project_to_collection(project: Project, config: BuildConfig | None):
|
|
21
21
|
collection = load_resources_files_to_collection(project.resources)
|
|
22
22
|
add_engine_to_collection(collection, project, project.engine, config)
|
|
23
23
|
for level in project.levels:
|
|
@@ -26,7 +26,7 @@ def build_project_to_collection(project: Project, config: BuildConfig):
|
|
|
26
26
|
return collection
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def add_engine_to_collection(collection: Collection, project: Project, engine: Engine, config: BuildConfig):
|
|
29
|
+
def add_engine_to_collection(collection: Collection, project: Project, engine: Engine, config: BuildConfig | None):
|
|
30
30
|
packaged_engine = package_engine(engine.data, config)
|
|
31
31
|
item = {
|
|
32
32
|
"name": engine.name,
|
sonolus/script/archetype.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# type: ignore
|
|
1
2
|
from __future__ import annotations
|
|
2
3
|
|
|
3
4
|
import inspect
|
|
@@ -117,7 +118,7 @@ class _ArchetypeField(SonolusDescriptor):
|
|
|
117
118
|
if result is None:
|
|
118
119
|
raise RuntimeError("Invalid storage type")
|
|
119
120
|
if ctx():
|
|
120
|
-
return result.
|
|
121
|
+
return result._get_readonly_()
|
|
121
122
|
else:
|
|
122
123
|
return result._as_py_()
|
|
123
124
|
|
|
@@ -327,7 +328,7 @@ def callback[T: Callable](*, order: int = 0) -> Callable[[T], T]:
|
|
|
327
328
|
"""
|
|
328
329
|
|
|
329
330
|
def decorator(func: T) -> T:
|
|
330
|
-
func._callback_order_ = order
|
|
331
|
+
func._callback_order_ = order # type: ignore
|
|
331
332
|
return func
|
|
332
333
|
|
|
333
334
|
return decorator
|
|
@@ -635,11 +636,11 @@ class _BaseArchetype:
|
|
|
635
636
|
"""
|
|
636
637
|
match self._data_:
|
|
637
638
|
case _ArchetypeSelfData():
|
|
638
|
-
return EntityRef[type(self)](index=self.index)
|
|
639
|
+
return EntityRef[type(self)](index=self.index) # type: ignore
|
|
639
640
|
case _ArchetypeReferenceData(index=index):
|
|
640
|
-
return EntityRef[type(self)](index=index)
|
|
641
|
+
return EntityRef[type(self)](index=index) # type: ignore
|
|
641
642
|
case _ArchetypeLevelData():
|
|
642
|
-
result = EntityRef[type(self)](index=-1)
|
|
643
|
+
result = EntityRef[type(self)](index=-1) # type: ignore
|
|
643
644
|
result._ref_ = self
|
|
644
645
|
return result
|
|
645
646
|
case _:
|
|
@@ -1016,8 +1017,8 @@ def archetype_life_of(archetype: type[_BaseArchetype] | _BaseArchetype) -> Arche
|
|
|
1016
1017
|
|
|
1017
1018
|
Available in play and watch mode.
|
|
1018
1019
|
"""
|
|
1019
|
-
archetype = validate_value(archetype)
|
|
1020
|
-
archetype = archetype._as_py_()
|
|
1020
|
+
archetype = validate_value(archetype) # type: ignore
|
|
1021
|
+
archetype = archetype._as_py_() # type: ignore
|
|
1021
1022
|
if not ctx():
|
|
1022
1023
|
raise RuntimeError("Calling archetype_life_of is only allowed within a callback")
|
|
1023
1024
|
match ctx().global_state.mode:
|
|
@@ -1110,7 +1111,7 @@ class EntityRef[A: _BaseArchetype](Record):
|
|
|
1110
1111
|
"""Get the archetype type."""
|
|
1111
1112
|
return cls.type_var_value(A)
|
|
1112
1113
|
|
|
1113
|
-
def with_archetype(self, archetype: type[
|
|
1114
|
+
def with_archetype[T: _BaseArchetype](self, archetype: type[T]) -> EntityRef[T]:
|
|
1114
1115
|
"""Return a new reference with the given archetype type."""
|
|
1115
1116
|
return EntityRef[archetype](index=self.index)
|
|
1116
1117
|
|
|
@@ -1137,11 +1138,13 @@ class EntityRef[A: _BaseArchetype](Record):
|
|
|
1137
1138
|
if ref is None:
|
|
1138
1139
|
return Num._accept_(self.index)._to_list_()
|
|
1139
1140
|
else:
|
|
1141
|
+
if level_refs is None:
|
|
1142
|
+
raise RuntimeError("Unexpected missing level_refs")
|
|
1140
1143
|
if ref not in level_refs:
|
|
1141
1144
|
raise KeyError("Reference to entity not in level data")
|
|
1142
1145
|
return [level_refs[ref]]
|
|
1143
1146
|
|
|
1144
|
-
def _copy_from_(self, value:
|
|
1147
|
+
def _copy_from_(self, value: Any):
|
|
1145
1148
|
super()._copy_from_(value)
|
|
1146
1149
|
if hasattr(value, "_ref_"):
|
|
1147
1150
|
self._ref_ = value._ref_
|
sonolus/script/array.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from abc import ABCMeta
|
|
3
4
|
from collections.abc import Iterable
|
|
4
|
-
from typing import Any, Self, final
|
|
5
|
+
from typing import Any, Literal, Self, final
|
|
5
6
|
|
|
6
7
|
from sonolus.backend.ir import IRConst, IRSet
|
|
7
8
|
from sonolus.backend.place import BlockPlace
|
|
@@ -14,8 +15,11 @@ from sonolus.script.internal.impl import meta_fn, validate_value
|
|
|
14
15
|
from sonolus.script.internal.value import BackingSource, DataValue, Value
|
|
15
16
|
from sonolus.script.num import Num
|
|
16
17
|
|
|
18
|
+
Dim = Literal
|
|
19
|
+
"""Shorthand for `Literal` intended for use in array dimensions for type checker compatibility."""
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
|
|
22
|
+
class ArrayMeta(ABCMeta):
|
|
19
23
|
@meta_fn
|
|
20
24
|
def __pos__[T](cls: type[T]) -> T:
|
|
21
25
|
"""Create a zero-initialized array instance."""
|
|
@@ -31,7 +35,8 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
31
35
|
array_1 = Array(1, 2, 3)
|
|
32
36
|
array_2 = Array[int, 0]()
|
|
33
37
|
array_3 = +Array[int, 3] # Create a zero-initialized array
|
|
34
|
-
array_4 = +
|
|
38
|
+
array_4 = +Array[int, Dim[3]] # Alternative syntax for compliance with type checkers
|
|
39
|
+
array_5 = +array_1 # Create a copy of array_1
|
|
35
40
|
```
|
|
36
41
|
"""
|
|
37
42
|
|
|
@@ -78,7 +83,7 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
78
83
|
super().__init__()
|
|
79
84
|
|
|
80
85
|
@classmethod
|
|
81
|
-
def _with_value(cls, value) ->
|
|
86
|
+
def _with_value(cls, value) -> Array[T, Size]:
|
|
82
87
|
result = object.__new__(cls)
|
|
83
88
|
result._value = value
|
|
84
89
|
return result
|
|
@@ -92,11 +97,11 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
92
97
|
return False
|
|
93
98
|
|
|
94
99
|
@classmethod
|
|
95
|
-
def _from_backing_source_(cls, source: BackingSource) ->
|
|
100
|
+
def _from_backing_source_(cls, source: BackingSource) -> Array[T, Size]:
|
|
96
101
|
return cls._with_value(source)
|
|
97
102
|
|
|
98
103
|
@classmethod
|
|
99
|
-
def _from_place_(cls, place: BlockPlace) ->
|
|
104
|
+
def _from_place_(cls, place: BlockPlace) -> Array[T, Size]:
|
|
100
105
|
return cls._with_value(place)
|
|
101
106
|
|
|
102
107
|
@classmethod
|
|
@@ -104,7 +109,7 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
104
109
|
return isinstance(value, cls)
|
|
105
110
|
|
|
106
111
|
@classmethod
|
|
107
|
-
def _accept_(cls, value: Any) ->
|
|
112
|
+
def _accept_(cls, value: Any) -> Array[T, Size]:
|
|
108
113
|
if not cls._accepts_(value):
|
|
109
114
|
raise TypeError(f"Cannot accept value {value} as {cls.__name__}")
|
|
110
115
|
return value
|
|
@@ -118,7 +123,7 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
118
123
|
return self
|
|
119
124
|
|
|
120
125
|
@classmethod
|
|
121
|
-
def _from_list_(cls, values: Iterable[DataValue]) ->
|
|
126
|
+
def _from_list_(cls, values: Iterable[DataValue]) -> Array[T, Size]:
|
|
122
127
|
iterator = iter(values)
|
|
123
128
|
return cls(*(cls.element_type()._from_list_(iterator) for _ in range(cls.size())))
|
|
124
129
|
|
|
@@ -143,19 +148,19 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
143
148
|
def _flat_keys_(cls, prefix: str) -> list[str]:
|
|
144
149
|
return [entry for i in range(cls.size()) for entry in cls.element_type()._flat_keys_(f"{prefix}[{i}]")]
|
|
145
150
|
|
|
146
|
-
def _get_(self) ->
|
|
151
|
+
def _get_(self) -> Array[T, Size]:
|
|
147
152
|
return self
|
|
148
153
|
|
|
149
|
-
def _set_(self, value:
|
|
154
|
+
def _set_(self, value: Any):
|
|
150
155
|
raise TypeError("Array does not support _set_")
|
|
151
156
|
|
|
152
|
-
def _copy_from_(self, value:
|
|
157
|
+
def _copy_from_(self, value: Any):
|
|
153
158
|
if not isinstance(value, type(self)):
|
|
154
159
|
raise TypeError("Cannot copy from different type")
|
|
155
160
|
for i in range(self.size()):
|
|
156
161
|
self[i] = value[i]
|
|
157
162
|
|
|
158
|
-
def _copy_(self) ->
|
|
163
|
+
def _copy_(self) -> Array[T, Size]:
|
|
159
164
|
if ctx():
|
|
160
165
|
place = ctx().alloc(size=self._size_())
|
|
161
166
|
result: Self = self._from_place_(place)
|
|
@@ -166,7 +171,7 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
166
171
|
return self._with_value([value._copy_() for value in self._value])
|
|
167
172
|
|
|
168
173
|
@classmethod
|
|
169
|
-
def _alloc_(cls) ->
|
|
174
|
+
def _alloc_(cls) -> Array[T, Size]:
|
|
170
175
|
if ctx():
|
|
171
176
|
place = ctx().alloc(size=cls._size_())
|
|
172
177
|
return cls._from_place_(place)
|
|
@@ -174,7 +179,7 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
174
179
|
return cls._with_value([cls.element_type()._alloc_() for _ in range(cls.size())])
|
|
175
180
|
|
|
176
181
|
@classmethod
|
|
177
|
-
def _zero_(cls) ->
|
|
182
|
+
def _zero_(cls) -> Array[T, Size]:
|
|
178
183
|
if ctx():
|
|
179
184
|
place = ctx().alloc(size=cls._size_())
|
|
180
185
|
result: Self = cls._from_place_(place)
|
|
@@ -207,7 +212,7 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
207
212
|
)
|
|
208
213
|
elif callable(self._value):
|
|
209
214
|
return self.element_type()._from_backing_source_(
|
|
210
|
-
lambda offset: self._value((Num(offset) + Num(const_index * self.element_type()._size_())).ir())
|
|
215
|
+
lambda offset: self._value((Num(offset) + Num(const_index * self.element_type()._size_())).ir()) # type: ignore
|
|
211
216
|
)
|
|
212
217
|
else:
|
|
213
218
|
raise InternalError("Unexpected array value")
|
|
@@ -225,7 +230,7 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
225
230
|
elif callable(self._value):
|
|
226
231
|
base_offset = index * Num(self.element_type()._size_())
|
|
227
232
|
return self.element_type()._from_backing_source_(
|
|
228
|
-
lambda offset: self._value((Num(offset) + base_offset).ir())
|
|
233
|
+
lambda offset: self._value((Num(offset) + base_offset).ir()) # type: ignore
|
|
229
234
|
)
|
|
230
235
|
else:
|
|
231
236
|
raise InternalError("Unexpected array value")
|
|
@@ -252,7 +257,7 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
252
257
|
elif callable(self._value):
|
|
253
258
|
base_offset = index * Num(self.element_type()._size_())
|
|
254
259
|
dst = self.element_type()._from_backing_source_(
|
|
255
|
-
lambda offset: self._value((Num(offset) + base_offset).ir())
|
|
260
|
+
lambda offset: self._value((Num(offset) + base_offset).ir()) # type: ignore
|
|
256
261
|
)
|
|
257
262
|
else:
|
|
258
263
|
raise InternalError("Unexpected array value")
|
|
@@ -306,6 +311,6 @@ class Array[T, Size](GenericValue, ArrayLike[T], metaclass=ArrayMeta):
|
|
|
306
311
|
return f"{type(self).__name__}({', '.join(repr(self[i]) for i in range(self.size()))})"
|
|
307
312
|
|
|
308
313
|
@meta_fn
|
|
309
|
-
def __pos__(self) ->
|
|
314
|
+
def __pos__(self) -> Array[T, Size]:
|
|
310
315
|
"""Return a copy of the array."""
|
|
311
316
|
return self._copy_()
|
sonolus/script/array_like.py
CHANGED
|
@@ -2,12 +2,13 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import random
|
|
4
4
|
from abc import abstractmethod
|
|
5
|
-
from collections.abc import Callable
|
|
5
|
+
from collections.abc import Callable, Sequence
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
8
|
from sonolus.script.internal.context import ctx
|
|
9
9
|
from sonolus.script.internal.impl import meta_fn
|
|
10
10
|
from sonolus.script.iterator import SonolusIterator
|
|
11
|
+
from sonolus.script.maybe import Maybe, Nothing, Some
|
|
11
12
|
from sonolus.script.num import Num
|
|
12
13
|
from sonolus.script.record import Record
|
|
13
14
|
from sonolus.script.values import copy
|
|
@@ -15,7 +16,7 @@ from sonolus.script.values import copy
|
|
|
15
16
|
# Note: we don't use Range in this file because Range itself inherits from ArrayLike
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
class ArrayLike[T]:
|
|
19
|
+
class ArrayLike[T](Sequence):
|
|
19
20
|
"""Mixin for array-like objects.
|
|
20
21
|
|
|
21
22
|
Inheritors must implement `__len__`, `__getitem__`, and `__setitem__`.
|
|
@@ -134,11 +135,11 @@ class ArrayLike[T]:
|
|
|
134
135
|
if len(self) == 0:
|
|
135
136
|
return -1
|
|
136
137
|
if key is None:
|
|
137
|
-
key = _identity
|
|
138
|
+
key = _identity # type: ignore
|
|
138
139
|
max_index = 0
|
|
139
140
|
i = 1
|
|
140
141
|
while i < len(self):
|
|
141
|
-
if key(self[i]) > key(self[max_index]):
|
|
142
|
+
if key(self[i]) > key(self[max_index]): # type: ignore
|
|
142
143
|
max_index = i
|
|
143
144
|
i += 1
|
|
144
145
|
return max_index
|
|
@@ -152,11 +153,11 @@ class ArrayLike[T]:
|
|
|
152
153
|
if len(self) == 0:
|
|
153
154
|
return -1
|
|
154
155
|
if key is None:
|
|
155
|
-
key = _identity
|
|
156
|
+
key = _identity # type: ignore
|
|
156
157
|
min_index = 0
|
|
157
158
|
i = 1
|
|
158
159
|
while i < len(self):
|
|
159
|
-
if key(self[i]) < key(self[min_index]):
|
|
160
|
+
if key(self[i]) < key(self[min_index]): # type: ignore
|
|
160
161
|
min_index = i
|
|
161
162
|
i += 1
|
|
162
163
|
return min_index
|
|
@@ -191,12 +192,12 @@ class ArrayLike[T]:
|
|
|
191
192
|
"""
|
|
192
193
|
if len(self) < 15 or key is not None:
|
|
193
194
|
if key is None:
|
|
194
|
-
key = _identity
|
|
195
|
+
key = _identity # type: ignore
|
|
195
196
|
# May be worth adding a block sort variant for better performance on large arrays in the future
|
|
196
|
-
_insertion_sort(self, 0, len(self), key, reverse)
|
|
197
|
+
_insertion_sort(self, 0, len(self), key, reverse) # type: ignore
|
|
197
198
|
else:
|
|
198
199
|
# Heap sort is unstable, so if there's a key, we can't rely on it
|
|
199
|
-
_heap_sort(self, 0, len(self), reverse)
|
|
200
|
+
_heap_sort(self, 0, len(self), reverse) # type: ignore
|
|
200
201
|
|
|
201
202
|
def shuffle(self):
|
|
202
203
|
"""Shuffle the values in the array in place."""
|
|
@@ -221,14 +222,14 @@ def _insertion_sort[T](array: ArrayLike[T], start: int, end: int, key: Callable[
|
|
|
221
222
|
if reverse:
|
|
222
223
|
while i < end:
|
|
223
224
|
j = i
|
|
224
|
-
while j > start and key(array[j - 1]) < key(array[j]):
|
|
225
|
+
while j > start and key(array[j - 1]) < key(array[j]): # type: ignore
|
|
225
226
|
array.swap(j - 1, j)
|
|
226
227
|
j -= 1
|
|
227
228
|
i += 1
|
|
228
229
|
else:
|
|
229
230
|
while i < end:
|
|
230
231
|
j = i
|
|
231
|
-
while j > start and key(array[j - 1]) > key(array[j]):
|
|
232
|
+
while j > start and key(array[j - 1]) > key(array[j]): # type: ignore
|
|
232
233
|
array.swap(j - 1, j)
|
|
233
234
|
j -= 1
|
|
234
235
|
i += 1
|
|
@@ -239,9 +240,9 @@ def _heapify[T](array: ArrayLike[T], end: int, index: int, reverse: bool):
|
|
|
239
240
|
left = index * 2 + 1
|
|
240
241
|
right = left + 1
|
|
241
242
|
largest = index
|
|
242
|
-
if left < end and (array[left] > array[largest]) != reverse:
|
|
243
|
+
if left < end and (array[left] > array[largest]) != reverse: # type: ignore
|
|
243
244
|
largest = left
|
|
244
|
-
if right < end and (array[right] > array[largest]) != reverse:
|
|
245
|
+
if right < end and (array[right] > array[largest]) != reverse: # type: ignore
|
|
245
246
|
largest = right
|
|
246
247
|
if largest == index:
|
|
247
248
|
break
|
|
@@ -265,14 +266,12 @@ class _ArrayIterator[V: ArrayLike](Record, SonolusIterator):
|
|
|
265
266
|
i: int
|
|
266
267
|
array: V
|
|
267
268
|
|
|
268
|
-
def
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
def advance(self):
|
|
275
|
-
self.i += 1
|
|
269
|
+
def next(self) -> Maybe[V]:
|
|
270
|
+
if self.i < len(self.array):
|
|
271
|
+
value = self.array[self.i]
|
|
272
|
+
self.i += 1
|
|
273
|
+
return Some(value)
|
|
274
|
+
return Nothing
|
|
276
275
|
|
|
277
276
|
|
|
278
277
|
class _ArrayReverser[V: ArrayLike](Record, ArrayLike):
|
|
@@ -296,14 +295,12 @@ class _ArrayEnumerator[V: ArrayLike](Record, SonolusIterator):
|
|
|
296
295
|
offset: int
|
|
297
296
|
array: V
|
|
298
297
|
|
|
299
|
-
def
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
def advance(self):
|
|
306
|
-
self.i += 1
|
|
298
|
+
def next(self) -> Maybe[tuple[int, Any]]:
|
|
299
|
+
if self.i < len(self.array):
|
|
300
|
+
result = (self.i + self.offset, self.array[self.i])
|
|
301
|
+
self.i += 1
|
|
302
|
+
return Some(result)
|
|
303
|
+
return Nothing
|
|
307
304
|
|
|
308
305
|
|
|
309
306
|
@meta_fn
|
sonolus/script/bucket.py
CHANGED
|
@@ -220,7 +220,7 @@ def bucket(*, sprites: list[_BucketSprite], unit: str | None = None) -> Any:
|
|
|
220
220
|
return _BucketInfo(sprites, unit)
|
|
221
221
|
|
|
222
222
|
|
|
223
|
-
type Buckets = NewType("Buckets", Any)
|
|
223
|
+
type Buckets = NewType("Buckets", Any) # type: ignore
|
|
224
224
|
|
|
225
225
|
|
|
226
226
|
@dataclass_transform()
|
sonolus/script/containers.py
CHANGED
|
@@ -8,6 +8,7 @@ from sonolus.script.internal.context import ctx
|
|
|
8
8
|
from sonolus.script.internal.impl import meta_fn
|
|
9
9
|
from sonolus.script.interval import clamp
|
|
10
10
|
from sonolus.script.iterator import SonolusIterator
|
|
11
|
+
from sonolus.script.maybe import Maybe, Nothing, Some
|
|
11
12
|
from sonolus.script.num import Num
|
|
12
13
|
from sonolus.script.pointer import _deref
|
|
13
14
|
from sonolus.script.record import Record
|
|
@@ -335,7 +336,8 @@ class ArrayPointer[T](Record, ArrayLike[T]):
|
|
|
335
336
|
if not ctx():
|
|
336
337
|
raise TypeError("ArrayPointer values cannot be accessed outside of a context")
|
|
337
338
|
return _deref(
|
|
338
|
-
|
|
339
|
+
# Allows a compile time constant block so we can warn based on callback read/write access
|
|
340
|
+
(self._value_["block"]._is_py_() and self._value_["block"]._as_py_()) or self.block,
|
|
339
341
|
self.offset + Num._accept_(item) * Num._accept_(self.element_type()._size_()),
|
|
340
342
|
self.element_type(),
|
|
341
343
|
)
|
|
@@ -600,40 +602,34 @@ class _ArrayMapKeyIterator[K, V, Capacity](Record, SonolusIterator):
|
|
|
600
602
|
_map: ArrayMap[K, V, Capacity]
|
|
601
603
|
_index: int
|
|
602
604
|
|
|
603
|
-
def
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
def advance(self):
|
|
610
|
-
self._index += 1
|
|
605
|
+
def next(self) -> Maybe[K]:
|
|
606
|
+
if self._index < len(self._map):
|
|
607
|
+
key = self._map._array[self._index].key
|
|
608
|
+
self._index += 1
|
|
609
|
+
return Some(key)
|
|
610
|
+
return Nothing
|
|
611
611
|
|
|
612
612
|
|
|
613
613
|
class _ArrayMapValueIterator[K, V, Capacity](Record, SonolusIterator):
|
|
614
614
|
_map: ArrayMap[K, V, Capacity]
|
|
615
615
|
_index: int
|
|
616
616
|
|
|
617
|
-
def
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
def advance(self):
|
|
624
|
-
self._index += 1
|
|
617
|
+
def next(self) -> Maybe[V]:
|
|
618
|
+
if self._index < len(self._map):
|
|
619
|
+
value = self._map._array[self._index].value
|
|
620
|
+
self._index += 1
|
|
621
|
+
return Some(value)
|
|
622
|
+
return Nothing
|
|
625
623
|
|
|
626
624
|
|
|
627
625
|
class _ArrayMapEntryIterator[K, V, Capacity](Record, SonolusIterator):
|
|
628
626
|
_map: ArrayMap[K, V, Capacity]
|
|
629
627
|
_index: int
|
|
630
628
|
|
|
631
|
-
def
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
def advance(self):
|
|
639
|
-
self._index += 1
|
|
629
|
+
def next(self) -> Maybe[tuple[K, V]]:
|
|
630
|
+
if self._index < len(self._map):
|
|
631
|
+
entry = self._map._array[self._index]
|
|
632
|
+
result = (entry.key, entry.value)
|
|
633
|
+
self._index += 1
|
|
634
|
+
return Some(result)
|
|
635
|
+
return Nothing
|