sonolus.py 0.1.9__tar.gz → 0.2.0__tar.gz
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_py-0.1.9 → sonolus_py-0.2.0}/PKG-INFO +3 -2
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/doc_stubs/builtins.pyi +22 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/concepts/resources.md +5 -1
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/concepts/types.md +14 -0
- sonolus_py-0.2.0/docs/reference/sonolus.script.metadata.md +3 -0
- sonolus_py-0.2.0/docs/reference/sonolus.script.printing.md +3 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/mkdocs.yml +2 -1
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/pyproject.toml +1 -1
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/optimize/constant_evaluation.py +2 -2
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/optimize/optimize.py +12 -4
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/optimize/passes.py +2 -1
- sonolus_py-0.2.0/sonolus/backend/place.py +163 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/visitor.py +51 -3
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/build/cli.py +60 -9
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/build/collection.py +68 -26
- sonolus_py-0.2.0/sonolus/build/compile.py +145 -0
- sonolus_py-0.2.0/sonolus/build/engine.py +346 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/build/node.py +8 -1
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/build/project.py +30 -11
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/archetype.py +110 -26
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/array.py +11 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/debug.py +2 -2
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/effect.py +2 -2
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/engine.py +123 -15
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/builtin_impls.py +21 -2
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/constant.py +5 -1
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/context.py +30 -25
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/math_impls.py +2 -1
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/transient.py +4 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/value.py +6 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/interval.py +16 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/iterator.py +17 -0
- sonolus_py-0.2.0/sonolus/script/level.py +198 -0
- sonolus_py-0.2.0/sonolus/script/metadata.py +32 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/num.py +9 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/options.py +5 -3
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/pointer.py +2 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/project.py +41 -5
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/record.py +7 -2
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/runtime.py +61 -10
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/sprite.py +18 -1
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/ui.py +7 -3
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/values.py +8 -5
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/vec.py +28 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_var_array.py +52 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/uv.lock +1 -1
- sonolus_py-0.1.9/docs/reference/sonolus.script.print.md +0 -3
- sonolus_py-0.1.9/sonolus/backend/place.py +0 -82
- sonolus_py-0.1.9/sonolus/build/compile.py +0 -87
- sonolus_py-0.1.9/sonolus/build/engine.py +0 -220
- sonolus_py-0.1.9/sonolus/script/level.py +0 -95
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/.github/workflows/publish.yaml +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/.gitignore +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/.python-version +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/LICENSE +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/README.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/doc_stubs/__init__.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/doc_stubs/math.pyi +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/doc_stubs/num.pyi +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/doc_stubs/random.pyi +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/CNAME +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/concepts/builtins.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/concepts/cli.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/concepts/constructs.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/concepts/index.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/concepts/project.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/index.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/builtins.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/index.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/math.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/random.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.archetype.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.array.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.array_like.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.bucket.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.containers.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.debug.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.easing.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.effect.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.engine.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.globals.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.instruction.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.interval.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.iterator.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.level.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.num.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.options.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.particle.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.project.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.quad.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.record.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.runtime.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.sprite.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.text.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.timing.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.transform.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.ui.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.values.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/docs/reference/sonolus.script.vec.md +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/scripts/generate.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/scripts/runtimes/Engine/Tutorial/Blocks.json +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/scripts/runtimes/Functions.json +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/scripts/runtimes/Level/Play/Blocks.json +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/scripts/runtimes/Level/Preview/Blocks.json +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/scripts/runtimes/Level/Watch/Blocks.json +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/__init__.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/__init__.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/blocks.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/excepthook.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/finalize.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/interpret.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/ir.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/mode.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/node.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/ops.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/optimize/__init__.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/optimize/allocate.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/optimize/copy_coalesce.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/optimize/dead_code.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/optimize/dominance.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/optimize/flow.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/optimize/inlining.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/optimize/liveness.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/optimize/simplify.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/optimize/ssa.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/backend/utils.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/build/__init__.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/build/level.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/py.typed +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/__init__.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/array_like.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/bucket.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/containers.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/easing.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/globals.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/instruction.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/__init__.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/callbacks.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/descriptor.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/dict_impl.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/error.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/generic.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/impl.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/introspection.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/native.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/random.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/range.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/internal/tuple_impl.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/particle.py +0 -0
- /sonolus_py-0.1.9/sonolus/script/print.py → /sonolus_py-0.2.0/sonolus/script/printing.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/quad.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/text.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/timing.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/sonolus/script/transform.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/__init__.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/__init__.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/conftest.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_array.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_array_map.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_assert.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_dict.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_flow.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_functions.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_helpers.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_interval.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_match.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_num.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_operator.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_quad.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_random.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_range.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_record.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_transform.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_tuple.py +0 -0
- {sonolus_py-0.1.9 → sonolus_py-0.2.0}/tests/script/test_vec.py +0 -0
|
@@ -9,6 +9,28 @@ from typing import (
|
|
|
9
9
|
overload,
|
|
10
10
|
)
|
|
11
11
|
|
|
12
|
+
def all(iterable: Iterable[builtins.bool]) -> builtins.bool:
|
|
13
|
+
"""Return True if all elements of the iterable are true.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
iterable: The iterable to evaluate.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
True if all elements are true, False otherwise.
|
|
20
|
+
"""
|
|
21
|
+
...
|
|
22
|
+
|
|
23
|
+
def any(iterable: Iterable[builtins.bool]) -> builtins.bool:
|
|
24
|
+
"""Return True if any element of the iterable is true.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
iterable: The iterable to evaluate.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
True if any element is true, False otherwise.
|
|
31
|
+
"""
|
|
32
|
+
...
|
|
33
|
+
|
|
12
34
|
def abs(x: builtins.int | builtins.float) -> builtins.int | builtins.float:
|
|
13
35
|
"""Return the absolute value of a number.
|
|
14
36
|
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
Skins are defined with the `@skin` decorator:
|
|
5
5
|
|
|
6
6
|
```python
|
|
7
|
-
from sonolus.script.sprite import skin, StandardSprite, sprite, Sprite
|
|
7
|
+
from sonolus.script.sprite import skin, StandardSprite, sprite, Sprite, RenderMode
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
@skin
|
|
11
11
|
class Skin:
|
|
12
|
+
render_mode: RenderMode = RenderMode.DEFAULT
|
|
13
|
+
|
|
12
14
|
note: StandardSprite.NOTE_HEAD_RED
|
|
13
15
|
other: Sprite = sprite("other")
|
|
14
16
|
```
|
|
@@ -17,6 +19,8 @@ Standard sprites are defined by annotating the field with the corresponding valu
|
|
|
17
19
|
|
|
18
20
|
Custom sprites are defined by annotating the field with `Sprite` and calling `skin_sprite` with the sprite name.
|
|
19
21
|
|
|
22
|
+
To set the render mode for the skin, set the `render_mode` field to the desired value from `RenderMode`.
|
|
23
|
+
|
|
20
24
|
## Sound Effects
|
|
21
25
|
Sound effects are defined with the `@effects` decorator:
|
|
22
26
|
|
|
@@ -242,6 +242,20 @@ assert not isinstance(a, Array[int, 2])
|
|
|
242
242
|
assert not isinstance(a, Array[Pair, 3])
|
|
243
243
|
```
|
|
244
244
|
|
|
245
|
+
### Enums
|
|
246
|
+
|
|
247
|
+
There is limited support for enums containing `Num` values. Methods on enums are not supported.
|
|
248
|
+
When used as a type, any enum class is treated as `Num` and no enforcement is done on the values.
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
class MyEnum(IntEnum):
|
|
252
|
+
A = 1
|
|
253
|
+
B = 2
|
|
254
|
+
|
|
255
|
+
a = Array[MyEnum, 2](MyEnum.A, MyEnum.B)
|
|
256
|
+
b = Array[MyEnum, 2](1, 2)
|
|
257
|
+
```
|
|
258
|
+
|
|
245
259
|
## Record
|
|
246
260
|
|
|
247
261
|
`Record` is the base class for user-defined types in Sonolus.py. It functions similarly to dataclasses.
|
|
@@ -96,10 +96,11 @@ nav:
|
|
|
96
96
|
- reference/sonolus.script.interval.md
|
|
97
97
|
- reference/sonolus.script.iterator.md
|
|
98
98
|
- reference/sonolus.script.level.md
|
|
99
|
+
- reference/sonolus.script.metadata.md
|
|
99
100
|
- reference/sonolus.script.num.md
|
|
100
101
|
- reference/sonolus.script.options.md
|
|
101
102
|
- reference/sonolus.script.particle.md
|
|
102
|
-
- reference/sonolus.script.
|
|
103
|
+
- reference/sonolus.script.printing.md
|
|
103
104
|
- reference/sonolus.script.project.md
|
|
104
105
|
- reference/sonolus.script.quad.md
|
|
105
106
|
- reference/sonolus.script.record.md
|
|
@@ -315,8 +315,8 @@ class SparseConditionalConstantPropagation(CompilerPass):
|
|
|
315
315
|
return 1
|
|
316
316
|
return functools.reduce(operator.pow, args)
|
|
317
317
|
case Op.Log:
|
|
318
|
-
assert len(args) ==
|
|
319
|
-
return math.log(args[0]
|
|
318
|
+
assert len(args) == 1
|
|
319
|
+
return math.log(args[0])
|
|
320
320
|
case Op.Ceil:
|
|
321
321
|
assert len(args) == 1
|
|
322
322
|
return math.ceil(args[0])
|
|
@@ -12,13 +12,21 @@ from sonolus.backend.optimize.passes import run_passes
|
|
|
12
12
|
from sonolus.backend.optimize.simplify import CoalesceFlow, NormalizeSwitch, RewriteToSwitch
|
|
13
13
|
from sonolus.backend.optimize.ssa import FromSSA, ToSSA
|
|
14
14
|
|
|
15
|
-
MINIMAL_PASSES =
|
|
15
|
+
MINIMAL_PASSES = (
|
|
16
16
|
CoalesceFlow(),
|
|
17
17
|
UnreachableCodeElimination(),
|
|
18
18
|
AllocateBasic(),
|
|
19
|
-
|
|
19
|
+
)
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
FAST_PASSES = (
|
|
22
|
+
CoalesceFlow(),
|
|
23
|
+
UnreachableCodeElimination(),
|
|
24
|
+
AdvancedDeadCodeElimination(),
|
|
25
|
+
CoalesceFlow(),
|
|
26
|
+
Allocate(),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
STANDARD_PASSES = (
|
|
22
30
|
CoalesceFlow(),
|
|
23
31
|
UnreachableCodeElimination(),
|
|
24
32
|
DeadCodeElimination(),
|
|
@@ -37,7 +45,7 @@ STANDARD_PASSES = [
|
|
|
37
45
|
CoalesceFlow(),
|
|
38
46
|
NormalizeSwitch(),
|
|
39
47
|
Allocate(),
|
|
40
|
-
|
|
48
|
+
)
|
|
41
49
|
|
|
42
50
|
|
|
43
51
|
def optimize_and_allocate(cfg: BasicBlock):
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
4
|
from collections import deque
|
|
5
|
+
from collections.abc import Sequence
|
|
5
6
|
|
|
6
7
|
from sonolus.backend.optimize.flow import BasicBlock
|
|
7
8
|
|
|
@@ -35,7 +36,7 @@ class CompilerPass(ABC):
|
|
|
35
36
|
pass
|
|
36
37
|
|
|
37
38
|
|
|
38
|
-
def run_passes(entry: BasicBlock, passes:
|
|
39
|
+
def run_passes(entry: BasicBlock, passes: Sequence[CompilerPass]) -> BasicBlock:
|
|
39
40
|
active_passes = set()
|
|
40
41
|
queue = deque(passes)
|
|
41
42
|
while queue:
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
from collections.abc import Iterator
|
|
2
|
+
from typing import Self
|
|
3
|
+
|
|
4
|
+
from sonolus.backend.blocks import Block
|
|
5
|
+
|
|
6
|
+
type Place = BlockPlace | SSAPlace
|
|
7
|
+
type BlockValue = Block | int | TempBlock | Place
|
|
8
|
+
type IndexValue = int | Place
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TempBlock:
|
|
12
|
+
__slots__ = ("__hash", "name", "size")
|
|
13
|
+
|
|
14
|
+
def __init__(self, name: str, size: int = 1):
|
|
15
|
+
self.name = name
|
|
16
|
+
self.size = size
|
|
17
|
+
self.__hash = hash(name) # Precompute hash based on name alone
|
|
18
|
+
|
|
19
|
+
def __repr__(self):
|
|
20
|
+
return f"TempBlock(name={self.name!r}, size={self.size!r})"
|
|
21
|
+
|
|
22
|
+
def __str__(self):
|
|
23
|
+
return f"{self.name}"
|
|
24
|
+
|
|
25
|
+
def __getitem__(self, item) -> "BlockPlace":
|
|
26
|
+
return BlockPlace(self, item)
|
|
27
|
+
|
|
28
|
+
def __iter__(self) -> "Iterator[BlockPlace]":
|
|
29
|
+
for i in range(self.size):
|
|
30
|
+
yield self[i]
|
|
31
|
+
|
|
32
|
+
def __eq__(self, other):
|
|
33
|
+
return isinstance(other, TempBlock) and self.name == other.name and self.size == other.size
|
|
34
|
+
|
|
35
|
+
def __lt__(self, other):
|
|
36
|
+
if not isinstance(other, TempBlock):
|
|
37
|
+
return NotImplemented
|
|
38
|
+
return str(self) < str(other)
|
|
39
|
+
|
|
40
|
+
def __le__(self, other):
|
|
41
|
+
if not isinstance(other, TempBlock):
|
|
42
|
+
return NotImplemented
|
|
43
|
+
return str(self) <= str(other)
|
|
44
|
+
|
|
45
|
+
def __gt__(self, other):
|
|
46
|
+
if not isinstance(other, TempBlock):
|
|
47
|
+
return NotImplemented
|
|
48
|
+
return str(self) > str(other)
|
|
49
|
+
|
|
50
|
+
def __ge__(self, other):
|
|
51
|
+
if not isinstance(other, TempBlock):
|
|
52
|
+
return NotImplemented
|
|
53
|
+
return str(self) >= str(other)
|
|
54
|
+
|
|
55
|
+
def __hash__(self):
|
|
56
|
+
return self.__hash
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class BlockPlace:
|
|
60
|
+
__slots__ = ("__hash", "block", "index", "offset")
|
|
61
|
+
|
|
62
|
+
def __init__(self, block: BlockValue, index: IndexValue = 0, offset: int = 0):
|
|
63
|
+
self.block = block
|
|
64
|
+
self.index = index
|
|
65
|
+
self.offset = offset
|
|
66
|
+
self.__hash = hash((block, index, offset))
|
|
67
|
+
|
|
68
|
+
def __repr__(self):
|
|
69
|
+
return f"BlockPlace(block={self.block!r}, index={self.index!r}, offset={self.offset!r})"
|
|
70
|
+
|
|
71
|
+
def __str__(self):
|
|
72
|
+
if isinstance(self.block, TempBlock) and self.block.size == 1 and self.index == 0 and self.offset == 0:
|
|
73
|
+
return f"{self.block}"
|
|
74
|
+
elif isinstance(self.index, int):
|
|
75
|
+
return f"{self.block}[{self.index + self.offset}]"
|
|
76
|
+
elif self.offset == 0:
|
|
77
|
+
return f"{self.block}[{self.index}]"
|
|
78
|
+
else:
|
|
79
|
+
return f"{self.block}[{self.index} + {self.offset}]"
|
|
80
|
+
|
|
81
|
+
def add_offset(self, offset: int) -> Self:
|
|
82
|
+
return BlockPlace(self.block, self.index, self.offset + offset)
|
|
83
|
+
|
|
84
|
+
def __eq__(self, other):
|
|
85
|
+
return (
|
|
86
|
+
isinstance(other, BlockPlace)
|
|
87
|
+
and self.block == other.block
|
|
88
|
+
and self.index == other.index
|
|
89
|
+
and self.offset == other.offset
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
def __lt__(self, other):
|
|
93
|
+
if not isinstance(other, BlockPlace):
|
|
94
|
+
return NotImplemented
|
|
95
|
+
return str(self) < str(other)
|
|
96
|
+
|
|
97
|
+
def __le__(self, other):
|
|
98
|
+
if not isinstance(other, BlockPlace):
|
|
99
|
+
return NotImplemented
|
|
100
|
+
return str(self) <= str(other)
|
|
101
|
+
|
|
102
|
+
def __gt__(self, other):
|
|
103
|
+
if not isinstance(other, BlockPlace):
|
|
104
|
+
return NotImplemented
|
|
105
|
+
return str(self) > str(other)
|
|
106
|
+
|
|
107
|
+
def __ge__(self, other):
|
|
108
|
+
if not isinstance(other, BlockPlace):
|
|
109
|
+
return NotImplemented
|
|
110
|
+
return str(self) >= str(other)
|
|
111
|
+
|
|
112
|
+
def __hash__(self):
|
|
113
|
+
return self.__hash
|
|
114
|
+
|
|
115
|
+
def __iter__(self):
|
|
116
|
+
yield self.block
|
|
117
|
+
yield self.index
|
|
118
|
+
yield self.offset
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class SSAPlace:
|
|
122
|
+
__slots__ = ("__hash", "name", "num")
|
|
123
|
+
|
|
124
|
+
def __init__(self, name: str, num: int):
|
|
125
|
+
self.name = name
|
|
126
|
+
self.num = num
|
|
127
|
+
self.__hash = hash((name, num))
|
|
128
|
+
|
|
129
|
+
def __repr__(self):
|
|
130
|
+
return f"SSAPlace(name={self.name!r}, num={self.num!r})"
|
|
131
|
+
|
|
132
|
+
def __str__(self):
|
|
133
|
+
return f"{self.name}.{self.num}"
|
|
134
|
+
|
|
135
|
+
def __eq__(self, other):
|
|
136
|
+
return isinstance(other, SSAPlace) and self.name == other.name and self.num == other.num
|
|
137
|
+
|
|
138
|
+
def __lt__(self, other):
|
|
139
|
+
if not isinstance(other, SSAPlace):
|
|
140
|
+
return NotImplemented
|
|
141
|
+
return str(self) < str(other)
|
|
142
|
+
|
|
143
|
+
def __le__(self, other):
|
|
144
|
+
if not isinstance(other, SSAPlace):
|
|
145
|
+
return NotImplemented
|
|
146
|
+
return str(self) <= str(other)
|
|
147
|
+
|
|
148
|
+
def __gt__(self, other):
|
|
149
|
+
if not isinstance(other, SSAPlace):
|
|
150
|
+
return NotImplemented
|
|
151
|
+
return str(self) > str(other)
|
|
152
|
+
|
|
153
|
+
def __ge__(self, other):
|
|
154
|
+
if not isinstance(other, SSAPlace):
|
|
155
|
+
return NotImplemented
|
|
156
|
+
return str(self) >= str(other)
|
|
157
|
+
|
|
158
|
+
def __hash__(self):
|
|
159
|
+
return self.__hash
|
|
160
|
+
|
|
161
|
+
def __iter__(self):
|
|
162
|
+
yield self.name
|
|
163
|
+
yield self.num
|
|
@@ -172,7 +172,9 @@ class Visitor(ast.NodeVisitor):
|
|
|
172
172
|
bound_args: inspect.BoundArguments
|
|
173
173
|
used_names: dict[str, int]
|
|
174
174
|
return_ctxs: list[Context] # Contexts at return statements, which will branch to the exit
|
|
175
|
-
loop_head_ctxs: list[
|
|
175
|
+
loop_head_ctxs: list[
|
|
176
|
+
Context | list[Context]
|
|
177
|
+
] # Contexts at loop heads, from outer to inner. Contains a list for unrolled (tuple) loops
|
|
176
178
|
break_ctxs: list[list[Context]] # Contexts at break statements, from outer to inner
|
|
177
179
|
active_ctx: Context | None # The active context for use in nested functions=
|
|
178
180
|
parent: Self | None # The parent visitor for use in nested functions
|
|
@@ -203,6 +205,8 @@ class Visitor(ast.NodeVisitor):
|
|
|
203
205
|
case ast.FunctionDef(body=body):
|
|
204
206
|
ctx().scope.set_value("$return", validate_value(None))
|
|
205
207
|
for stmt in body:
|
|
208
|
+
if not ctx().live:
|
|
209
|
+
break
|
|
206
210
|
self.visit(stmt)
|
|
207
211
|
case ast.Lambda(body=body):
|
|
208
212
|
result = self.visit(body)
|
|
@@ -318,11 +322,21 @@ class Visitor(ast.NodeVisitor):
|
|
|
318
322
|
iterable = self.visit(node.iter)
|
|
319
323
|
if isinstance(iterable, TupleImpl):
|
|
320
324
|
# Unroll the loop
|
|
325
|
+
break_ctxs = []
|
|
321
326
|
for value in iterable.value:
|
|
322
327
|
set_ctx(ctx().branch(None))
|
|
328
|
+
self.loop_head_ctxs.append([])
|
|
329
|
+
self.break_ctxs.append([])
|
|
323
330
|
self.handle_assign(node.target, validate_value(value))
|
|
324
331
|
for stmt in node.body:
|
|
332
|
+
if not ctx().live:
|
|
333
|
+
break
|
|
325
334
|
self.visit(stmt)
|
|
335
|
+
continue_ctxs = [*self.loop_head_ctxs.pop(), ctx()]
|
|
336
|
+
break_ctxs.extend(self.break_ctxs.pop())
|
|
337
|
+
set_ctx(Context.meet(continue_ctxs))
|
|
338
|
+
if break_ctxs:
|
|
339
|
+
set_ctx(Context.meet([*break_ctxs, ctx()]))
|
|
326
340
|
return
|
|
327
341
|
iterator = self.handle_call(node, iterable.__iter__)
|
|
328
342
|
if not isinstance(iterator, SonolusIterator):
|
|
@@ -338,6 +352,8 @@ class Visitor(ast.NodeVisitor):
|
|
|
338
352
|
self.loop_head_ctxs.pop()
|
|
339
353
|
self.break_ctxs.pop()
|
|
340
354
|
for stmt in node.orelse:
|
|
355
|
+
if not ctx().live:
|
|
356
|
+
break
|
|
341
357
|
self.visit(stmt)
|
|
342
358
|
return
|
|
343
359
|
ctx().test = has_next.ir()
|
|
@@ -347,6 +363,8 @@ class Visitor(ast.NodeVisitor):
|
|
|
347
363
|
set_ctx(body_ctx)
|
|
348
364
|
self.handle_assign(node.target, self.handle_call(node, iterator.next))
|
|
349
365
|
for stmt in node.body:
|
|
366
|
+
if not ctx().live:
|
|
367
|
+
break
|
|
350
368
|
self.visit(stmt)
|
|
351
369
|
ctx().branch_to_loop_header(header_ctx)
|
|
352
370
|
|
|
@@ -355,6 +373,8 @@ class Visitor(ast.NodeVisitor):
|
|
|
355
373
|
|
|
356
374
|
set_ctx(else_ctx)
|
|
357
375
|
for stmt in node.orelse:
|
|
376
|
+
if not ctx().live:
|
|
377
|
+
break
|
|
358
378
|
self.visit(stmt)
|
|
359
379
|
else_end_ctx = ctx()
|
|
360
380
|
|
|
@@ -374,6 +394,8 @@ class Visitor(ast.NodeVisitor):
|
|
|
374
394
|
body_ctx = ctx().branch(None)
|
|
375
395
|
set_ctx(body_ctx)
|
|
376
396
|
for stmt in node.body:
|
|
397
|
+
if not ctx().live:
|
|
398
|
+
break
|
|
377
399
|
self.visit(stmt)
|
|
378
400
|
ctx().branch_to_loop_header(header_ctx)
|
|
379
401
|
|
|
@@ -390,6 +412,8 @@ class Visitor(ast.NodeVisitor):
|
|
|
390
412
|
self.loop_head_ctxs.pop()
|
|
391
413
|
self.break_ctxs.pop()
|
|
392
414
|
for stmt in node.orelse:
|
|
415
|
+
if not ctx().live:
|
|
416
|
+
break
|
|
393
417
|
self.visit(stmt)
|
|
394
418
|
return
|
|
395
419
|
ctx().test = test.ir()
|
|
@@ -398,6 +422,8 @@ class Visitor(ast.NodeVisitor):
|
|
|
398
422
|
|
|
399
423
|
set_ctx(body_ctx)
|
|
400
424
|
for stmt in node.body:
|
|
425
|
+
if not ctx().live:
|
|
426
|
+
break
|
|
401
427
|
self.visit(stmt)
|
|
402
428
|
ctx().branch_to_loop_header(header_ctx)
|
|
403
429
|
|
|
@@ -406,6 +432,8 @@ class Visitor(ast.NodeVisitor):
|
|
|
406
432
|
|
|
407
433
|
set_ctx(else_ctx)
|
|
408
434
|
for stmt in node.orelse:
|
|
435
|
+
if not ctx().live:
|
|
436
|
+
break
|
|
409
437
|
self.visit(stmt)
|
|
410
438
|
else_end_ctx = ctx()
|
|
411
439
|
|
|
@@ -418,9 +446,13 @@ class Visitor(ast.NodeVisitor):
|
|
|
418
446
|
if test._is_py_():
|
|
419
447
|
if test._as_py_():
|
|
420
448
|
for stmt in node.body:
|
|
449
|
+
if not ctx().live:
|
|
450
|
+
break
|
|
421
451
|
self.visit(stmt)
|
|
422
452
|
else:
|
|
423
453
|
for stmt in node.orelse:
|
|
454
|
+
if not ctx().live:
|
|
455
|
+
break
|
|
424
456
|
self.visit(stmt)
|
|
425
457
|
return
|
|
426
458
|
|
|
@@ -431,11 +463,15 @@ class Visitor(ast.NodeVisitor):
|
|
|
431
463
|
|
|
432
464
|
set_ctx(true_ctx)
|
|
433
465
|
for stmt in node.body:
|
|
466
|
+
if not ctx().live:
|
|
467
|
+
break
|
|
434
468
|
self.visit(stmt)
|
|
435
469
|
true_end_ctx = ctx()
|
|
436
470
|
|
|
437
471
|
set_ctx(false_ctx)
|
|
438
472
|
for stmt in node.orelse:
|
|
473
|
+
if not ctx().live:
|
|
474
|
+
break
|
|
439
475
|
self.visit(stmt)
|
|
440
476
|
false_end_ctx = ctx()
|
|
441
477
|
|
|
@@ -462,6 +498,8 @@ class Visitor(ast.NodeVisitor):
|
|
|
462
498
|
if guard._is_py_():
|
|
463
499
|
if guard._as_py_():
|
|
464
500
|
for stmt in case.body:
|
|
501
|
+
if not ctx().live:
|
|
502
|
+
break
|
|
465
503
|
self.visit(stmt)
|
|
466
504
|
end_ctxs.append(ctx())
|
|
467
505
|
else:
|
|
@@ -473,6 +511,8 @@ class Visitor(ast.NodeVisitor):
|
|
|
473
511
|
guard_false_ctx = ctx().branch(0)
|
|
474
512
|
set_ctx(guard_true_ctx)
|
|
475
513
|
for stmt in case.body:
|
|
514
|
+
if not ctx().live:
|
|
515
|
+
break
|
|
476
516
|
self.visit(stmt)
|
|
477
517
|
end_ctxs.append(ctx())
|
|
478
518
|
false_ctx = Context.meet([false_ctx, guard_false_ctx])
|
|
@@ -633,7 +673,11 @@ class Visitor(ast.NodeVisitor):
|
|
|
633
673
|
set_ctx(ctx().into_dead())
|
|
634
674
|
|
|
635
675
|
def visit_Continue(self, node):
|
|
636
|
-
|
|
676
|
+
loop_head = self.loop_head_ctxs[-1]
|
|
677
|
+
if isinstance(loop_head, list):
|
|
678
|
+
loop_head.append(ctx())
|
|
679
|
+
else:
|
|
680
|
+
ctx().branch_to_loop_header(loop_head)
|
|
637
681
|
set_ctx(ctx().into_dead())
|
|
638
682
|
|
|
639
683
|
def visit_BoolOp(self, node) -> Value:
|
|
@@ -982,7 +1026,11 @@ class Visitor(ast.NodeVisitor):
|
|
|
982
1026
|
if isinstance(target, ConstantValue):
|
|
983
1027
|
# Unwrap so we can access fields
|
|
984
1028
|
target = target._as_py_()
|
|
985
|
-
descriptor =
|
|
1029
|
+
descriptor = None
|
|
1030
|
+
for cls in type.mro(type(target)):
|
|
1031
|
+
descriptor = cls.__dict__.get(key, None)
|
|
1032
|
+
if descriptor is not None:
|
|
1033
|
+
break
|
|
986
1034
|
match descriptor:
|
|
987
1035
|
case property(fget=getter):
|
|
988
1036
|
return self.handle_call(node, getter, target)
|
|
@@ -10,10 +10,11 @@ import sys
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from time import perf_counter
|
|
12
12
|
|
|
13
|
+
from sonolus.backend.optimize.optimize import FAST_PASSES, MINIMAL_PASSES, STANDARD_PASSES
|
|
13
14
|
from sonolus.build.engine import package_engine
|
|
14
15
|
from sonolus.build.level import package_level_data
|
|
15
16
|
from sonolus.build.project import build_project_to_collection, get_project_schema
|
|
16
|
-
from sonolus.script.project import Project
|
|
17
|
+
from sonolus.script.project import BuildConfig, Project
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
def find_default_module() -> str | None:
|
|
@@ -63,29 +64,29 @@ def import_project(module_path: str) -> Project | None:
|
|
|
63
64
|
return project
|
|
64
65
|
except Exception as e:
|
|
65
66
|
print(f"Error: Failed to import project: {e}")
|
|
66
|
-
|
|
67
|
+
raise e from None
|
|
67
68
|
|
|
68
69
|
|
|
69
|
-
def build_project(project: Project, build_dir: Path):
|
|
70
|
+
def build_project(project: Project, build_dir: Path, config: BuildConfig):
|
|
70
71
|
dist_dir = build_dir / "dist"
|
|
71
72
|
levels_dir = dist_dir / "levels"
|
|
72
73
|
shutil.rmtree(dist_dir, ignore_errors=True)
|
|
73
74
|
dist_dir.mkdir(parents=True, exist_ok=True)
|
|
74
75
|
levels_dir.mkdir(parents=True, exist_ok=True)
|
|
75
76
|
|
|
76
|
-
package_engine(project.engine.data).write(dist_dir / "engine")
|
|
77
|
+
package_engine(project.engine.data, config).write(dist_dir / "engine")
|
|
77
78
|
|
|
78
79
|
for level in project.levels:
|
|
79
80
|
level_path = levels_dir / level.name
|
|
80
81
|
level_path.write_bytes(package_level_data(level.data))
|
|
81
82
|
|
|
82
83
|
|
|
83
|
-
def build_collection(project: Project, build_dir: Path):
|
|
84
|
+
def build_collection(project: Project, build_dir: Path, config: BuildConfig):
|
|
84
85
|
site_dir = build_dir / "site"
|
|
85
86
|
shutil.rmtree(site_dir, ignore_errors=True)
|
|
86
87
|
site_dir.mkdir(parents=True, exist_ok=True)
|
|
87
88
|
|
|
88
|
-
collection = build_project_to_collection(project)
|
|
89
|
+
collection = build_project_to_collection(project, config)
|
|
89
90
|
collection.write(site_dir)
|
|
90
91
|
|
|
91
92
|
|
|
@@ -125,10 +126,55 @@ def run_server(base_dir: Path, port: int = 8000):
|
|
|
125
126
|
httpd.shutdown()
|
|
126
127
|
|
|
127
128
|
|
|
129
|
+
def get_config(args: argparse.Namespace) -> BuildConfig:
|
|
130
|
+
if hasattr(args, "optimize_minimal") and args.optimize_minimal:
|
|
131
|
+
optimization_passes = MINIMAL_PASSES
|
|
132
|
+
elif hasattr(args, "optimize_fast") and args.optimize_fast:
|
|
133
|
+
optimization_passes = FAST_PASSES
|
|
134
|
+
elif hasattr(args, "optimize_standard") and args.optimize_standard:
|
|
135
|
+
optimization_passes = STANDARD_PASSES
|
|
136
|
+
else:
|
|
137
|
+
optimization_passes = FAST_PASSES if args.command == "dev" else STANDARD_PASSES
|
|
138
|
+
|
|
139
|
+
if any(hasattr(args, attr) and getattr(args, attr) for attr in ["play", "watch", "preview", "tutorial"]):
|
|
140
|
+
build_play = hasattr(args, "play") and args.play
|
|
141
|
+
build_watch = hasattr(args, "watch") and args.watch
|
|
142
|
+
build_preview = hasattr(args, "preview") and args.preview
|
|
143
|
+
build_tutorial = hasattr(args, "tutorial") and args.tutorial
|
|
144
|
+
else:
|
|
145
|
+
build_play = build_watch = build_preview = build_tutorial = True
|
|
146
|
+
|
|
147
|
+
return BuildConfig(
|
|
148
|
+
passes=optimization_passes,
|
|
149
|
+
build_play=build_play,
|
|
150
|
+
build_watch=build_watch,
|
|
151
|
+
build_preview=build_preview,
|
|
152
|
+
build_tutorial=build_tutorial,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
128
156
|
def main():
|
|
129
157
|
parser = argparse.ArgumentParser(description="Sonolus project build and development tools")
|
|
130
158
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
131
159
|
|
|
160
|
+
def add_common_arguments(parser):
|
|
161
|
+
optimization_group = parser.add_mutually_exclusive_group()
|
|
162
|
+
optimization_group.add_argument(
|
|
163
|
+
"-o0", "--optimize-minimal", action="store_true", help="Use minimal optimization passes"
|
|
164
|
+
)
|
|
165
|
+
optimization_group.add_argument(
|
|
166
|
+
"-o1", "--optimize-fast", action="store_true", help="Use fast optimization passes"
|
|
167
|
+
)
|
|
168
|
+
optimization_group.add_argument(
|
|
169
|
+
"-o2", "--optimize-standard", action="store_true", help="Use standard optimization passes"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
build_components = parser.add_argument_group("build components")
|
|
173
|
+
build_components.add_argument("--play", action="store_true", help="Build play component")
|
|
174
|
+
build_components.add_argument("--watch", action="store_true", help="Build watch component")
|
|
175
|
+
build_components.add_argument("--preview", action="store_true", help="Build preview component")
|
|
176
|
+
build_components.add_argument("--tutorial", action="store_true", help="Build tutorial component")
|
|
177
|
+
|
|
132
178
|
build_parser = subparsers.add_parser("build")
|
|
133
179
|
build_parser.add_argument(
|
|
134
180
|
"module",
|
|
@@ -137,6 +183,7 @@ def main():
|
|
|
137
183
|
help="Module path (e.g., 'module.name'). If omitted, will auto-detect if only one module exists.",
|
|
138
184
|
)
|
|
139
185
|
build_parser.add_argument("--build-dir", type=str, default="./build")
|
|
186
|
+
add_common_arguments(build_parser)
|
|
140
187
|
|
|
141
188
|
dev_parser = subparsers.add_parser("dev")
|
|
142
189
|
dev_parser.add_argument(
|
|
@@ -147,6 +194,7 @@ def main():
|
|
|
147
194
|
)
|
|
148
195
|
dev_parser.add_argument("--build-dir", type=str, default="./build")
|
|
149
196
|
dev_parser.add_argument("--port", type=int, default=8000)
|
|
197
|
+
add_common_arguments(dev_parser)
|
|
150
198
|
|
|
151
199
|
schema_parser = subparsers.add_parser("schema")
|
|
152
200
|
schema_parser.add_argument(
|
|
@@ -161,7 +209,8 @@ def main():
|
|
|
161
209
|
if not args.module:
|
|
162
210
|
default_module = find_default_module()
|
|
163
211
|
if default_module:
|
|
164
|
-
|
|
212
|
+
if args.command != "schema":
|
|
213
|
+
print(f"Using auto-detected module: {default_module}")
|
|
165
214
|
args.module = default_module
|
|
166
215
|
else:
|
|
167
216
|
parser.error("Module argument is required when multiple or no modules are found")
|
|
@@ -173,13 +222,15 @@ def main():
|
|
|
173
222
|
if args.command == "build":
|
|
174
223
|
build_dir = Path(args.build_dir)
|
|
175
224
|
start_time = perf_counter()
|
|
176
|
-
|
|
225
|
+
config = get_config(args)
|
|
226
|
+
build_project(project, build_dir, config)
|
|
177
227
|
end_time = perf_counter()
|
|
178
228
|
print(f"Project built successfully to '{build_dir.resolve()}' in {end_time - start_time:.2f}s")
|
|
179
229
|
elif args.command == "dev":
|
|
180
230
|
build_dir = Path(args.build_dir)
|
|
181
231
|
start_time = perf_counter()
|
|
182
|
-
|
|
232
|
+
config = get_config(args)
|
|
233
|
+
build_collection(project, build_dir, config)
|
|
183
234
|
end_time = perf_counter()
|
|
184
235
|
print(f"Build finished in {end_time - start_time:.2f}s")
|
|
185
236
|
run_server(build_dir / "site", port=args.port)
|