sonolus.py 0.1.3__py3-none-any.whl → 0.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sonolus.py might be problematic. Click here for more details.

Files changed (90) hide show
  1. sonolus/backend/blocks.py +756 -756
  2. sonolus/backend/excepthook.py +37 -37
  3. sonolus/backend/finalize.py +77 -69
  4. sonolus/backend/interpret.py +7 -7
  5. sonolus/backend/ir.py +29 -3
  6. sonolus/backend/mode.py +24 -24
  7. sonolus/backend/node.py +40 -40
  8. sonolus/backend/ops.py +197 -197
  9. sonolus/backend/optimize/__init__.py +0 -0
  10. sonolus/backend/optimize/allocate.py +126 -0
  11. sonolus/backend/optimize/constant_evaluation.py +374 -0
  12. sonolus/backend/optimize/copy_coalesce.py +85 -0
  13. sonolus/backend/optimize/dead_code.py +185 -0
  14. sonolus/backend/optimize/dominance.py +96 -0
  15. sonolus/backend/{flow.py → optimize/flow.py} +122 -92
  16. sonolus/backend/optimize/inlining.py +137 -0
  17. sonolus/backend/optimize/liveness.py +177 -0
  18. sonolus/backend/optimize/optimize.py +44 -0
  19. sonolus/backend/optimize/passes.py +52 -0
  20. sonolus/backend/optimize/simplify.py +191 -0
  21. sonolus/backend/optimize/ssa.py +200 -0
  22. sonolus/backend/place.py +17 -25
  23. sonolus/backend/utils.py +58 -48
  24. sonolus/backend/visitor.py +1151 -882
  25. sonolus/build/cli.py +7 -1
  26. sonolus/build/compile.py +88 -90
  27. sonolus/build/engine.py +10 -5
  28. sonolus/build/level.py +24 -23
  29. sonolus/build/node.py +43 -43
  30. sonolus/script/archetype.py +438 -139
  31. sonolus/script/array.py +27 -10
  32. sonolus/script/array_like.py +297 -0
  33. sonolus/script/bucket.py +253 -191
  34. sonolus/script/containers.py +257 -51
  35. sonolus/script/debug.py +26 -10
  36. sonolus/script/easing.py +365 -0
  37. sonolus/script/effect.py +191 -131
  38. sonolus/script/engine.py +71 -4
  39. sonolus/script/globals.py +303 -269
  40. sonolus/script/instruction.py +205 -151
  41. sonolus/script/internal/__init__.py +5 -5
  42. sonolus/script/internal/builtin_impls.py +255 -144
  43. sonolus/script/{callbacks.py → internal/callbacks.py} +127 -127
  44. sonolus/script/internal/constant.py +139 -0
  45. sonolus/script/internal/context.py +26 -9
  46. sonolus/script/internal/descriptor.py +17 -17
  47. sonolus/script/internal/dict_impl.py +65 -0
  48. sonolus/script/internal/generic.py +6 -9
  49. sonolus/script/internal/impl.py +38 -13
  50. sonolus/script/internal/introspection.py +17 -14
  51. sonolus/script/internal/math_impls.py +121 -0
  52. sonolus/script/internal/native.py +40 -38
  53. sonolus/script/internal/random.py +67 -0
  54. sonolus/script/internal/range.py +81 -0
  55. sonolus/script/internal/transient.py +51 -0
  56. sonolus/script/internal/tuple_impl.py +113 -0
  57. sonolus/script/internal/value.py +3 -3
  58. sonolus/script/interval.py +338 -112
  59. sonolus/script/iterator.py +167 -214
  60. sonolus/script/level.py +24 -0
  61. sonolus/script/num.py +80 -48
  62. sonolus/script/options.py +257 -191
  63. sonolus/script/particle.py +190 -157
  64. sonolus/script/pointer.py +30 -30
  65. sonolus/script/print.py +102 -81
  66. sonolus/script/project.py +8 -0
  67. sonolus/script/quad.py +263 -0
  68. sonolus/script/record.py +47 -16
  69. sonolus/script/runtime.py +52 -1
  70. sonolus/script/sprite.py +418 -333
  71. sonolus/script/text.py +409 -407
  72. sonolus/script/timing.py +114 -42
  73. sonolus/script/transform.py +332 -48
  74. sonolus/script/ui.py +216 -160
  75. sonolus/script/values.py +6 -13
  76. sonolus/script/vec.py +196 -78
  77. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/METADATA +1 -1
  78. sonolus_py-0.1.5.dist-info/RECORD +89 -0
  79. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/WHEEL +1 -1
  80. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/licenses/LICENSE +21 -21
  81. sonolus/backend/allocate.py +0 -51
  82. sonolus/backend/optimize.py +0 -9
  83. sonolus/backend/passes.py +0 -6
  84. sonolus/backend/simplify.py +0 -30
  85. sonolus/script/comptime.py +0 -160
  86. sonolus/script/graphics.py +0 -150
  87. sonolus/script/math.py +0 -92
  88. sonolus/script/range.py +0 -58
  89. sonolus_py-0.1.3.dist-info/RECORD +0 -75
  90. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/entry_points.txt +0 -0
sonolus/script/options.py CHANGED
@@ -1,191 +1,257 @@
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
+ """Define a slider option.
102
+
103
+ Args:
104
+ name: The name of the option.
105
+ standard: Whether the option is standard.
106
+ advanced: Whether the option is advanced.
107
+ default: The default value of the option.
108
+ min: The minimum value of the option.
109
+ max: The maximum value of the option.
110
+ step: The step value of the option.
111
+ unit: The unit of the option.
112
+ scope: The scope of the option.
113
+ """
114
+ return _SliderOption(name, standard, advanced, scope, default, min, max, step, unit)
115
+
116
+
117
+ def toggle_option(
118
+ *,
119
+ name: str | None = None,
120
+ standard: bool = False,
121
+ advanced: bool = False,
122
+ default: bool,
123
+ scope: str | None = None,
124
+ ) -> Any:
125
+ """Define a toggle option.
126
+
127
+ Args:
128
+ name: The name of the option.
129
+ standard: Whether the option is standard.
130
+ advanced: Whether the option is advanced.
131
+ default: The default value of the option.
132
+ scope: The scope of the option.
133
+ """
134
+ return _ToggleOption(name, standard, advanced, scope, default)
135
+
136
+
137
+ def select_option(
138
+ *,
139
+ name: str | None = None,
140
+ standard: bool = False,
141
+ advanced: bool = False,
142
+ default: str,
143
+ values: list[str],
144
+ scope: str | None = None,
145
+ ) -> Any:
146
+ """Define a select option.
147
+
148
+ Args:
149
+ name: The name of the option.
150
+ standard: Whether the option is standard.
151
+ advanced: Whether the option is advanced.
152
+ default: The default value of the option.
153
+ values: The values of the option.
154
+ scope: The scope of the option.
155
+ """
156
+ return _SelectOption(name, standard, advanced, scope, default, values)
157
+
158
+
159
+ type Options = NewType("Options", Any)
160
+ type _OptionInfo = _SliderOption | _ToggleOption | _SelectOption
161
+
162
+
163
+ class _OptionField(SonolusDescriptor):
164
+ info: _OptionInfo
165
+ index: int
166
+
167
+ def __init__(self, info: _OptionInfo, index: int):
168
+ self.info = info
169
+ self.index = index
170
+
171
+ def __get__(self, instance, owner):
172
+ if ctx():
173
+ match ctx().global_state.mode:
174
+ case Mode.PLAY:
175
+ block = ctx().blocks.LevelOption
176
+ case Mode.WATCH:
177
+ block = ctx().blocks.LevelOption
178
+ case Mode.PREVIEW:
179
+ block = ctx().blocks.PreviewOption
180
+ case Mode.TUTORIAL:
181
+ block = None
182
+ case _:
183
+ assert_unreachable()
184
+ if block is not None:
185
+ return Num._from_place_(BlockPlace(block, self.index))
186
+ else:
187
+ return Num._accept_(self.info.default)
188
+
189
+ def __set__(self, instance, value):
190
+ raise AttributeError("Options are read-only")
191
+
192
+
193
+ @dataclass_transform()
194
+ def options[T](cls: type[T]) -> T | Options:
195
+ """Decorator to define options.
196
+
197
+ Usage:
198
+ ```python
199
+ @options
200
+ class Options:
201
+ slider_option: float = slider_option(
202
+ name='Slider Option',
203
+ standard=True,
204
+ advanced=False,
205
+ default=0.5,
206
+ min=0,
207
+ max=1,
208
+ step=0.1,
209
+ unit='unit',
210
+ scope='scope',
211
+ )
212
+ toggle_option: bool = toggle_option(
213
+ name='Toggle Option',
214
+ standard=True,
215
+ advanced=False,
216
+ default=True,
217
+ scope='scope',
218
+ )
219
+ select_option: int = select_option(
220
+ name='Select Option',
221
+ standard=True,
222
+ advanced=False,
223
+ default='value',
224
+ values=['value'],
225
+ scope='scope',
226
+ )
227
+ ```
228
+ """
229
+ if len(cls.__bases__) != 1:
230
+ raise ValueError("Options class must not inherit from any class (except object)")
231
+ instance = cls()
232
+ entries = []
233
+ for i, (name, annotation) in enumerate(get_field_specifiers(cls).items()):
234
+ if get_origin(annotation) is not Annotated:
235
+ raise TypeError(f"Invalid annotation for options: {annotation}")
236
+ annotation_type = annotation.__args__[0]
237
+ annotation_values = annotation.__metadata__
238
+ if len(annotation_values) != 1:
239
+ raise ValueError("Invalid annotation values for options")
240
+ annotation_type = validate_concrete_type(annotation_type)
241
+ if annotation_type is not Num:
242
+ raise TypeError(f"Invalid annotation type for options: {annotation_type}")
243
+ annotation_value = annotation_values[0]
244
+ if not isinstance(annotation_value, _SliderOption | _ToggleOption | _SelectOption):
245
+ raise TypeError(f"Invalid annotation value for options: {annotation_value}")
246
+ if annotation_value.name is None:
247
+ annotation_value.name = name
248
+ entries.append(annotation_value)
249
+ setattr(cls, name, _OptionField(annotation_value, i))
250
+ instance._options_ = entries
251
+ instance._is_comptime_value_ = True
252
+ return instance
253
+
254
+
255
+ @options
256
+ class EmptyOptions:
257
+ pass