sonolus.py 0.3.4__py3-none-any.whl → 0.4.0__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 +24 -0
- sonolus/backend/visitor.py +260 -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 +38 -107
- sonolus/script/maybe.py +139 -0
- sonolus/script/num.py +17 -2
- 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 +16 -12
- 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.0.dist-info}/METADATA +1 -1
- sonolus_py-0.4.0.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.0.dist-info}/WHEEL +0 -0
- {sonolus_py-0.3.4.dist-info → sonolus_py-0.4.0.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.3.4.dist-info → sonolus_py-0.4.0.dist-info}/licenses/LICENSE +0 -0
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
|
sonolus/script/debug.py
CHANGED
|
@@ -4,18 +4,9 @@ from typing import Any, Literal, Never
|
|
|
4
4
|
|
|
5
5
|
from sonolus.backend.mode import Mode
|
|
6
6
|
from sonolus.backend.ops import Op
|
|
7
|
-
from sonolus.backend.optimize.constant_evaluation import SparseConditionalConstantPropagation
|
|
8
|
-
from sonolus.backend.optimize.copy_coalesce import CopyCoalesce
|
|
9
|
-
from sonolus.backend.optimize.dead_code import (
|
|
10
|
-
AdvancedDeadCodeElimination,
|
|
11
|
-
DeadCodeElimination,
|
|
12
|
-
UnreachableCodeElimination,
|
|
13
|
-
)
|
|
14
7
|
from sonolus.backend.optimize.flow import cfg_to_mermaid
|
|
15
|
-
from sonolus.backend.optimize.
|
|
16
|
-
from sonolus.backend.optimize.
|
|
17
|
-
from sonolus.backend.optimize.simplify import CoalesceFlow, NormalizeSwitch, RewriteToSwitch
|
|
18
|
-
from sonolus.backend.optimize.ssa import FromSSA, ToSSA
|
|
8
|
+
from sonolus.backend.optimize.passes import CompilerPass, OptimizerConfig, run_passes
|
|
9
|
+
from sonolus.backend.optimize.simplify import RenumberVars
|
|
19
10
|
from sonolus.script.internal.context import GlobalContextState, ReadOnlyMemory, ctx, set_ctx
|
|
20
11
|
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
21
12
|
from sonolus.script.internal.native import native_function
|
|
@@ -26,13 +17,13 @@ debug_log_callback = ContextVar[Callable[[Num], None]]("debug_log_callback")
|
|
|
26
17
|
|
|
27
18
|
|
|
28
19
|
@meta_fn
|
|
29
|
-
def error(message: str | None = None) -> Never:
|
|
20
|
+
def error(message: str | None = None) -> Never: # type: ignore
|
|
30
21
|
"""Raise an error.
|
|
31
22
|
|
|
32
23
|
This function is used to raise an error during runtime.
|
|
33
24
|
When this happens, the game will pause in debug mode. The current callback will also immediately return 0.
|
|
34
25
|
"""
|
|
35
|
-
message = validate_value(message)._as_py_()
|
|
26
|
+
message = validate_value(message)._as_py_() or "Error" # type: ignore
|
|
36
27
|
if not isinstance(message, str):
|
|
37
28
|
raise ValueError("Expected a string")
|
|
38
29
|
if ctx():
|
|
@@ -50,7 +41,7 @@ def static_error(message: str | None = None) -> Never:
|
|
|
50
41
|
This function is used to raise an error during compile-time if the compiler cannot guarantee that
|
|
51
42
|
this function will not be called during runtime.
|
|
52
43
|
"""
|
|
53
|
-
message = validate_value(message)._as_py_()
|
|
44
|
+
message = validate_value(message)._as_py_() or "Error" # type: ignore
|
|
54
45
|
if not isinstance(message, str):
|
|
55
46
|
raise ValueError("Expected a string")
|
|
56
47
|
raise RuntimeError(message)
|
|
@@ -60,7 +51,7 @@ def static_error(message: str | None = None) -> Never:
|
|
|
60
51
|
def debug_log(value: int | float | bool):
|
|
61
52
|
"""Log a value in debug mode."""
|
|
62
53
|
if debug_log_callback.get(None):
|
|
63
|
-
return debug_log_callback.get()(value)
|
|
54
|
+
return debug_log_callback.get()(value) # type: ignore
|
|
64
55
|
else:
|
|
65
56
|
return _debug_log(value)
|
|
66
57
|
|
|
@@ -91,7 +82,7 @@ def assert_false(value: int | float | bool, message: str | None = None):
|
|
|
91
82
|
|
|
92
83
|
@meta_fn
|
|
93
84
|
def assert_unreachable(message: str | None = None) -> Never:
|
|
94
|
-
message = validate_value(message)._as_py_() or "Unreachable code reached"
|
|
85
|
+
message = validate_value(message)._as_py_() or "Unreachable code reached" # type: ignore
|
|
95
86
|
raise RuntimeError(message)
|
|
96
87
|
|
|
97
88
|
|
|
@@ -108,43 +99,29 @@ def visualize_cfg(
|
|
|
108
99
|
/,
|
|
109
100
|
*,
|
|
110
101
|
mode: Mode = Mode.PLAY,
|
|
102
|
+
callback: str = "",
|
|
111
103
|
archetype: type | None = None,
|
|
112
|
-
archetypes: list[type] | None,
|
|
113
|
-
passes: Sequence[CompilerPass] | Literal["minimal", "
|
|
104
|
+
archetypes: list[type] | None = None,
|
|
105
|
+
passes: Sequence[CompilerPass] | Literal["minimal", "fast", "standard"] = "fast",
|
|
114
106
|
) -> str:
|
|
107
|
+
from sonolus.backend.optimize.optimize import FAST_PASSES, MINIMAL_PASSES, STANDARD_PASSES
|
|
115
108
|
from sonolus.build.compile import callback_to_cfg
|
|
116
109
|
|
|
117
110
|
match passes:
|
|
118
111
|
case "minimal":
|
|
119
112
|
passes = [
|
|
120
|
-
|
|
113
|
+
*MINIMAL_PASSES[:-1],
|
|
114
|
+
RenumberVars(),
|
|
121
115
|
]
|
|
122
|
-
case "
|
|
116
|
+
case "fast":
|
|
123
117
|
passes = [
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
AdvancedDeadCodeElimination(),
|
|
127
|
-
CoalesceFlow(),
|
|
118
|
+
*FAST_PASSES[:-1],
|
|
119
|
+
RenumberVars(),
|
|
128
120
|
]
|
|
129
121
|
case "standard":
|
|
130
122
|
passes = [
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
DeadCodeElimination(),
|
|
134
|
-
ToSSA(),
|
|
135
|
-
SparseConditionalConstantPropagation(),
|
|
136
|
-
UnreachableCodeElimination(),
|
|
137
|
-
DeadCodeElimination(),
|
|
138
|
-
CoalesceFlow(),
|
|
139
|
-
InlineVars(),
|
|
140
|
-
DeadCodeElimination(),
|
|
141
|
-
RewriteToSwitch(),
|
|
142
|
-
FromSSA(),
|
|
143
|
-
CoalesceFlow(),
|
|
144
|
-
CopyCoalesce(),
|
|
145
|
-
AdvancedDeadCodeElimination(),
|
|
146
|
-
CoalesceFlow(),
|
|
147
|
-
NormalizeSwitch(),
|
|
123
|
+
*STANDARD_PASSES[:-1],
|
|
124
|
+
RenumberVars(),
|
|
148
125
|
]
|
|
149
126
|
|
|
150
127
|
global_state = GlobalContextState(
|
|
@@ -153,8 +130,8 @@ def visualize_cfg(
|
|
|
153
130
|
ReadOnlyMemory(),
|
|
154
131
|
)
|
|
155
132
|
|
|
156
|
-
cfg = callback_to_cfg(global_state, fn,
|
|
157
|
-
cfg = run_passes(cfg, passes)
|
|
133
|
+
cfg = callback_to_cfg(global_state, fn, callback, archetype=archetype) # type: ignore
|
|
134
|
+
cfg = run_passes(cfg, passes, OptimizerConfig(mode=mode))
|
|
158
135
|
return cfg_to_mermaid(cfg)
|
|
159
136
|
|
|
160
137
|
|
sonolus/script/effect.py
CHANGED
sonolus/script/globals.py
CHANGED
|
@@ -39,7 +39,7 @@ class _GlobalField(SonolusDescriptor):
|
|
|
39
39
|
if not ctx():
|
|
40
40
|
raise RuntimeError("Global field access outside of compilation")
|
|
41
41
|
base = ctx().get_global_base(info)
|
|
42
|
-
return self.type._from_place_(base.add_offset(self.offset)).
|
|
42
|
+
return self.type._from_place_(base.add_offset(self.offset))._get_readonly_()
|
|
43
43
|
|
|
44
44
|
def __set__(self, instance, value):
|
|
45
45
|
from sonolus.script.internal.context import ctx
|
|
@@ -96,8 +96,8 @@ def _create_global(cls: type, blocks: dict[Mode, Block], offset: int | None):
|
|
|
96
96
|
type_ = validate_concrete_type(annotation)
|
|
97
97
|
setattr(cls, name, _GlobalField(name, type_, i, field_offset))
|
|
98
98
|
field_offset += type_._size_()
|
|
99
|
-
cls._global_info_ = _GlobalInfo(cls.__name__, field_offset, blocks, offset)
|
|
100
|
-
cls._is_comptime_value_ = True
|
|
99
|
+
cls._global_info_ = _GlobalInfo(cls.__name__, field_offset, blocks, offset) # type: ignore
|
|
100
|
+
cls._is_comptime_value_ = True # type: ignore
|
|
101
101
|
return cls()
|
|
102
102
|
|
|
103
103
|
|
sonolus/script/instruction.py
CHANGED
|
@@ -70,8 +70,8 @@ def instruction_icon(name: str) -> Any:
|
|
|
70
70
|
return _InstructionIconInfo(name=name)
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
type TutorialInstructions = NewType("TutorialInstructions", Any)
|
|
74
|
-
type TutorialInstructionIcons = NewType("TutorialInstructionIcons", Any)
|
|
73
|
+
type TutorialInstructions = NewType("TutorialInstructions", Any) # type: ignore
|
|
74
|
+
type TutorialInstructionIcons = NewType("TutorialInstructionIcons", Any) # type: ignore
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
@dataclass_transform()
|