sonolus.py 0.4.0__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/utils.py +20 -16
- sonolus/backend/visitor.py +32 -4
- sonolus/script/containers.py +1 -1
- sonolus/script/iterator.py +15 -0
- sonolus/script/level.py +2 -2
- sonolus/script/maybe.py +105 -3
- sonolus/script/num.py +12 -12
- sonolus/script/project.py +1 -1
- sonolus/script/record.py +36 -36
- {sonolus_py-0.4.0.dist-info → sonolus_py-0.4.1.dist-info}/METADATA +1 -1
- {sonolus_py-0.4.0.dist-info → sonolus_py-0.4.1.dist-info}/RECORD +14 -14
- {sonolus_py-0.4.0.dist-info → sonolus_py-0.4.1.dist-info}/WHEEL +0 -0
- {sonolus_py-0.4.0.dist-info → sonolus_py-0.4.1.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.4.0.dist-info → sonolus_py-0.4.1.dist-info}/licenses/LICENSE +0 -0
sonolus/backend/utils.py
CHANGED
|
@@ -23,29 +23,33 @@ def get_tree_from_file(file: str | Path) -> ast.Module:
|
|
|
23
23
|
class FindFunction(ast.NodeVisitor):
|
|
24
24
|
def __init__(self, line):
|
|
25
25
|
self.line = line
|
|
26
|
-
self.
|
|
26
|
+
self.results: list[ast.FunctionDef | ast.Lambda] = []
|
|
27
27
|
|
|
28
28
|
def visit_FunctionDef(self, node: ast.FunctionDef):
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
):
|
|
32
|
-
self.node = node
|
|
33
|
-
else:
|
|
34
|
-
self.generic_visit(node)
|
|
29
|
+
self.results.append(node)
|
|
30
|
+
self.generic_visit(node)
|
|
35
31
|
|
|
36
32
|
def visit_Lambda(self, node: ast.Lambda):
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
raise ValueError("Multiple functions defined on the same line are not supported")
|
|
40
|
-
self.node = node
|
|
41
|
-
else:
|
|
42
|
-
self.generic_visit(node)
|
|
33
|
+
self.results.append(node)
|
|
34
|
+
self.generic_visit(node)
|
|
43
35
|
|
|
44
36
|
|
|
45
|
-
|
|
46
|
-
|
|
37
|
+
@cache
|
|
38
|
+
def get_functions(tree: ast.Module) -> list[ast.FunctionDef | ast.Lambda]:
|
|
39
|
+
visitor = FindFunction(0)
|
|
47
40
|
visitor.visit(tree)
|
|
48
|
-
return visitor.
|
|
41
|
+
return visitor.results
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def find_function(tree: ast.Module, line: int):
|
|
45
|
+
for node in get_functions(tree):
|
|
46
|
+
if node.lineno == line or (
|
|
47
|
+
isinstance(node, ast.FunctionDef)
|
|
48
|
+
and node.decorator_list
|
|
49
|
+
and (node.decorator_list[-1].end_lineno <= line <= node.lineno)
|
|
50
|
+
):
|
|
51
|
+
return node
|
|
52
|
+
raise ValueError("Function not found")
|
|
49
53
|
|
|
50
54
|
|
|
51
55
|
class ScanWrites(ast.NodeVisitor):
|
sonolus/backend/visitor.py
CHANGED
|
@@ -261,7 +261,21 @@ class Visitor(ast.NodeVisitor):
|
|
|
261
261
|
result = self.visit(body)
|
|
262
262
|
ctx().scope.set_value("$return", result)
|
|
263
263
|
case ast.GeneratorExp(elt=elt, generators=generators):
|
|
264
|
-
|
|
264
|
+
first_generator = generators[0]
|
|
265
|
+
iterable = self.visit(first_generator.iter)
|
|
266
|
+
if isinstance(iterable, TupleImpl):
|
|
267
|
+
initial_iterator = iterable
|
|
268
|
+
else:
|
|
269
|
+
if not hasattr(iterable, "__iter__"):
|
|
270
|
+
raise TypeError(f"Object of type '{type(iterable).__name__}' is not iterable")
|
|
271
|
+
initial_iterator = self.handle_call(first_generator.iter, iterable.__iter__)
|
|
272
|
+
if not isinstance(initial_iterator, SonolusIterator):
|
|
273
|
+
raise ValueError("Unsupported iterator")
|
|
274
|
+
# The initial iterator is evaluated eagerly in Python
|
|
275
|
+
before_ctx = ctx().branch_with_scope(None, before_ctx.scope.copy())
|
|
276
|
+
start_ctx = before_ctx.branch_with_scope(None, Scope())
|
|
277
|
+
set_ctx(start_ctx)
|
|
278
|
+
self.construct_genexpr(generators, elt, initial_iterator)
|
|
265
279
|
ctx().scope.set_value("$return", validate_value(None))
|
|
266
280
|
case _:
|
|
267
281
|
raise NotImplementedError("Unsupported syntax")
|
|
@@ -323,7 +337,9 @@ class Visitor(ast.NodeVisitor):
|
|
|
323
337
|
set_ctx(after_ctx.branch_with_scope(None, before_ctx.scope.copy()))
|
|
324
338
|
return result_binding.value
|
|
325
339
|
|
|
326
|
-
def construct_genexpr(
|
|
340
|
+
def construct_genexpr(
|
|
341
|
+
self, generators: Iterable[ast.comprehension], elt: ast.expr, initial_iterator: Value | None = None
|
|
342
|
+
):
|
|
327
343
|
if not generators:
|
|
328
344
|
# Note that there may effectively be multiple yields in an expression since
|
|
329
345
|
# tuples are unrolled.
|
|
@@ -335,14 +351,22 @@ class Visitor(ast.NodeVisitor):
|
|
|
335
351
|
set_ctx(resume_ctx)
|
|
336
352
|
return
|
|
337
353
|
generator, *others = generators
|
|
338
|
-
|
|
354
|
+
if initial_iterator is not None:
|
|
355
|
+
iterable = initial_iterator
|
|
356
|
+
else:
|
|
357
|
+
iterable = self.visit(generator.iter)
|
|
339
358
|
if isinstance(iterable, TupleImpl):
|
|
340
359
|
for value in iterable.value:
|
|
341
360
|
set_ctx(ctx().branch(None))
|
|
342
361
|
self.handle_assign(generator.target, validate_value(value))
|
|
343
362
|
self.construct_genexpr(others, elt)
|
|
344
363
|
else:
|
|
345
|
-
|
|
364
|
+
if initial_iterator is not None:
|
|
365
|
+
iterator = initial_iterator
|
|
366
|
+
else:
|
|
367
|
+
if not hasattr(iterable, "__iter__"):
|
|
368
|
+
raise TypeError(f"Object of type '{type(iterable).__name__}' is not iterable")
|
|
369
|
+
iterator = self.handle_call(generator.iter, iterable.__iter__)
|
|
346
370
|
if not isinstance(iterator, SonolusIterator):
|
|
347
371
|
raise ValueError("Unsupported iterator")
|
|
348
372
|
header_ctx = ctx().branch(None)
|
|
@@ -493,6 +517,8 @@ class Visitor(ast.NodeVisitor):
|
|
|
493
517
|
if break_ctxs:
|
|
494
518
|
set_ctx(Context.meet([*break_ctxs, ctx()]))
|
|
495
519
|
return
|
|
520
|
+
if not hasattr(iterable, "__iter__"):
|
|
521
|
+
raise TypeError(f"Object of type '{type(iterable).__name__}' is not iterable")
|
|
496
522
|
iterator = self.handle_call(node, iterable.__iter__)
|
|
497
523
|
if not isinstance(iterator, SonolusIterator):
|
|
498
524
|
raise ValueError("Unsupported iterator")
|
|
@@ -980,6 +1006,8 @@ class Visitor(ast.NodeVisitor):
|
|
|
980
1006
|
self.resume_ctxs.append(resume_ctx)
|
|
981
1007
|
set_ctx(resume_ctx)
|
|
982
1008
|
return validate_value(None)
|
|
1009
|
+
if not hasattr(value, "__iter__"):
|
|
1010
|
+
raise TypeError(f"Object of type '{type(value).__name__}' is not iterable")
|
|
983
1011
|
iterator = self.handle_call(node, value.__iter__)
|
|
984
1012
|
if not isinstance(iterator, SonolusIterator):
|
|
985
1013
|
raise ValueError("Expected a SonolusIterator")
|
sonolus/script/containers.py
CHANGED
|
@@ -337,7 +337,7 @@ class ArrayPointer[T](Record, ArrayLike[T]):
|
|
|
337
337
|
raise TypeError("ArrayPointer values cannot be accessed outside of a context")
|
|
338
338
|
return _deref(
|
|
339
339
|
# Allows a compile time constant block so we can warn based on callback read/write access
|
|
340
|
-
(self.
|
|
340
|
+
(self._value_["block"]._is_py_() and self._value_["block"]._as_py_()) or self.block,
|
|
341
341
|
self.offset + Num._accept_(item) * Num._accept_(self.element_type()._size_()),
|
|
342
342
|
self.element_type(),
|
|
343
343
|
)
|
sonolus/script/iterator.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from collections.abc import Iterator
|
|
3
4
|
from typing import Any
|
|
4
5
|
|
|
6
|
+
from sonolus.script.internal.context import ctx
|
|
5
7
|
from sonolus.script.internal.impl import meta_fn
|
|
6
8
|
from sonolus.script.maybe import Maybe, Nothing, Some
|
|
7
9
|
from sonolus.script.record import Record
|
|
@@ -114,3 +116,16 @@ class _FilteringIterator[T, Fn](Record, SonolusIterator):
|
|
|
114
116
|
inside = value.get_unsafe()
|
|
115
117
|
if self.fn(inside):
|
|
116
118
|
return Some(inside)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@meta_fn
|
|
122
|
+
def maybe_next[T](iterator: Iterator[T]) -> Maybe[T]:
|
|
123
|
+
"""Get the next item from an iterator as a `Maybe` if it exists or `Nothing` otherwise."""
|
|
124
|
+
from sonolus.backend.visitor import compile_and_call
|
|
125
|
+
|
|
126
|
+
if not isinstance(iterator, SonolusIterator):
|
|
127
|
+
raise TypeError("Iterator must be an instance of SonolusIterator.")
|
|
128
|
+
if ctx():
|
|
129
|
+
return compile_and_call(iterator.next)
|
|
130
|
+
else:
|
|
131
|
+
return iterator.next()
|
sonolus/script/level.py
CHANGED
|
@@ -145,7 +145,7 @@ class Level:
|
|
|
145
145
|
)
|
|
146
146
|
|
|
147
147
|
|
|
148
|
-
type EntityListArg = list[PlayArchetype |
|
|
148
|
+
type EntityListArg = list[list[PlayArchetype] | PlayArchetype] | PlayArchetype
|
|
149
149
|
|
|
150
150
|
|
|
151
151
|
def flatten_entities(entities: EntityListArg) -> Iterator[PlayArchetype]:
|
|
@@ -175,7 +175,7 @@ class LevelData:
|
|
|
175
175
|
bgm_offset: float
|
|
176
176
|
entities: list[PlayArchetype]
|
|
177
177
|
|
|
178
|
-
def __init__(self, bgm_offset: float, entities:
|
|
178
|
+
def __init__(self, bgm_offset: float, entities: EntityListArg) -> None:
|
|
179
179
|
self.bgm_offset = bgm_offset
|
|
180
180
|
self.entities = [*flatten_entities(entities)]
|
|
181
181
|
|
sonolus/script/maybe.py
CHANGED
|
@@ -4,10 +4,11 @@ from collections.abc import Callable
|
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
6
|
from sonolus.script.internal.context import ctx
|
|
7
|
-
from sonolus.script.internal.impl import meta_fn
|
|
7
|
+
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
8
8
|
from sonolus.script.internal.transient import TransientValue
|
|
9
9
|
from sonolus.script.internal.value import Value
|
|
10
10
|
from sonolus.script.num import Num
|
|
11
|
+
from sonolus.script.values import copy, zeros
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class Maybe[T](TransientValue):
|
|
@@ -38,13 +39,16 @@ class Maybe[T](TransientValue):
|
|
|
38
39
|
|
|
39
40
|
def __init__(self, *, present: bool, value: T):
|
|
40
41
|
self._present = Num._accept_(present)
|
|
41
|
-
self._value = value
|
|
42
|
+
self._value = validate_value(value)
|
|
42
43
|
|
|
43
44
|
@property
|
|
44
45
|
@meta_fn
|
|
45
46
|
def is_some(self) -> bool:
|
|
46
47
|
"""Check if the value is present."""
|
|
47
48
|
if ctx():
|
|
49
|
+
if self._present._is_py_():
|
|
50
|
+
# Makes this a compile time constant.
|
|
51
|
+
return self._present
|
|
48
52
|
return self._present._get_readonly_()
|
|
49
53
|
else:
|
|
50
54
|
return self._present._as_py_()
|
|
@@ -61,13 +65,104 @@ class Maybe[T](TransientValue):
|
|
|
61
65
|
|
|
62
66
|
@meta_fn
|
|
63
67
|
def get_unsafe(self) -> T:
|
|
64
|
-
|
|
68
|
+
if ctx():
|
|
69
|
+
return self._value
|
|
70
|
+
else:
|
|
71
|
+
return self._value._as_py_()
|
|
65
72
|
|
|
66
73
|
def map[R](self, fn: Callable[[T], R], /) -> Maybe[R]:
|
|
74
|
+
"""Map the contained value to a new value using the provided function.
|
|
75
|
+
|
|
76
|
+
If the value is not present, returns `Nothing`.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
fn: A function that takes the contained value and returns a new value.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
A `Maybe` instance containing the result of the function if the value is present, otherwise `Nothing`.
|
|
83
|
+
"""
|
|
67
84
|
if self.is_some:
|
|
68
85
|
return Some(fn(self.get_unsafe()))
|
|
69
86
|
return Nothing
|
|
70
87
|
|
|
88
|
+
def flat_map[R](self, fn: Callable[[T], Maybe[R]], /) -> Maybe[R]:
|
|
89
|
+
"""Flat map the contained value to a new `Maybe` using the provided function.
|
|
90
|
+
|
|
91
|
+
If the value is not present, returns `Nothing`.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
fn: A function that takes the contained value and returns a new `Maybe`.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
A `Maybe` instance containing the result of the function if the value is present, otherwise `Nothing`.
|
|
98
|
+
"""
|
|
99
|
+
if self.is_some:
|
|
100
|
+
return fn(self.get_unsafe())
|
|
101
|
+
return Nothing
|
|
102
|
+
|
|
103
|
+
def or_default(self, default: T) -> T:
|
|
104
|
+
"""Return a copy of the contained value if present, otherwise return a copy of the given default value.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
default: The default value to return if the contained value is not present.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
A copy of the contained value if present, otherwise a copy of the default value.
|
|
111
|
+
"""
|
|
112
|
+
result = _box(copy(default))
|
|
113
|
+
if self.is_some:
|
|
114
|
+
result.value = self.get_unsafe()
|
|
115
|
+
return result.value
|
|
116
|
+
|
|
117
|
+
@meta_fn
|
|
118
|
+
def or_else(self, fn: Callable[[], T], /) -> T:
|
|
119
|
+
"""Return a copy of the contained value if present, otherwise return a copy of the result of the given function.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
fn: A function that returns a value to use if the contained value is not present.
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
A copy of the contained value if present, otherwise a copy of the result of calling the function.
|
|
126
|
+
"""
|
|
127
|
+
from sonolus.backend.visitor import compile_and_call
|
|
128
|
+
|
|
129
|
+
if ctx():
|
|
130
|
+
if self.is_some._is_py_(): # type: ignore
|
|
131
|
+
if self.is_some._as_py_(): # type: ignore
|
|
132
|
+
return copy(self.get_unsafe())
|
|
133
|
+
else:
|
|
134
|
+
return copy(compile_and_call(fn))
|
|
135
|
+
else:
|
|
136
|
+
return compile_and_call(self._or_else, fn)
|
|
137
|
+
elif self.is_some:
|
|
138
|
+
return copy(self.get_unsafe())
|
|
139
|
+
else:
|
|
140
|
+
return copy(fn())
|
|
141
|
+
|
|
142
|
+
def _or_else(self, fn: Callable[[], T], /) -> T:
|
|
143
|
+
result = _box(zeros(self.contained_type))
|
|
144
|
+
if self.is_some:
|
|
145
|
+
result.value = self.get_unsafe()
|
|
146
|
+
else:
|
|
147
|
+
result.value = fn()
|
|
148
|
+
return result.value
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def tuple(self) -> tuple[bool, T]:
|
|
152
|
+
"""Return whether the value is present and a copy of the contained value if present as a tuple.
|
|
153
|
+
|
|
154
|
+
If the value is not present, the tuple will contain `False` and a zero initialized value of the contained type.
|
|
155
|
+
"""
|
|
156
|
+
result_value = _box(zeros(self.contained_type))
|
|
157
|
+
if self.is_some:
|
|
158
|
+
result_value.value = self.get_unsafe()
|
|
159
|
+
return self.is_some, result_value.value
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
@meta_fn
|
|
163
|
+
def contained_type(self):
|
|
164
|
+
return type(self._value)
|
|
165
|
+
|
|
71
166
|
@classmethod
|
|
72
167
|
def _accepts_(cls, value: Any) -> bool:
|
|
73
168
|
return isinstance(value, cls)
|
|
@@ -137,3 +232,10 @@ Nothing: Maybe[Any] = Maybe(present=False, value=None) # type: ignore
|
|
|
137
232
|
# Note: has to come after the definition to hide the definition in the docs.
|
|
138
233
|
Nothing: Maybe[Any]
|
|
139
234
|
"""The empty `Maybe` instance."""
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@meta_fn
|
|
238
|
+
def _box(value):
|
|
239
|
+
from sonolus.script.containers import Box
|
|
240
|
+
|
|
241
|
+
return Box(value)
|
sonolus/script/num.py
CHANGED
|
@@ -453,14 +453,14 @@ if TYPE_CHECKING:
|
|
|
453
453
|
from typing import Protocol
|
|
454
454
|
|
|
455
455
|
@runtime_checkable
|
|
456
|
-
class Num
|
|
457
|
-
def __add__(self, other:
|
|
458
|
-
def __sub__(self, other:
|
|
459
|
-
def __mul__(self, other:
|
|
460
|
-
def __truediv__(self, other:
|
|
461
|
-
def __floordiv__(self, other:
|
|
462
|
-
def __mod__(self, other:
|
|
463
|
-
def __pow__(self, other:
|
|
456
|
+
class Num(Protocol, int, bool, float):
|
|
457
|
+
def __add__(self, other: Any, /) -> Num | int | bool | float: ...
|
|
458
|
+
def __sub__(self, other: Any, /) -> Num | int | bool | float: ...
|
|
459
|
+
def __mul__(self, other: Any, /) -> Num | int | bool | float: ...
|
|
460
|
+
def __truediv__(self, other: Any, /) -> Num | int | bool | float: ...
|
|
461
|
+
def __floordiv__(self, other: Any, /) -> Num | int | bool | float: ...
|
|
462
|
+
def __mod__(self, other: Any, /) -> Num | int | bool | float: ...
|
|
463
|
+
def __pow__(self, other: Any, /) -> Num | int | bool | float: ...
|
|
464
464
|
|
|
465
465
|
def __neg__(self, /) -> Num | int | bool | float: ...
|
|
466
466
|
def __pos__(self, /) -> Num | int | bool | float: ...
|
|
@@ -468,10 +468,10 @@ if TYPE_CHECKING:
|
|
|
468
468
|
|
|
469
469
|
def __eq__(self, other: Any, /) -> bool: ...
|
|
470
470
|
def __ne__(self, other: Any, /) -> bool: ...
|
|
471
|
-
def __lt__(self, other:
|
|
472
|
-
def __le__(self, other:
|
|
473
|
-
def __gt__(self, other:
|
|
474
|
-
def __ge__(self, other:
|
|
471
|
+
def __lt__(self, other: Any, /) -> bool: ...
|
|
472
|
+
def __le__(self, other: Any, /) -> bool: ...
|
|
473
|
+
def __gt__(self, other: Any, /) -> bool: ...
|
|
474
|
+
def __ge__(self, other: Any, /) -> bool: ...
|
|
475
475
|
|
|
476
476
|
def __hash__(self, /) -> int: ...
|
|
477
477
|
|
sonolus/script/project.py
CHANGED
|
@@ -41,7 +41,7 @@ class Project:
|
|
|
41
41
|
self._levels = None
|
|
42
42
|
self.resources = Path(resources or "resources")
|
|
43
43
|
|
|
44
|
-
def with_levels(self, levels:
|
|
44
|
+
def with_levels(self, levels: Iterable[Level] | Callable[[], Iterable[Level]] | None) -> Project:
|
|
45
45
|
"""Create a new project with the specified levels.
|
|
46
46
|
|
|
47
47
|
Args:
|
sonolus/script/record.py
CHANGED
|
@@ -62,13 +62,13 @@ class Record(GenericValue, metaclass=RecordMeta):
|
|
|
62
62
|
```
|
|
63
63
|
"""
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
65
|
+
_value_: dict[str, Value]
|
|
66
|
+
_fields_: ClassVar[list[_RecordField] | None] = None
|
|
67
|
+
_constructor_signature_: ClassVar[inspect.Signature]
|
|
68
68
|
|
|
69
69
|
@classmethod
|
|
70
70
|
def _validate_type_args_(cls, args: tuple[Any, ...]) -> tuple[Any, ...]:
|
|
71
|
-
if cls.
|
|
71
|
+
if cls._fields_ is None:
|
|
72
72
|
raise TypeError("Base Record class cannot have type arguments")
|
|
73
73
|
return super()._validate_type_args_(args)
|
|
74
74
|
|
|
@@ -80,16 +80,16 @@ class Record(GenericValue, metaclass=RecordMeta):
|
|
|
80
80
|
if is_parameterizing:
|
|
81
81
|
fields = []
|
|
82
82
|
offset = 0
|
|
83
|
-
for generic_field in cls.
|
|
83
|
+
for generic_field in cls._fields_:
|
|
84
84
|
resolved_type = validate_and_resolve_type(generic_field.type, cls._type_vars_to_args_)
|
|
85
85
|
resolved_type = validate_concrete_type(resolved_type)
|
|
86
86
|
field = _RecordField(generic_field.name, resolved_type, generic_field.index, offset)
|
|
87
87
|
fields.append(field)
|
|
88
88
|
setattr(cls, field.name, field)
|
|
89
89
|
offset += resolved_type._size_()
|
|
90
|
-
cls.
|
|
90
|
+
cls._fields_ = fields
|
|
91
91
|
return
|
|
92
|
-
is_inheriting_from_existing_record_class = cls.
|
|
92
|
+
is_inheriting_from_existing_record_class = cls._fields_ is not None
|
|
93
93
|
if is_inheriting_from_existing_record_class and not is_parameterizing:
|
|
94
94
|
# The main reason this is disallowed is that subclasses wouldn't be substitutable for their parent classes
|
|
95
95
|
# Assignment of a subclass instance to a variable of the parent class would either be disallowed or would
|
|
@@ -124,8 +124,8 @@ class Record(GenericValue, metaclass=RecordMeta):
|
|
|
124
124
|
)
|
|
125
125
|
|
|
126
126
|
cls._parameterized_ = {}
|
|
127
|
-
cls.
|
|
128
|
-
cls.
|
|
127
|
+
cls._fields_ = fields
|
|
128
|
+
cls._constructor_signature_ = inspect.Signature(params)
|
|
129
129
|
|
|
130
130
|
_add_inplace_ops(cls)
|
|
131
131
|
|
|
@@ -139,13 +139,13 @@ class Record(GenericValue, metaclass=RecordMeta):
|
|
|
139
139
|
|
|
140
140
|
def __new__(cls, *args, **kwargs):
|
|
141
141
|
# We override __new__ to allow changing to the parameterized version
|
|
142
|
-
if cls.
|
|
142
|
+
if cls._constructor_signature_ is None:
|
|
143
143
|
raise TypeError(f"Cannot instantiate {cls.__name__}")
|
|
144
|
-
bound = cls.
|
|
144
|
+
bound = cls._constructor_signature_.bind(*args, **kwargs)
|
|
145
145
|
bound.apply_defaults()
|
|
146
146
|
values = {}
|
|
147
147
|
type_vars = {}
|
|
148
|
-
for field in cls.
|
|
148
|
+
for field in cls._fields_:
|
|
149
149
|
value = bound.arguments[field.name]
|
|
150
150
|
value = accept_and_infer_types(field.type, value, type_vars)
|
|
151
151
|
values[field.name] = value._get_()
|
|
@@ -158,7 +158,7 @@ class Record(GenericValue, metaclass=RecordMeta):
|
|
|
158
158
|
else:
|
|
159
159
|
parameterized = cls[type_args]
|
|
160
160
|
result: cls = object.__new__(parameterized) # type: ignore
|
|
161
|
-
result.
|
|
161
|
+
result._value_ = values
|
|
162
162
|
return result
|
|
163
163
|
|
|
164
164
|
def __init__(self, *args, **kwargs):
|
|
@@ -168,12 +168,12 @@ class Record(GenericValue, metaclass=RecordMeta):
|
|
|
168
168
|
@classmethod
|
|
169
169
|
def _raw(cls, **kwargs) -> Self:
|
|
170
170
|
result = object.__new__(cls)
|
|
171
|
-
result.
|
|
171
|
+
result._value_ = kwargs
|
|
172
172
|
return result
|
|
173
173
|
|
|
174
174
|
@classmethod
|
|
175
175
|
def _size_(cls) -> int:
|
|
176
|
-
return sum(field.type._size_() for field in cls.
|
|
176
|
+
return sum(field.type._size_() for field in cls._fields_)
|
|
177
177
|
|
|
178
178
|
@classmethod
|
|
179
179
|
def _is_value_type_(cls) -> bool:
|
|
@@ -182,18 +182,18 @@ class Record(GenericValue, metaclass=RecordMeta):
|
|
|
182
182
|
@classmethod
|
|
183
183
|
def _from_backing_source_(cls, source: BackingSource) -> Self:
|
|
184
184
|
result = object.__new__(cls)
|
|
185
|
-
result.
|
|
185
|
+
result._value_ = {
|
|
186
186
|
field.name: field.type._from_backing_source_(
|
|
187
187
|
lambda offset, field_offset=field.offset: source((Num(offset) + Num(field_offset)).ir()) # type: ignore
|
|
188
188
|
)
|
|
189
|
-
for field in cls.
|
|
189
|
+
for field in cls._fields_
|
|
190
190
|
}
|
|
191
191
|
return result
|
|
192
192
|
|
|
193
193
|
@classmethod
|
|
194
194
|
def _from_place_(cls, place: BlockPlace) -> Self:
|
|
195
195
|
result = object.__new__(cls)
|
|
196
|
-
result.
|
|
196
|
+
result._value_ = {field.name: field.type._from_place_(place.add_offset(field.offset)) for field in cls._fields_}
|
|
197
197
|
return result
|
|
198
198
|
|
|
199
199
|
@classmethod
|
|
@@ -207,7 +207,7 @@ class Record(GenericValue, metaclass=RecordMeta):
|
|
|
207
207
|
return value
|
|
208
208
|
|
|
209
209
|
def _is_py_(self) -> bool:
|
|
210
|
-
return all(value._is_py_() for value in self.
|
|
210
|
+
return all(value._is_py_() for value in self._value_.values())
|
|
211
211
|
|
|
212
212
|
def _as_py_(self) -> Self:
|
|
213
213
|
if not self._is_py_():
|
|
@@ -217,18 +217,18 @@ class Record(GenericValue, metaclass=RecordMeta):
|
|
|
217
217
|
@classmethod
|
|
218
218
|
def _from_list_(cls, values: Iterable[DataValue]) -> Self:
|
|
219
219
|
iterator = iter(values)
|
|
220
|
-
return cls(**{field.name: field.type._from_list_(iterator) for field in cls.
|
|
220
|
+
return cls(**{field.name: field.type._from_list_(iterator) for field in cls._fields_})
|
|
221
221
|
|
|
222
222
|
def _to_list_(self, level_refs: dict[Any, str] | None = None) -> list[DataValue | str]:
|
|
223
223
|
result = []
|
|
224
|
-
for field in self.
|
|
225
|
-
result.extend(self.
|
|
224
|
+
for field in self._fields_:
|
|
225
|
+
result.extend(self._value_[field.name]._to_list_(level_refs))
|
|
226
226
|
return result
|
|
227
227
|
|
|
228
228
|
@classmethod
|
|
229
229
|
def _flat_keys_(cls, prefix: str) -> list[str]:
|
|
230
230
|
result = []
|
|
231
|
-
for field in cls.
|
|
231
|
+
for field in cls._fields_:
|
|
232
232
|
result.extend(field.type._flat_keys_(f"{prefix}.{field.name}"))
|
|
233
233
|
return result
|
|
234
234
|
|
|
@@ -240,33 +240,33 @@ class Record(GenericValue, metaclass=RecordMeta):
|
|
|
240
240
|
|
|
241
241
|
def _copy_from_(self, value: Any):
|
|
242
242
|
value = self._accept_(value)
|
|
243
|
-
for field in self.
|
|
243
|
+
for field in self._fields_:
|
|
244
244
|
field.__set__(self, field.__get__(value))
|
|
245
245
|
|
|
246
246
|
def _copy_(self) -> Self:
|
|
247
|
-
return type(self)._raw(**{field.name: self.
|
|
247
|
+
return type(self)._raw(**{field.name: self._value_[field.name]._copy_() for field in self._fields_})
|
|
248
248
|
|
|
249
249
|
@classmethod
|
|
250
250
|
def _alloc_(cls) -> Self:
|
|
251
251
|
# Compared to using the constructor, this avoids unnecessary _get_ calls
|
|
252
252
|
result = object.__new__(cls)
|
|
253
|
-
result.
|
|
253
|
+
result._value_ = {field.name: field.type._alloc_() for field in cls._fields_}
|
|
254
254
|
return result
|
|
255
255
|
|
|
256
256
|
@classmethod
|
|
257
257
|
def _zero_(cls) -> Self:
|
|
258
258
|
result = object.__new__(cls)
|
|
259
|
-
result.
|
|
259
|
+
result._value_ = {field.name: field.type._zero_() for field in cls._fields_}
|
|
260
260
|
return result
|
|
261
261
|
|
|
262
262
|
def __str__(self):
|
|
263
263
|
return f"{self.__class__.__name__}({
|
|
264
|
-
', '.join(f'{field.name}={field.get_internal(self)}' for field in self.
|
|
264
|
+
', '.join(f'{field.name}={field.get_internal(self)}' for field in self._fields_)
|
|
265
265
|
})"
|
|
266
266
|
|
|
267
267
|
def __repr__(self):
|
|
268
268
|
return f"{self.__class__.__name__}({
|
|
269
|
-
', '.join(f'{field.name}={field.get_internal(self)!r}' for field in self.
|
|
269
|
+
', '.join(f'{field.name}={field.get_internal(self)!r}' for field in self._fields_)
|
|
270
270
|
})"
|
|
271
271
|
|
|
272
272
|
@meta_fn
|
|
@@ -274,7 +274,7 @@ class Record(GenericValue, metaclass=RecordMeta):
|
|
|
274
274
|
if not isinstance(other, type(self)):
|
|
275
275
|
return False
|
|
276
276
|
result: Num = Num._accept_(True)
|
|
277
|
-
for field in self.
|
|
277
|
+
for field in self._fields_:
|
|
278
278
|
result = result.and_(field.__get__(self) == field.__get__(other))
|
|
279
279
|
return result
|
|
280
280
|
|
|
@@ -283,12 +283,12 @@ class Record(GenericValue, metaclass=RecordMeta):
|
|
|
283
283
|
if not isinstance(other, type(self)):
|
|
284
284
|
return True
|
|
285
285
|
result: Num = Num._accept_(False)
|
|
286
|
-
for field in self.
|
|
286
|
+
for field in self._fields_:
|
|
287
287
|
result = result.or_(field.__get__(self) != field.__get__(other))
|
|
288
288
|
return result
|
|
289
289
|
|
|
290
290
|
def __hash__(self):
|
|
291
|
-
return hash(tuple(field.__get__(self) for field in self.
|
|
291
|
+
return hash(tuple(field.__get__(self) for field in self._fields_))
|
|
292
292
|
|
|
293
293
|
@meta_fn
|
|
294
294
|
def __pos__(self) -> Self:
|
|
@@ -317,12 +317,12 @@ class _RecordField(SonolusDescriptor):
|
|
|
317
317
|
self.offset = offset
|
|
318
318
|
|
|
319
319
|
def get_internal(self, instance: Record) -> Value:
|
|
320
|
-
return instance.
|
|
320
|
+
return instance._value_[self.name]
|
|
321
321
|
|
|
322
322
|
def __get__(self, instance: Record | None, owner=None):
|
|
323
323
|
if instance is None:
|
|
324
324
|
return self
|
|
325
|
-
result = instance.
|
|
325
|
+
result = instance._value_[self.name]._get_readonly_()
|
|
326
326
|
if ctx():
|
|
327
327
|
return result
|
|
328
328
|
else:
|
|
@@ -331,9 +331,9 @@ class _RecordField(SonolusDescriptor):
|
|
|
331
331
|
def __set__(self, instance: Record, value):
|
|
332
332
|
value = self.type._accept_(value)
|
|
333
333
|
if self.type._is_value_type_():
|
|
334
|
-
instance.
|
|
334
|
+
instance._value_[self.name]._set_(value)
|
|
335
335
|
else:
|
|
336
|
-
instance.
|
|
336
|
+
instance._value_[self.name]._copy_from_(value)
|
|
337
337
|
|
|
338
338
|
|
|
339
339
|
_ops_to_inplace_ops = {
|
|
@@ -10,8 +10,8 @@ sonolus/backend/mode.py,sha256=NkcPZJm8dn83LX35uP24MtQOCnfRDFZ280dHeEEfauE,613
|
|
|
10
10
|
sonolus/backend/node.py,sha256=eEzPP14jzWJp2xrZCAaPlNtokxdoqg0bSM7xQiwx1j8,1254
|
|
11
11
|
sonolus/backend/ops.py,sha256=5weB_vIxbkwCSJuzYZyKUk7vVXsSIEDJYRlvE-2ke8A,10572
|
|
12
12
|
sonolus/backend/place.py,sha256=7qwV732hZ4WP-9GNN8FQSEKssPJZELip1wLXTWfop7Y,4717
|
|
13
|
-
sonolus/backend/utils.py,sha256=
|
|
14
|
-
sonolus/backend/visitor.py,sha256=
|
|
13
|
+
sonolus/backend/utils.py,sha256=c8zje-7-lmRHW14YbVZPGbLbvN9pe3eFqTRus5i2NR8,2441
|
|
14
|
+
sonolus/backend/visitor.py,sha256=NIs3OPBhGoHjoApem2MSLxqKwhA8xvuVSxKJlG3HrMI,62160
|
|
15
15
|
sonolus/backend/optimize/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
sonolus/backend/optimize/allocate.py,sha256=CuumoMphkpQlGRNeKLHT4FBGE0XVj5pwhfNdrqiLFSs,7535
|
|
17
17
|
sonolus/backend/optimize/constant_evaluation.py,sha256=OyjlgHIT6oPKCyBtNzEpo1nWYjr-_mwYUo8FrNV4eO4,21331
|
|
@@ -38,7 +38,7 @@ sonolus/script/archetype.py,sha256=vNNX6YGi_CUQcjf0xG1Rkpz9kUd_84hEPuY1Ew9uFxA,4
|
|
|
38
38
|
sonolus/script/array.py,sha256=lnobVrrEOE6PgPasq3DovrshlDKPbX8kKUsGmJz2oK0,12360
|
|
39
39
|
sonolus/script/array_like.py,sha256=jFOfXkniTLrIK4ER6HO_tUyKn_TvwjyM4B3SDd9cUS8,9678
|
|
40
40
|
sonolus/script/bucket.py,sha256=5VU-Bh7qYifl6NcgZFCo7eAEgi3iGTX-PUTmXiPCJaQ,7535
|
|
41
|
-
sonolus/script/containers.py,sha256=
|
|
41
|
+
sonolus/script/containers.py,sha256=FDz3cJlxUdxtXe_vbmuonob1xnb_QzH66uVF--bfDu0,18669
|
|
42
42
|
sonolus/script/debug.py,sha256=_Hg1cXQJ8fBXMiwhmoPb2X9CKcQ8QO26WNa59K518og,4305
|
|
43
43
|
sonolus/script/easing.py,sha256=7zaDKIfM_whUpb4FBz1DAF4NNG2vk_nDjl8kL2Y90aU,11396
|
|
44
44
|
sonolus/script/effect.py,sha256=pqfsOhmGVDMkifumkRr9rD2trWZU6Rw-_gTApNIaz7g,5854
|
|
@@ -46,18 +46,18 @@ sonolus/script/engine.py,sha256=etI9dJsQ7V9YZICVNZg54WqpLijPxG8eTPHiV-_EiG8,1068
|
|
|
46
46
|
sonolus/script/globals.py,sha256=FOv8uiLM5U6X-yMLCXukMvLgV3rs-xVJbobST_MhLcU,9782
|
|
47
47
|
sonolus/script/instruction.py,sha256=iBjY7nCNDT3w0SBJKlix3Z-85e7eE2qKeHp6C2Nq7KA,6753
|
|
48
48
|
sonolus/script/interval.py,sha256=dj6F2wn5uP6I6_mcZn-wIREgRUQbsLzhvhzB0oEyAdU,11290
|
|
49
|
-
sonolus/script/iterator.py,sha256=
|
|
50
|
-
sonolus/script/level.py,sha256=
|
|
51
|
-
sonolus/script/maybe.py,sha256=
|
|
49
|
+
sonolus/script/iterator.py,sha256=ESZ3opzDTXmkRMxF8itp9_bywXDiufn9cRtgWAw9m-w,3601
|
|
50
|
+
sonolus/script/level.py,sha256=vnotMbdr_4-MJUsTXMbvWiw2MlMjMHme3q0XRdNFXRg,6349
|
|
51
|
+
sonolus/script/maybe.py,sha256=UfF26g6BF_KGuHYGuEeuxeGKSmIH-fYAVHi9I-adp9E,7785
|
|
52
52
|
sonolus/script/metadata.py,sha256=ttRK27eojHf3So50KQJ-8yj3udZoN1bli5iD-knaeLw,753
|
|
53
|
-
sonolus/script/num.py,sha256=
|
|
53
|
+
sonolus/script/num.py,sha256=924kWWZusW7oaWuvtQzdAMzkb4ZItWSJwNj3W9XrqZU,16041
|
|
54
54
|
sonolus/script/options.py,sha256=KlOud4QOf_lW1o6avKXbkjcMCDPkhLcEwt5PW7ZCH3s,9435
|
|
55
55
|
sonolus/script/particle.py,sha256=XczhwTJvU3dMOXXTxJI_5Mskro2LgVlNgrCSwYreO0Q,8369
|
|
56
56
|
sonolus/script/pointer.py,sha256=FoOfyD93r0G5d_2BaKfeOT9SqkOP3hq6sqtOs_Rb0c8,1511
|
|
57
57
|
sonolus/script/printing.py,sha256=mNYu9QWiacBBGZrnePZQMVwbbguoelUps9GiOK_aVRU,2096
|
|
58
|
-
sonolus/script/project.py,sha256=
|
|
58
|
+
sonolus/script/project.py,sha256=2XVUXcW49iiTfljvcFuYqtFzqhQIRvD7H7OwH1Mm98w,4009
|
|
59
59
|
sonolus/script/quad.py,sha256=XoAjaUqR60zIrC_CwheZs7HwS-DRS58yUmlj9GIjX7k,11179
|
|
60
|
-
sonolus/script/record.py,sha256
|
|
60
|
+
sonolus/script/record.py,sha256=-Ff60wBoF1v4-MJWzCNI9n5K3os6WphswZpdTBezeRs,12713
|
|
61
61
|
sonolus/script/runtime.py,sha256=rJZM_KbKmnwpjhDEpR0DrM6EMSEu46apIErWA_pfLJA,33321
|
|
62
62
|
sonolus/script/sprite.py,sha256=mMDTXckn58YR8mrx3fzdBaduQ8cn9YTtTVruXXK1ow0,16272
|
|
63
63
|
sonolus/script/stream.py,sha256=4b0AV5MRPo4wzTHmaN95jwODxPVt6WuN3QxmGccdwRU,24517
|
|
@@ -86,8 +86,8 @@ sonolus/script/internal/simulation_context.py,sha256=LGxLTvxbqBIhoe1R-SfwGajNIDw
|
|
|
86
86
|
sonolus/script/internal/transient.py,sha256=y2AWABqF1aoaP6H4_2u4MMpNioC4OsZQCtPyNI0txqo,1634
|
|
87
87
|
sonolus/script/internal/tuple_impl.py,sha256=DPNdmmRmupU8Ah4_XKq6-PdT336l4nt15_uCJKQGkkk,3587
|
|
88
88
|
sonolus/script/internal/value.py,sha256=OngrCdmY_h6mV2Zgwqhuo4eYFad0kTk6263UAxctZcY,6963
|
|
89
|
-
sonolus_py-0.4.
|
|
90
|
-
sonolus_py-0.4.
|
|
91
|
-
sonolus_py-0.4.
|
|
92
|
-
sonolus_py-0.4.
|
|
93
|
-
sonolus_py-0.4.
|
|
89
|
+
sonolus_py-0.4.1.dist-info/METADATA,sha256=WBzfeJ7FRbh942MqkM1vjS7k98pHoZ52YsseEMn9OlY,302
|
|
90
|
+
sonolus_py-0.4.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
91
|
+
sonolus_py-0.4.1.dist-info/entry_points.txt,sha256=oTYspY_b7SA8TptEMTDxh4-Aj-ZVPnYC9f1lqH6s9G4,54
|
|
92
|
+
sonolus_py-0.4.1.dist-info/licenses/LICENSE,sha256=JEKpqVhQYfEc7zg3Mj462sKbKYmO1K7WmvX1qvg9IJk,1067
|
|
93
|
+
sonolus_py-0.4.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|