sonolus.py 0.3.4__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/excepthook.py +30 -0
- sonolus/backend/finalize.py +15 -1
- sonolus/backend/ops.py +4 -0
- sonolus/backend/optimize/allocate.py +5 -5
- sonolus/backend/optimize/constant_evaluation.py +124 -19
- sonolus/backend/optimize/copy_coalesce.py +15 -12
- sonolus/backend/optimize/dead_code.py +7 -6
- sonolus/backend/optimize/dominance.py +2 -2
- sonolus/backend/optimize/flow.py +54 -8
- sonolus/backend/optimize/inlining.py +137 -30
- sonolus/backend/optimize/liveness.py +2 -2
- sonolus/backend/optimize/optimize.py +15 -1
- sonolus/backend/optimize/passes.py +11 -3
- sonolus/backend/optimize/simplify.py +137 -8
- sonolus/backend/optimize/ssa.py +47 -13
- sonolus/backend/place.py +5 -4
- sonolus/backend/utils.py +44 -16
- sonolus/backend/visitor.py +288 -17
- sonolus/build/cli.py +47 -19
- sonolus/build/compile.py +12 -5
- sonolus/build/engine.py +70 -1
- sonolus/build/level.py +3 -3
- sonolus/build/project.py +2 -2
- sonolus/script/archetype.py +12 -9
- sonolus/script/array.py +23 -18
- sonolus/script/array_like.py +26 -29
- sonolus/script/bucket.py +1 -1
- sonolus/script/containers.py +22 -26
- sonolus/script/debug.py +20 -43
- sonolus/script/effect.py +1 -1
- sonolus/script/globals.py +3 -3
- sonolus/script/instruction.py +2 -2
- sonolus/script/internal/builtin_impls.py +155 -28
- sonolus/script/internal/constant.py +13 -3
- sonolus/script/internal/context.py +46 -15
- sonolus/script/internal/impl.py +9 -3
- sonolus/script/internal/introspection.py +8 -1
- sonolus/script/internal/native.py +2 -2
- sonolus/script/internal/range.py +8 -11
- sonolus/script/internal/simulation_context.py +1 -1
- sonolus/script/internal/transient.py +2 -2
- sonolus/script/internal/value.py +41 -3
- sonolus/script/interval.py +13 -13
- sonolus/script/iterator.py +53 -107
- sonolus/script/level.py +2 -2
- sonolus/script/maybe.py +241 -0
- sonolus/script/num.py +29 -14
- sonolus/script/options.py +1 -1
- sonolus/script/particle.py +1 -1
- sonolus/script/project.py +24 -5
- sonolus/script/quad.py +15 -15
- sonolus/script/record.py +48 -44
- sonolus/script/runtime.py +22 -18
- sonolus/script/sprite.py +1 -1
- sonolus/script/stream.py +66 -82
- sonolus/script/transform.py +35 -34
- sonolus/script/values.py +10 -10
- sonolus/script/vec.py +21 -18
- {sonolus_py-0.3.4.dist-info → sonolus_py-0.4.1.dist-info}/METADATA +1 -1
- sonolus_py-0.4.1.dist-info/RECORD +93 -0
- sonolus_py-0.3.4.dist-info/RECORD +0 -92
- {sonolus_py-0.3.4.dist-info → sonolus_py-0.4.1.dist-info}/WHEEL +0 -0
- {sonolus_py-0.3.4.dist-info → sonolus_py-0.4.1.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.3.4.dist-info → sonolus_py-0.4.1.dist-info}/licenses/LICENSE +0 -0
sonolus/script/debug.py
CHANGED
|
@@ -4,18 +4,9 @@ from typing import Any, Literal, Never
|
|
|
4
4
|
|
|
5
5
|
from sonolus.backend.mode import Mode
|
|
6
6
|
from sonolus.backend.ops import Op
|
|
7
|
-
from sonolus.backend.optimize.constant_evaluation import SparseConditionalConstantPropagation
|
|
8
|
-
from sonolus.backend.optimize.copy_coalesce import CopyCoalesce
|
|
9
|
-
from sonolus.backend.optimize.dead_code import (
|
|
10
|
-
AdvancedDeadCodeElimination,
|
|
11
|
-
DeadCodeElimination,
|
|
12
|
-
UnreachableCodeElimination,
|
|
13
|
-
)
|
|
14
7
|
from sonolus.backend.optimize.flow import cfg_to_mermaid
|
|
15
|
-
from sonolus.backend.optimize.
|
|
16
|
-
from sonolus.backend.optimize.
|
|
17
|
-
from sonolus.backend.optimize.simplify import CoalesceFlow, NormalizeSwitch, RewriteToSwitch
|
|
18
|
-
from sonolus.backend.optimize.ssa import FromSSA, ToSSA
|
|
8
|
+
from sonolus.backend.optimize.passes import CompilerPass, OptimizerConfig, run_passes
|
|
9
|
+
from sonolus.backend.optimize.simplify import RenumberVars
|
|
19
10
|
from sonolus.script.internal.context import GlobalContextState, ReadOnlyMemory, ctx, set_ctx
|
|
20
11
|
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
21
12
|
from sonolus.script.internal.native import native_function
|
|
@@ -26,13 +17,13 @@ debug_log_callback = ContextVar[Callable[[Num], None]]("debug_log_callback")
|
|
|
26
17
|
|
|
27
18
|
|
|
28
19
|
@meta_fn
|
|
29
|
-
def error(message: str | None = None) -> Never:
|
|
20
|
+
def error(message: str | None = None) -> Never: # type: ignore
|
|
30
21
|
"""Raise an error.
|
|
31
22
|
|
|
32
23
|
This function is used to raise an error during runtime.
|
|
33
24
|
When this happens, the game will pause in debug mode. The current callback will also immediately return 0.
|
|
34
25
|
"""
|
|
35
|
-
message = validate_value(message)._as_py_()
|
|
26
|
+
message = validate_value(message)._as_py_() or "Error" # type: ignore
|
|
36
27
|
if not isinstance(message, str):
|
|
37
28
|
raise ValueError("Expected a string")
|
|
38
29
|
if ctx():
|
|
@@ -50,7 +41,7 @@ def static_error(message: str | None = None) -> Never:
|
|
|
50
41
|
This function is used to raise an error during compile-time if the compiler cannot guarantee that
|
|
51
42
|
this function will not be called during runtime.
|
|
52
43
|
"""
|
|
53
|
-
message = validate_value(message)._as_py_()
|
|
44
|
+
message = validate_value(message)._as_py_() or "Error" # type: ignore
|
|
54
45
|
if not isinstance(message, str):
|
|
55
46
|
raise ValueError("Expected a string")
|
|
56
47
|
raise RuntimeError(message)
|
|
@@ -60,7 +51,7 @@ def static_error(message: str | None = None) -> Never:
|
|
|
60
51
|
def debug_log(value: int | float | bool):
|
|
61
52
|
"""Log a value in debug mode."""
|
|
62
53
|
if debug_log_callback.get(None):
|
|
63
|
-
return debug_log_callback.get()(value)
|
|
54
|
+
return debug_log_callback.get()(value) # type: ignore
|
|
64
55
|
else:
|
|
65
56
|
return _debug_log(value)
|
|
66
57
|
|
|
@@ -91,7 +82,7 @@ def assert_false(value: int | float | bool, message: str | None = None):
|
|
|
91
82
|
|
|
92
83
|
@meta_fn
|
|
93
84
|
def assert_unreachable(message: str | None = None) -> Never:
|
|
94
|
-
message = validate_value(message)._as_py_() or "Unreachable code reached"
|
|
85
|
+
message = validate_value(message)._as_py_() or "Unreachable code reached" # type: ignore
|
|
95
86
|
raise RuntimeError(message)
|
|
96
87
|
|
|
97
88
|
|
|
@@ -108,43 +99,29 @@ def visualize_cfg(
|
|
|
108
99
|
/,
|
|
109
100
|
*,
|
|
110
101
|
mode: Mode = Mode.PLAY,
|
|
102
|
+
callback: str = "",
|
|
111
103
|
archetype: type | None = None,
|
|
112
|
-
archetypes: list[type] | None,
|
|
113
|
-
passes: Sequence[CompilerPass] | Literal["minimal", "
|
|
104
|
+
archetypes: list[type] | None = None,
|
|
105
|
+
passes: Sequence[CompilerPass] | Literal["minimal", "fast", "standard"] = "fast",
|
|
114
106
|
) -> str:
|
|
107
|
+
from sonolus.backend.optimize.optimize import FAST_PASSES, MINIMAL_PASSES, STANDARD_PASSES
|
|
115
108
|
from sonolus.build.compile import callback_to_cfg
|
|
116
109
|
|
|
117
110
|
match passes:
|
|
118
111
|
case "minimal":
|
|
119
112
|
passes = [
|
|
120
|
-
|
|
113
|
+
*MINIMAL_PASSES[:-1],
|
|
114
|
+
RenumberVars(),
|
|
121
115
|
]
|
|
122
|
-
case "
|
|
116
|
+
case "fast":
|
|
123
117
|
passes = [
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
AdvancedDeadCodeElimination(),
|
|
127
|
-
CoalesceFlow(),
|
|
118
|
+
*FAST_PASSES[:-1],
|
|
119
|
+
RenumberVars(),
|
|
128
120
|
]
|
|
129
121
|
case "standard":
|
|
130
122
|
passes = [
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
DeadCodeElimination(),
|
|
134
|
-
ToSSA(),
|
|
135
|
-
SparseConditionalConstantPropagation(),
|
|
136
|
-
UnreachableCodeElimination(),
|
|
137
|
-
DeadCodeElimination(),
|
|
138
|
-
CoalesceFlow(),
|
|
139
|
-
InlineVars(),
|
|
140
|
-
DeadCodeElimination(),
|
|
141
|
-
RewriteToSwitch(),
|
|
142
|
-
FromSSA(),
|
|
143
|
-
CoalesceFlow(),
|
|
144
|
-
CopyCoalesce(),
|
|
145
|
-
AdvancedDeadCodeElimination(),
|
|
146
|
-
CoalesceFlow(),
|
|
147
|
-
NormalizeSwitch(),
|
|
123
|
+
*STANDARD_PASSES[:-1],
|
|
124
|
+
RenumberVars(),
|
|
148
125
|
]
|
|
149
126
|
|
|
150
127
|
global_state = GlobalContextState(
|
|
@@ -153,8 +130,8 @@ def visualize_cfg(
|
|
|
153
130
|
ReadOnlyMemory(),
|
|
154
131
|
)
|
|
155
132
|
|
|
156
|
-
cfg = callback_to_cfg(global_state, fn,
|
|
157
|
-
cfg = run_passes(cfg, passes)
|
|
133
|
+
cfg = callback_to_cfg(global_state, fn, callback, archetype=archetype) # type: ignore
|
|
134
|
+
cfg = run_passes(cfg, passes, OptimizerConfig(mode=mode))
|
|
158
135
|
return cfg_to_mermaid(cfg)
|
|
159
136
|
|
|
160
137
|
|
sonolus/script/effect.py
CHANGED
sonolus/script/globals.py
CHANGED
|
@@ -39,7 +39,7 @@ class _GlobalField(SonolusDescriptor):
|
|
|
39
39
|
if not ctx():
|
|
40
40
|
raise RuntimeError("Global field access outside of compilation")
|
|
41
41
|
base = ctx().get_global_base(info)
|
|
42
|
-
return self.type._from_place_(base.add_offset(self.offset)).
|
|
42
|
+
return self.type._from_place_(base.add_offset(self.offset))._get_readonly_()
|
|
43
43
|
|
|
44
44
|
def __set__(self, instance, value):
|
|
45
45
|
from sonolus.script.internal.context import ctx
|
|
@@ -96,8 +96,8 @@ def _create_global(cls: type, blocks: dict[Mode, Block], offset: int | None):
|
|
|
96
96
|
type_ = validate_concrete_type(annotation)
|
|
97
97
|
setattr(cls, name, _GlobalField(name, type_, i, field_offset))
|
|
98
98
|
field_offset += type_._size_()
|
|
99
|
-
cls._global_info_ = _GlobalInfo(cls.__name__, field_offset, blocks, offset)
|
|
100
|
-
cls._is_comptime_value_ = True
|
|
99
|
+
cls._global_info_ = _GlobalInfo(cls.__name__, field_offset, blocks, offset) # type: ignore
|
|
100
|
+
cls._is_comptime_value_ = True # type: ignore
|
|
101
101
|
return cls()
|
|
102
102
|
|
|
103
103
|
|
sonolus/script/instruction.py
CHANGED
|
@@ -70,8 +70,8 @@ def instruction_icon(name: str) -> Any:
|
|
|
70
70
|
return _InstructionIconInfo(name=name)
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
type TutorialInstructions = NewType("TutorialInstructions", Any)
|
|
74
|
-
type TutorialInstructionIcons = NewType("TutorialInstructionIcons", Any)
|
|
73
|
+
type TutorialInstructions = NewType("TutorialInstructions", Any) # type: ignore
|
|
74
|
+
type TutorialInstructionIcons = NewType("TutorialInstructionIcons", Any) # type: ignore
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
@dataclass_transform()
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
from
|
|
2
|
-
from typing import overload
|
|
3
|
-
|
|
1
|
+
from sonolus.backend.ops import Op
|
|
4
2
|
from sonolus.script.array import Array
|
|
5
3
|
from sonolus.script.array_like import ArrayLike
|
|
4
|
+
from sonolus.script.debug import error
|
|
6
5
|
from sonolus.script.internal.context import ctx
|
|
7
6
|
from sonolus.script.internal.dict_impl import DictImpl
|
|
8
7
|
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
9
8
|
from sonolus.script.internal.math_impls import MATH_BUILTIN_IMPLS, _trunc
|
|
9
|
+
from sonolus.script.internal.native import native_function
|
|
10
10
|
from sonolus.script.internal.random import RANDOM_BUILTIN_IMPLS
|
|
11
11
|
from sonolus.script.internal.range import Range
|
|
12
12
|
from sonolus.script.internal.tuple_impl import TupleImpl
|
|
@@ -21,6 +21,8 @@ from sonolus.script.iterator import (
|
|
|
21
21
|
)
|
|
22
22
|
from sonolus.script.num import Num, _is_num
|
|
23
23
|
|
|
24
|
+
_empty = object()
|
|
25
|
+
|
|
24
26
|
|
|
25
27
|
@meta_fn
|
|
26
28
|
def _isinstance(value, type_):
|
|
@@ -44,7 +46,7 @@ def _len(value):
|
|
|
44
46
|
value = validate_value(value)
|
|
45
47
|
if not hasattr(value, "__len__"):
|
|
46
48
|
raise TypeError(f"object of type '{type(value).__name__}' has no len()")
|
|
47
|
-
return compile_and_call(value.__len__)
|
|
49
|
+
return compile_and_call(value.__len__) # type: ignore
|
|
48
50
|
|
|
49
51
|
|
|
50
52
|
@meta_fn
|
|
@@ -59,7 +61,7 @@ def _enumerate(iterable, start=0):
|
|
|
59
61
|
elif isinstance(iterable, ArrayLike):
|
|
60
62
|
return compile_and_call(iterable._enumerate_, start)
|
|
61
63
|
else:
|
|
62
|
-
iterator = compile_and_call(iterable.__iter__)
|
|
64
|
+
iterator = compile_and_call(iterable.__iter__) # type: ignore
|
|
63
65
|
if not isinstance(iterator, SonolusIterator):
|
|
64
66
|
raise TypeError("Only subclasses of SonolusIterator are supported as iterators")
|
|
65
67
|
return _Enumerator(0, start, iterator)
|
|
@@ -104,25 +106,20 @@ def _abs(value):
|
|
|
104
106
|
value = validate_value(value)
|
|
105
107
|
if not hasattr(value, "__abs__"):
|
|
106
108
|
raise TypeError(f"bad operand type for abs(): '{type(value).__name__}'")
|
|
107
|
-
return compile_and_call(value.__abs__)
|
|
109
|
+
return compile_and_call(value.__abs__) # type: ignore
|
|
108
110
|
|
|
109
111
|
|
|
110
112
|
def _identity(value):
|
|
111
113
|
return value
|
|
112
114
|
|
|
113
115
|
|
|
114
|
-
@overload
|
|
115
|
-
def _max[T](iterable: Iterable[T], *, key: callable = ...) -> T: ...
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
@overload
|
|
119
|
-
def _max[T](a: T, b: T, *args: T, key: callable = ...) -> T: ...
|
|
120
|
-
|
|
121
|
-
|
|
122
116
|
@meta_fn
|
|
123
|
-
def _max(*args, key
|
|
117
|
+
def _max(*args, default=_empty, key=None):
|
|
124
118
|
from sonolus.backend.visitor import compile_and_call
|
|
125
119
|
|
|
120
|
+
if key is None:
|
|
121
|
+
key = _identity
|
|
122
|
+
|
|
126
123
|
args = tuple(validate_value(arg) for arg in args)
|
|
127
124
|
if len(args) == 0:
|
|
128
125
|
raise ValueError("Expected at least one argument to max")
|
|
@@ -131,40 +128,91 @@ def _max(*args, key: callable = _identity):
|
|
|
131
128
|
if isinstance(iterable, ArrayLike):
|
|
132
129
|
return compile_and_call(iterable._max_, key=key)
|
|
133
130
|
elif isinstance(iterable, TupleImpl) and all(_is_num(v) for v in iterable.value):
|
|
131
|
+
if len(iterable.value) == 0:
|
|
132
|
+
if default is not _empty:
|
|
133
|
+
return default
|
|
134
|
+
raise ValueError("max() arg is an empty sequence")
|
|
134
135
|
return compile_and_call(Array(*iterable.value)._max_, key=key)
|
|
136
|
+
elif isinstance(iterable, SonolusIterator):
|
|
137
|
+
if not (default is _empty or Num._accepts_(default)):
|
|
138
|
+
raise TypeError("default argument must be a number")
|
|
139
|
+
return compile_and_call(
|
|
140
|
+
_max_num_iterator,
|
|
141
|
+
iterable,
|
|
142
|
+
Num._accept_(default) if default is not _empty else None,
|
|
143
|
+
key=key if key is not _identity else None,
|
|
144
|
+
)
|
|
135
145
|
else:
|
|
136
146
|
raise TypeError(f"Unsupported type: {type(iterable)} for max")
|
|
137
147
|
else:
|
|
148
|
+
if default is not _empty:
|
|
149
|
+
raise TypeError("default argument is not supported for max with multiple arguments")
|
|
138
150
|
if not all(_is_num(arg) for arg in args):
|
|
139
151
|
raise TypeError("Arguments to max must be numbers")
|
|
140
152
|
if ctx():
|
|
141
|
-
result =
|
|
153
|
+
result = _max2(args[0], args[1], key=key)
|
|
142
154
|
for arg in args[2:]:
|
|
143
|
-
result =
|
|
155
|
+
result = _max2(result, arg, key=key)
|
|
144
156
|
return result
|
|
145
157
|
else:
|
|
146
158
|
return max(arg._as_py_() for arg in args)
|
|
147
159
|
|
|
148
160
|
|
|
149
161
|
def _max2(a, b, key=_identity):
|
|
150
|
-
|
|
162
|
+
from sonolus.backend.visitor import compile_and_call
|
|
163
|
+
|
|
164
|
+
a = validate_value(a)
|
|
165
|
+
b = validate_value(b)
|
|
166
|
+
if _is_num(a) and _is_num(b) and key == _identity:
|
|
167
|
+
return compile_and_call(_max2_num, a, b)
|
|
168
|
+
return compile_and_call(_max2_generic, a, b, key=key)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@native_function(Op.Max)
|
|
172
|
+
def _max2_num(a, b):
|
|
173
|
+
if a > b:
|
|
151
174
|
return a
|
|
152
175
|
else:
|
|
153
176
|
return b
|
|
154
177
|
|
|
155
178
|
|
|
156
|
-
|
|
157
|
-
|
|
179
|
+
def _max2_generic(a, b, key=_identity):
|
|
180
|
+
if key(a) > key(b):
|
|
181
|
+
return a
|
|
182
|
+
else:
|
|
183
|
+
return b
|
|
158
184
|
|
|
159
185
|
|
|
160
|
-
|
|
161
|
-
|
|
186
|
+
def _max_num_iterator(iterable, default, key):
|
|
187
|
+
iterator = iterable.__iter__() # noqa: PLC2801
|
|
188
|
+
initial = iterator.next()
|
|
189
|
+
if initial.is_nothing:
|
|
190
|
+
assert default is not None
|
|
191
|
+
return default
|
|
192
|
+
if key is not None:
|
|
193
|
+
result = initial.get_unsafe()
|
|
194
|
+
best_key = key(result)
|
|
195
|
+
for value in iterator:
|
|
196
|
+
new_key = key(value)
|
|
197
|
+
if new_key > best_key:
|
|
198
|
+
result = value
|
|
199
|
+
best_key = new_key
|
|
200
|
+
return result
|
|
201
|
+
else:
|
|
202
|
+
result = initial.get_unsafe()
|
|
203
|
+
for value in iterator:
|
|
204
|
+
if value > result: # noqa: PLR1730
|
|
205
|
+
result = value
|
|
206
|
+
return result
|
|
162
207
|
|
|
163
208
|
|
|
164
209
|
@meta_fn
|
|
165
|
-
def _min(*args, key
|
|
210
|
+
def _min(*args, default=_empty, key=None):
|
|
166
211
|
from sonolus.backend.visitor import compile_and_call
|
|
167
212
|
|
|
213
|
+
if key is None:
|
|
214
|
+
key = _identity
|
|
215
|
+
|
|
168
216
|
args = tuple(validate_value(arg) for arg in args)
|
|
169
217
|
if len(args) == 0:
|
|
170
218
|
raise ValueError("Expected at least one argument to min")
|
|
@@ -173,34 +221,92 @@ def _min(*args, key: callable = _identity):
|
|
|
173
221
|
if isinstance(iterable, ArrayLike):
|
|
174
222
|
return compile_and_call(iterable._min_, key=key)
|
|
175
223
|
elif isinstance(iterable, TupleImpl) and all(_is_num(v) for v in iterable.value):
|
|
224
|
+
if len(iterable.value) == 0:
|
|
225
|
+
if default is not _empty:
|
|
226
|
+
return default
|
|
227
|
+
raise ValueError("min() arg is an empty sequence")
|
|
176
228
|
return compile_and_call(Array(*iterable.value)._min_, key=key)
|
|
229
|
+
elif isinstance(iterable, SonolusIterator):
|
|
230
|
+
if not (default is _empty or Num._accepts_(default)):
|
|
231
|
+
raise TypeError("default argument must be a number")
|
|
232
|
+
return compile_and_call(
|
|
233
|
+
_min_num_iterator,
|
|
234
|
+
iterable,
|
|
235
|
+
Num._accept_(default) if default is not _empty else None,
|
|
236
|
+
key=key if key is not _identity else None,
|
|
237
|
+
)
|
|
177
238
|
else:
|
|
178
239
|
raise TypeError(f"Unsupported type: {type(iterable)} for min")
|
|
179
240
|
else:
|
|
241
|
+
if default is not _empty:
|
|
242
|
+
raise TypeError("default argument is not supported for min with multiple arguments")
|
|
180
243
|
if not all(_is_num(arg) for arg in args):
|
|
181
244
|
raise TypeError("Arguments to min must be numbers")
|
|
182
245
|
if ctx():
|
|
183
|
-
result =
|
|
246
|
+
result = _min2(args[0], args[1], key=key)
|
|
184
247
|
for arg in args[2:]:
|
|
185
|
-
result =
|
|
248
|
+
result = _min2(result, arg, key=key)
|
|
186
249
|
return result
|
|
187
250
|
else:
|
|
188
251
|
return min(arg._as_py_() for arg in args)
|
|
189
252
|
|
|
190
253
|
|
|
191
254
|
def _min2(a, b, key=_identity):
|
|
255
|
+
from sonolus.backend.visitor import compile_and_call
|
|
256
|
+
|
|
257
|
+
a = validate_value(a)
|
|
258
|
+
b = validate_value(b)
|
|
259
|
+
if _is_num(a) and _is_num(b) and key == _identity:
|
|
260
|
+
return compile_and_call(_min2_num, a, b)
|
|
261
|
+
return compile_and_call(_min2_generic, a, b, key=key)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@native_function(Op.Min)
|
|
265
|
+
def _min2_num(a, b):
|
|
266
|
+
if a < b:
|
|
267
|
+
return a
|
|
268
|
+
else:
|
|
269
|
+
return b
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _min2_generic(a, b, key=_identity):
|
|
192
273
|
if key(a) < key(b):
|
|
193
274
|
return a
|
|
194
275
|
else:
|
|
195
276
|
return b
|
|
196
277
|
|
|
197
278
|
|
|
279
|
+
def _min_num_iterator(iterable, default, key):
|
|
280
|
+
iterator = iterable.__iter__() # noqa: PLC2801
|
|
281
|
+
initial = iterator.next()
|
|
282
|
+
if initial.is_nothing:
|
|
283
|
+
assert default is not None
|
|
284
|
+
return default
|
|
285
|
+
if key is not None:
|
|
286
|
+
result = initial.get_unsafe()
|
|
287
|
+
best_key = key(result)
|
|
288
|
+
for value in iterator:
|
|
289
|
+
new_key = key(value)
|
|
290
|
+
if new_key < best_key:
|
|
291
|
+
result = value
|
|
292
|
+
best_key = new_key
|
|
293
|
+
return result
|
|
294
|
+
else:
|
|
295
|
+
result = initial.get_unsafe()
|
|
296
|
+
for value in iterator:
|
|
297
|
+
if value < result: # noqa: PLR1730
|
|
298
|
+
result = value
|
|
299
|
+
return result
|
|
300
|
+
|
|
301
|
+
|
|
198
302
|
@meta_fn
|
|
199
303
|
def _callable(value):
|
|
200
304
|
return callable(value)
|
|
201
305
|
|
|
202
306
|
|
|
203
307
|
def _map(fn, iterable, *iterables):
|
|
308
|
+
if len(iterables) == 0:
|
|
309
|
+
return _MappingIterator(fn, iterable.__iter__()) # noqa: PLC2801
|
|
204
310
|
return _MappingIterator(lambda args: fn(*args), zip(iterable, *iterables)) # noqa: B905
|
|
205
311
|
|
|
206
312
|
|
|
@@ -234,9 +340,9 @@ def _bool(value=False):
|
|
|
234
340
|
return False
|
|
235
341
|
|
|
236
342
|
|
|
237
|
-
_int._type_mapping_ = Num
|
|
238
|
-
_float._type_mapping_ = Num
|
|
239
|
-
_bool._type_mapping_ = Num
|
|
343
|
+
_int._type_mapping_ = Num # type: ignore
|
|
344
|
+
_float._type_mapping_ = Num # type: ignore
|
|
345
|
+
_bool._type_mapping_ = Num # type: ignore
|
|
240
346
|
|
|
241
347
|
|
|
242
348
|
def _any(iterable):
|
|
@@ -253,12 +359,31 @@ def _all(iterable):
|
|
|
253
359
|
return True
|
|
254
360
|
|
|
255
361
|
|
|
362
|
+
def _sum(iterable, /, start=0):
|
|
363
|
+
for value in iterable:
|
|
364
|
+
start += value
|
|
365
|
+
return start
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def _next(iterator):
|
|
369
|
+
assert isinstance(iterator, SonolusIterator)
|
|
370
|
+
value = iterator.next()
|
|
371
|
+
if value.is_some:
|
|
372
|
+
return value.get_unsafe()
|
|
373
|
+
error("Iterator has been exhausted")
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def _iter(iterable):
|
|
377
|
+
return iterable.__iter__() # type: ignore # noqa: PLC2801
|
|
378
|
+
|
|
379
|
+
|
|
256
380
|
# classmethod, property, staticmethod are supported as decorators, but not within functions
|
|
257
381
|
|
|
258
382
|
BUILTIN_IMPLS = {
|
|
259
383
|
id(abs): _abs,
|
|
260
384
|
id(all): _all,
|
|
261
385
|
id(any): _any,
|
|
386
|
+
id(sum): _sum,
|
|
262
387
|
id(bool): _bool,
|
|
263
388
|
id(callable): _callable,
|
|
264
389
|
id(enumerate): _enumerate,
|
|
@@ -266,10 +391,12 @@ BUILTIN_IMPLS = {
|
|
|
266
391
|
id(float): _float,
|
|
267
392
|
id(int): _int,
|
|
268
393
|
id(isinstance): _isinstance,
|
|
394
|
+
id(iter): _iter,
|
|
269
395
|
id(len): _len,
|
|
270
396
|
id(map): _map,
|
|
271
397
|
id(max): _max,
|
|
272
398
|
id(min): _min,
|
|
399
|
+
id(next): _next,
|
|
273
400
|
id(range): Range,
|
|
274
401
|
id(reversed): _reversed,
|
|
275
402
|
id(zip): _zip,
|
|
@@ -48,8 +48,8 @@ class ConstantValue(Value):
|
|
|
48
48
|
class Parameterized(cls):
|
|
49
49
|
_value = (parameter,)
|
|
50
50
|
|
|
51
|
-
Parameterized.__name__ = f"{parameter}"
|
|
52
|
-
Parameterized.__qualname__ =
|
|
51
|
+
Parameterized.__name__ = f"Const[{object.__repr__(parameter)}]" # noqa: PLC2801
|
|
52
|
+
Parameterized.__qualname__ = Parameterized.__name__
|
|
53
53
|
Parameterized.__module__ = cls.__module__
|
|
54
54
|
Parameterized.instance = object.__new__(Parameterized)
|
|
55
55
|
return Parameterized
|
|
@@ -113,7 +113,7 @@ class ConstantValue(Value):
|
|
|
113
113
|
if value is not self:
|
|
114
114
|
raise ValueError(f"{type(self).__name__} is immutable")
|
|
115
115
|
|
|
116
|
-
def _copy_from_(self, value:
|
|
116
|
+
def _copy_from_(self, value: Any):
|
|
117
117
|
if value is not self:
|
|
118
118
|
raise ValueError(f"{type(self).__name__} is immutable")
|
|
119
119
|
|
|
@@ -143,3 +143,13 @@ class ConstantValue(Value):
|
|
|
143
143
|
|
|
144
144
|
class BasicConstantValue(ConstantValue):
|
|
145
145
|
"""For constants without any special behavior."""
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class TypingSpecialFormConstant(ConstantValue):
|
|
149
|
+
"""For constants that are typing special forms that have a [] operator."""
|
|
150
|
+
|
|
151
|
+
@meta_fn
|
|
152
|
+
def __getitem__(self, item: Any) -> Self:
|
|
153
|
+
if not item._is_py_():
|
|
154
|
+
raise TypeError(f"Invalid value for type parameter: {item}")
|
|
155
|
+
return self.value()[item._as_py_()]
|