sonolus.py 0.3.1__tar.gz → 0.3.3__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.3.1 → sonolus_py-0.3.3}/.github/workflows/publish.yaml +3 -3
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/.gitignore +1 -1
- sonolus_py-0.3.3/.python-version +1 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/PKG-INFO +1 -1
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/concepts/builtins.md +5 -1
- sonolus_py-0.3.3/docs/concepts/index.md +6 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/concepts/project.md +27 -13
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/concepts/resources.md +108 -7
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/concepts/types.md +26 -7
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/pyproject.toml +6 -4
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/scripts/generate.py +4 -6
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/finalize.py +16 -4
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/node.py +13 -5
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/optimize/allocate.py +41 -4
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/optimize/flow.py +24 -7
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/optimize/optimize.py +2 -9
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/utils.py +6 -1
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/visitor.py +72 -23
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/build/cli.py +6 -1
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/build/engine.py +1 -1
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/archetype.py +52 -24
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/array.py +20 -8
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/array_like.py +30 -3
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/containers.py +27 -7
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/debug.py +66 -8
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/globals.py +17 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/builtin_impls.py +12 -8
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/context.py +55 -1
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/range.py +25 -2
- sonolus_py-0.3.3/sonolus/script/internal/simulation_context.py +131 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/tuple_impl.py +18 -11
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/interval.py +60 -2
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/iterator.py +3 -2
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/num.py +11 -2
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/options.py +24 -1
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/quad.py +41 -3
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/record.py +24 -3
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/runtime.py +411 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/stream.py +133 -16
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/transform.py +291 -2
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/values.py +9 -3
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/vec.py +14 -2
- sonolus_py-0.3.3/tests/script/conftest.py +210 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_array.py +82 -26
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_array_map.py +111 -29
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_array_set.py +27 -17
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_assert.py +7 -7
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_dict.py +4 -4
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_flow.py +56 -56
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_functions.py +53 -53
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_helpers.py +11 -11
- sonolus_py-0.3.3/tests/script/test_interval.py +570 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_match.py +11 -11
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_num.py +12 -12
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_operator.py +159 -26
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_quad.py +17 -32
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_random.py +10 -27
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_range.py +82 -9
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_record.py +18 -18
- sonolus_py-0.3.3/tests/script/test_transform.py +601 -0
- sonolus_py-0.3.3/tests/script/test_tuple.py +140 -0
- sonolus_py-0.3.3/tests/script/test_values.py +169 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_var_array.py +140 -68
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/test_vec.py +15 -15
- sonolus_py-0.3.3/uv.lock +849 -0
- sonolus_py-0.3.1/.python-version +0 -1
- sonolus_py-0.3.1/docs/concepts/index.md +0 -2
- sonolus_py-0.3.1/tests/script/conftest.py +0 -126
- sonolus_py-0.3.1/tests/script/test_interval.py +0 -223
- sonolus_py-0.3.1/tests/script/test_transform.py +0 -301
- sonolus_py-0.3.1/tests/script/test_tuple.py +0 -70
- sonolus_py-0.3.1/uv.lock +0 -872
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/.run/Python tests in tests.run.xml +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/LICENSE +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/README.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/doc_stubs/__init__.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/doc_stubs/builtins.pyi +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/doc_stubs/math.pyi +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/doc_stubs/num.pyi +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/doc_stubs/random.pyi +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/CNAME +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/concepts/cli.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/concepts/constructs.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/index.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/builtins.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/index.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/math.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/random.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.archetype.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.array.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.array_like.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.bucket.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.containers.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.debug.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.easing.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.effect.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.engine.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.globals.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.instruction.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.interval.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.iterator.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.level.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.metadata.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.num.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.options.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.particle.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.printing.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.project.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.quad.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.record.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.runtime.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.sprite.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.stream.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.text.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.timing.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.transform.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.ui.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.values.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/docs/reference/sonolus.script.vec.md +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/mkdocs.yml +1 -1
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/scripts/runtimes/Engine/Tutorial/Blocks.json +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/scripts/runtimes/Functions.json +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/scripts/runtimes/Level/Play/Blocks.json +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/scripts/runtimes/Level/Preview/Blocks.json +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/scripts/runtimes/Level/Watch/Blocks.json +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/__init__.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/__init__.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/blocks.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/excepthook.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/interpret.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/ir.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/mode.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/ops.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/optimize/__init__.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/optimize/constant_evaluation.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/optimize/copy_coalesce.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/optimize/dead_code.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/optimize/dominance.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/optimize/inlining.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/optimize/liveness.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/optimize/passes.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/optimize/simplify.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/optimize/ssa.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/backend/place.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/build/__init__.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/build/collection.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/build/compile.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/build/level.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/build/node.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/build/project.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/py.typed +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/__init__.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/bucket.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/easing.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/effect.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/engine.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/instruction.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/__init__.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/callbacks.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/constant.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/descriptor.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/dict_impl.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/error.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/generic.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/impl.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/introspection.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/math_impls.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/native.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/random.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/transient.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/internal/value.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/level.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/metadata.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/particle.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/pointer.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/printing.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/project.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/sprite.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/text.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/timing.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/sonolus/script/ui.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/__init__.py +0 -0
- {sonolus_py-0.3.1 → sonolus_py-0.3.3}/tests/script/__init__.py +0 -0
|
@@ -13,16 +13,16 @@ jobs:
|
|
|
13
13
|
- name: Install uv
|
|
14
14
|
uses: astral-sh/setup-uv@v3
|
|
15
15
|
with:
|
|
16
|
-
version: 0.
|
|
16
|
+
version: 0.7.16
|
|
17
17
|
- name: Install Python
|
|
18
18
|
run: |
|
|
19
|
-
uv python install 3.12 3.13
|
|
19
|
+
uv python install 3.12 3.13 3.14
|
|
20
20
|
- name: Install project
|
|
21
21
|
run: |
|
|
22
22
|
uv sync --all-extras --dev
|
|
23
23
|
- name: Run tests
|
|
24
24
|
run: |
|
|
25
|
-
uv run tox
|
|
25
|
+
CI=true uv run tox
|
|
26
26
|
- name: Build
|
|
27
27
|
run: |
|
|
28
28
|
uv build
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14.0b3t
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Sonolus.py comes with support for a number of built-in functions.
|
|
3
3
|
|
|
4
4
|
- `abs(x)`
|
|
5
|
-
- `bool(object)`
|
|
5
|
+
- `bool(object)`
|
|
6
6
|
- `callable(object)`
|
|
7
7
|
- `enumerate(iterable, start=0)`
|
|
8
8
|
- `filter(function, iterable)`
|
|
@@ -38,6 +38,10 @@ Sonolus.py also comes with support for some standard library modules.
|
|
|
38
38
|
- `ceil(x)`
|
|
39
39
|
- `trunc(x)`
|
|
40
40
|
- `log(x[, base])`
|
|
41
|
+
- `pi`
|
|
42
|
+
- `e`
|
|
43
|
+
- `tau`
|
|
44
|
+
- `inf`
|
|
41
45
|
|
|
42
46
|
### random
|
|
43
47
|
- `randrange(stop)`, `random.randrange(start, stop[, step])`
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Concepts
|
|
2
|
+
This section provides a reference for the concepts and features of Sonolus.py.
|
|
3
|
+
|
|
4
|
+
It is not intended to serve as a tutorial or introduction, and assumes familiarity with Python and Sonolus.py.
|
|
5
|
+
|
|
6
|
+
For information on how to get started with Sonolus.py, see the [home page](../index.md).
|
|
@@ -80,9 +80,16 @@ play_mode = PlayMode(
|
|
|
80
80
|
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
Play mode archetypes subclass [`PlayArchetype`][sonolus.script.archetype.PlayArchetype] and
|
|
84
|
-
|
|
85
|
-
[`
|
|
83
|
+
Play mode archetypes subclass [`PlayArchetype`][sonolus.script.archetype.PlayArchetype] and implement the following callbacks:
|
|
84
|
+
|
|
85
|
+
- [`should_spawn`][sonolus.script.archetype.PlayArchetype.should_spawn] (required)
|
|
86
|
+
- [`preprocess`][sonolus.script.archetype.PlayArchetype.preprocess]
|
|
87
|
+
- [`spawn_order`][sonolus.script.archetype.PlayArchetype.spawn_order]
|
|
88
|
+
- [`initialize`][sonolus.script.archetype.PlayArchetype.initialize]
|
|
89
|
+
- [`update_sequential`][sonolus.script.archetype.PlayArchetype.update_sequential]
|
|
90
|
+
- [`update_parallel`][sonolus.script.archetype.PlayArchetype.update_parallel]
|
|
91
|
+
- [`touch`][sonolus.script.archetype.PlayArchetype.touch]
|
|
92
|
+
- [`terminate`][sonolus.script.archetype.PlayArchetype.terminate]
|
|
86
93
|
|
|
87
94
|
Archetypes for scored notes should have the [`is_scored`][sonolus.script.archetype.PlayArchetype.is_scored] class variable set to `True`.
|
|
88
95
|
|
|
@@ -110,9 +117,15 @@ watch_mode = WatchMode(
|
|
|
110
117
|
)
|
|
111
118
|
```
|
|
112
119
|
|
|
113
|
-
Watch mode archetypes subclass [`WatchArchetype`][sonolus.script.archetype.WatchArchetype] and
|
|
114
|
-
|
|
115
|
-
[`
|
|
120
|
+
Watch mode archetypes subclass [`WatchArchetype`][sonolus.script.archetype.WatchArchetype] and implement the following callbacks:
|
|
121
|
+
|
|
122
|
+
- [`spawn_time`][sonolus.script.archetype.WatchArchetype.spawn_time] (required)
|
|
123
|
+
- [`despawn_time`][sonolus.script.archetype.WatchArchetype.despawn_time] (required)
|
|
124
|
+
- [`preprocess`][sonolus.script.archetype.WatchArchetype.preprocess]
|
|
125
|
+
- [`initialize`][sonolus.script.archetype.WatchArchetype.initialize]
|
|
126
|
+
- [`update_sequential`][sonolus.script.archetype.WatchArchetype.update_sequential]
|
|
127
|
+
- [`update_parallel`][sonolus.script.archetype.WatchArchetype.update_parallel]
|
|
128
|
+
- [`terminate`][sonolus.script.archetype.WatchArchetype.terminate]
|
|
116
129
|
|
|
117
130
|
Watch mode also has the `update_spawn` global callback, which is invoked every frame and should return the reference
|
|
118
131
|
time to compare against spawn and despawn times of archetypes. Typically, this can be either the current time or the
|
|
@@ -135,7 +148,10 @@ preview_mode = PreviewMode(
|
|
|
135
148
|
)
|
|
136
149
|
```
|
|
137
150
|
|
|
138
|
-
Preview mode archetypes subclass [`PreviewArchetype`][sonolus.script.archetype.PreviewArchetype] and
|
|
151
|
+
Preview mode archetypes subclass [`PreviewArchetype`][sonolus.script.archetype.PreviewArchetype] and implement the following callbacks:
|
|
152
|
+
|
|
153
|
+
- [`preprocess`][sonolus.script.archetype.PreviewArchetype.preprocess]
|
|
154
|
+
- [`render`][sonolus.script.archetype.PreviewArchetype.render]
|
|
139
155
|
|
|
140
156
|
### Tutorial Mode
|
|
141
157
|
|
|
@@ -162,13 +178,11 @@ tutorial_mode = TutorialMode(
|
|
|
162
178
|
)
|
|
163
179
|
```
|
|
164
180
|
|
|
165
|
-
Tutorial mode does not have archetypes, but
|
|
166
|
-
|
|
167
|
-
`preprocess` is invoked once before the tutorial starts.
|
|
168
|
-
|
|
169
|
-
`navigate` is invoked when the player navigates forward or backward in the tutorial.
|
|
181
|
+
Tutorial mode does not have archetypes, but has the following global callbacks:
|
|
170
182
|
|
|
171
|
-
`
|
|
183
|
+
- `preprocess` - Invoked once before the tutorial starts
|
|
184
|
+
- `navigate` - Invoked when the player navigates forward or backward in the tutorial
|
|
185
|
+
- `update` - Invoked every frame and should handle most of the drawing logic
|
|
172
186
|
|
|
173
187
|
## Levels
|
|
174
188
|
Levels are defined using the [`Level`][sonolus.script.level.Level] class:
|
|
@@ -1,32 +1,133 @@
|
|
|
1
1
|
# Resources & Declarations
|
|
2
2
|
|
|
3
|
-
##
|
|
4
|
-
|
|
3
|
+
## Global Variables
|
|
4
|
+
|
|
5
|
+
### Level Memory
|
|
6
|
+
Level memory is defined with the [`@level_memory`][sonolus.script.globals.level_memory] class decorator:
|
|
5
7
|
|
|
6
8
|
```python
|
|
7
|
-
from sonolus.script.globals import level_memory
|
|
9
|
+
from sonolus.script.globals import level_memory
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
@level_memory
|
|
11
13
|
class LevelMemory:
|
|
12
14
|
value: int
|
|
13
|
-
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Alternatively, it may be called as a function as well by passing the type as an argument:
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from sonolus.script.globals import level_memory
|
|
21
|
+
from sonolus.script.vec import Vec2
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
level_memory_value = level_memory(Vec2)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Level memory may be modified in sequential callbacks:
|
|
28
|
+
|
|
29
|
+
- `preprocess`
|
|
30
|
+
- `update_sequential`
|
|
31
|
+
- `touch`
|
|
32
|
+
|
|
33
|
+
and may be read in any callback.
|
|
34
|
+
|
|
35
|
+
### Level Data
|
|
36
|
+
Level data is defined with the [`@level_data`][sonolus.script.globals.level_data] class decorator:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from sonolus.script.globals import level_data
|
|
40
|
+
|
|
14
41
|
|
|
15
42
|
@level_data
|
|
16
43
|
class LevelData:
|
|
17
44
|
value: int
|
|
18
45
|
```
|
|
19
46
|
|
|
20
|
-
Alternatively,
|
|
47
|
+
Alternatively, it may be called as a function as well by passing the type as an argument:
|
|
48
|
+
|
|
21
49
|
```python
|
|
22
|
-
from sonolus.script.globals import
|
|
50
|
+
from sonolus.script.globals import level_data
|
|
23
51
|
from sonolus.script.vec import Vec2
|
|
24
52
|
|
|
25
53
|
|
|
26
|
-
level_memory_value = level_memory(Vec2)
|
|
27
54
|
level_data_value = level_data(Vec2)
|
|
28
55
|
```
|
|
29
56
|
|
|
57
|
+
Level data may only be modified in the `preprocess` callback and may be read in any callback.
|
|
58
|
+
|
|
59
|
+
## Archetype Variables
|
|
60
|
+
|
|
61
|
+
### Imported
|
|
62
|
+
Imported fields are declared with [`imported()`][sonolus.script.archetype.imported]:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from sonolus.script.archetype import PlayArchetype, imported
|
|
66
|
+
|
|
67
|
+
class MyArchetype(PlayArchetype):
|
|
68
|
+
field: int = imported()
|
|
69
|
+
field_with_explicit_name: int = imported(name="field_name")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Imported fields may be loaded from the level data. In watch mode, data may also be loaded from a corresponding exported field in play mode.
|
|
73
|
+
|
|
74
|
+
Imported fields may only be updated in the `preprocess` callback, and are read-only in other callbacks.
|
|
75
|
+
|
|
76
|
+
### Exported
|
|
77
|
+
Exported fields are declared with [`exported()`][sonolus.script.archetype.exported]:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from sonolus.script.archetype import PlayArchetype, exported
|
|
81
|
+
|
|
82
|
+
class MyArchetype(PlayArchetype):
|
|
83
|
+
field: int = exported()
|
|
84
|
+
field_with_explicit_name: int = exported(name="#FIELD")
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
This is only usable in play mode to export data to be loaded in watch mode. Exported fields are write-only.
|
|
88
|
+
|
|
89
|
+
### Entity Data
|
|
90
|
+
Entity data fields are declared with [`entity_data()`][sonolus.script.archetype.entity_data]:
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
from sonolus.script.archetype import PlayArchetype, entity_data
|
|
94
|
+
|
|
95
|
+
class MyArchetype(PlayArchetype):
|
|
96
|
+
field: int = entity_data()
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Entity data is accessible from other entities, but may only be updated in the `preprocess` callback and is read-only in other callbacks.
|
|
100
|
+
|
|
101
|
+
It functions like [`imported()`][sonolus.script.archetype.imported] and shares the same underlying storage, except that it is not loaded from a level.
|
|
102
|
+
|
|
103
|
+
### Entity Memory
|
|
104
|
+
Entity memory fields are declared with [`entity_memory()`][sonolus.script.archetype.entity_memory]:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from sonolus.script.archetype import PlayArchetype, entity_memory
|
|
108
|
+
|
|
109
|
+
class MyArchetype(PlayArchetype):
|
|
110
|
+
field: int = entity_memory()
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Entity memory is private to the entity and is not accessible from other entities. It may be read or updated in any callback associated with the entity.
|
|
114
|
+
|
|
115
|
+
Entity memory fields may also be set when an entity is spawned using the [`spawn()`][sonolus.script.archetype.PlayArchetype.spawn] method.
|
|
116
|
+
|
|
117
|
+
### Shared Memory
|
|
118
|
+
Shared memory fields are declared with [`shared_memory()`][sonolus.script.archetype.shared_memory]:
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from sonolus.script.archetype import PlayArchetype, shared_memory
|
|
122
|
+
|
|
123
|
+
class MyArchetype(PlayArchetype):
|
|
124
|
+
field: int = shared_memory()
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Shared memory is accessible from other entities.
|
|
128
|
+
|
|
129
|
+
Shared memory may be read in any callback, but may only be updated by sequential callbacks (`preprocess`, `update_sequential`, and `touch`).
|
|
130
|
+
|
|
30
131
|
## Streams
|
|
31
132
|
Streams are defined with the [`@streams`][sonolus.script.stream.streams] decorator:
|
|
32
133
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from sonolus.script.archetype import PlayArchetype
|
|
2
|
-
|
|
3
1
|
# Types
|
|
4
2
|
Sonolus.py has 3 core types: [`Num`](#num), [`Array`](#array), and [`Record`](#record). representing numeric values, fixed-size arrays,
|
|
5
3
|
and custom data structures, respectively. Arrays and records can be nested within each other to create complex data
|
|
@@ -16,7 +14,7 @@ Sonolus.py will treat any of these types as `Num`, but it's recommended to use w
|
|
|
16
14
|
The Sonolus app uses 32-bit floating-point numbers for all numeric values, so precision may be lower compared to Python
|
|
17
15
|
when running on Sonolus.
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
NaN and values outside the range of 32-bit floating-point numbers are not supported.
|
|
20
18
|
|
|
21
19
|
You can import `Num` from `sonolus.script.num`:
|
|
22
20
|
|
|
@@ -44,8 +42,10 @@ Nums support most of the standard Python operations:
|
|
|
44
42
|
Floating point precision may be lower when running on Sonolus compared to Python.
|
|
45
43
|
Care should be taken when performing precision-sensitive operations.
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
As in regular Python, `0` is considered `False`, while any non-zero value is considered `True`.
|
|
46
|
+
|
|
47
|
+
Objects with an explicit `__bool__` method may also be used in `if`, `while`, `case ... if` expressions as well as with
|
|
48
|
+
the `not` operator. However, the operands of the `and` and `or` operators must be of type `Num`.
|
|
49
49
|
|
|
50
50
|
- Logical operators: `and`, `or`, `not`
|
|
51
51
|
- Ternary expressions: `... if <condition> else ...`
|
|
@@ -106,11 +106,12 @@ from sonolus.script.array import Array
|
|
|
106
106
|
|
|
107
107
|
### Declaration
|
|
108
108
|
|
|
109
|
-
Arrays can be created using its constructor
|
|
109
|
+
Arrays can be created using its constructor or the unary `+` operator.
|
|
110
110
|
|
|
111
111
|
```python
|
|
112
112
|
a1 = Array[int, 3](1, 2, 3)
|
|
113
113
|
a2 = Array[int, 0]()
|
|
114
|
+
a3 = +Array[int, 3] # Create a zero-initialized array
|
|
114
115
|
```
|
|
115
116
|
|
|
116
117
|
If at least one element is provided, the element type and size can be inferred:
|
|
@@ -152,6 +153,14 @@ assert a[0] == Pair(1, 2) # The value in the array is independent of the origin
|
|
|
152
153
|
|
|
153
154
|
### Operations
|
|
154
155
|
|
|
156
|
+
An array can be copied with the unary `+` operator, which creates a new array with the same elements:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
a = Array(1, 2, 3)
|
|
160
|
+
b = +a
|
|
161
|
+
assert b == Array(1, 2, 3)
|
|
162
|
+
```
|
|
163
|
+
|
|
155
164
|
The value of an array can be copied from another array using the copy from operator (`@=`)[^1]:
|
|
156
165
|
|
|
157
166
|
```python
|
|
@@ -299,11 +308,13 @@ class MyPairSubclass(MyPair):
|
|
|
299
308
|
|
|
300
309
|
### Instantiation
|
|
301
310
|
|
|
302
|
-
A constructor is automatically generated for the [`Record`][sonolus.script.record.Record] class
|
|
311
|
+
A constructor is automatically generated for the [`Record`][sonolus.script.record.Record] class and the unary `+`
|
|
312
|
+
operator can also be used to create a zero-initialized record.
|
|
303
313
|
|
|
304
314
|
```python
|
|
305
315
|
pair_1 = MyPair(1, 2)
|
|
306
316
|
pair_2 = MyPair(first=1, second=2)
|
|
317
|
+
pair_3 = +MyPair # Create a zero-initialized record
|
|
307
318
|
```
|
|
308
319
|
|
|
309
320
|
### Generics
|
|
@@ -342,6 +353,14 @@ assert MyGenericRecord(1).my_type() == Num
|
|
|
342
353
|
|
|
343
354
|
### Operations
|
|
344
355
|
|
|
356
|
+
A record can be copied with the unary `+` operator, which creates a new record with the same field values:
|
|
357
|
+
|
|
358
|
+
```python
|
|
359
|
+
pair = MyPair(1, 2)
|
|
360
|
+
copy_pair = +pair
|
|
361
|
+
assert copy_pair == MyPair(1, 2)
|
|
362
|
+
```
|
|
363
|
+
|
|
345
364
|
The value of a record can be copied from another record using the copy from operator (`@=`)[^1]:
|
|
346
365
|
|
|
347
366
|
```python
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sonolus.py"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.3"
|
|
4
4
|
description = "Sonolus engine development in Python"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -13,12 +13,12 @@ default-groups = ["dev", "docs"]
|
|
|
13
13
|
|
|
14
14
|
[tool.ruff]
|
|
15
15
|
line-length = 120
|
|
16
|
-
target-version = "
|
|
16
|
+
target-version = "py312"
|
|
17
17
|
|
|
18
18
|
[tool.ruff.lint]
|
|
19
19
|
preview = true
|
|
20
20
|
select = ["F", "E", "W", "I", "N", "D", "UP", "YTT", "B", "A", "COM", "C4", "DTZ", "PIE", "PT", "Q", "SLOT", "SIM", "PTH", "PL", "PERF", "FURB", "LOG", "RUF"]
|
|
21
|
-
ignore = ["E402", "D1", "COM812", "PLW2901", "PLW3201", "PLR6301", "PLC0415", "PLR2004", "PLR09", "SIM108", "FURB113", "A005"]
|
|
21
|
+
ignore = ["E402", "D1", "COM812", "PLW2901", "PLW3201", "PLR6301", "PLC0415", "PLR2004", "PLR09", "SIM108", "FURB113", "A005", "B903"]
|
|
22
22
|
|
|
23
23
|
[tool.ruff.lint.pydocstyle]
|
|
24
24
|
convention = "google"
|
|
@@ -49,13 +49,15 @@ packages = ["sonolus"]
|
|
|
49
49
|
|
|
50
50
|
[tool.tox]
|
|
51
51
|
requires = ["tox>=4.19"]
|
|
52
|
-
env_list = ["py312", "py313"]
|
|
52
|
+
env_list = ["py312", "py313", "py314"]
|
|
53
53
|
|
|
54
54
|
[tool.tox.env_run_base]
|
|
55
55
|
description = "Run tests"
|
|
56
|
+
passenv = ["CI"]
|
|
56
57
|
deps = [
|
|
57
58
|
"hypothesis>=6.115.3",
|
|
58
59
|
"pytest-xdist>=3.6.1",
|
|
59
60
|
"pytest>=8.3.3",
|
|
60
61
|
]
|
|
62
|
+
uv_python_preference = "managed"
|
|
61
63
|
commands = [["pytest", "tests", "-n", "auto"]]
|
|
@@ -51,11 +51,9 @@ def block(name: str, f: TextIO, out: TextIO):
|
|
|
51
51
|
readable = block["readable"]
|
|
52
52
|
writable = block["writable"]
|
|
53
53
|
out.write(
|
|
54
|
-
f
|
|
55
|
-
|
|
56
|
-
}}}
|
|
57
|
-
", ".join(f'"{e}"' for e in writable)
|
|
58
|
-
}}})\n'
|
|
54
|
+
f" {name} = ({id_}, {{{', '.join(f'"{e}"' for e in readable)}}}, {{{
|
|
55
|
+
', '.join(f'"{e}"' for e in writable)
|
|
56
|
+
}}})\n"
|
|
59
57
|
)
|
|
60
58
|
|
|
61
59
|
|
|
@@ -78,7 +76,7 @@ def blocks():
|
|
|
78
76
|
(runtimes_dir / file).open("r", encoding="utf-8") as f,
|
|
79
77
|
):
|
|
80
78
|
block(name, f, out)
|
|
81
|
-
out.write(f"\n
|
|
79
|
+
out.write(f"\n\ntype Block = {' | '.join(f'{name}Block' for name in block_files)}\n")
|
|
82
80
|
|
|
83
81
|
|
|
84
82
|
def main():
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from math import isfinite, isinf, isnan
|
|
2
|
+
|
|
1
3
|
from sonolus.backend.ir import IRConst, IRGet, IRInstr, IRPureInstr, IRSet
|
|
2
4
|
from sonolus.backend.node import ConstantNode, EngineNode, FunctionNode
|
|
3
5
|
from sonolus.backend.ops import Op
|
|
@@ -54,10 +56,20 @@ def cfg_to_engine_node(entry: BasicBlock):
|
|
|
54
56
|
|
|
55
57
|
def ir_to_engine_node(stmt) -> EngineNode:
|
|
56
58
|
match stmt:
|
|
57
|
-
case int() | float():
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
case int(value) | float(value) | IRConst(value=int(value) | float(value)):
|
|
60
|
+
value = float(value)
|
|
61
|
+
if value.is_integer():
|
|
62
|
+
return ConstantNode(value=int(value))
|
|
63
|
+
elif isfinite(value):
|
|
64
|
+
return ConstantNode(value=value)
|
|
65
|
+
elif isinf(value):
|
|
66
|
+
# Read values from ROM
|
|
67
|
+
return FunctionNode(Op.Get, args=[ConstantNode(value=3000), ConstantNode(value=1 if value > 0 else 2)])
|
|
68
|
+
elif isnan(value):
|
|
69
|
+
# Read value from ROM
|
|
70
|
+
return FunctionNode(Op.Get, args=[ConstantNode(value=3000), ConstantNode(value=0)])
|
|
71
|
+
else:
|
|
72
|
+
raise ValueError(f"Invalid constant value: {value}")
|
|
61
73
|
case IRPureInstr(op=op, args=args) | IRInstr(op=op, args=args):
|
|
62
74
|
return FunctionNode(func=op, args=[ir_to_engine_node(arg) for arg in args])
|
|
63
75
|
case IRGet(place=place):
|
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
import textwrap
|
|
2
|
-
from dataclasses import dataclass
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
3
|
|
|
4
4
|
from sonolus.backend.ops import Op
|
|
5
5
|
|
|
6
6
|
type EngineNode = ConstantNode | FunctionNode
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
@dataclass
|
|
9
|
+
@dataclass(slots=True)
|
|
10
10
|
class ConstantNode:
|
|
11
11
|
value: float
|
|
12
|
+
_hash: int = field(init=False, repr=False)
|
|
13
|
+
|
|
14
|
+
def __post_init__(self):
|
|
15
|
+
self._hash = hash(self.value)
|
|
12
16
|
|
|
13
17
|
def __hash__(self):
|
|
14
18
|
return hash(self.value)
|
|
15
19
|
|
|
16
20
|
|
|
17
|
-
@dataclass
|
|
21
|
+
@dataclass(slots=True)
|
|
18
22
|
class FunctionNode:
|
|
19
23
|
func: Op
|
|
20
24
|
args: list[EngineNode]
|
|
25
|
+
_hash: int = field(init=False, repr=False)
|
|
26
|
+
|
|
27
|
+
def __post_init__(self):
|
|
28
|
+
self._hash = hash((self.func, tuple(self.args)))
|
|
21
29
|
|
|
22
30
|
def __hash__(self):
|
|
23
|
-
return
|
|
31
|
+
return self._hash
|
|
24
32
|
|
|
25
33
|
|
|
26
34
|
def format_engine_node(node: EngineNode) -> str:
|
|
@@ -34,7 +42,7 @@ def format_engine_node(node: EngineNode) -> str:
|
|
|
34
42
|
return f"{node.func.name}({format_engine_node(node.args[0])})"
|
|
35
43
|
case _:
|
|
36
44
|
return f"{node.func.name}(\n{
|
|
37
|
-
textwrap.indent(
|
|
45
|
+
textwrap.indent('\n'.join(format_engine_node(arg) for arg in node.args), ' ')
|
|
38
46
|
}\n)"
|
|
39
47
|
else:
|
|
40
48
|
raise ValueError(f"Invalid engine node: {node}")
|
|
@@ -65,7 +65,8 @@ class Allocate(CompilerPass):
|
|
|
65
65
|
def run(self, entry: BasicBlock):
|
|
66
66
|
mapping = self.get_mapping(entry)
|
|
67
67
|
for block in traverse_cfg_preorder(entry):
|
|
68
|
-
|
|
68
|
+
updated_statements = [self.update_stmt(statement, mapping) for statement in block.statements]
|
|
69
|
+
block.statements = [stmt for stmt in updated_statements if stmt is not None]
|
|
69
70
|
block.test = self.update_stmt(block.test, mapping)
|
|
70
71
|
return entry
|
|
71
72
|
|
|
@@ -80,7 +81,19 @@ class Allocate(CompilerPass):
|
|
|
80
81
|
case IRGet(place=place):
|
|
81
82
|
return IRGet(place=self.update_stmt(place, mapping))
|
|
82
83
|
case IRSet(place=place, value=value):
|
|
83
|
-
|
|
84
|
+
# Do some dead code elimination here which is pretty much free since we already have liveness analysis,
|
|
85
|
+
# and prevents an error from the dead block place being missing from the mapping.
|
|
86
|
+
live = get_live(stmt)
|
|
87
|
+
is_live = not (
|
|
88
|
+
(isinstance(place, BlockPlace) and isinstance(place.block, TempBlock) and place.block not in live)
|
|
89
|
+
or (isinstance(value, IRGet) and place == value.place)
|
|
90
|
+
)
|
|
91
|
+
if is_live:
|
|
92
|
+
return IRSet(place=self.update_stmt(place, mapping), value=self.update_stmt(value, mapping))
|
|
93
|
+
elif isinstance(value, IRInstr) and value.op.side_effects:
|
|
94
|
+
return self.update_stmt(value, mapping)
|
|
95
|
+
else:
|
|
96
|
+
return None
|
|
84
97
|
case BlockPlace(block=block, index=index, offset=offset):
|
|
85
98
|
if isinstance(block, TempBlock):
|
|
86
99
|
if block.size == 0:
|
|
@@ -119,8 +132,32 @@ class Allocate(CompilerPass):
|
|
|
119
132
|
def get_interference(self, entry: BasicBlock) -> dict[TempBlock, set[TempBlock]]:
|
|
120
133
|
result = {}
|
|
121
134
|
for block in traverse_cfg_preorder(entry):
|
|
122
|
-
for stmt in
|
|
135
|
+
for stmt in block.statements:
|
|
136
|
+
if not isinstance(stmt, IRSet):
|
|
137
|
+
continue
|
|
123
138
|
live = {p for p in get_live(stmt) if isinstance(p, TempBlock) and p.size > 0}
|
|
124
139
|
for place in live:
|
|
125
|
-
|
|
140
|
+
if place not in result:
|
|
141
|
+
result[place] = set(live)
|
|
142
|
+
else:
|
|
143
|
+
result[place].update(live)
|
|
126
144
|
return result
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class AllocateFast(Allocate):
|
|
148
|
+
"""A bit faster than Allocate but a bit less optimal."""
|
|
149
|
+
|
|
150
|
+
def get_mapping(self, entry: BasicBlock) -> dict[TempBlock, int]:
|
|
151
|
+
interference = self.get_interference(entry)
|
|
152
|
+
offsets: dict[TempBlock, int] = dict.fromkeys(interference, 0)
|
|
153
|
+
end_offsets: dict[TempBlock, int] = dict.fromkeys(interference, 0)
|
|
154
|
+
|
|
155
|
+
for block, others in interference.items():
|
|
156
|
+
size = block.size
|
|
157
|
+
offset = max((end_offsets[other] for other in others), default=0)
|
|
158
|
+
if offset + size > TEMP_SIZE:
|
|
159
|
+
raise ValueError("Temporary memory limit exceeded")
|
|
160
|
+
offsets[block] = offset
|
|
161
|
+
end_offsets[block] = offset + size
|
|
162
|
+
|
|
163
|
+
return offsets
|
|
@@ -91,12 +91,29 @@ def cfg_to_mermaid(entry: BasicBlock):
|
|
|
91
91
|
|
|
92
92
|
lines = ["Entry([Entry]) --> 0"]
|
|
93
93
|
for block, index in block_indexes.items():
|
|
94
|
-
lines.append(
|
|
95
|
-
f"{
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
lines.append(
|
|
95
|
+
f"{index}[{
|
|
96
|
+
pre(
|
|
97
|
+
fmt(
|
|
98
|
+
[
|
|
99
|
+
f'#{index}',
|
|
100
|
+
*(
|
|
101
|
+
f'{dst} := phi({
|
|
102
|
+
", ".join(
|
|
103
|
+
f"{block_indexes.get(src_block, '<dead>')}: {src_place}"
|
|
104
|
+
for src_block, src_place in sorted(
|
|
105
|
+
phis.items(), key=lambda x: block_indexes.get(x[0])
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
})'
|
|
109
|
+
for dst, phis in block.phis.items()
|
|
110
|
+
),
|
|
111
|
+
*block.statements,
|
|
112
|
+
]
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
}]"
|
|
116
|
+
)
|
|
100
117
|
|
|
101
118
|
outgoing = {edge.cond: edge.dst for edge in block.outgoing}
|
|
102
119
|
match outgoing:
|
|
@@ -114,7 +131,7 @@ def cfg_to_mermaid(entry: BasicBlock):
|
|
|
114
131
|
lines.append(f"{index} --> {index}_")
|
|
115
132
|
for cond, target in tgt.items():
|
|
116
133
|
lines.append(
|
|
117
|
-
f"{index}_ --> |{pre(fmt([cond if cond is not None else
|
|
134
|
+
f"{index}_ --> |{pre(fmt([cond if cond is not None else 'default']))}| {block_indexes[target]}"
|
|
118
135
|
)
|
|
119
136
|
lines.append("Exit([Exit])")
|
|
120
137
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from sonolus.backend.optimize.allocate import Allocate, AllocateBasic
|
|
1
|
+
from sonolus.backend.optimize.allocate import Allocate, AllocateBasic, AllocateFast
|
|
2
2
|
from sonolus.backend.optimize.constant_evaluation import SparseConditionalConstantPropagation
|
|
3
3
|
from sonolus.backend.optimize.copy_coalesce import CopyCoalesce
|
|
4
4
|
from sonolus.backend.optimize.dead_code import (
|
|
@@ -6,9 +6,7 @@ from sonolus.backend.optimize.dead_code import (
|
|
|
6
6
|
DeadCodeElimination,
|
|
7
7
|
UnreachableCodeElimination,
|
|
8
8
|
)
|
|
9
|
-
from sonolus.backend.optimize.flow import BasicBlock
|
|
10
9
|
from sonolus.backend.optimize.inlining import InlineVars
|
|
11
|
-
from sonolus.backend.optimize.passes import run_passes
|
|
12
10
|
from sonolus.backend.optimize.simplify import CoalesceFlow, NormalizeSwitch, RewriteToSwitch
|
|
13
11
|
from sonolus.backend.optimize.ssa import FromSSA, ToSSA
|
|
14
12
|
|
|
@@ -21,9 +19,8 @@ MINIMAL_PASSES = (
|
|
|
21
19
|
FAST_PASSES = (
|
|
22
20
|
CoalesceFlow(),
|
|
23
21
|
UnreachableCodeElimination(),
|
|
24
|
-
|
|
22
|
+
AllocateFast(), # Does dead code elimination too, so no need for a separate pass
|
|
25
23
|
CoalesceFlow(),
|
|
26
|
-
Allocate(),
|
|
27
24
|
)
|
|
28
25
|
|
|
29
26
|
STANDARD_PASSES = (
|
|
@@ -46,7 +43,3 @@ STANDARD_PASSES = (
|
|
|
46
43
|
NormalizeSwitch(),
|
|
47
44
|
Allocate(),
|
|
48
45
|
)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def optimize_and_allocate(cfg: BasicBlock):
|
|
52
|
-
return run_passes(cfg, STANDARD_PASSES)
|