sonolus.py 0.1.2__py3-none-any.whl → 0.1.4__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/allocate.py +125 -51
- sonolus/backend/blocks.py +756 -756
- sonolus/backend/coalesce.py +85 -0
- sonolus/backend/constant_evaluation.py +374 -0
- sonolus/backend/dead_code.py +80 -0
- sonolus/backend/dominance.py +111 -0
- sonolus/backend/excepthook.py +37 -37
- sonolus/backend/finalize.py +69 -69
- sonolus/backend/flow.py +121 -92
- sonolus/backend/inlining.py +150 -0
- sonolus/backend/ir.py +5 -3
- sonolus/backend/liveness.py +173 -0
- sonolus/backend/mode.py +24 -24
- sonolus/backend/node.py +40 -40
- sonolus/backend/ops.py +197 -197
- sonolus/backend/optimize.py +37 -9
- sonolus/backend/passes.py +52 -6
- sonolus/backend/simplify.py +47 -30
- sonolus/backend/ssa.py +187 -0
- sonolus/backend/utils.py +48 -48
- sonolus/backend/visitor.py +892 -880
- sonolus/build/cli.py +7 -1
- sonolus/build/compile.py +88 -90
- sonolus/build/engine.py +55 -5
- sonolus/build/level.py +24 -23
- sonolus/build/node.py +43 -43
- sonolus/script/archetype.py +23 -6
- sonolus/script/array.py +2 -2
- sonolus/script/bucket.py +191 -191
- sonolus/script/callbacks.py +127 -115
- sonolus/script/comptime.py +1 -1
- sonolus/script/containers.py +23 -0
- sonolus/script/debug.py +19 -3
- sonolus/script/easing.py +323 -0
- sonolus/script/effect.py +131 -131
- sonolus/script/engine.py +37 -1
- sonolus/script/globals.py +269 -269
- sonolus/script/graphics.py +200 -150
- sonolus/script/instruction.py +151 -0
- sonolus/script/internal/__init__.py +5 -5
- sonolus/script/internal/builtin_impls.py +144 -144
- sonolus/script/internal/context.py +12 -4
- sonolus/script/internal/descriptor.py +17 -17
- sonolus/script/internal/introspection.py +14 -14
- sonolus/script/internal/native.py +40 -38
- sonolus/script/internal/value.py +3 -3
- sonolus/script/interval.py +120 -112
- sonolus/script/iterator.py +214 -211
- sonolus/script/math.py +30 -1
- sonolus/script/num.py +1 -1
- sonolus/script/options.py +191 -191
- sonolus/script/particle.py +157 -157
- sonolus/script/pointer.py +30 -30
- sonolus/script/{preview.py → print.py} +81 -81
- sonolus/script/random.py +14 -0
- sonolus/script/range.py +58 -58
- sonolus/script/record.py +3 -3
- sonolus/script/runtime.py +45 -6
- sonolus/script/sprite.py +333 -333
- sonolus/script/text.py +407 -407
- sonolus/script/timing.py +42 -42
- sonolus/script/transform.py +77 -23
- sonolus/script/ui.py +160 -160
- sonolus/script/vec.py +81 -72
- {sonolus_py-0.1.2.dist-info → sonolus_py-0.1.4.dist-info}/METADATA +1 -2
- sonolus_py-0.1.4.dist-info/RECORD +84 -0
- {sonolus_py-0.1.2.dist-info → sonolus_py-0.1.4.dist-info}/WHEEL +1 -1
- {sonolus_py-0.1.2.dist-info → sonolus_py-0.1.4.dist-info}/licenses/LICENSE +21 -21
- sonolus/build/defaults.py +0 -32
- sonolus/script/icon.py +0 -73
- sonolus_py-0.1.2.dist-info/RECORD +0 -76
- {sonolus_py-0.1.2.dist-info → sonolus_py-0.1.4.dist-info}/entry_points.txt +0 -0
|
@@ -1,144 +1,144 @@
|
|
|
1
|
-
from collections.abc import Iterable
|
|
2
|
-
from typing import overload
|
|
3
|
-
|
|
4
|
-
from sonolus.script.internal.context import ctx
|
|
5
|
-
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
6
|
-
from sonolus.script.iterator import ArrayLike, Enumerator, SonolusIterator
|
|
7
|
-
from sonolus.script.math import MATH_BUILTIN_IMPLS
|
|
8
|
-
from sonolus.script.num import is_num
|
|
9
|
-
from sonolus.script.range import Range
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@meta_fn
|
|
13
|
-
def _isinstance(value, type_):
|
|
14
|
-
value = validate_value(value)
|
|
15
|
-
type_ = validate_value(type_)._as_py_()
|
|
16
|
-
return validate_value(isinstance(value, type_))
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@meta_fn
|
|
20
|
-
def _len(value):
|
|
21
|
-
from sonolus.backend.visitor import compile_and_call
|
|
22
|
-
|
|
23
|
-
value = validate_value(value)
|
|
24
|
-
if not hasattr(value, "__len__"):
|
|
25
|
-
raise TypeError(f"object of type '{type(value).__name__}' has no len()")
|
|
26
|
-
return compile_and_call(value.__len__)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@meta_fn
|
|
30
|
-
def _enumerate(iterable, start=0):
|
|
31
|
-
from sonolus.backend.visitor import compile_and_call
|
|
32
|
-
|
|
33
|
-
iterable = validate_value(iterable)
|
|
34
|
-
if not hasattr(iterable, "__iter__"):
|
|
35
|
-
raise TypeError(f"'{type(iterable).__name__}' object is not iterable")
|
|
36
|
-
if isinstance(iterable, ArrayLike):
|
|
37
|
-
return compile_and_call(iterable.enumerate, start)
|
|
38
|
-
else:
|
|
39
|
-
iterator = compile_and_call(iterable.__iter__)
|
|
40
|
-
if not isinstance(iterator, SonolusIterator):
|
|
41
|
-
raise TypeError("Only subclasses of SonolusIterator are supported as iterators")
|
|
42
|
-
return Enumerator(0, start, iterator)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@meta_fn
|
|
46
|
-
def _abs(value):
|
|
47
|
-
from sonolus.backend.visitor import compile_and_call
|
|
48
|
-
|
|
49
|
-
value = validate_value(value)
|
|
50
|
-
if not hasattr(value, "__abs__"):
|
|
51
|
-
raise TypeError(f"bad operand type for abs(): '{type(value).__name__}'")
|
|
52
|
-
return compile_and_call(value.__abs__)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@overload
|
|
56
|
-
def _max[T](iterable: Iterable[T]) -> T: ...
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
@overload
|
|
60
|
-
def _max[T](a: T, b: T, *args: T) -> T: ...
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
@meta_fn
|
|
64
|
-
def _max(*args):
|
|
65
|
-
from sonolus.backend.visitor import compile_and_call
|
|
66
|
-
|
|
67
|
-
args = tuple(validate_value(arg) for arg in args)
|
|
68
|
-
if len(args) == 0:
|
|
69
|
-
raise ValueError("Expected at least one argument to max")
|
|
70
|
-
elif len(args) == 1:
|
|
71
|
-
(iterable,) = args
|
|
72
|
-
if isinstance(iterable, ArrayLike):
|
|
73
|
-
return iterable.max()
|
|
74
|
-
else:
|
|
75
|
-
raise TypeError(f"Unsupported type: {type(iterable)} for max")
|
|
76
|
-
else:
|
|
77
|
-
if not all(is_num(arg) for arg in args):
|
|
78
|
-
raise TypeError("Arguments to max must be numbers")
|
|
79
|
-
if ctx():
|
|
80
|
-
result = compile_and_call(_max2, args[0], args[1])
|
|
81
|
-
for arg in args[2:]:
|
|
82
|
-
result = compile_and_call(_max2, result, arg)
|
|
83
|
-
return result
|
|
84
|
-
else:
|
|
85
|
-
return max(arg._as_py_() for arg in args)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
def _max2(a, b):
|
|
89
|
-
if a > b:
|
|
90
|
-
return a
|
|
91
|
-
else:
|
|
92
|
-
return b
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
@overload
|
|
96
|
-
def _min[T](iterable: Iterable[T]) -> T: ...
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
@overload
|
|
100
|
-
def _min[T](a: T, b: T, *args: T) -> T: ...
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
@meta_fn
|
|
104
|
-
def _min(*args):
|
|
105
|
-
from sonolus.backend.visitor import compile_and_call
|
|
106
|
-
|
|
107
|
-
args = tuple(validate_value(arg) for arg in args)
|
|
108
|
-
if len(args) == 0:
|
|
109
|
-
raise ValueError("Expected at least one argument to min")
|
|
110
|
-
elif len(args) == 1:
|
|
111
|
-
(iterable,) = args
|
|
112
|
-
if isinstance(iterable, ArrayLike):
|
|
113
|
-
return iterable.min()
|
|
114
|
-
else:
|
|
115
|
-
raise TypeError(f"Unsupported type: {type(iterable)} for min")
|
|
116
|
-
else:
|
|
117
|
-
if not all(is_num(arg) for arg in args):
|
|
118
|
-
raise TypeError("Arguments to min must be numbers")
|
|
119
|
-
if ctx():
|
|
120
|
-
result = compile_and_call(_min2, args[0], args[1])
|
|
121
|
-
for arg in args[2:]:
|
|
122
|
-
result = compile_and_call(_min2, result, arg)
|
|
123
|
-
return result
|
|
124
|
-
else:
|
|
125
|
-
return min(arg._as_py_() for arg in args)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def _min2(a, b):
|
|
129
|
-
if a < b:
|
|
130
|
-
return a
|
|
131
|
-
else:
|
|
132
|
-
return b
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
BUILTIN_IMPLS = {
|
|
136
|
-
id(isinstance): _isinstance,
|
|
137
|
-
id(len): _len,
|
|
138
|
-
id(enumerate): _enumerate,
|
|
139
|
-
id(abs): _abs,
|
|
140
|
-
id(max): _max,
|
|
141
|
-
id(min): _min,
|
|
142
|
-
id(range): Range,
|
|
143
|
-
**MATH_BUILTIN_IMPLS,
|
|
144
|
-
}
|
|
1
|
+
from collections.abc import Iterable
|
|
2
|
+
from typing import overload
|
|
3
|
+
|
|
4
|
+
from sonolus.script.internal.context import ctx
|
|
5
|
+
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
6
|
+
from sonolus.script.iterator import ArrayLike, Enumerator, SonolusIterator
|
|
7
|
+
from sonolus.script.math import MATH_BUILTIN_IMPLS
|
|
8
|
+
from sonolus.script.num import is_num
|
|
9
|
+
from sonolus.script.range import Range
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@meta_fn
|
|
13
|
+
def _isinstance(value, type_):
|
|
14
|
+
value = validate_value(value)
|
|
15
|
+
type_ = validate_value(type_)._as_py_()
|
|
16
|
+
return validate_value(isinstance(value, type_))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@meta_fn
|
|
20
|
+
def _len(value):
|
|
21
|
+
from sonolus.backend.visitor import compile_and_call
|
|
22
|
+
|
|
23
|
+
value = validate_value(value)
|
|
24
|
+
if not hasattr(value, "__len__"):
|
|
25
|
+
raise TypeError(f"object of type '{type(value).__name__}' has no len()")
|
|
26
|
+
return compile_and_call(value.__len__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@meta_fn
|
|
30
|
+
def _enumerate(iterable, start=0):
|
|
31
|
+
from sonolus.backend.visitor import compile_and_call
|
|
32
|
+
|
|
33
|
+
iterable = validate_value(iterable)
|
|
34
|
+
if not hasattr(iterable, "__iter__"):
|
|
35
|
+
raise TypeError(f"'{type(iterable).__name__}' object is not iterable")
|
|
36
|
+
if isinstance(iterable, ArrayLike):
|
|
37
|
+
return compile_and_call(iterable.enumerate, start)
|
|
38
|
+
else:
|
|
39
|
+
iterator = compile_and_call(iterable.__iter__)
|
|
40
|
+
if not isinstance(iterator, SonolusIterator):
|
|
41
|
+
raise TypeError("Only subclasses of SonolusIterator are supported as iterators")
|
|
42
|
+
return Enumerator(0, start, iterator)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@meta_fn
|
|
46
|
+
def _abs(value):
|
|
47
|
+
from sonolus.backend.visitor import compile_and_call
|
|
48
|
+
|
|
49
|
+
value = validate_value(value)
|
|
50
|
+
if not hasattr(value, "__abs__"):
|
|
51
|
+
raise TypeError(f"bad operand type for abs(): '{type(value).__name__}'")
|
|
52
|
+
return compile_and_call(value.__abs__)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@overload
|
|
56
|
+
def _max[T](iterable: Iterable[T]) -> T: ...
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@overload
|
|
60
|
+
def _max[T](a: T, b: T, *args: T) -> T: ...
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@meta_fn
|
|
64
|
+
def _max(*args):
|
|
65
|
+
from sonolus.backend.visitor import compile_and_call
|
|
66
|
+
|
|
67
|
+
args = tuple(validate_value(arg) for arg in args)
|
|
68
|
+
if len(args) == 0:
|
|
69
|
+
raise ValueError("Expected at least one argument to max")
|
|
70
|
+
elif len(args) == 1:
|
|
71
|
+
(iterable,) = args
|
|
72
|
+
if isinstance(iterable, ArrayLike):
|
|
73
|
+
return iterable.max()
|
|
74
|
+
else:
|
|
75
|
+
raise TypeError(f"Unsupported type: {type(iterable)} for max")
|
|
76
|
+
else:
|
|
77
|
+
if not all(is_num(arg) for arg in args):
|
|
78
|
+
raise TypeError("Arguments to max must be numbers")
|
|
79
|
+
if ctx():
|
|
80
|
+
result = compile_and_call(_max2, args[0], args[1])
|
|
81
|
+
for arg in args[2:]:
|
|
82
|
+
result = compile_and_call(_max2, result, arg)
|
|
83
|
+
return result
|
|
84
|
+
else:
|
|
85
|
+
return max(arg._as_py_() for arg in args)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _max2(a, b):
|
|
89
|
+
if a > b:
|
|
90
|
+
return a
|
|
91
|
+
else:
|
|
92
|
+
return b
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@overload
|
|
96
|
+
def _min[T](iterable: Iterable[T]) -> T: ...
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@overload
|
|
100
|
+
def _min[T](a: T, b: T, *args: T) -> T: ...
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@meta_fn
|
|
104
|
+
def _min(*args):
|
|
105
|
+
from sonolus.backend.visitor import compile_and_call
|
|
106
|
+
|
|
107
|
+
args = tuple(validate_value(arg) for arg in args)
|
|
108
|
+
if len(args) == 0:
|
|
109
|
+
raise ValueError("Expected at least one argument to min")
|
|
110
|
+
elif len(args) == 1:
|
|
111
|
+
(iterable,) = args
|
|
112
|
+
if isinstance(iterable, ArrayLike):
|
|
113
|
+
return iterable.min()
|
|
114
|
+
else:
|
|
115
|
+
raise TypeError(f"Unsupported type: {type(iterable)} for min")
|
|
116
|
+
else:
|
|
117
|
+
if not all(is_num(arg) for arg in args):
|
|
118
|
+
raise TypeError("Arguments to min must be numbers")
|
|
119
|
+
if ctx():
|
|
120
|
+
result = compile_and_call(_min2, args[0], args[1])
|
|
121
|
+
for arg in args[2:]:
|
|
122
|
+
result = compile_and_call(_min2, result, arg)
|
|
123
|
+
return result
|
|
124
|
+
else:
|
|
125
|
+
return min(arg._as_py_() for arg in args)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _min2(a, b):
|
|
129
|
+
if a < b:
|
|
130
|
+
return a
|
|
131
|
+
else:
|
|
132
|
+
return b
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
BUILTIN_IMPLS = {
|
|
136
|
+
id(isinstance): _isinstance,
|
|
137
|
+
id(len): _len,
|
|
138
|
+
id(enumerate): _enumerate,
|
|
139
|
+
id(abs): _abs,
|
|
140
|
+
id(max): _max,
|
|
141
|
+
id(min): _min,
|
|
142
|
+
id(range): Range,
|
|
143
|
+
**MATH_BUILTIN_IMPLS,
|
|
144
|
+
}
|
|
@@ -164,7 +164,8 @@ class Context:
|
|
|
164
164
|
header.scope.set_value(name, target_value)
|
|
165
165
|
header.loop_variables[name] = target_value
|
|
166
166
|
else:
|
|
167
|
-
header.scope.
|
|
167
|
+
header.scope.set_value(name, value)
|
|
168
|
+
header.loop_variables[name] = value
|
|
168
169
|
return header
|
|
169
170
|
|
|
170
171
|
def branch_to_loop_header(self, header: Self):
|
|
@@ -174,9 +175,16 @@ class Context:
|
|
|
174
175
|
self.outgoing[None] = header
|
|
175
176
|
for name, target_value in header.loop_variables.items():
|
|
176
177
|
with using_ctx(self):
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
178
|
+
if type(target_value)._is_value_type_():
|
|
179
|
+
value = self.scope.get_value(name)
|
|
180
|
+
value = type(target_value)._accept_(value)
|
|
181
|
+
target_value._set_(value)
|
|
182
|
+
else:
|
|
183
|
+
value = self.scope.get_value(name)
|
|
184
|
+
if target_value is not value:
|
|
185
|
+
raise RuntimeError(
|
|
186
|
+
f"Variable '{name}' may have conflicting definitions between loop iterations"
|
|
187
|
+
)
|
|
180
188
|
|
|
181
189
|
def map_constant(self, value: Any) -> int:
|
|
182
190
|
if value not in self.const_mappings:
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
from abc import abstractmethod
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class SonolusDescriptor:
|
|
5
|
-
"""Base class for Sonolus descriptors.
|
|
6
|
-
|
|
7
|
-
The compiler checks if a descriptor is an instance of a subclass of this class,
|
|
8
|
-
so it knows that it's a supported descriptor.
|
|
9
|
-
"""
|
|
10
|
-
|
|
11
|
-
@abstractmethod
|
|
12
|
-
def __get__(self, instance, owner):
|
|
13
|
-
pass
|
|
14
|
-
|
|
15
|
-
@abstractmethod
|
|
16
|
-
def __set__(self, instance, value):
|
|
17
|
-
pass
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SonolusDescriptor:
|
|
5
|
+
"""Base class for Sonolus descriptors.
|
|
6
|
+
|
|
7
|
+
The compiler checks if a descriptor is an instance of a subclass of this class,
|
|
8
|
+
so it knows that it's a supported descriptor.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
def __get__(self, instance, owner):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def __set__(self, instance, value):
|
|
17
|
+
pass
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import inspect
|
|
2
|
-
from typing import Annotated
|
|
3
|
-
|
|
4
|
-
_missing = object()
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def get_field_specifiers(cls, *, globals=None, locals=None, eval_str=True): # noqa: A002
|
|
8
|
-
"""Like inspect.get_annotations, but also turns class attributes into Annotated."""
|
|
9
|
-
results = inspect.get_annotations(cls, globals=globals, locals=locals, eval_str=eval_str)
|
|
10
|
-
for key, value in results.items():
|
|
11
|
-
class_value = getattr(cls, key, _missing)
|
|
12
|
-
if class_value is not _missing:
|
|
13
|
-
results[key] = Annotated[value, class_value]
|
|
14
|
-
return results
|
|
1
|
+
import inspect
|
|
2
|
+
from typing import Annotated
|
|
3
|
+
|
|
4
|
+
_missing = object()
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_field_specifiers(cls, *, globals=None, locals=None, eval_str=True): # noqa: A002
|
|
8
|
+
"""Like inspect.get_annotations, but also turns class attributes into Annotated."""
|
|
9
|
+
results = inspect.get_annotations(cls, globals=globals, locals=locals, eval_str=eval_str)
|
|
10
|
+
for key, value in results.items():
|
|
11
|
+
class_value = getattr(cls, key, _missing)
|
|
12
|
+
if class_value is not _missing:
|
|
13
|
+
results[key] = Annotated[value, class_value]
|
|
14
|
+
return results
|
|
@@ -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, 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)
|
|
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, *bound_args.args)
|
|
36
|
+
return fn(*args)
|
|
37
|
+
|
|
38
|
+
return wrapper
|
|
39
|
+
|
|
40
|
+
return decorator
|
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:
|