sonolus.py 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sonolus.py might be problematic. Click here for more details.
- sonolus/backend/blocks.py +756 -756
- sonolus/backend/excepthook.py +37 -37
- sonolus/backend/finalize.py +77 -69
- sonolus/backend/interpret.py +7 -7
- sonolus/backend/ir.py +29 -3
- sonolus/backend/mode.py +24 -24
- sonolus/backend/node.py +40 -40
- sonolus/backend/ops.py +197 -197
- sonolus/backend/optimize/__init__.py +0 -0
- sonolus/backend/optimize/allocate.py +126 -0
- sonolus/backend/optimize/constant_evaluation.py +374 -0
- sonolus/backend/optimize/copy_coalesce.py +85 -0
- sonolus/backend/optimize/dead_code.py +185 -0
- sonolus/backend/optimize/dominance.py +96 -0
- sonolus/backend/{flow.py → optimize/flow.py} +122 -92
- sonolus/backend/optimize/inlining.py +137 -0
- sonolus/backend/optimize/liveness.py +177 -0
- sonolus/backend/optimize/optimize.py +44 -0
- sonolus/backend/optimize/passes.py +52 -0
- sonolus/backend/optimize/simplify.py +191 -0
- sonolus/backend/optimize/ssa.py +200 -0
- sonolus/backend/place.py +17 -25
- sonolus/backend/utils.py +58 -48
- sonolus/backend/visitor.py +1151 -882
- sonolus/build/cli.py +7 -1
- sonolus/build/compile.py +88 -90
- sonolus/build/engine.py +10 -5
- sonolus/build/level.py +24 -23
- sonolus/build/node.py +43 -43
- sonolus/script/archetype.py +438 -139
- sonolus/script/array.py +27 -10
- sonolus/script/array_like.py +297 -0
- sonolus/script/bucket.py +253 -191
- sonolus/script/containers.py +257 -51
- sonolus/script/debug.py +26 -10
- sonolus/script/easing.py +365 -0
- sonolus/script/effect.py +191 -131
- sonolus/script/engine.py +71 -4
- sonolus/script/globals.py +303 -269
- sonolus/script/instruction.py +205 -151
- sonolus/script/internal/__init__.py +5 -5
- sonolus/script/internal/builtin_impls.py +255 -144
- sonolus/script/{callbacks.py → internal/callbacks.py} +127 -127
- sonolus/script/internal/constant.py +139 -0
- sonolus/script/internal/context.py +26 -9
- sonolus/script/internal/descriptor.py +17 -17
- sonolus/script/internal/dict_impl.py +65 -0
- sonolus/script/internal/generic.py +6 -9
- sonolus/script/internal/impl.py +38 -13
- sonolus/script/internal/introspection.py +17 -14
- sonolus/script/internal/math_impls.py +121 -0
- sonolus/script/internal/native.py +40 -38
- sonolus/script/internal/random.py +67 -0
- sonolus/script/internal/range.py +81 -0
- sonolus/script/internal/transient.py +51 -0
- sonolus/script/internal/tuple_impl.py +113 -0
- sonolus/script/internal/value.py +3 -3
- sonolus/script/interval.py +338 -112
- sonolus/script/iterator.py +167 -214
- sonolus/script/level.py +24 -0
- sonolus/script/num.py +80 -48
- sonolus/script/options.py +257 -191
- sonolus/script/particle.py +190 -157
- sonolus/script/pointer.py +30 -30
- sonolus/script/print.py +102 -81
- sonolus/script/project.py +8 -0
- sonolus/script/quad.py +263 -0
- sonolus/script/record.py +47 -16
- sonolus/script/runtime.py +52 -1
- sonolus/script/sprite.py +418 -333
- sonolus/script/text.py +409 -407
- sonolus/script/timing.py +114 -42
- sonolus/script/transform.py +332 -48
- sonolus/script/ui.py +216 -160
- sonolus/script/values.py +6 -13
- sonolus/script/vec.py +196 -78
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/METADATA +1 -1
- sonolus_py-0.1.5.dist-info/RECORD +89 -0
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/WHEEL +1 -1
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/licenses/LICENSE +21 -21
- sonolus/backend/allocate.py +0 -51
- sonolus/backend/optimize.py +0 -9
- sonolus/backend/passes.py +0 -6
- sonolus/backend/simplify.py +0 -30
- sonolus/script/comptime.py +0 -160
- sonolus/script/graphics.py +0 -150
- sonolus/script/math.py +0 -92
- sonolus/script/range.py +0 -58
- sonolus_py-0.1.3.dist-info/RECORD +0 -75
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/entry_points.txt +0 -0
|
@@ -1,38 +1,40 @@
|
|
|
1
|
-
import functools
|
|
2
|
-
import inspect
|
|
3
|
-
from collections.abc import Callable
|
|
4
|
-
|
|
5
|
-
from sonolus.backend.ir import IRInstr, IRPureInstr, IRSet
|
|
6
|
-
from sonolus.backend.ops import Op
|
|
7
|
-
from sonolus.script.internal.context import ctx
|
|
8
|
-
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
9
|
-
from sonolus.script.num import Num,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def native_call(op: Op, *args: Num) -> Num:
|
|
13
|
-
if not ctx():
|
|
14
|
-
raise RuntimeError("Unexpected native call")
|
|
15
|
-
args = tuple(validate_value(arg) for arg in args)
|
|
16
|
-
if not all(
|
|
17
|
-
raise RuntimeError("All arguments must be of type Num")
|
|
18
|
-
result = ctx().alloc(size=1)
|
|
19
|
-
ctx().add_statements(IRSet(result, (IRPureInstr if op.pure else IRInstr)(op, [arg.ir() for arg in args])))
|
|
20
|
-
return Num._from_place_(result)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def native_function[**P, R](op: Op) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
|
24
|
-
def decorator(fn: Callable[P, Num]) -> Callable[P, Num]:
|
|
25
|
-
signature = inspect.signature(fn)
|
|
26
|
-
|
|
27
|
-
@functools.wraps(fn)
|
|
28
|
-
@meta_fn
|
|
29
|
-
def wrapper(*args: Num) -> Num:
|
|
30
|
-
if len(args)
|
|
31
|
-
raise TypeError(f"Expected {len(signature.parameters)} arguments, got {len(args)}")
|
|
32
|
-
if ctx():
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
1
|
+
import functools
|
|
2
|
+
import inspect
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
|
|
5
|
+
from sonolus.backend.ir import IRInstr, IRPureInstr, IRSet
|
|
6
|
+
from sonolus.backend.ops import Op
|
|
7
|
+
from sonolus.script.internal.context import ctx
|
|
8
|
+
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
9
|
+
from sonolus.script.num import Num, _is_num
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def native_call(op: Op, *args: Num) -> Num:
|
|
13
|
+
if not ctx():
|
|
14
|
+
raise RuntimeError("Unexpected native call")
|
|
15
|
+
args = tuple(validate_value(arg) for arg in args)
|
|
16
|
+
if not all(_is_num(arg) for arg in args):
|
|
17
|
+
raise RuntimeError("All arguments must be of type Num")
|
|
18
|
+
result = ctx().alloc(size=1)
|
|
19
|
+
ctx().add_statements(IRSet(result, (IRPureInstr if op.pure else IRInstr)(op, [arg.ir() for arg in args])))
|
|
20
|
+
return Num._from_place_(result)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def native_function[**P, R](op: Op) -> Callable[[Callable[P, R]], Callable[P, R]]:
|
|
24
|
+
def decorator(fn: Callable[P, Num]) -> Callable[P, Num]:
|
|
25
|
+
signature = inspect.signature(fn)
|
|
26
|
+
|
|
27
|
+
@functools.wraps(fn)
|
|
28
|
+
@meta_fn
|
|
29
|
+
def wrapper(*args: Num) -> Num:
|
|
30
|
+
if len(args) < sum(1 for p in signature.parameters.values() if p.default == inspect.Parameter.empty):
|
|
31
|
+
raise TypeError(f"Expected {len(signature.parameters)} arguments, got {len(args)}")
|
|
32
|
+
if ctx():
|
|
33
|
+
bound_args = signature.bind(*args)
|
|
34
|
+
bound_args.apply_defaults()
|
|
35
|
+
return native_call(op, *(Num._accept_(arg) for arg in bound_args.args))
|
|
36
|
+
return fn(*args)
|
|
37
|
+
|
|
38
|
+
return wrapper
|
|
39
|
+
|
|
40
|
+
return decorator
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import random as pyrand
|
|
2
|
+
from collections.abc import MutableSequence, Sequence
|
|
3
|
+
|
|
4
|
+
from sonolus.backend.ops import Op
|
|
5
|
+
from sonolus.script.internal.native import native_function
|
|
6
|
+
from sonolus.script.values import copy
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@native_function(Op.Random)
|
|
10
|
+
def _random_float(a: float, b: float) -> float:
|
|
11
|
+
"""Returns a random float between a and b, inclusive."""
|
|
12
|
+
return pyrand.uniform(a, b)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@native_function(Op.RandomInteger)
|
|
16
|
+
def _random_integer(a: int, b: int) -> int:
|
|
17
|
+
"""Returns a random integer between a (inclusive) and b (exclusive)."""
|
|
18
|
+
return pyrand.randrange(a, b)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _randrange(start: int, stop: int | None = None, step: int = 1) -> int:
|
|
22
|
+
if stop is None:
|
|
23
|
+
stop = start
|
|
24
|
+
start = 0
|
|
25
|
+
range_len = (stop - start + step - 1) // step
|
|
26
|
+
return start + step * _random_integer(0, range_len)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _randint(a: int, b: int) -> int:
|
|
30
|
+
return _random_integer(a, b + 1)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _choice[T](seq: Sequence[T]) -> T:
|
|
34
|
+
return seq[_randrange(len(seq))]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _swap(seq: MutableSequence, i: int, j: int) -> None:
|
|
38
|
+
temp = copy(seq[i])
|
|
39
|
+
seq[i] = seq[j]
|
|
40
|
+
seq[j] = temp
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _shuffle[T: MutableSequence](seq: T) -> None:
|
|
44
|
+
i = len(seq) - 1
|
|
45
|
+
while i > 0:
|
|
46
|
+
j = _randrange(i + 1)
|
|
47
|
+
_swap(seq, i, j)
|
|
48
|
+
i -= 1
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _random() -> float:
|
|
52
|
+
# The end needs to exclude 1, and Sonolus uses 32-bit floats.
|
|
53
|
+
return _random_float(0.0, 0.99999994)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _uniform(a: float, b: float) -> float:
|
|
57
|
+
return _random_float(a, b)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
RANDOM_BUILTIN_IMPLS = {
|
|
61
|
+
id(pyrand.randrange): _randrange,
|
|
62
|
+
id(pyrand.randint): _randint,
|
|
63
|
+
id(pyrand.choice): _choice,
|
|
64
|
+
id(pyrand.shuffle): _shuffle,
|
|
65
|
+
id(pyrand.random): _random,
|
|
66
|
+
id(pyrand.uniform): _uniform,
|
|
67
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from sonolus.script.array_like import ArrayLike
|
|
2
|
+
from sonolus.script.iterator import SonolusIterator
|
|
3
|
+
from sonolus.script.num import Num
|
|
4
|
+
from sonolus.script.record import Record
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Range(Record, ArrayLike[Num]):
|
|
8
|
+
start: int
|
|
9
|
+
stop: int
|
|
10
|
+
step: int
|
|
11
|
+
|
|
12
|
+
def __new__(cls, start: Num, stop: Num | None = None, step: Num = 1):
|
|
13
|
+
if stop is None:
|
|
14
|
+
start, stop = 0, start
|
|
15
|
+
return super().__new__(cls, start, stop, step)
|
|
16
|
+
|
|
17
|
+
def __iter__(self) -> SonolusIterator:
|
|
18
|
+
return RangeIterator(self.start, self.stop, self.step)
|
|
19
|
+
|
|
20
|
+
def __contains__(self, item):
|
|
21
|
+
if self.step > 0:
|
|
22
|
+
return self.start <= item < self.stop and (item - self.start) % self.step == 0
|
|
23
|
+
else:
|
|
24
|
+
return self.stop < item <= self.start and (self.start - item) % -self.step == 0
|
|
25
|
+
|
|
26
|
+
def __len__(self) -> int:
|
|
27
|
+
if self.step > 0:
|
|
28
|
+
diff = self.stop - self.start
|
|
29
|
+
if diff <= 0:
|
|
30
|
+
return 0
|
|
31
|
+
return (diff + self.step - 1) // self.step
|
|
32
|
+
else:
|
|
33
|
+
diff = self.start - self.stop
|
|
34
|
+
if diff <= 0:
|
|
35
|
+
return 0
|
|
36
|
+
return (diff - self.step - 1) // -self.step
|
|
37
|
+
|
|
38
|
+
def __getitem__(self, index: Num) -> Num:
|
|
39
|
+
return self.start + index * self.step
|
|
40
|
+
|
|
41
|
+
def __setitem__(self, index: Num, value: Num):
|
|
42
|
+
raise TypeError("Range does not support item assignment")
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def last(self) -> Num:
|
|
46
|
+
return self[len(self) - 1]
|
|
47
|
+
|
|
48
|
+
def __eq__(self, other):
|
|
49
|
+
if not isinstance(other, Range):
|
|
50
|
+
return False
|
|
51
|
+
len_self = len(self)
|
|
52
|
+
len_other = len(other)
|
|
53
|
+
if len_self != len_other:
|
|
54
|
+
return False
|
|
55
|
+
if len_self == 0:
|
|
56
|
+
return True
|
|
57
|
+
return self.start == other.start and self.last == other.last
|
|
58
|
+
|
|
59
|
+
def __ne__(self, other):
|
|
60
|
+
return not self == other
|
|
61
|
+
|
|
62
|
+
def __hash__(self):
|
|
63
|
+
raise TypeError("Range is not hashable")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class RangeIterator(Record, SonolusIterator):
|
|
67
|
+
value: int
|
|
68
|
+
stop: int
|
|
69
|
+
step: int
|
|
70
|
+
|
|
71
|
+
def has_next(self) -> bool:
|
|
72
|
+
if self.step > 0:
|
|
73
|
+
return self.value < self.stop
|
|
74
|
+
else:
|
|
75
|
+
return self.value > self.stop
|
|
76
|
+
|
|
77
|
+
def get(self) -> int:
|
|
78
|
+
return self.value
|
|
79
|
+
|
|
80
|
+
def advance(self):
|
|
81
|
+
self.value += self.step
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
from typing import Any, Self
|
|
3
|
+
|
|
4
|
+
from sonolus.backend.place import BlockPlace
|
|
5
|
+
from sonolus.script.internal.value import Value
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TransientValue(Value):
|
|
9
|
+
@classmethod
|
|
10
|
+
def _is_concrete_(cls) -> bool:
|
|
11
|
+
return True
|
|
12
|
+
|
|
13
|
+
@classmethod
|
|
14
|
+
def _size_(cls) -> int:
|
|
15
|
+
return 0
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def _is_value_type_(cls) -> bool:
|
|
19
|
+
return False
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def _from_place_(cls, place: BlockPlace) -> Self:
|
|
23
|
+
raise TypeError(f"{cls.__name__} cannot be dereferenced")
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def _from_list_(cls, values: Iterable[float | BlockPlace]) -> Self:
|
|
27
|
+
raise TypeError(f"{cls.__name__} cannot be constructed from list")
|
|
28
|
+
|
|
29
|
+
def _to_list_(self, level_refs: dict[Any, int] | None = None) -> list[float | BlockPlace]:
|
|
30
|
+
raise TypeError(f"{type(self).__name__} cannot be deconstructed to list")
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def _flat_keys_(cls, prefix: str) -> list[str]:
|
|
34
|
+
raise TypeError(f"{cls.__name__} cannot be flattened")
|
|
35
|
+
|
|
36
|
+
def _get_(self) -> Self:
|
|
37
|
+
return self
|
|
38
|
+
|
|
39
|
+
def _set_(self, value: Self) -> None:
|
|
40
|
+
if value is not self:
|
|
41
|
+
raise TypeError(f"{type(self).__name__} is immutable")
|
|
42
|
+
|
|
43
|
+
def _copy_from_(self, value: Self):
|
|
44
|
+
raise TypeError(f"{type(self).__name__} is immutable")
|
|
45
|
+
|
|
46
|
+
def _copy_(self) -> Self:
|
|
47
|
+
raise TypeError(f"{type(self).__name__} cannot be copied")
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def _alloc_(cls) -> Self:
|
|
51
|
+
raise TypeError(f"{cls.__name__} is not allocatable")
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# ruff: noqa: B905
|
|
2
|
+
from typing import Any, Self
|
|
3
|
+
|
|
4
|
+
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
5
|
+
from sonolus.script.internal.transient import TransientValue
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TupleImpl(TransientValue):
|
|
9
|
+
value: tuple
|
|
10
|
+
|
|
11
|
+
def __init__(self, value: tuple):
|
|
12
|
+
self.value = value
|
|
13
|
+
|
|
14
|
+
@meta_fn
|
|
15
|
+
def __getitem__(self, item):
|
|
16
|
+
item = validate_value(item)
|
|
17
|
+
if not item._is_py_():
|
|
18
|
+
raise TypeError(f"Cannot index tuple with non compile-time constant {item}")
|
|
19
|
+
item = item._as_py_()
|
|
20
|
+
if not isinstance(item, int | float):
|
|
21
|
+
raise TypeError(f"Cannot index tuple with {item}")
|
|
22
|
+
if int(item) != item:
|
|
23
|
+
raise TypeError(f"Cannot index tuple with non-integer {item}")
|
|
24
|
+
if not (0 <= item < len(self.value)):
|
|
25
|
+
raise IndexError(f"Tuple index out of range: {item}")
|
|
26
|
+
return self.value[int(item)]
|
|
27
|
+
|
|
28
|
+
@meta_fn
|
|
29
|
+
def __len__(self):
|
|
30
|
+
return len(self.value)
|
|
31
|
+
|
|
32
|
+
def __eq__(self, other):
|
|
33
|
+
if not isinstance(other, tuple):
|
|
34
|
+
return False
|
|
35
|
+
if len(self) != len(other):
|
|
36
|
+
return False
|
|
37
|
+
for a, b in zip(self, other): # noqa: SIM110
|
|
38
|
+
if a != b:
|
|
39
|
+
return False
|
|
40
|
+
return True
|
|
41
|
+
|
|
42
|
+
def __ne__(self, other):
|
|
43
|
+
if not isinstance(other, tuple):
|
|
44
|
+
return True
|
|
45
|
+
if len(self) != len(other):
|
|
46
|
+
return True
|
|
47
|
+
for a, b in zip(self, other): # noqa: SIM110
|
|
48
|
+
if a != b:
|
|
49
|
+
return True
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
def __lt__(self, other):
|
|
53
|
+
if not isinstance(other, tuple):
|
|
54
|
+
return NotImplemented
|
|
55
|
+
for a, b in zip(self.value, other.value):
|
|
56
|
+
if a != b:
|
|
57
|
+
return a < b
|
|
58
|
+
return len(self.value) < len(other.value)
|
|
59
|
+
|
|
60
|
+
def __le__(self, other):
|
|
61
|
+
if not isinstance(other, tuple):
|
|
62
|
+
return NotImplemented
|
|
63
|
+
for a, b in zip(self.value, other.value):
|
|
64
|
+
if a != b:
|
|
65
|
+
return a < b
|
|
66
|
+
return len(self.value) <= len(other.value)
|
|
67
|
+
|
|
68
|
+
def __gt__(self, other):
|
|
69
|
+
if not isinstance(other, tuple):
|
|
70
|
+
return NotImplemented
|
|
71
|
+
for a, b in zip(self.value, other.value):
|
|
72
|
+
if a != b:
|
|
73
|
+
return a > b
|
|
74
|
+
return len(self.value) > len(other.value)
|
|
75
|
+
|
|
76
|
+
def __ge__(self, other):
|
|
77
|
+
if not isinstance(other, tuple):
|
|
78
|
+
return NotImplemented
|
|
79
|
+
for a, b in zip(self.value, other.value):
|
|
80
|
+
if a != b:
|
|
81
|
+
return a > b
|
|
82
|
+
return len(self.value) >= len(other.value)
|
|
83
|
+
|
|
84
|
+
def __hash__(self):
|
|
85
|
+
return hash(self.value)
|
|
86
|
+
|
|
87
|
+
@meta_fn
|
|
88
|
+
def __add__(self, other) -> Self:
|
|
89
|
+
other = TupleImpl._accept_(other)
|
|
90
|
+
return TupleImpl._accept_(self.value + other.value)
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
def _accepts_(cls, value: Any) -> bool:
|
|
94
|
+
return isinstance(value, cls | tuple)
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def _accept_(cls, value: Any) -> Self:
|
|
98
|
+
if not cls._accepts_(value):
|
|
99
|
+
raise TypeError(f"Cannot accept {value} as {cls.__name__}")
|
|
100
|
+
if isinstance(value, cls):
|
|
101
|
+
return value
|
|
102
|
+
else:
|
|
103
|
+
return cls(tuple(validate_value(item) for item in value))
|
|
104
|
+
|
|
105
|
+
def _is_py_(self) -> bool:
|
|
106
|
+
return all(item._is_py_() for item in self.value)
|
|
107
|
+
|
|
108
|
+
def _as_py_(self) -> tuple:
|
|
109
|
+
return tuple(item._as_py_() for item in self.value)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
TupleImpl.__name__ = "tuple"
|
|
113
|
+
TupleImpl.__qualname__ = "tuple"
|
sonolus/script/internal/value.py
CHANGED
|
@@ -66,7 +66,7 @@ class Value:
|
|
|
66
66
|
raise NotImplementedError
|
|
67
67
|
|
|
68
68
|
@abstractmethod
|
|
69
|
-
def _to_list_(self) -> list[float | BlockPlace]:
|
|
69
|
+
def _to_list_(self, level_refs: dict[Any, int] | None = None) -> list[float | BlockPlace]:
|
|
70
70
|
"""Converts this value to a list of floats."""
|
|
71
71
|
raise NotImplementedError
|
|
72
72
|
|
|
@@ -76,9 +76,9 @@ class Value:
|
|
|
76
76
|
"""Returns the keys to a flat representation of this value."""
|
|
77
77
|
raise NotImplementedError
|
|
78
78
|
|
|
79
|
-
def _to_flat_dict_(self, prefix: str) -> dict[str, float | BlockPlace]:
|
|
79
|
+
def _to_flat_dict_(self, prefix: str, level_refs: dict[Any, int] | None = None) -> dict[str, float | BlockPlace]:
|
|
80
80
|
"""Converts this value to a flat dictionary."""
|
|
81
|
-
return dict(zip(self._flat_keys_(prefix), self._to_list_(), strict=False))
|
|
81
|
+
return dict(zip(self._flat_keys_(prefix), self._to_list_(level_refs), strict=False))
|
|
82
82
|
|
|
83
83
|
@abstractmethod
|
|
84
84
|
def _get_(self) -> Self:
|