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
sonolus/script/math.py
CHANGED
|
@@ -70,10 +70,38 @@ def trunc(x: float) -> float:
|
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
@native_function(Op.Round)
|
|
73
|
-
def
|
|
73
|
+
def __round(x: float) -> float:
|
|
74
74
|
return round(x)
|
|
75
75
|
|
|
76
76
|
|
|
77
|
+
def _round(x: float, n: int = 0) -> float:
|
|
78
|
+
if n == 0:
|
|
79
|
+
return __round(x)
|
|
80
|
+
return __round(x * 10**n) / 10**n
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@native_function(Op.Frac)
|
|
84
|
+
def frac(x: float) -> float:
|
|
85
|
+
return x % 1
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@native_function(Op.Log)
|
|
89
|
+
def _ln(x: float) -> float:
|
|
90
|
+
return math.log(x)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def log(x: float, base: float | None = None) -> float:
|
|
94
|
+
if base is None:
|
|
95
|
+
return _ln(x)
|
|
96
|
+
return _ln(x) / _ln(base)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@native_function(Op.Rem)
|
|
100
|
+
def _remainder(x: float, y: float) -> float:
|
|
101
|
+
# This is different from math.remainder in Python's math package, which could be confusing
|
|
102
|
+
return math.copysign(abs(x) % abs(y), x)
|
|
103
|
+
|
|
104
|
+
|
|
77
105
|
MATH_BUILTIN_IMPLS = {
|
|
78
106
|
id(math.sin): sin,
|
|
79
107
|
id(math.cos): cos,
|
|
@@ -89,4 +117,5 @@ MATH_BUILTIN_IMPLS = {
|
|
|
89
117
|
id(math.ceil): ceil,
|
|
90
118
|
id(math.trunc): trunc,
|
|
91
119
|
id(round): _round,
|
|
120
|
+
id(math.log): log,
|
|
92
121
|
}
|
sonolus/script/num.py
CHANGED
|
@@ -92,7 +92,7 @@ class _Num(Value, metaclass=NumMeta):
|
|
|
92
92
|
value = next(iter(values))
|
|
93
93
|
return Num(value)
|
|
94
94
|
|
|
95
|
-
def _to_list_(self) -> list[float | BlockPlace]:
|
|
95
|
+
def _to_list_(self, level_refs: dict[Any, int] | None = None) -> list[float | BlockPlace]:
|
|
96
96
|
return [self.data]
|
|
97
97
|
|
|
98
98
|
@classmethod
|
sonolus/script/options.py
CHANGED
|
@@ -1,191 +1,191 @@
|
|
|
1
|
-
# ruff: noqa: A002
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
4
|
-
|
|
5
|
-
from sonolus.backend.mode import Mode
|
|
6
|
-
from sonolus.backend.place import BlockPlace
|
|
7
|
-
from sonolus.script.debug import assert_unreachable
|
|
8
|
-
from sonolus.script.internal.context import ctx
|
|
9
|
-
from sonolus.script.internal.descriptor import SonolusDescriptor
|
|
10
|
-
from sonolus.script.internal.generic import validate_concrete_type
|
|
11
|
-
from sonolus.script.internal.introspection import get_field_specifiers
|
|
12
|
-
from sonolus.script.num import Num
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@dataclass
|
|
16
|
-
class SliderOption:
|
|
17
|
-
name: str | None
|
|
18
|
-
standard: bool
|
|
19
|
-
advanced: bool
|
|
20
|
-
scope: str | None
|
|
21
|
-
default: float
|
|
22
|
-
min: float
|
|
23
|
-
max: float
|
|
24
|
-
step: float
|
|
25
|
-
unit: str | None
|
|
26
|
-
|
|
27
|
-
def to_dict(self):
|
|
28
|
-
result = {
|
|
29
|
-
"type": "slider",
|
|
30
|
-
"name": self.name,
|
|
31
|
-
"standard": self.standard,
|
|
32
|
-
"advanced": self.advanced,
|
|
33
|
-
"def": self.default,
|
|
34
|
-
"min": self.min,
|
|
35
|
-
"max": self.max,
|
|
36
|
-
"step": self.step,
|
|
37
|
-
}
|
|
38
|
-
if self.scope is not None:
|
|
39
|
-
result["scope"] = self.scope
|
|
40
|
-
if self.unit is not None:
|
|
41
|
-
result["unit"] = self.unit
|
|
42
|
-
return result
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@dataclass
|
|
46
|
-
class ToggleOption:
|
|
47
|
-
name: str | None
|
|
48
|
-
standard: bool
|
|
49
|
-
advanced: bool
|
|
50
|
-
scope: str | None
|
|
51
|
-
default: bool
|
|
52
|
-
|
|
53
|
-
def to_dict(self):
|
|
54
|
-
result = {
|
|
55
|
-
"type": "toggle",
|
|
56
|
-
"name": self.name,
|
|
57
|
-
"standard": self.standard,
|
|
58
|
-
"advanced": self.advanced,
|
|
59
|
-
"def": int(self.default),
|
|
60
|
-
}
|
|
61
|
-
if self.scope is not None:
|
|
62
|
-
result["scope"] = self.scope
|
|
63
|
-
return result
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
@dataclass
|
|
67
|
-
class SelectOption:
|
|
68
|
-
name: str | None
|
|
69
|
-
standard: bool
|
|
70
|
-
advanced: bool
|
|
71
|
-
scope: str | None
|
|
72
|
-
default: str
|
|
73
|
-
values: list[str]
|
|
74
|
-
|
|
75
|
-
def to_dict(self):
|
|
76
|
-
result = {
|
|
77
|
-
"type": "select",
|
|
78
|
-
"name": self.name,
|
|
79
|
-
"standard": self.standard,
|
|
80
|
-
"advanced": self.advanced,
|
|
81
|
-
"def": self.values.index(self.default),
|
|
82
|
-
"values": self.values,
|
|
83
|
-
}
|
|
84
|
-
if self.scope is not None:
|
|
85
|
-
result["scope"] = self.scope
|
|
86
|
-
return result
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def slider_option(
|
|
90
|
-
*,
|
|
91
|
-
name: str | None = None,
|
|
92
|
-
standard: bool = False,
|
|
93
|
-
advanced: bool = False,
|
|
94
|
-
default: float,
|
|
95
|
-
min: float,
|
|
96
|
-
max: float,
|
|
97
|
-
step: float,
|
|
98
|
-
unit: str | None = None,
|
|
99
|
-
scope: str | None = None,
|
|
100
|
-
) -> Any:
|
|
101
|
-
return SliderOption(name, standard, advanced, scope, default, min, max, step, unit)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def toggle_option(
|
|
105
|
-
*,
|
|
106
|
-
name: str | None = None,
|
|
107
|
-
standard: bool = False,
|
|
108
|
-
advanced: bool = False,
|
|
109
|
-
default: bool,
|
|
110
|
-
scope: str | None = None,
|
|
111
|
-
) -> Any:
|
|
112
|
-
return ToggleOption(name, standard, advanced, scope, default)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def select_option(
|
|
116
|
-
*,
|
|
117
|
-
name: str | None = None,
|
|
118
|
-
standard: bool = False,
|
|
119
|
-
advanced: bool = False,
|
|
120
|
-
default: str,
|
|
121
|
-
values: list[str],
|
|
122
|
-
scope: str | None = None,
|
|
123
|
-
) -> Any:
|
|
124
|
-
return SelectOption(name, standard, advanced, scope, default, values)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
type Options = NewType("Options", Any)
|
|
128
|
-
type OptionInfo = SliderOption | ToggleOption | SelectOption
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
class OptionField(SonolusDescriptor):
|
|
132
|
-
info: OptionInfo
|
|
133
|
-
index: int
|
|
134
|
-
|
|
135
|
-
def __init__(self, info: OptionInfo, index: int):
|
|
136
|
-
self.info = info
|
|
137
|
-
self.index = index
|
|
138
|
-
|
|
139
|
-
def __get__(self, instance, owner):
|
|
140
|
-
if ctx():
|
|
141
|
-
match ctx().global_state.mode:
|
|
142
|
-
case Mode.PLAY:
|
|
143
|
-
block = ctx().blocks.LevelOption
|
|
144
|
-
case Mode.WATCH:
|
|
145
|
-
block = ctx().blocks.LevelOption
|
|
146
|
-
case Mode.PREVIEW:
|
|
147
|
-
block = ctx().blocks.PreviewOption
|
|
148
|
-
case Mode.TUTORIAL:
|
|
149
|
-
block = None
|
|
150
|
-
case _:
|
|
151
|
-
assert_unreachable()
|
|
152
|
-
if block is not None:
|
|
153
|
-
return Num._from_place_(BlockPlace(block, self.index))
|
|
154
|
-
else:
|
|
155
|
-
return Num._accept_(self.info.default)
|
|
156
|
-
|
|
157
|
-
def __set__(self, instance, value):
|
|
158
|
-
raise AttributeError("Options are read-only")
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
@dataclass_transform()
|
|
162
|
-
def options[T](cls: type[T]) -> T | Options:
|
|
163
|
-
if len(cls.__bases__) != 1:
|
|
164
|
-
raise ValueError("Options class must not inherit from any class (except object)")
|
|
165
|
-
instance = cls()
|
|
166
|
-
entries = []
|
|
167
|
-
for i, (name, annotation) in enumerate(get_field_specifiers(cls).items()):
|
|
168
|
-
if get_origin(annotation) is not Annotated:
|
|
169
|
-
raise TypeError(f"Invalid annotation for options: {annotation}")
|
|
170
|
-
annotation_type = annotation.__args__[0]
|
|
171
|
-
annotation_values = annotation.__metadata__
|
|
172
|
-
if len(annotation_values) != 1:
|
|
173
|
-
raise ValueError("Invalid annotation values for options")
|
|
174
|
-
annotation_type = validate_concrete_type(annotation_type)
|
|
175
|
-
if annotation_type is not Num:
|
|
176
|
-
raise TypeError(f"Invalid annotation type for options: {annotation_type}")
|
|
177
|
-
annotation_value = annotation_values[0]
|
|
178
|
-
if not isinstance(annotation_value, SliderOption | ToggleOption | SelectOption):
|
|
179
|
-
raise TypeError(f"Invalid annotation value for options: {annotation_value}")
|
|
180
|
-
if annotation_value.name is None:
|
|
181
|
-
annotation_value.name = name
|
|
182
|
-
entries.append(annotation_value)
|
|
183
|
-
setattr(cls, name, OptionField(annotation_value, i))
|
|
184
|
-
instance._options_ = entries
|
|
185
|
-
instance._is_comptime_value_ = True
|
|
186
|
-
return instance
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
@options
|
|
190
|
-
class EmptyOptions:
|
|
191
|
-
pass
|
|
1
|
+
# ruff: noqa: A002
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
4
|
+
|
|
5
|
+
from sonolus.backend.mode import Mode
|
|
6
|
+
from sonolus.backend.place import BlockPlace
|
|
7
|
+
from sonolus.script.debug import assert_unreachable
|
|
8
|
+
from sonolus.script.internal.context import ctx
|
|
9
|
+
from sonolus.script.internal.descriptor import SonolusDescriptor
|
|
10
|
+
from sonolus.script.internal.generic import validate_concrete_type
|
|
11
|
+
from sonolus.script.internal.introspection import get_field_specifiers
|
|
12
|
+
from sonolus.script.num import Num
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class SliderOption:
|
|
17
|
+
name: str | None
|
|
18
|
+
standard: bool
|
|
19
|
+
advanced: bool
|
|
20
|
+
scope: str | None
|
|
21
|
+
default: float
|
|
22
|
+
min: float
|
|
23
|
+
max: float
|
|
24
|
+
step: float
|
|
25
|
+
unit: str | None
|
|
26
|
+
|
|
27
|
+
def to_dict(self):
|
|
28
|
+
result = {
|
|
29
|
+
"type": "slider",
|
|
30
|
+
"name": self.name,
|
|
31
|
+
"standard": self.standard,
|
|
32
|
+
"advanced": self.advanced,
|
|
33
|
+
"def": self.default,
|
|
34
|
+
"min": self.min,
|
|
35
|
+
"max": self.max,
|
|
36
|
+
"step": self.step,
|
|
37
|
+
}
|
|
38
|
+
if self.scope is not None:
|
|
39
|
+
result["scope"] = self.scope
|
|
40
|
+
if self.unit is not None:
|
|
41
|
+
result["unit"] = self.unit
|
|
42
|
+
return result
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass
|
|
46
|
+
class ToggleOption:
|
|
47
|
+
name: str | None
|
|
48
|
+
standard: bool
|
|
49
|
+
advanced: bool
|
|
50
|
+
scope: str | None
|
|
51
|
+
default: bool
|
|
52
|
+
|
|
53
|
+
def to_dict(self):
|
|
54
|
+
result = {
|
|
55
|
+
"type": "toggle",
|
|
56
|
+
"name": self.name,
|
|
57
|
+
"standard": self.standard,
|
|
58
|
+
"advanced": self.advanced,
|
|
59
|
+
"def": int(self.default),
|
|
60
|
+
}
|
|
61
|
+
if self.scope is not None:
|
|
62
|
+
result["scope"] = self.scope
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass
|
|
67
|
+
class SelectOption:
|
|
68
|
+
name: str | None
|
|
69
|
+
standard: bool
|
|
70
|
+
advanced: bool
|
|
71
|
+
scope: str | None
|
|
72
|
+
default: str
|
|
73
|
+
values: list[str]
|
|
74
|
+
|
|
75
|
+
def to_dict(self):
|
|
76
|
+
result = {
|
|
77
|
+
"type": "select",
|
|
78
|
+
"name": self.name,
|
|
79
|
+
"standard": self.standard,
|
|
80
|
+
"advanced": self.advanced,
|
|
81
|
+
"def": self.values.index(self.default),
|
|
82
|
+
"values": self.values,
|
|
83
|
+
}
|
|
84
|
+
if self.scope is not None:
|
|
85
|
+
result["scope"] = self.scope
|
|
86
|
+
return result
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def slider_option(
|
|
90
|
+
*,
|
|
91
|
+
name: str | None = None,
|
|
92
|
+
standard: bool = False,
|
|
93
|
+
advanced: bool = False,
|
|
94
|
+
default: float,
|
|
95
|
+
min: float,
|
|
96
|
+
max: float,
|
|
97
|
+
step: float,
|
|
98
|
+
unit: str | None = None,
|
|
99
|
+
scope: str | None = None,
|
|
100
|
+
) -> Any:
|
|
101
|
+
return SliderOption(name, standard, advanced, scope, default, min, max, step, unit)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def toggle_option(
|
|
105
|
+
*,
|
|
106
|
+
name: str | None = None,
|
|
107
|
+
standard: bool = False,
|
|
108
|
+
advanced: bool = False,
|
|
109
|
+
default: bool,
|
|
110
|
+
scope: str | None = None,
|
|
111
|
+
) -> Any:
|
|
112
|
+
return ToggleOption(name, standard, advanced, scope, default)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def select_option(
|
|
116
|
+
*,
|
|
117
|
+
name: str | None = None,
|
|
118
|
+
standard: bool = False,
|
|
119
|
+
advanced: bool = False,
|
|
120
|
+
default: str,
|
|
121
|
+
values: list[str],
|
|
122
|
+
scope: str | None = None,
|
|
123
|
+
) -> Any:
|
|
124
|
+
return SelectOption(name, standard, advanced, scope, default, values)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
type Options = NewType("Options", Any)
|
|
128
|
+
type OptionInfo = SliderOption | ToggleOption | SelectOption
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class OptionField(SonolusDescriptor):
|
|
132
|
+
info: OptionInfo
|
|
133
|
+
index: int
|
|
134
|
+
|
|
135
|
+
def __init__(self, info: OptionInfo, index: int):
|
|
136
|
+
self.info = info
|
|
137
|
+
self.index = index
|
|
138
|
+
|
|
139
|
+
def __get__(self, instance, owner):
|
|
140
|
+
if ctx():
|
|
141
|
+
match ctx().global_state.mode:
|
|
142
|
+
case Mode.PLAY:
|
|
143
|
+
block = ctx().blocks.LevelOption
|
|
144
|
+
case Mode.WATCH:
|
|
145
|
+
block = ctx().blocks.LevelOption
|
|
146
|
+
case Mode.PREVIEW:
|
|
147
|
+
block = ctx().blocks.PreviewOption
|
|
148
|
+
case Mode.TUTORIAL:
|
|
149
|
+
block = None
|
|
150
|
+
case _:
|
|
151
|
+
assert_unreachable()
|
|
152
|
+
if block is not None:
|
|
153
|
+
return Num._from_place_(BlockPlace(block, self.index))
|
|
154
|
+
else:
|
|
155
|
+
return Num._accept_(self.info.default)
|
|
156
|
+
|
|
157
|
+
def __set__(self, instance, value):
|
|
158
|
+
raise AttributeError("Options are read-only")
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@dataclass_transform()
|
|
162
|
+
def options[T](cls: type[T]) -> T | Options:
|
|
163
|
+
if len(cls.__bases__) != 1:
|
|
164
|
+
raise ValueError("Options class must not inherit from any class (except object)")
|
|
165
|
+
instance = cls()
|
|
166
|
+
entries = []
|
|
167
|
+
for i, (name, annotation) in enumerate(get_field_specifiers(cls).items()):
|
|
168
|
+
if get_origin(annotation) is not Annotated:
|
|
169
|
+
raise TypeError(f"Invalid annotation for options: {annotation}")
|
|
170
|
+
annotation_type = annotation.__args__[0]
|
|
171
|
+
annotation_values = annotation.__metadata__
|
|
172
|
+
if len(annotation_values) != 1:
|
|
173
|
+
raise ValueError("Invalid annotation values for options")
|
|
174
|
+
annotation_type = validate_concrete_type(annotation_type)
|
|
175
|
+
if annotation_type is not Num:
|
|
176
|
+
raise TypeError(f"Invalid annotation type for options: {annotation_type}")
|
|
177
|
+
annotation_value = annotation_values[0]
|
|
178
|
+
if not isinstance(annotation_value, SliderOption | ToggleOption | SelectOption):
|
|
179
|
+
raise TypeError(f"Invalid annotation value for options: {annotation_value}")
|
|
180
|
+
if annotation_value.name is None:
|
|
181
|
+
annotation_value.name = name
|
|
182
|
+
entries.append(annotation_value)
|
|
183
|
+
setattr(cls, name, OptionField(annotation_value, i))
|
|
184
|
+
instance._options_ = entries
|
|
185
|
+
instance._is_comptime_value_ = True
|
|
186
|
+
return instance
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@options
|
|
190
|
+
class EmptyOptions:
|
|
191
|
+
pass
|