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

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

Potentially problematic release.


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

Files changed (90) hide show
  1. sonolus/backend/blocks.py +756 -756
  2. sonolus/backend/excepthook.py +37 -37
  3. sonolus/backend/finalize.py +77 -69
  4. sonolus/backend/interpret.py +7 -7
  5. sonolus/backend/ir.py +29 -3
  6. sonolus/backend/mode.py +24 -24
  7. sonolus/backend/node.py +40 -40
  8. sonolus/backend/ops.py +197 -197
  9. sonolus/backend/optimize/__init__.py +0 -0
  10. sonolus/backend/optimize/allocate.py +126 -0
  11. sonolus/backend/optimize/constant_evaluation.py +374 -0
  12. sonolus/backend/optimize/copy_coalesce.py +85 -0
  13. sonolus/backend/optimize/dead_code.py +185 -0
  14. sonolus/backend/optimize/dominance.py +96 -0
  15. sonolus/backend/{flow.py → optimize/flow.py} +122 -92
  16. sonolus/backend/optimize/inlining.py +137 -0
  17. sonolus/backend/optimize/liveness.py +177 -0
  18. sonolus/backend/optimize/optimize.py +44 -0
  19. sonolus/backend/optimize/passes.py +52 -0
  20. sonolus/backend/optimize/simplify.py +191 -0
  21. sonolus/backend/optimize/ssa.py +200 -0
  22. sonolus/backend/place.py +17 -25
  23. sonolus/backend/utils.py +58 -48
  24. sonolus/backend/visitor.py +1151 -882
  25. sonolus/build/cli.py +7 -1
  26. sonolus/build/compile.py +88 -90
  27. sonolus/build/engine.py +10 -5
  28. sonolus/build/level.py +24 -23
  29. sonolus/build/node.py +43 -43
  30. sonolus/script/archetype.py +438 -139
  31. sonolus/script/array.py +27 -10
  32. sonolus/script/array_like.py +297 -0
  33. sonolus/script/bucket.py +253 -191
  34. sonolus/script/containers.py +257 -51
  35. sonolus/script/debug.py +26 -10
  36. sonolus/script/easing.py +365 -0
  37. sonolus/script/effect.py +191 -131
  38. sonolus/script/engine.py +71 -4
  39. sonolus/script/globals.py +303 -269
  40. sonolus/script/instruction.py +205 -151
  41. sonolus/script/internal/__init__.py +5 -5
  42. sonolus/script/internal/builtin_impls.py +255 -144
  43. sonolus/script/{callbacks.py → internal/callbacks.py} +127 -127
  44. sonolus/script/internal/constant.py +139 -0
  45. sonolus/script/internal/context.py +26 -9
  46. sonolus/script/internal/descriptor.py +17 -17
  47. sonolus/script/internal/dict_impl.py +65 -0
  48. sonolus/script/internal/generic.py +6 -9
  49. sonolus/script/internal/impl.py +38 -13
  50. sonolus/script/internal/introspection.py +17 -14
  51. sonolus/script/internal/math_impls.py +121 -0
  52. sonolus/script/internal/native.py +40 -38
  53. sonolus/script/internal/random.py +67 -0
  54. sonolus/script/internal/range.py +81 -0
  55. sonolus/script/internal/transient.py +51 -0
  56. sonolus/script/internal/tuple_impl.py +113 -0
  57. sonolus/script/internal/value.py +3 -3
  58. sonolus/script/interval.py +338 -112
  59. sonolus/script/iterator.py +167 -214
  60. sonolus/script/level.py +24 -0
  61. sonolus/script/num.py +80 -48
  62. sonolus/script/options.py +257 -191
  63. sonolus/script/particle.py +190 -157
  64. sonolus/script/pointer.py +30 -30
  65. sonolus/script/print.py +102 -81
  66. sonolus/script/project.py +8 -0
  67. sonolus/script/quad.py +263 -0
  68. sonolus/script/record.py +47 -16
  69. sonolus/script/runtime.py +52 -1
  70. sonolus/script/sprite.py +418 -333
  71. sonolus/script/text.py +409 -407
  72. sonolus/script/timing.py +114 -42
  73. sonolus/script/transform.py +332 -48
  74. sonolus/script/ui.py +216 -160
  75. sonolus/script/values.py +6 -13
  76. sonolus/script/vec.py +196 -78
  77. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/METADATA +1 -1
  78. sonolus_py-0.1.5.dist-info/RECORD +89 -0
  79. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/WHEEL +1 -1
  80. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/licenses/LICENSE +21 -21
  81. sonolus/backend/allocate.py +0 -51
  82. sonolus/backend/optimize.py +0 -9
  83. sonolus/backend/passes.py +0 -6
  84. sonolus/backend/simplify.py +0 -30
  85. sonolus/script/comptime.py +0 -160
  86. sonolus/script/graphics.py +0 -150
  87. sonolus/script/math.py +0 -92
  88. sonolus/script/range.py +0 -58
  89. sonolus_py-0.1.3.dist-info/RECORD +0 -75
  90. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/entry_points.txt +0 -0
@@ -1,214 +1,167 @@
1
- from __future__ import annotations
2
-
3
- from abc import abstractmethod
4
- from collections.abc import Collection, Iterator
5
-
6
- from sonolus.script.num import Num
7
- from sonolus.script.record import Record
8
- from sonolus.script.values import copy
9
-
10
-
11
- class SonolusIterator[T](Iterator[T]):
12
- @abstractmethod
13
- def has_next(self) -> bool:
14
- raise NotImplementedError
15
-
16
- @abstractmethod
17
- def next(self) -> T:
18
- raise NotImplementedError
19
-
20
- def __next__(self) -> T:
21
- if not self.has_next():
22
- raise StopIteration
23
- return self.next()
24
-
25
-
26
- class ArrayLike[T](Collection):
27
- @abstractmethod
28
- def size(self) -> int:
29
- pass
30
-
31
- @abstractmethod
32
- def __getitem__(self, index: Num) -> T:
33
- pass
34
-
35
- @abstractmethod
36
- def __setitem__(self, index: Num, value: T):
37
- pass
38
-
39
- def __len__(self) -> int:
40
- return self.size()
41
-
42
- def __iter__(self) -> SonolusIterator[T]:
43
- return ArrayIterator(0, self)
44
-
45
- def __contains__(self, value: T) -> bool:
46
- i = 0
47
- while i < self.size():
48
- if self[i] == value:
49
- return True
50
- i += 1
51
- return False
52
-
53
- def reversed(self) -> ArrayLike[T]:
54
- return ArrayReverser(self)
55
-
56
- def iter(self) -> SonolusIterator[T]:
57
- return self.__iter__() # noqa: PLC2801
58
-
59
- def enumerate(self, start: Num = 0) -> SonolusIterator[T]:
60
- return ArrayEnumerator(0, start, self)
61
-
62
- def index_of(self, value: T, start: Num = 0) -> Num:
63
- i = start
64
- while i < self.size():
65
- if self[i] == value:
66
- return i
67
- i += 1
68
- return -1
69
-
70
- def last_index_of(self, value: T) -> Num:
71
- i = self.size() - 1
72
- while i >= 0:
73
- if self[i] == value:
74
- return i
75
- i -= 1
76
- return -1
77
-
78
- def index_of_max(self) -> Num:
79
- if self.size() == 0:
80
- return -1
81
- max_index = 0
82
- i = 1
83
- while i < self.size():
84
- if self[i] > self[max_index]:
85
- max_index = i
86
- i += 1
87
- return max_index
88
-
89
- def index_of_min(self) -> Num:
90
- if self.size() == 0:
91
- return -1
92
- min_index = 0
93
- i = 1
94
- while i < self.size():
95
- if self[i] < self[min_index]:
96
- min_index = i
97
- i += 1
98
- return min_index
99
-
100
- def max(self) -> T:
101
- return self[self.index_of_max()]
102
-
103
- def min(self) -> T:
104
- return self[self.index_of_min()]
105
-
106
- def swap(self, i: Num, j: Num):
107
- temp = copy(self[i])
108
- self[i] = self[j]
109
- self[j] = temp
110
-
111
- def sort(self, *, reverse: bool = False):
112
- if self.size() < 15:
113
- _insertion_sort(self, 0, self.size(), reverse)
114
- else:
115
- _heap_sort(self, 0, self.size(), reverse)
116
-
117
-
118
- def _insertion_sort[T](array: ArrayLike[T], start: Num, end: Num, reverse: bool):
119
- i = start + 1
120
- while i < end:
121
- value = copy(array[i])
122
- j = i - 1
123
- while j >= start and (array[j] > value) != reverse:
124
- array[j + 1] = array[j]
125
- j -= 1
126
- array[j + 1] = value
127
- i += 1
128
-
129
-
130
- def _heapify[T](array: ArrayLike[T], end: Num, index: Num, reverse: bool):
131
- while True:
132
- left = index * 2 + 1
133
- right = left + 1
134
- largest = index
135
- if left < end and (array[left] > array[largest]) != reverse:
136
- largest = left
137
- if right < end and (array[right] > array[largest]) != reverse:
138
- largest = right
139
- if largest == index:
140
- break
141
- array.swap(index, largest)
142
- index = largest
143
-
144
-
145
- # Heap sort is simple to implement iteratively without dynamic memory allocation
146
- def _heap_sort[T](array: ArrayLike[T], start: Num, end: Num, reverse: bool):
147
- i = end // 2 - 1
148
- while i >= start:
149
- _heapify(array, end, i, reverse)
150
- i -= 1
151
- i = end - 1
152
- while i > start:
153
- array.swap(start, i)
154
- _heapify(array, i, start, reverse)
155
- i -= 1
156
-
157
-
158
- class ArrayIterator[V: ArrayLike](Record, SonolusIterator):
159
- i: int
160
- array: V
161
-
162
- def has_next(self) -> bool:
163
- return self.i < self.array.size()
164
-
165
- def next(self) -> V:
166
- value = self.array[self.i]
167
- self.i += 1
168
- return value
169
-
170
-
171
- class ArrayReverser[V: ArrayLike](Record, ArrayLike):
172
- array: V
173
-
174
- def size(self) -> int:
175
- return self.array.size()
176
-
177
- def __getitem__(self, index: Num) -> V:
178
- return self.array[self.size() - 1 - index]
179
-
180
- def __setitem__(self, index: Num, value: V):
181
- self.array[self.size() - 1 - index] = value
182
-
183
- def reversed(self) -> ArrayLike[V]:
184
- return self.array
185
-
186
-
187
- class Enumerator[V: SonolusIterator](Record, SonolusIterator):
188
- i: int
189
- offset: int
190
- iterator: V
191
-
192
- def has_next(self) -> bool:
193
- return self.iterator.has_next()
194
-
195
- def next(self):
196
- value = self.iterator.next()
197
- index = self.i + self.offset
198
- self.i += 1
199
- return index, value
200
-
201
-
202
- class ArrayEnumerator[V: ArrayLike](Record, SonolusIterator):
203
- i: int
204
- offset: int
205
- array: V
206
-
207
- def has_next(self) -> bool:
208
- return self.i < self.array.size()
209
-
210
- def next(self):
211
- value = self.array[self.i]
212
- index = self.i + self.offset
213
- self.i += 1
214
- return index, value
1
+ from __future__ import annotations
2
+
3
+ from abc import abstractmethod
4
+ from collections.abc import Iterator
5
+ from typing import Any
6
+
7
+ from sonolus.script.internal.impl import meta_fn
8
+ from sonolus.script.record import Record
9
+
10
+
11
+ class SonolusIterator[T](Iterator[T]):
12
+ """Base class for Sonolus iterators.
13
+
14
+ This class is used to define custom iterators that can be used in Sonolus.py.
15
+
16
+ Inheritors must implement the `has_next`, `get`, and `advance` methods.
17
+ The `__next__` and `__iter__` methods are implemented by default.
18
+
19
+ Usage:
20
+ ```python
21
+ class MyIterator(Record, SonolusIterator):
22
+ def has_next(self) -> bool:
23
+ ...
24
+
25
+ def get(self) -> Any:
26
+ ...
27
+
28
+ def advance(self):
29
+ ...
30
+ ```
31
+ """
32
+
33
+ def next(self) -> T:
34
+ result = self.get()
35
+ self.advance()
36
+ return result
37
+
38
+ @abstractmethod
39
+ def has_next(self) -> bool:
40
+ """Return whether the iterator has more elements."""
41
+ raise NotImplementedError
42
+
43
+ @abstractmethod
44
+ def get(self) -> T:
45
+ """Return the next element of the iterator.
46
+
47
+ May be called multiple times before calling `advance`.
48
+
49
+ Must not be called if `has_next` returns `False`.
50
+ """
51
+ raise NotImplementedError
52
+
53
+ @abstractmethod
54
+ def advance(self):
55
+ """Advance the iterator to the next element.
56
+
57
+ Must not be called if `has_next` returns `False`.
58
+ """
59
+ raise NotImplementedError
60
+
61
+ def __next__(self) -> T:
62
+ if not self.has_next():
63
+ raise StopIteration
64
+ return self.next()
65
+
66
+ def __iter__(self) -> SonolusIterator[T]:
67
+ return self
68
+
69
+
70
+ class _Enumerator[V: SonolusIterator](Record, SonolusIterator):
71
+ i: int
72
+ offset: int
73
+ iterator: V
74
+
75
+ def has_next(self) -> bool:
76
+ return self.iterator.has_next()
77
+
78
+ def get(self) -> tuple[int, Any]:
79
+ return self.i + self.offset, self.iterator.get()
80
+
81
+ def advance(self):
82
+ self.i += 1
83
+ self.iterator.advance()
84
+
85
+
86
+ class _Zipper[T](Record, SonolusIterator):
87
+ # Can be a, Pair[a, b], Pair[a, Pair[b, c]], etc.
88
+ iterators: T
89
+
90
+ @meta_fn
91
+ def has_next(self) -> bool:
92
+ from sonolus.backend.visitor import compile_and_call
93
+
94
+ return compile_and_call(self._has_next, self._get_iterators())
95
+
96
+ def _get_iterators(self) -> tuple[SonolusIterator, ...]:
97
+ from sonolus.script.containers import Pair
98
+
99
+ iterators = []
100
+ v = self.iterators
101
+ while isinstance(v, Pair):
102
+ iterators.append(v.first)
103
+ v = v.second
104
+ iterators.append(v)
105
+ return tuple(iterators)
106
+
107
+ def _has_next(self, iterators: tuple[SonolusIterator, ...]) -> bool:
108
+ for iterator in iterators: # noqa: SIM110
109
+ if not iterator.has_next():
110
+ return False
111
+ return True
112
+
113
+ @meta_fn
114
+ def get(self) -> tuple[Any, ...]:
115
+ from sonolus.backend.visitor import compile_and_call
116
+
117
+ return tuple(compile_and_call(iterator.get) for iterator in self._get_iterators())
118
+
119
+ @meta_fn
120
+ def advance(self):
121
+ from sonolus.backend.visitor import compile_and_call
122
+
123
+ for iterator in self._get_iterators():
124
+ compile_and_call(iterator.advance)
125
+
126
+
127
+ class _EmptyIterator(Record, SonolusIterator):
128
+ def has_next(self) -> bool:
129
+ return False
130
+
131
+ def get(self) -> Any:
132
+ return None
133
+
134
+ def advance(self):
135
+ pass
136
+
137
+
138
+ class _MappingIterator[T, Fn](Record, SonolusIterator):
139
+ fn: Fn
140
+ iterator: T
141
+
142
+ def has_next(self) -> bool:
143
+ return self.iterator.has_next()
144
+
145
+ def get(self) -> Any:
146
+ return self.fn(self.iterator.get())
147
+
148
+ def advance(self):
149
+ self.iterator.advance()
150
+
151
+
152
+ class _FilteringIterator[T, Fn](Record, SonolusIterator):
153
+ fn: Fn
154
+ iterator: T
155
+
156
+ def has_next(self) -> bool:
157
+ while self.iterator.has_next():
158
+ if self.fn(self.iterator.get()):
159
+ return True
160
+ self.iterator.advance()
161
+ return False
162
+
163
+ def get(self) -> Any:
164
+ return self.iterator.get()
165
+
166
+ def advance(self):
167
+ self.iterator.advance()
sonolus/script/level.py CHANGED
@@ -5,6 +5,19 @@ from sonolus.script.archetype import PlayArchetype, StandardArchetypeName, Stand
5
5
 
6
6
 
7
7
  class Level:
8
+ """A Sonolus level.
9
+
10
+ Args:
11
+ name: The name of the level.
12
+ title: The title of the level.
13
+ rating: The rating of the level.
14
+ artists: The artists of the level.
15
+ author: The author of the level.
16
+ cover: The cover of the level.
17
+ bgm: The background music of the level.
18
+ data: The data of the level.
19
+ """
20
+
8
21
  version = 1
9
22
 
10
23
  def __init__(
@@ -30,6 +43,13 @@ class Level:
30
43
 
31
44
 
32
45
  class LevelData:
46
+ """The data of a Sonolus level.
47
+
48
+ Args:
49
+ bgm_offset: The background music audio offset.
50
+ entities: The entities of the level.
51
+ """
52
+
33
53
  bgm_offset: float
34
54
  entities: list[PlayArchetype]
35
55
 
@@ -39,6 +59,8 @@ class LevelData:
39
59
 
40
60
 
41
61
  class BpmChange(PlayArchetype):
62
+ """The standard bpm change archetype."""
63
+
42
64
  name = StandardArchetypeName.BPM_CHANGE
43
65
 
44
66
  beat: StandardImport.BEAT
@@ -46,6 +68,8 @@ class BpmChange(PlayArchetype):
46
68
 
47
69
 
48
70
  class TimescaleChange(PlayArchetype):
71
+ """The standard timescale change archetype."""
72
+
49
73
  name = StandardArchetypeName.TIMESCALE_CHANGE
50
74
 
51
75
  beat: StandardImport.BEAT