sonolus.py 0.1.4__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/finalize.py +18 -10
- sonolus/backend/interpret.py +7 -7
- sonolus/backend/ir.py +24 -0
- sonolus/backend/optimize/__init__.py +0 -0
- sonolus/backend/{allocate.py → optimize/allocate.py} +4 -3
- sonolus/backend/{constant_evaluation.py → optimize/constant_evaluation.py} +7 -7
- sonolus/backend/{coalesce.py → optimize/copy_coalesce.py} +3 -3
- sonolus/backend/optimize/dead_code.py +185 -0
- sonolus/backend/{dominance.py → optimize/dominance.py} +2 -17
- sonolus/backend/{flow.py → optimize/flow.py} +6 -5
- sonolus/backend/{inlining.py → optimize/inlining.py} +4 -17
- sonolus/backend/{liveness.py → optimize/liveness.py} +69 -65
- sonolus/backend/optimize/optimize.py +44 -0
- sonolus/backend/{passes.py → optimize/passes.py} +1 -1
- sonolus/backend/optimize/simplify.py +191 -0
- sonolus/backend/{ssa.py → optimize/ssa.py} +31 -18
- sonolus/backend/place.py +17 -25
- sonolus/backend/utils.py +10 -0
- sonolus/backend/visitor.py +360 -101
- sonolus/build/compile.py +8 -8
- sonolus/build/engine.py +10 -5
- sonolus/script/archetype.py +419 -137
- sonolus/script/array.py +25 -8
- sonolus/script/array_like.py +297 -0
- sonolus/script/bucket.py +73 -11
- sonolus/script/containers.py +234 -51
- sonolus/script/debug.py +8 -8
- sonolus/script/easing.py +147 -105
- sonolus/script/effect.py +60 -0
- sonolus/script/engine.py +71 -4
- sonolus/script/globals.py +66 -32
- sonolus/script/instruction.py +79 -25
- sonolus/script/internal/builtin_impls.py +138 -27
- sonolus/script/internal/constant.py +139 -0
- sonolus/script/internal/context.py +14 -5
- 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 +5 -2
- sonolus/script/{math.py → internal/math_impls.py} +28 -28
- sonolus/script/internal/native.py +3 -3
- 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/interval.py +234 -16
- sonolus/script/iterator.py +120 -167
- sonolus/script/level.py +24 -0
- sonolus/script/num.py +79 -47
- sonolus/script/options.py +78 -12
- sonolus/script/particle.py +37 -4
- sonolus/script/pointer.py +4 -4
- sonolus/script/print.py +22 -1
- sonolus/script/project.py +8 -0
- sonolus/script/{graphics.py → quad.py} +75 -12
- sonolus/script/record.py +44 -13
- sonolus/script/runtime.py +50 -1
- sonolus/script/sprite.py +197 -112
- sonolus/script/text.py +2 -0
- sonolus/script/timing.py +72 -0
- sonolus/script/transform.py +296 -66
- sonolus/script/ui.py +134 -78
- sonolus/script/values.py +6 -13
- sonolus/script/vec.py +118 -3
- {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.5.dist-info}/METADATA +1 -1
- sonolus_py-0.1.5.dist-info/RECORD +89 -0
- sonolus/backend/dead_code.py +0 -80
- sonolus/backend/optimize.py +0 -37
- sonolus/backend/simplify.py +0 -47
- sonolus/script/comptime.py +0 -160
- sonolus/script/random.py +0 -14
- sonolus/script/range.py +0 -58
- sonolus_py-0.1.4.dist-info/RECORD +0 -84
- /sonolus/script/{callbacks.py → internal/callbacks.py} +0 -0
- {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.5.dist-info}/WHEEL +0 -0
- {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.5.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.1.4.dist-info → sonolus_py-0.1.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -4,11 +4,14 @@ from typing import Annotated
|
|
|
4
4
|
_missing = object()
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
def get_field_specifiers(cls, *, globals=None, locals=None, eval_str=True): # noqa: A002
|
|
7
|
+
def get_field_specifiers(cls, *, skip: set[str] = frozenset(), globals=None, locals=None, eval_str=True): # noqa: A002
|
|
8
8
|
"""Like inspect.get_annotations, but also turns class attributes into Annotated."""
|
|
9
9
|
results = inspect.get_annotations(cls, globals=globals, locals=locals, eval_str=eval_str)
|
|
10
10
|
for key, value in results.items():
|
|
11
11
|
class_value = getattr(cls, key, _missing)
|
|
12
|
-
if class_value is not _missing:
|
|
12
|
+
if class_value is not _missing and key not in skip:
|
|
13
13
|
results[key] = Annotated[value, class_value]
|
|
14
|
+
for key, value in cls.__dict__.items():
|
|
15
|
+
if key not in results and key not in skip and not key.startswith("__") and not callable(value):
|
|
16
|
+
raise ValueError(f"Missing annotation for {cls.__name__}.{key}")
|
|
14
17
|
return results
|
|
@@ -5,67 +5,67 @@ from sonolus.script.internal.native import native_function
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
@native_function(Op.Sin)
|
|
8
|
-
def
|
|
8
|
+
def _sin(x: float) -> float:
|
|
9
9
|
return math.sin(x)
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
@native_function(Op.Cos)
|
|
13
|
-
def
|
|
13
|
+
def _cos(x: float) -> float:
|
|
14
14
|
return math.cos(x)
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
@native_function(Op.Tan)
|
|
18
|
-
def
|
|
18
|
+
def _tan(x: float) -> float:
|
|
19
19
|
return math.tan(x)
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
@native_function(Op.Arcsin)
|
|
23
|
-
def
|
|
23
|
+
def _asin(x: float) -> float:
|
|
24
24
|
return math.asin(x)
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
@native_function(Op.Arccos)
|
|
28
|
-
def
|
|
28
|
+
def _acos(x: float) -> float:
|
|
29
29
|
return math.acos(x)
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
@native_function(Op.Arctan)
|
|
33
|
-
def
|
|
33
|
+
def _atan(x: float) -> float:
|
|
34
34
|
return math.atan(x)
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
@native_function(Op.Arctan2)
|
|
38
|
-
def
|
|
38
|
+
def _atan2(y: float, x: float) -> float:
|
|
39
39
|
return math.atan2(y, x)
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
@native_function(Op.Sinh)
|
|
43
|
-
def
|
|
43
|
+
def _sinh(x: float) -> float:
|
|
44
44
|
return math.sinh(x)
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
@native_function(Op.Cosh)
|
|
48
|
-
def
|
|
48
|
+
def _cosh(x: float) -> float:
|
|
49
49
|
return math.cosh(x)
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
@native_function(Op.Tanh)
|
|
53
|
-
def
|
|
53
|
+
def _tanh(x: float) -> float:
|
|
54
54
|
return math.tanh(x)
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
@native_function(Op.Floor)
|
|
58
|
-
def
|
|
58
|
+
def _floor(x: float) -> float:
|
|
59
59
|
return math.floor(x)
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
@native_function(Op.Ceil)
|
|
63
|
-
def
|
|
63
|
+
def _ceil(x: float) -> float:
|
|
64
64
|
return math.ceil(x)
|
|
65
65
|
|
|
66
66
|
|
|
67
67
|
@native_function(Op.Trunc)
|
|
68
|
-
def
|
|
68
|
+
def _trunc(x: float) -> float:
|
|
69
69
|
return math.trunc(x)
|
|
70
70
|
|
|
71
71
|
|
|
@@ -90,7 +90,7 @@ def _ln(x: float) -> float:
|
|
|
90
90
|
return math.log(x)
|
|
91
91
|
|
|
92
92
|
|
|
93
|
-
def
|
|
93
|
+
def _log(x: float, base: float | None = None) -> float:
|
|
94
94
|
if base is None:
|
|
95
95
|
return _ln(x)
|
|
96
96
|
return _ln(x) / _ln(base)
|
|
@@ -103,19 +103,19 @@ def _remainder(x: float, y: float) -> float:
|
|
|
103
103
|
|
|
104
104
|
|
|
105
105
|
MATH_BUILTIN_IMPLS = {
|
|
106
|
-
id(math.sin):
|
|
107
|
-
id(math.cos):
|
|
108
|
-
id(math.tan):
|
|
109
|
-
id(math.asin):
|
|
110
|
-
id(math.acos):
|
|
111
|
-
id(math.atan):
|
|
112
|
-
id(math.atan2):
|
|
113
|
-
id(math.sinh):
|
|
114
|
-
id(math.cosh):
|
|
115
|
-
id(math.tanh):
|
|
116
|
-
id(math.floor):
|
|
117
|
-
id(math.ceil):
|
|
118
|
-
id(math.trunc):
|
|
106
|
+
id(math.sin): _sin,
|
|
107
|
+
id(math.cos): _cos,
|
|
108
|
+
id(math.tan): _tan,
|
|
109
|
+
id(math.asin): _asin,
|
|
110
|
+
id(math.acos): _acos,
|
|
111
|
+
id(math.atan): _atan,
|
|
112
|
+
id(math.atan2): _atan2,
|
|
113
|
+
id(math.sinh): _sinh,
|
|
114
|
+
id(math.cosh): _cosh,
|
|
115
|
+
id(math.tanh): _tanh,
|
|
116
|
+
id(math.floor): _floor,
|
|
117
|
+
id(math.ceil): _ceil,
|
|
118
|
+
id(math.trunc): _trunc,
|
|
119
119
|
id(round): _round,
|
|
120
|
-
id(math.log):
|
|
120
|
+
id(math.log): _log,
|
|
121
121
|
}
|
|
@@ -6,14 +6,14 @@ from sonolus.backend.ir import IRInstr, IRPureInstr, IRSet
|
|
|
6
6
|
from sonolus.backend.ops import Op
|
|
7
7
|
from sonolus.script.internal.context import ctx
|
|
8
8
|
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
9
|
-
from sonolus.script.num import Num,
|
|
9
|
+
from sonolus.script.num import Num, _is_num
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def native_call(op: Op, *args: Num) -> Num:
|
|
13
13
|
if not ctx():
|
|
14
14
|
raise RuntimeError("Unexpected native call")
|
|
15
15
|
args = tuple(validate_value(arg) for arg in args)
|
|
16
|
-
if not all(
|
|
16
|
+
if not all(_is_num(arg) for arg in args):
|
|
17
17
|
raise RuntimeError("All arguments must be of type Num")
|
|
18
18
|
result = ctx().alloc(size=1)
|
|
19
19
|
ctx().add_statements(IRSet(result, (IRPureInstr if op.pure else IRInstr)(op, [arg.ir() for arg in args])))
|
|
@@ -32,7 +32,7 @@ def native_function[**P, R](op: Op) -> Callable[[Callable[P, R]], Callable[P, R]
|
|
|
32
32
|
if ctx():
|
|
33
33
|
bound_args = signature.bind(*args)
|
|
34
34
|
bound_args.apply_defaults()
|
|
35
|
-
return native_call(op, *bound_args.args)
|
|
35
|
+
return native_call(op, *(Num._accept_(arg) for arg in bound_args.args))
|
|
36
36
|
return fn(*args)
|
|
37
37
|
|
|
38
38
|
return wrapper
|
|
@@ -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"
|