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

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

Potentially problematic release.


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

Files changed (90) hide show
  1. sonolus/backend/blocks.py +756 -756
  2. sonolus/backend/excepthook.py +37 -37
  3. sonolus/backend/finalize.py +77 -69
  4. sonolus/backend/interpret.py +7 -7
  5. sonolus/backend/ir.py +29 -3
  6. sonolus/backend/mode.py +24 -24
  7. sonolus/backend/node.py +40 -40
  8. sonolus/backend/ops.py +197 -197
  9. sonolus/backend/optimize/__init__.py +0 -0
  10. sonolus/backend/optimize/allocate.py +126 -0
  11. sonolus/backend/optimize/constant_evaluation.py +374 -0
  12. sonolus/backend/optimize/copy_coalesce.py +85 -0
  13. sonolus/backend/optimize/dead_code.py +185 -0
  14. sonolus/backend/optimize/dominance.py +96 -0
  15. sonolus/backend/{flow.py → optimize/flow.py} +122 -92
  16. sonolus/backend/optimize/inlining.py +137 -0
  17. sonolus/backend/optimize/liveness.py +177 -0
  18. sonolus/backend/optimize/optimize.py +44 -0
  19. sonolus/backend/optimize/passes.py +52 -0
  20. sonolus/backend/optimize/simplify.py +191 -0
  21. sonolus/backend/optimize/ssa.py +200 -0
  22. sonolus/backend/place.py +17 -25
  23. sonolus/backend/utils.py +58 -48
  24. sonolus/backend/visitor.py +1151 -882
  25. sonolus/build/cli.py +7 -1
  26. sonolus/build/compile.py +88 -90
  27. sonolus/build/engine.py +10 -5
  28. sonolus/build/level.py +24 -23
  29. sonolus/build/node.py +43 -43
  30. sonolus/script/archetype.py +438 -139
  31. sonolus/script/array.py +27 -10
  32. sonolus/script/array_like.py +297 -0
  33. sonolus/script/bucket.py +253 -191
  34. sonolus/script/containers.py +257 -51
  35. sonolus/script/debug.py +26 -10
  36. sonolus/script/easing.py +365 -0
  37. sonolus/script/effect.py +191 -131
  38. sonolus/script/engine.py +71 -4
  39. sonolus/script/globals.py +303 -269
  40. sonolus/script/instruction.py +205 -151
  41. sonolus/script/internal/__init__.py +5 -5
  42. sonolus/script/internal/builtin_impls.py +255 -144
  43. sonolus/script/{callbacks.py → internal/callbacks.py} +127 -127
  44. sonolus/script/internal/constant.py +139 -0
  45. sonolus/script/internal/context.py +26 -9
  46. sonolus/script/internal/descriptor.py +17 -17
  47. sonolus/script/internal/dict_impl.py +65 -0
  48. sonolus/script/internal/generic.py +6 -9
  49. sonolus/script/internal/impl.py +38 -13
  50. sonolus/script/internal/introspection.py +17 -14
  51. sonolus/script/internal/math_impls.py +121 -0
  52. sonolus/script/internal/native.py +40 -38
  53. sonolus/script/internal/random.py +67 -0
  54. sonolus/script/internal/range.py +81 -0
  55. sonolus/script/internal/transient.py +51 -0
  56. sonolus/script/internal/tuple_impl.py +113 -0
  57. sonolus/script/internal/value.py +3 -3
  58. sonolus/script/interval.py +338 -112
  59. sonolus/script/iterator.py +167 -214
  60. sonolus/script/level.py +24 -0
  61. sonolus/script/num.py +80 -48
  62. sonolus/script/options.py +257 -191
  63. sonolus/script/particle.py +190 -157
  64. sonolus/script/pointer.py +30 -30
  65. sonolus/script/print.py +102 -81
  66. sonolus/script/project.py +8 -0
  67. sonolus/script/quad.py +263 -0
  68. sonolus/script/record.py +47 -16
  69. sonolus/script/runtime.py +52 -1
  70. sonolus/script/sprite.py +418 -333
  71. sonolus/script/text.py +409 -407
  72. sonolus/script/timing.py +114 -42
  73. sonolus/script/transform.py +332 -48
  74. sonolus/script/ui.py +216 -160
  75. sonolus/script/values.py +6 -13
  76. sonolus/script/vec.py +196 -78
  77. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/METADATA +1 -1
  78. sonolus_py-0.1.5.dist-info/RECORD +89 -0
  79. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/WHEEL +1 -1
  80. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/licenses/LICENSE +21 -21
  81. sonolus/backend/allocate.py +0 -51
  82. sonolus/backend/optimize.py +0 -9
  83. sonolus/backend/passes.py +0 -6
  84. sonolus/backend/simplify.py +0 -30
  85. sonolus/script/comptime.py +0 -160
  86. sonolus/script/graphics.py +0 -150
  87. sonolus/script/math.py +0 -92
  88. sonolus/script/range.py +0 -58
  89. sonolus_py-0.1.3.dist-info/RECORD +0 -75
  90. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/entry_points.txt +0 -0
sonolus/script/array.py CHANGED
@@ -5,29 +5,43 @@ from collections.abc import Iterable
5
5
  from typing import Any, Self, final
6
6
 
7
7
  from sonolus.backend.place import BlockPlace
8
+ from sonolus.script.array_like import ArrayLike
8
9
  from sonolus.script.debug import assert_unreachable
9
10
  from sonolus.script.internal.context import ctx
10
11
  from sonolus.script.internal.error import InternalError
11
12
  from sonolus.script.internal.generic import GenericValue
12
13
  from sonolus.script.internal.impl import meta_fn, validate_value
13
14
  from sonolus.script.internal.value import Value
14
- from sonolus.script.iterator import ArrayLike
15
15
  from sonolus.script.num import Num
16
16
 
17
17
 
18
18
  @final
19
19
  class Array[T, Size](GenericValue, ArrayLike[T]):
20
+ """A fixed size array of values.
21
+
22
+ Usage:
23
+ ```python
24
+ array_1 = Array(1, 2, 3)
25
+ array_2 = Array[int, 0]()
26
+ ```
27
+ """
28
+
20
29
  _value: list[T] | BlockPlace
21
30
 
22
31
  @classmethod
23
32
  @meta_fn
24
33
  def element_type(cls) -> type[T] | type[Value]:
25
- return cls._get_type_arg_(T)
34
+ """Return the type of elements in this array type."""
35
+ return cls.type_var_value(T)
26
36
 
27
37
  @classmethod
28
38
  @meta_fn
29
39
  def size(cls) -> int:
30
- return cls._get_type_arg_(Size)
40
+ """Return the size of this array type.
41
+
42
+ On instances, use `len(array)` instead.
43
+ """
44
+ return cls.type_var_value(Size)
31
45
 
32
46
  def __new__(cls, *args: T) -> Array[T, Any]:
33
47
  if cls._type_args_ is None:
@@ -97,10 +111,10 @@ class Array[T, Size](GenericValue, ArrayLike[T]):
97
111
  iterator = iter(values)
98
112
  return cls(*(cls.element_type()._from_list_(iterator) for _ in range(cls.size())))
99
113
 
100
- def _to_list_(self) -> list[float | BlockPlace]:
114
+ def _to_list_(self, level_refs: dict[Any, int] | None = None) -> list[float | BlockPlace]:
101
115
  match self._value:
102
116
  case list():
103
- return [entry for value in self._value for entry in value._to_list_()]
117
+ return [entry for value in self._value for entry in value._to_list_(level_refs)]
104
118
  case BlockPlace():
105
119
  return [
106
120
  entry
@@ -130,7 +144,7 @@ class Array[T, Size](GenericValue, ArrayLike[T]):
130
144
 
131
145
  def _copy_(self) -> Self:
132
146
  if ctx():
133
- place = ctx().alloc(size=self.size())
147
+ place = ctx().alloc(size=self._size_())
134
148
  result: Self = self._from_place_(place)
135
149
  result._copy_from_(self)
136
150
  return result
@@ -145,16 +159,17 @@ class Array[T, Size](GenericValue, ArrayLike[T]):
145
159
  else:
146
160
  return cls._with_value([cls.element_type()._alloc_() for _ in range(cls.size())])
147
161
 
162
+ def __len__(self):
163
+ return self.size()
164
+
148
165
  @meta_fn
149
166
  def __getitem__(self, index: Num) -> T:
150
167
  index: Num = Num._accept_(index)
151
- if index._is_py_():
168
+ if index._is_py_() and 0 <= index._as_py_() < self.size():
152
169
  const_index = index._as_py_()
153
170
  if isinstance(const_index, float) and not const_index.is_integer():
154
171
  raise ValueError("Array index must be an integer")
155
172
  const_index = int(const_index)
156
- if not 0 <= const_index < self.size():
157
- raise IndexError("Array index out of range")
158
173
  if isinstance(self._value, list):
159
174
  if ctx():
160
175
  return self._value[const_index]._get_()
@@ -215,7 +230,9 @@ class Array[T, Size](GenericValue, ArrayLike[T]):
215
230
  dst._copy_from_(value)
216
231
 
217
232
  def __eq__(self, other):
218
- if self.size() != other.size():
233
+ if not isinstance(other, ArrayLike):
234
+ return False
235
+ if len(self) != len(other):
219
236
  return False
220
237
  i = 0
221
238
  while i < self.size():
@@ -0,0 +1,297 @@
1
+ from __future__ import annotations
2
+
3
+ import random
4
+ from abc import ABC, abstractmethod
5
+ from collections.abc import Callable, Sequence
6
+ from typing import Any
7
+
8
+ from sonolus.script.iterator import SonolusIterator
9
+ from sonolus.script.num import Num
10
+ from sonolus.script.record import Record
11
+ from sonolus.script.values import copy
12
+
13
+ # Note: we don't use Range in this file because Range itself inherits from ArrayLike
14
+
15
+
16
+ class ArrayLike[T](Sequence, ABC):
17
+ """Mixin for array-like objects.
18
+
19
+ Inheritors must implement `__len__`, `__getitem__`, and `__setitem__`.
20
+
21
+ Usage:
22
+ ```python
23
+ class MyArrayLike[T](Record, ArrayLike[T]):
24
+ def __len__(self) -> int:
25
+ ...
26
+
27
+ def __getitem__(self, index: Num) -> T:
28
+ ...
29
+
30
+ def __setitem__(self, index: Num, value: T):
31
+ ...
32
+ ```
33
+ """
34
+
35
+ @abstractmethod
36
+ def __len__(self) -> int:
37
+ """Return the length of the array."""
38
+
39
+ @abstractmethod
40
+ def __getitem__(self, index: Num) -> T:
41
+ """Return the item at the given index.
42
+
43
+ Args:
44
+ index: The index of the item. Must be an integer between 0 and `len(self) - 1`.
45
+ """
46
+
47
+ @abstractmethod
48
+ def __setitem__(self, index: Num, value: T):
49
+ """Set the value of the item at the given index.
50
+
51
+ Args:
52
+ index: The index of the item. Must be an integer between 0 and `len(self) - 1`.
53
+ value: The value to set.
54
+ """
55
+
56
+ def __iter__(self) -> SonolusIterator[T]:
57
+ """Return an iterator over the array."""
58
+ return _ArrayIterator(0, self)
59
+
60
+ def __contains__(self, value: Any) -> bool:
61
+ """Return whether any element in the array is equal to the given value.
62
+
63
+ Args:
64
+ value: The value to check for.
65
+ """
66
+ i = 0
67
+ while i < len(self):
68
+ if self[i] == value:
69
+ return True
70
+ i += 1
71
+ return False
72
+
73
+ def __reversed__(self):
74
+ """Return a reversed view of the array."""
75
+ return _ArrayReverser(self)
76
+
77
+ def _enumerate_(self, start: Num = 0) -> SonolusIterator[T]:
78
+ return _ArrayEnumerator(0, start, self)
79
+
80
+ def index(self, value: T, start: Num = 0, stop: Num | None = None) -> Num:
81
+ """Return the index of the value in the array equal to the given value.
82
+
83
+ Args:
84
+ value: The value to search for.
85
+ start: The index to start searching from.
86
+ stop: The index to stop searching at. If `None`, search to the end of the array.
87
+ """
88
+ if stop is None:
89
+ stop = len(self)
90
+ i = start
91
+ while i < stop:
92
+ if self[i] == value:
93
+ return i
94
+ i += 1
95
+ return -1
96
+
97
+ def count(self, value: T) -> Num:
98
+ """Return the number of elements in the array equal to the given value.
99
+
100
+ Args:
101
+ value: The value to count.
102
+ """
103
+ count = 0
104
+ i = 0
105
+ while i < len(self):
106
+ if self[i] == value:
107
+ count += 1
108
+ i += 1
109
+ return count
110
+
111
+ def last_index(self, value: T) -> Num:
112
+ """Return the last index of the value in the array equal to the given value.
113
+
114
+ Args:
115
+ value: The value to search for.
116
+ """
117
+ i = len(self) - 1
118
+ while i >= 0:
119
+ if self[i] == value:
120
+ return i
121
+ i -= 1
122
+ return -1
123
+
124
+ def index_of_max(self, *, key: Callable[T, Any] | None = None) -> Num:
125
+ """Return the index of the maximum value in the array.
126
+
127
+ Args:
128
+ key: A one-argument ordering function to use for comparison like the one used in `max()`.
129
+ """
130
+ if len(self) == 0:
131
+ return -1
132
+ if key is None:
133
+ key = _identity
134
+ max_index = 0
135
+ i = 1
136
+ while i < len(self):
137
+ if key(self[i]) > key(self[max_index]):
138
+ max_index = i
139
+ i += 1
140
+ return max_index
141
+
142
+ def index_of_min(self, *, key: Callable[T, Any] | None = None) -> Num:
143
+ """Return the index of the minimum value in the array.
144
+
145
+ Args:
146
+ key: A one-argument ordering function to use for comparison like the one used in `min()`.
147
+ """
148
+ if len(self) == 0:
149
+ return -1
150
+ if key is None:
151
+ key = _identity
152
+ min_index = 0
153
+ i = 1
154
+ while i < len(self):
155
+ if key(self[i]) < key(self[min_index]):
156
+ min_index = i
157
+ i += 1
158
+ return min_index
159
+
160
+ def _max_(self, key: Callable[T, Any] | None = None) -> T:
161
+ return self[self.index_of_max(key=key)]
162
+
163
+ def _min_(self, key: Callable[T, Any] | None = None) -> T:
164
+ return self[self.index_of_min(key=key)]
165
+
166
+ def swap(self, i: Num, j: Num, /):
167
+ """Swap the values at the given indices.
168
+
169
+ Args:
170
+ i: The first index.
171
+ j: The second index.
172
+ """
173
+ temp = copy(self[i])
174
+ self[i] = self[j]
175
+ self[j] = temp
176
+
177
+ def sort(self, *, key: Callable[T, Any] | None = None, reverse: bool = False):
178
+ """Sort the values in the array in place.
179
+
180
+ Args:
181
+ key: A one-argument ordering function to use for comparison.
182
+ reverse: If `True`, sort in descending order, otherwise sort in ascending order.
183
+ """
184
+ if len(self) < 15 or key is not None:
185
+ if key is None:
186
+ key = _identity
187
+ _insertion_sort(self, 0, len(self), key, reverse)
188
+ else:
189
+ # Heap sort is unstable, so if there's a key, we can't rely on it
190
+ _heap_sort(self, 0, len(self), reverse)
191
+
192
+ def shuffle(self):
193
+ """Shuffle the values in the array in place."""
194
+ random.shuffle(self) # type: ignore
195
+
196
+ def reverse(self):
197
+ """Reverse the values in the array in place."""
198
+ i = 0
199
+ j = len(self) - 1
200
+ while i < j:
201
+ self.swap(i, j)
202
+ i += 1
203
+ j -= 1
204
+
205
+
206
+ def _identity[T](value: T) -> T:
207
+ return value
208
+
209
+
210
+ def _insertion_sort[T](array: ArrayLike[T], start: Num, end: Num, key: Callable[T, Any], reverse: bool):
211
+ i = start + 1
212
+ if reverse:
213
+ while i < end:
214
+ j = i
215
+ while j > start and key(array[j - 1]) < key(array[j]):
216
+ array.swap(j - 1, j)
217
+ j -= 1
218
+ i += 1
219
+ else:
220
+ while i < end:
221
+ j = i
222
+ while j > start and key(array[j - 1]) > key(array[j]):
223
+ array.swap(j - 1, j)
224
+ j -= 1
225
+ i += 1
226
+
227
+
228
+ def _heapify[T](array: ArrayLike[T], end: Num, index: Num, reverse: bool):
229
+ while True:
230
+ left = index * 2 + 1
231
+ right = left + 1
232
+ largest = index
233
+ if left < end and (array[left] > array[largest]) != reverse:
234
+ largest = left
235
+ if right < end and (array[right] > array[largest]) != reverse:
236
+ largest = right
237
+ if largest == index:
238
+ break
239
+ array.swap(index, largest)
240
+ index = largest
241
+
242
+
243
+ def _heap_sort[T](array: ArrayLike[T], start: Num, end: Num, reverse: bool):
244
+ i = end // 2 - 1
245
+ while i >= start:
246
+ _heapify(array, end, i, reverse)
247
+ i -= 1
248
+ i = end - 1
249
+ while i > start:
250
+ array.swap(start, i)
251
+ _heapify(array, i, start, reverse)
252
+ i -= 1
253
+
254
+
255
+ class _ArrayIterator[V: ArrayLike](Record, SonolusIterator):
256
+ i: int
257
+ array: V
258
+
259
+ def has_next(self) -> bool:
260
+ return self.i < len(self.array)
261
+
262
+ def get(self) -> V:
263
+ return self.array[self.i]
264
+
265
+ def advance(self):
266
+ self.i += 1
267
+
268
+
269
+ class _ArrayReverser[V: ArrayLike](Record, ArrayLike):
270
+ array: V
271
+
272
+ def __len__(self) -> int:
273
+ return len(self.array)
274
+
275
+ def __getitem__(self, index: Num) -> V:
276
+ return self.array[len(self) - 1 - index]
277
+
278
+ def __setitem__(self, index: Num, value: V):
279
+ self.array[len(self) - 1 - index] = value
280
+
281
+ def reversed(self) -> ArrayLike[V]:
282
+ return self.array
283
+
284
+
285
+ class _ArrayEnumerator[V: ArrayLike](Record, SonolusIterator):
286
+ i: int
287
+ offset: int
288
+ array: V
289
+
290
+ def has_next(self) -> bool:
291
+ return self.i < len(self.array)
292
+
293
+ def get(self) -> tuple[int, Any]:
294
+ return self.i + self.offset, self.array[self.i]
295
+
296
+ def advance(self):
297
+ self.i += 1