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.
- sonolus/backend/blocks.py +756 -756
- sonolus/backend/excepthook.py +37 -37
- sonolus/backend/finalize.py +77 -69
- sonolus/backend/interpret.py +7 -7
- sonolus/backend/ir.py +29 -3
- sonolus/backend/mode.py +24 -24
- sonolus/backend/node.py +40 -40
- sonolus/backend/ops.py +197 -197
- sonolus/backend/optimize/__init__.py +0 -0
- sonolus/backend/optimize/allocate.py +126 -0
- sonolus/backend/optimize/constant_evaluation.py +374 -0
- sonolus/backend/optimize/copy_coalesce.py +85 -0
- sonolus/backend/optimize/dead_code.py +185 -0
- sonolus/backend/optimize/dominance.py +96 -0
- sonolus/backend/{flow.py → optimize/flow.py} +122 -92
- sonolus/backend/optimize/inlining.py +137 -0
- sonolus/backend/optimize/liveness.py +177 -0
- sonolus/backend/optimize/optimize.py +44 -0
- sonolus/backend/optimize/passes.py +52 -0
- sonolus/backend/optimize/simplify.py +191 -0
- sonolus/backend/optimize/ssa.py +200 -0
- sonolus/backend/place.py +17 -25
- sonolus/backend/utils.py +58 -48
- sonolus/backend/visitor.py +1151 -882
- sonolus/build/cli.py +7 -1
- sonolus/build/compile.py +88 -90
- sonolus/build/engine.py +10 -5
- sonolus/build/level.py +24 -23
- sonolus/build/node.py +43 -43
- sonolus/script/archetype.py +438 -139
- sonolus/script/array.py +27 -10
- sonolus/script/array_like.py +297 -0
- sonolus/script/bucket.py +253 -191
- sonolus/script/containers.py +257 -51
- sonolus/script/debug.py +26 -10
- sonolus/script/easing.py +365 -0
- sonolus/script/effect.py +191 -131
- sonolus/script/engine.py +71 -4
- sonolus/script/globals.py +303 -269
- sonolus/script/instruction.py +205 -151
- sonolus/script/internal/__init__.py +5 -5
- sonolus/script/internal/builtin_impls.py +255 -144
- sonolus/script/{callbacks.py → internal/callbacks.py} +127 -127
- sonolus/script/internal/constant.py +139 -0
- sonolus/script/internal/context.py +26 -9
- sonolus/script/internal/descriptor.py +17 -17
- sonolus/script/internal/dict_impl.py +65 -0
- sonolus/script/internal/generic.py +6 -9
- sonolus/script/internal/impl.py +38 -13
- sonolus/script/internal/introspection.py +17 -14
- sonolus/script/internal/math_impls.py +121 -0
- sonolus/script/internal/native.py +40 -38
- sonolus/script/internal/random.py +67 -0
- sonolus/script/internal/range.py +81 -0
- sonolus/script/internal/transient.py +51 -0
- sonolus/script/internal/tuple_impl.py +113 -0
- sonolus/script/internal/value.py +3 -3
- sonolus/script/interval.py +338 -112
- sonolus/script/iterator.py +167 -214
- sonolus/script/level.py +24 -0
- sonolus/script/num.py +80 -48
- sonolus/script/options.py +257 -191
- sonolus/script/particle.py +190 -157
- sonolus/script/pointer.py +30 -30
- sonolus/script/print.py +102 -81
- sonolus/script/project.py +8 -0
- sonolus/script/quad.py +263 -0
- sonolus/script/record.py +47 -16
- sonolus/script/runtime.py +52 -1
- sonolus/script/sprite.py +418 -333
- sonolus/script/text.py +409 -407
- sonolus/script/timing.py +114 -42
- sonolus/script/transform.py +332 -48
- sonolus/script/ui.py +216 -160
- sonolus/script/values.py +6 -13
- sonolus/script/vec.py +196 -78
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/METADATA +1 -1
- sonolus_py-0.1.5.dist-info/RECORD +89 -0
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/WHEEL +1 -1
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/licenses/LICENSE +21 -21
- sonolus/backend/allocate.py +0 -51
- sonolus/backend/optimize.py +0 -9
- sonolus/backend/passes.py +0 -6
- sonolus/backend/simplify.py +0 -30
- sonolus/script/comptime.py +0 -160
- sonolus/script/graphics.py +0 -150
- sonolus/script/math.py +0 -92
- sonolus/script/range.py +0 -58
- sonolus_py-0.1.3.dist-info/RECORD +0 -75
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/entry_points.txt +0 -0
sonolus/backend/excepthook.py
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
"""Exception hook to filter out compiler internal frames from tracebacks."""
|
|
2
|
-
|
|
3
|
-
import sys
|
|
4
|
-
from types import TracebackType
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def should_filter_traceback(tb: TracebackType | None) -> bool:
|
|
8
|
-
return tb is not None and (
|
|
9
|
-
tb.tb_frame.f_globals.get("_filter_traceback_", False) or should_filter_traceback(tb.tb_next)
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def is_compiler_internal(tb: TracebackType):
|
|
14
|
-
return tb.tb_frame.f_locals.get("_compiler_internal_", False) or tb.tb_frame.f_globals.get(
|
|
15
|
-
"_compiler_internal_", False
|
|
16
|
-
)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
def filter_traceback(tb: TracebackType | None) -> TracebackType | None:
|
|
20
|
-
if tb is None:
|
|
21
|
-
return None
|
|
22
|
-
if is_compiler_internal(tb):
|
|
23
|
-
return filter_traceback(tb.tb_next)
|
|
24
|
-
tb.tb_next = filter_traceback(tb.tb_next)
|
|
25
|
-
return tb
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def excepthook(exc, value, tb):
|
|
29
|
-
import traceback
|
|
30
|
-
|
|
31
|
-
if should_filter_traceback(tb):
|
|
32
|
-
tb = filter_traceback(tb)
|
|
33
|
-
traceback.print_exception(exc, value, tb)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def install_excepthook():
|
|
37
|
-
sys.excepthook = excepthook
|
|
1
|
+
"""Exception hook to filter out compiler internal frames from tracebacks."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from types import TracebackType
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def should_filter_traceback(tb: TracebackType | None) -> bool:
|
|
8
|
+
return tb is not None and (
|
|
9
|
+
tb.tb_frame.f_globals.get("_filter_traceback_", False) or should_filter_traceback(tb.tb_next)
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def is_compiler_internal(tb: TracebackType):
|
|
14
|
+
return tb.tb_frame.f_locals.get("_compiler_internal_", False) or tb.tb_frame.f_globals.get(
|
|
15
|
+
"_compiler_internal_", False
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def filter_traceback(tb: TracebackType | None) -> TracebackType | None:
|
|
20
|
+
if tb is None:
|
|
21
|
+
return None
|
|
22
|
+
if is_compiler_internal(tb):
|
|
23
|
+
return filter_traceback(tb.tb_next)
|
|
24
|
+
tb.tb_next = filter_traceback(tb.tb_next)
|
|
25
|
+
return tb
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def excepthook(exc, value, tb):
|
|
29
|
+
import traceback
|
|
30
|
+
|
|
31
|
+
if should_filter_traceback(tb):
|
|
32
|
+
tb = filter_traceback(tb)
|
|
33
|
+
traceback.print_exception(exc, value, tb)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def install_excepthook():
|
|
37
|
+
sys.excepthook = excepthook
|
sonolus/backend/finalize.py
CHANGED
|
@@ -1,69 +1,77 @@
|
|
|
1
|
-
from sonolus.backend.
|
|
2
|
-
from sonolus.backend.
|
|
3
|
-
from sonolus.backend.
|
|
4
|
-
from sonolus.backend.
|
|
5
|
-
from sonolus.backend.place import BlockPlace
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def cfg_to_engine_node(entry: BasicBlock):
|
|
9
|
-
block_indexes = {block: i for i, block in enumerate(
|
|
10
|
-
block_statements = []
|
|
11
|
-
for block in block_indexes:
|
|
12
|
-
statements = []
|
|
13
|
-
statements.extend(ir_to_engine_node(stmt) for stmt in block.statements)
|
|
14
|
-
outgoing = {edge.cond: edge.dst for edge in block.outgoing}
|
|
15
|
-
match outgoing:
|
|
16
|
-
case {**other} if not other:
|
|
17
|
-
statements.append(ConstantNode(value=len(block_indexes)))
|
|
18
|
-
case {None: target, **other} if not other:
|
|
19
|
-
statements.append(ConstantNode(value=block_indexes[target]))
|
|
20
|
-
case {0: f_branch, None: t_branch, **other} if not other:
|
|
21
|
-
statements.append(
|
|
22
|
-
FunctionNode(
|
|
23
|
-
func=Op.If,
|
|
24
|
-
args=[
|
|
25
|
-
ir_to_engine_node(block.test),
|
|
26
|
-
ConstantNode(value=block_indexes[t_branch]),
|
|
27
|
-
ConstantNode(value=block_indexes[f_branch]),
|
|
28
|
-
],
|
|
29
|
-
)
|
|
30
|
-
)
|
|
31
|
-
case dict() as
|
|
32
|
-
args = [ir_to_engine_node(block.test)]
|
|
33
|
-
default = len(block_indexes)
|
|
34
|
-
for cond
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
case
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
1
|
+
from sonolus.backend.ir import IRConst, IRGet, IRInstr, IRPureInstr, IRSet
|
|
2
|
+
from sonolus.backend.node import ConstantNode, EngineNode, FunctionNode
|
|
3
|
+
from sonolus.backend.ops import Op
|
|
4
|
+
from sonolus.backend.optimize.flow import BasicBlock, traverse_cfg_reverse_postorder
|
|
5
|
+
from sonolus.backend.place import BlockPlace
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def cfg_to_engine_node(entry: BasicBlock):
|
|
9
|
+
block_indexes = {block: i for i, block in enumerate(traverse_cfg_reverse_postorder(entry))}
|
|
10
|
+
block_statements = []
|
|
11
|
+
for block in block_indexes:
|
|
12
|
+
statements = []
|
|
13
|
+
statements.extend(ir_to_engine_node(stmt) for stmt in block.statements)
|
|
14
|
+
outgoing = {edge.cond: edge.dst for edge in block.outgoing}
|
|
15
|
+
match outgoing:
|
|
16
|
+
case {**other} if not other:
|
|
17
|
+
statements.append(ConstantNode(value=len(block_indexes)))
|
|
18
|
+
case {None: target, **other} if not other:
|
|
19
|
+
statements.append(ConstantNode(value=block_indexes[target]))
|
|
20
|
+
case {0: f_branch, None: t_branch, **other} if not other:
|
|
21
|
+
statements.append(
|
|
22
|
+
FunctionNode(
|
|
23
|
+
func=Op.If,
|
|
24
|
+
args=[
|
|
25
|
+
ir_to_engine_node(block.test),
|
|
26
|
+
ConstantNode(value=block_indexes[t_branch]),
|
|
27
|
+
ConstantNode(value=block_indexes[f_branch]),
|
|
28
|
+
],
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
case dict() as targets:
|
|
32
|
+
args = [ir_to_engine_node(block.test)]
|
|
33
|
+
default = len(block_indexes)
|
|
34
|
+
conds = [cond for cond in targets if cond is not None]
|
|
35
|
+
if min(conds) == 0 and max(conds) == len(conds) - 1 and all(int(cond) == cond for cond in conds):
|
|
36
|
+
args.extend(ConstantNode(value=block_indexes[targets[cond]]) for cond in range(len(conds)))
|
|
37
|
+
if None in targets:
|
|
38
|
+
default = block_indexes[targets[None]]
|
|
39
|
+
args.append(ConstantNode(value=default))
|
|
40
|
+
statements.append(FunctionNode(Op.SwitchIntegerWithDefault, args))
|
|
41
|
+
else:
|
|
42
|
+
for cond, target in targets.items():
|
|
43
|
+
if cond is None:
|
|
44
|
+
default = block_indexes[target]
|
|
45
|
+
args.append(ConstantNode(value=cond))
|
|
46
|
+
args.append(ConstantNode(value=block_indexes[target]))
|
|
47
|
+
args.append(ConstantNode(value=default))
|
|
48
|
+
statements.append(FunctionNode(Op.SwitchWithDefault, args))
|
|
49
|
+
block_statements.append(FunctionNode(Op.Execute, statements))
|
|
50
|
+
block_statements.append(ConstantNode(value=0))
|
|
51
|
+
return FunctionNode(Op.Block, [FunctionNode(Op.JumpLoop, block_statements)])
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def ir_to_engine_node(stmt) -> EngineNode:
|
|
55
|
+
match stmt:
|
|
56
|
+
case int() | float():
|
|
57
|
+
return ConstantNode(value=float(stmt))
|
|
58
|
+
case IRConst(value=value):
|
|
59
|
+
return ConstantNode(value=value)
|
|
60
|
+
case IRPureInstr(op=op, args=args) | IRInstr(op=op, args=args):
|
|
61
|
+
return FunctionNode(func=op, args=[ir_to_engine_node(arg) for arg in args])
|
|
62
|
+
case IRGet(place=place):
|
|
63
|
+
return ir_to_engine_node(place)
|
|
64
|
+
case BlockPlace() as place:
|
|
65
|
+
if place.offset == 0:
|
|
66
|
+
index = ir_to_engine_node(place.index)
|
|
67
|
+
elif place.index == 0:
|
|
68
|
+
index = ConstantNode(value=place.offset)
|
|
69
|
+
else:
|
|
70
|
+
index = FunctionNode(
|
|
71
|
+
func=Op.Add, args=[ir_to_engine_node(place.index), ConstantNode(value=place.offset)]
|
|
72
|
+
)
|
|
73
|
+
return FunctionNode(func=Op.Get, args=[ir_to_engine_node(place.block), index])
|
|
74
|
+
case IRSet(place=place, value=value):
|
|
75
|
+
return FunctionNode(func=Op.Set, args=[*ir_to_engine_node(place).args, ir_to_engine_node(value)])
|
|
76
|
+
case _:
|
|
77
|
+
raise TypeError(f"Unsupported IR statement: {stmt}")
|
sonolus/backend/interpret.py
CHANGED
|
@@ -62,16 +62,16 @@ class Interpreter:
|
|
|
62
62
|
return self.run(default)
|
|
63
63
|
case Op.SwitchInteger:
|
|
64
64
|
test, *branches = args
|
|
65
|
-
test_result =
|
|
66
|
-
if 0 <= test_result < len(branches):
|
|
65
|
+
test_result = self.run(test)
|
|
66
|
+
if 0 <= test_result < len(branches) and int(test_result) == test_result:
|
|
67
67
|
return self.run(branches[test_result])
|
|
68
68
|
else:
|
|
69
69
|
return 0.0
|
|
70
70
|
case Op.SwitchIntegerWithDefault:
|
|
71
71
|
test, *branches, default = args
|
|
72
|
-
test_result =
|
|
73
|
-
if 0 <= test_result < len(branches):
|
|
74
|
-
return self.run(branches[test_result])
|
|
72
|
+
test_result = self.run(test)
|
|
73
|
+
if 0 <= test_result < len(branches) and int(test_result) == test_result:
|
|
74
|
+
return self.run(branches[int(test_result)])
|
|
75
75
|
else:
|
|
76
76
|
return self.run(default)
|
|
77
77
|
case Op.While:
|
|
@@ -242,10 +242,10 @@ class Interpreter:
|
|
|
242
242
|
return math.radians(self.run(args[0]))
|
|
243
243
|
case Op.Random:
|
|
244
244
|
lo, hi = (self.run(arg) for arg in args)
|
|
245
|
-
return lo
|
|
245
|
+
return random.uniform(lo, hi)
|
|
246
246
|
case Op.RandomInteger:
|
|
247
247
|
lo, hi = (self.ensure_int(self.run(arg)) for arg in args)
|
|
248
|
-
return random.
|
|
248
|
+
return random.randrange(lo, hi)
|
|
249
249
|
case Op.Rem:
|
|
250
250
|
return self.reduce_args(args, math.remainder)
|
|
251
251
|
case Op.Remap:
|
sonolus/backend/ir.py
CHANGED
|
@@ -6,9 +6,11 @@ type IRStmt = IRExpr | IRInstr | IRSet
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class IRConst:
|
|
9
|
-
value: float
|
|
9
|
+
value: float | int
|
|
10
10
|
|
|
11
11
|
def __init__(self, value: float):
|
|
12
|
+
if isinstance(value, bool):
|
|
13
|
+
value = int(value)
|
|
12
14
|
self.value = value
|
|
13
15
|
|
|
14
16
|
def __repr__(self):
|
|
@@ -39,6 +41,12 @@ class IRPureInstr:
|
|
|
39
41
|
def __str__(self):
|
|
40
42
|
return f"{self.op.name}({', '.join(map(str, self.args))})"
|
|
41
43
|
|
|
44
|
+
def __eq__(self, other):
|
|
45
|
+
return isinstance(other, IRPureInstr) and self.op == other.op and self.args == other.args
|
|
46
|
+
|
|
47
|
+
def __hash__(self):
|
|
48
|
+
return hash((self.op, tuple(self.args)))
|
|
49
|
+
|
|
42
50
|
|
|
43
51
|
class IRInstr:
|
|
44
52
|
op: Op
|
|
@@ -54,6 +62,12 @@ class IRInstr:
|
|
|
54
62
|
def __str__(self):
|
|
55
63
|
return f"{self.op.name}({', '.join(map(str, self.args))})"
|
|
56
64
|
|
|
65
|
+
def __eq__(self, other):
|
|
66
|
+
return isinstance(other, IRInstr) and self.op == other.op and self.args == other.args
|
|
67
|
+
|
|
68
|
+
def __hash__(self):
|
|
69
|
+
return hash((self.op, tuple(self.args)))
|
|
70
|
+
|
|
57
71
|
|
|
58
72
|
class IRGet:
|
|
59
73
|
place: Place
|
|
@@ -67,12 +81,18 @@ class IRGet:
|
|
|
67
81
|
def __str__(self):
|
|
68
82
|
return f"{self.place}"
|
|
69
83
|
|
|
84
|
+
def __eq__(self, other):
|
|
85
|
+
return isinstance(other, IRGet) and self.place == other.place
|
|
86
|
+
|
|
87
|
+
def __hash__(self):
|
|
88
|
+
return hash(self.place)
|
|
89
|
+
|
|
70
90
|
|
|
71
91
|
class IRSet:
|
|
72
92
|
place: Place
|
|
73
|
-
value:
|
|
93
|
+
value: IRExpr | IRInstr
|
|
74
94
|
|
|
75
|
-
def __init__(self, place: Place, value:
|
|
95
|
+
def __init__(self, place: Place, value: IRExpr | IRInstr):
|
|
76
96
|
self.place = place
|
|
77
97
|
self.value = value
|
|
78
98
|
|
|
@@ -87,3 +107,9 @@ class IRSet:
|
|
|
87
107
|
return f"{self.place} := {self.value}"
|
|
88
108
|
case _:
|
|
89
109
|
raise TypeError(f"Invalid place: {self.place}")
|
|
110
|
+
|
|
111
|
+
def __eq__(self, other):
|
|
112
|
+
return isinstance(other, IRSet) and self.place == other.place and self.value == other.value
|
|
113
|
+
|
|
114
|
+
def __hash__(self):
|
|
115
|
+
return hash((self.place, self.value))
|
sonolus/backend/mode.py
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
from enum import Enum
|
|
2
|
-
from functools import cached_property
|
|
3
|
-
|
|
4
|
-
from sonolus.backend.blocks import Block, PlayBlock, PreviewBlock, TutorialBlock, WatchBlock
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class Mode(Enum):
|
|
8
|
-
blocks: type[Block]
|
|
9
|
-
|
|
10
|
-
PLAY = (PlayBlock,)
|
|
11
|
-
WATCH = (WatchBlock,)
|
|
12
|
-
PREVIEW = (PreviewBlock,)
|
|
13
|
-
TUTORIAL = (TutorialBlock,)
|
|
14
|
-
|
|
15
|
-
def __init__(self, blocks: type[Block]):
|
|
16
|
-
self.blocks = blocks
|
|
17
|
-
|
|
18
|
-
@cached_property
|
|
19
|
-
def callbacks(self) -> frozenset[str]:
|
|
20
|
-
cbs = set()
|
|
21
|
-
for block in self.blocks:
|
|
22
|
-
cbs.update(block.readable)
|
|
23
|
-
cbs.update(block.writable)
|
|
24
|
-
return frozenset(cbs)
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from functools import cached_property
|
|
3
|
+
|
|
4
|
+
from sonolus.backend.blocks import Block, PlayBlock, PreviewBlock, TutorialBlock, WatchBlock
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Mode(Enum):
|
|
8
|
+
blocks: type[Block]
|
|
9
|
+
|
|
10
|
+
PLAY = (PlayBlock,)
|
|
11
|
+
WATCH = (WatchBlock,)
|
|
12
|
+
PREVIEW = (PreviewBlock,)
|
|
13
|
+
TUTORIAL = (TutorialBlock,)
|
|
14
|
+
|
|
15
|
+
def __init__(self, blocks: type[Block]):
|
|
16
|
+
self.blocks = blocks
|
|
17
|
+
|
|
18
|
+
@cached_property
|
|
19
|
+
def callbacks(self) -> frozenset[str]:
|
|
20
|
+
cbs = set()
|
|
21
|
+
for block in self.blocks:
|
|
22
|
+
cbs.update(block.readable)
|
|
23
|
+
cbs.update(block.writable)
|
|
24
|
+
return frozenset(cbs)
|
sonolus/backend/node.py
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
import textwrap
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
|
|
4
|
-
from sonolus.backend.ops import Op
|
|
5
|
-
|
|
6
|
-
type EngineNode = ConstantNode | FunctionNode
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@dataclass
|
|
10
|
-
class ConstantNode:
|
|
11
|
-
value: float
|
|
12
|
-
|
|
13
|
-
def __hash__(self):
|
|
14
|
-
return hash(self.value)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@dataclass
|
|
18
|
-
class FunctionNode:
|
|
19
|
-
func: Op
|
|
20
|
-
args: list[EngineNode]
|
|
21
|
-
|
|
22
|
-
def __hash__(self):
|
|
23
|
-
return hash((self.func, tuple(self.args)))
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def format_engine_node(node: EngineNode) -> str:
|
|
27
|
-
if isinstance(node, ConstantNode):
|
|
28
|
-
return str(node.value)
|
|
29
|
-
elif isinstance(node, FunctionNode):
|
|
30
|
-
match len(node.args):
|
|
31
|
-
case 0:
|
|
32
|
-
return f"{node.func.name}()"
|
|
33
|
-
case 1:
|
|
34
|
-
return f"{node.func.name}({format_engine_node(node.args[0])})"
|
|
35
|
-
case _:
|
|
36
|
-
return f"{node.func.name}(\n{
|
|
37
|
-
textwrap.indent("\n".join(format_engine_node(arg) for arg in node.args), " ")
|
|
38
|
-
}\n)"
|
|
39
|
-
else:
|
|
40
|
-
raise ValueError(f"Invalid engine node: {node}")
|
|
1
|
+
import textwrap
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
from sonolus.backend.ops import Op
|
|
5
|
+
|
|
6
|
+
type EngineNode = ConstantNode | FunctionNode
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class ConstantNode:
|
|
11
|
+
value: float
|
|
12
|
+
|
|
13
|
+
def __hash__(self):
|
|
14
|
+
return hash(self.value)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class FunctionNode:
|
|
19
|
+
func: Op
|
|
20
|
+
args: list[EngineNode]
|
|
21
|
+
|
|
22
|
+
def __hash__(self):
|
|
23
|
+
return hash((self.func, tuple(self.args)))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def format_engine_node(node: EngineNode) -> str:
|
|
27
|
+
if isinstance(node, ConstantNode):
|
|
28
|
+
return str(node.value)
|
|
29
|
+
elif isinstance(node, FunctionNode):
|
|
30
|
+
match len(node.args):
|
|
31
|
+
case 0:
|
|
32
|
+
return f"{node.func.name}()"
|
|
33
|
+
case 1:
|
|
34
|
+
return f"{node.func.name}({format_engine_node(node.args[0])})"
|
|
35
|
+
case _:
|
|
36
|
+
return f"{node.func.name}(\n{
|
|
37
|
+
textwrap.indent("\n".join(format_engine_node(arg) for arg in node.args), " ")
|
|
38
|
+
}\n)"
|
|
39
|
+
else:
|
|
40
|
+
raise ValueError(f"Invalid engine node: {node}")
|