sonolus.py 0.3.3__py3-none-any.whl → 0.4.0__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 (66) hide show
  1. sonolus/backend/excepthook.py +30 -0
  2. sonolus/backend/finalize.py +15 -1
  3. sonolus/backend/ops.py +4 -0
  4. sonolus/backend/optimize/allocate.py +5 -5
  5. sonolus/backend/optimize/constant_evaluation.py +124 -19
  6. sonolus/backend/optimize/copy_coalesce.py +15 -12
  7. sonolus/backend/optimize/dead_code.py +7 -6
  8. sonolus/backend/optimize/dominance.py +2 -2
  9. sonolus/backend/optimize/flow.py +54 -8
  10. sonolus/backend/optimize/inlining.py +137 -30
  11. sonolus/backend/optimize/liveness.py +2 -2
  12. sonolus/backend/optimize/optimize.py +15 -1
  13. sonolus/backend/optimize/passes.py +11 -3
  14. sonolus/backend/optimize/simplify.py +137 -8
  15. sonolus/backend/optimize/ssa.py +47 -13
  16. sonolus/backend/place.py +5 -4
  17. sonolus/backend/utils.py +24 -0
  18. sonolus/backend/visitor.py +260 -17
  19. sonolus/build/cli.py +47 -19
  20. sonolus/build/compile.py +12 -5
  21. sonolus/build/engine.py +70 -1
  22. sonolus/build/level.py +3 -3
  23. sonolus/build/project.py +2 -2
  24. sonolus/script/archetype.py +27 -24
  25. sonolus/script/array.py +25 -19
  26. sonolus/script/array_like.py +46 -49
  27. sonolus/script/bucket.py +1 -1
  28. sonolus/script/containers.py +22 -26
  29. sonolus/script/debug.py +24 -47
  30. sonolus/script/effect.py +1 -1
  31. sonolus/script/engine.py +2 -2
  32. sonolus/script/globals.py +3 -3
  33. sonolus/script/instruction.py +3 -3
  34. sonolus/script/internal/builtin_impls.py +155 -28
  35. sonolus/script/internal/constant.py +13 -3
  36. sonolus/script/internal/context.py +46 -15
  37. sonolus/script/internal/impl.py +9 -3
  38. sonolus/script/internal/introspection.py +8 -1
  39. sonolus/script/internal/math_impls.py +17 -0
  40. sonolus/script/internal/native.py +5 -5
  41. sonolus/script/internal/range.py +14 -17
  42. sonolus/script/internal/simulation_context.py +1 -1
  43. sonolus/script/internal/transient.py +2 -2
  44. sonolus/script/internal/value.py +42 -4
  45. sonolus/script/interval.py +15 -15
  46. sonolus/script/iterator.py +38 -107
  47. sonolus/script/maybe.py +139 -0
  48. sonolus/script/num.py +30 -15
  49. sonolus/script/options.py +1 -1
  50. sonolus/script/particle.py +1 -1
  51. sonolus/script/pointer.py +1 -1
  52. sonolus/script/project.py +24 -5
  53. sonolus/script/quad.py +15 -15
  54. sonolus/script/record.py +21 -12
  55. sonolus/script/runtime.py +22 -18
  56. sonolus/script/sprite.py +1 -1
  57. sonolus/script/stream.py +69 -85
  58. sonolus/script/transform.py +35 -34
  59. sonolus/script/values.py +10 -10
  60. sonolus/script/vec.py +23 -20
  61. {sonolus_py-0.3.3.dist-info → sonolus_py-0.4.0.dist-info}/METADATA +1 -1
  62. sonolus_py-0.4.0.dist-info/RECORD +93 -0
  63. sonolus_py-0.3.3.dist-info/RECORD +0 -92
  64. {sonolus_py-0.3.3.dist-info → sonolus_py-0.4.0.dist-info}/WHEEL +0 -0
  65. {sonolus_py-0.3.3.dist-info → sonolus_py-0.4.0.dist-info}/entry_points.txt +0 -0
  66. {sonolus_py-0.3.3.dist-info → sonolus_py-0.4.0.dist-info}/licenses/LICENSE +0 -0
sonolus/script/record.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import inspect
4
+ from abc import ABCMeta
4
5
  from collections.abc import Iterable
5
6
  from inspect import getmro
6
7
  from typing import Any, ClassVar, Self, TypeVar, dataclass_transform, get_origin
@@ -20,7 +21,7 @@ from sonolus.script.internal.value import BackingSource, DataValue, Value
20
21
  from sonolus.script.num import Num
21
22
 
22
23
 
23
- class RecordMeta(type):
24
+ class RecordMeta(ABCMeta):
24
25
  @meta_fn
25
26
  def __pos__[T](cls: type[T]) -> T:
26
27
  """Create a zero-initialized record instance."""
@@ -54,6 +55,11 @@ class Record(GenericValue, metaclass=RecordMeta):
54
55
  record_4 = +MyRecord # Create a zero-initialized record
55
56
  record_5 = +MyGenericRecord[int, int]
56
57
  ```
58
+
59
+ Copying a record:
60
+ ```python
61
+ record_copy = +record
62
+ ```
57
63
  """
58
64
 
59
65
  _value: dict[str, Value]
@@ -178,7 +184,7 @@ class Record(GenericValue, metaclass=RecordMeta):
178
184
  result = object.__new__(cls)
179
185
  result._value = {
180
186
  field.name: field.type._from_backing_source_(
181
- lambda offset, field_offset=field.offset: source((Num(offset) + Num(field_offset)).ir())
187
+ lambda offset, field_offset=field.offset: source((Num(offset) + Num(field_offset)).ir()) # type: ignore
182
188
  )
183
189
  for field in cls._fields
184
190
  }
@@ -229,10 +235,10 @@ class Record(GenericValue, metaclass=RecordMeta):
229
235
  def _get_(self) -> Self:
230
236
  return self
231
237
 
232
- def _set_(self, value: Self):
238
+ def _set_(self, value: Any):
233
239
  raise TypeError("Record does not support _set_")
234
240
 
235
- def _copy_from_(self, value: Self):
241
+ def _copy_from_(self, value: Any):
236
242
  value = self._accept_(value)
237
243
  for field in self._fields:
238
244
  field.__set__(self, field.__get__(value))
@@ -254,14 +260,14 @@ class Record(GenericValue, metaclass=RecordMeta):
254
260
  return result
255
261
 
256
262
  def __str__(self):
257
- return (
258
- f"{self.__class__.__name__}({', '.join(f'{field.name}={field.__get__(self)}' for field in self._fields)})"
259
- )
263
+ return f"{self.__class__.__name__}({
264
+ ', '.join(f'{field.name}={field.get_internal(self)}' for field in self._fields)
265
+ })"
260
266
 
261
267
  def __repr__(self):
262
- return (
263
- f"{self.__class__.__name__}({', '.join(f'{field.name}={field.__get__(self)!r}' for field in self._fields)})"
264
- )
268
+ return f"{self.__class__.__name__}({
269
+ ', '.join(f'{field.name}={field.get_internal(self)!r}' for field in self._fields)
270
+ })"
265
271
 
266
272
  @meta_fn
267
273
  def __eq__(self, other: Any) -> bool:
@@ -304,16 +310,19 @@ class Record(GenericValue, metaclass=RecordMeta):
304
310
 
305
311
 
306
312
  class _RecordField(SonolusDescriptor):
307
- def __init__(self, name: str, type_: type[Value], index: int, offset: int):
313
+ def __init__(self, name: str, type_: type[Value] | Any, index: int, offset: int):
308
314
  self.name = name
309
315
  self.type = type_
310
316
  self.index = index
311
317
  self.offset = offset
312
318
 
319
+ def get_internal(self, instance: Record) -> Value:
320
+ return instance._value[self.name]
321
+
313
322
  def __get__(self, instance: Record | None, owner=None):
314
323
  if instance is None:
315
324
  return self
316
- result = instance._value[self.name]._get_()
325
+ result = instance._value[self.name]._get_readonly_()
317
326
  if ctx():
318
327
  return result
319
328
  else:
sonolus/script/runtime.py CHANGED
@@ -698,7 +698,7 @@ class _TouchArray:
698
698
 
699
699
  @_runtime_skin_transform
700
700
  class _SkinTransform:
701
- value: Array[Array[float, 4], 4]
701
+ value: Array[Array[float, 4], 4] # type: ignore
702
702
 
703
703
  @property
704
704
  @meta_fn
@@ -719,7 +719,7 @@ class _SkinTransform:
719
719
 
720
720
  @_runtime_particle_transform
721
721
  class _ParticleTransform:
722
- value: Array[Array[float, 4], 4]
722
+ value: Array[Array[float, 4], 4] # type: ignore
723
723
 
724
724
  @property
725
725
  @meta_fn
@@ -857,25 +857,25 @@ def is_debug() -> bool:
857
857
  @meta_fn
858
858
  def is_play() -> bool:
859
859
  """Check if the game is running in play mode."""
860
- return ctx() and ctx().global_state.mode == Mode.PLAY
860
+ return bool(ctx() and ctx().global_state.mode == Mode.PLAY)
861
861
 
862
862
 
863
863
  @meta_fn
864
864
  def is_preview() -> bool:
865
865
  """Check if the game is running in preview mode."""
866
- return ctx() and ctx().global_state.mode == Mode.PREVIEW
866
+ return bool(ctx() and ctx().global_state.mode == Mode.PREVIEW)
867
867
 
868
868
 
869
869
  @meta_fn
870
870
  def is_watch() -> bool:
871
871
  """Check if the game is running in watch mode."""
872
- return ctx() and ctx().global_state.mode == Mode.WATCH
872
+ return bool(ctx() and ctx().global_state.mode == Mode.WATCH)
873
873
 
874
874
 
875
875
  @meta_fn
876
876
  def is_tutorial() -> bool:
877
877
  """Check if the game is running in tutorial mode."""
878
- return ctx() and ctx().global_state.mode == Mode.TUTORIAL
878
+ return bool(ctx() and ctx().global_state.mode == Mode.TUTORIAL)
879
879
 
880
880
 
881
881
  @meta_fn
@@ -884,7 +884,7 @@ def is_preprocessing() -> bool:
884
884
 
885
885
  Returns True if the current callback is one of preprocess, spawn_order, spawn_time, or despawn_time.
886
886
  """
887
- return ctx() and ctx().callback in {"preprocess", "spawnOrder", "spawnTime", "despawnTime"}
887
+ return bool(ctx() and ctx().callback in {"preprocess", "spawnOrder", "spawnTime", "despawnTime"})
888
888
 
889
889
 
890
890
  @meta_fn
@@ -1058,12 +1058,16 @@ def prev_time() -> float:
1058
1058
  def touches() -> ArrayLike[Touch]:
1059
1059
  """Get the current touches of the game."""
1060
1060
  if not ctx():
1061
- return Array[Touch, 0]()
1061
+ return Array[Touch, 0]() # type: ignore
1062
1062
  match ctx().global_state.mode:
1063
1063
  case Mode.PLAY:
1064
- return ArrayPointer[Touch](_PlayRuntimeUpdate.touch_count, ctx().blocks.RuntimeTouchArray, 0)
1064
+ return ArrayPointer[Touch]._raw(
1065
+ size=Num._accept_(_PlayRuntimeUpdate.touch_count),
1066
+ block=Num._accept_(ctx().blocks.RuntimeTouchArray),
1067
+ offset=Num._accept_(0),
1068
+ )
1065
1069
  case _:
1066
- return Array[Touch, 0]()
1070
+ return Array[Touch, 0]() # type: ignore
1067
1071
 
1068
1072
 
1069
1073
  @meta_fn
@@ -1098,24 +1102,24 @@ def navigation_direction() -> int:
1098
1102
 
1099
1103
  def skin_transform() -> Transform2d:
1100
1104
  """Get the global skin transform."""
1101
- return _SkinTransform.transform
1105
+ return _SkinTransform.transform # type: ignore
1102
1106
 
1103
1107
 
1104
1108
  @meta_fn
1105
1109
  def set_skin_transform(value: Transform2d):
1106
1110
  """Set the global skin transform."""
1107
- _SkinTransform.transform._copy_from_(value)
1111
+ _SkinTransform.transform._copy_from_(value) # type: ignore
1108
1112
 
1109
1113
 
1110
1114
  def particle_transform() -> Transform2d:
1111
1115
  """Get the global particle transform."""
1112
- return _ParticleTransform.transform
1116
+ return _ParticleTransform.transform # type: ignore
1113
1117
 
1114
1118
 
1115
1119
  @meta_fn
1116
1120
  def set_particle_transform(value: Transform2d):
1117
1121
  """Set the global particle transform."""
1118
- _ParticleTransform.transform._copy_from_(value)
1122
+ _ParticleTransform.transform._copy_from_(value) # type: ignore
1119
1123
 
1120
1124
 
1121
1125
  def background() -> Quad:
@@ -1142,12 +1146,12 @@ _runtime_ui = RuntimeUi()
1142
1146
 
1143
1147
  def runtime_ui() -> RuntimeUi:
1144
1148
  """Get the runtime UI configuration."""
1145
- return _runtime_ui
1149
+ return _runtime_ui # type: ignore
1146
1150
 
1147
1151
 
1148
1152
  def canvas() -> _PreviewRuntimeCanvas:
1149
1153
  """Get the preview canvas."""
1150
- return _PreviewRuntimeCanvas
1154
+ return _PreviewRuntimeCanvas # type: ignore
1151
1155
 
1152
1156
 
1153
1157
  def screen() -> Rect:
@@ -1157,9 +1161,9 @@ def screen() -> Rect:
1157
1161
 
1158
1162
  def level_score() -> _LevelScore:
1159
1163
  """Get the level score configuration."""
1160
- return _LevelScore
1164
+ return _LevelScore # type: ignore
1161
1165
 
1162
1166
 
1163
1167
  def level_life() -> _LevelLife:
1164
1168
  """Get the level life configuration."""
1165
- return _LevelLife
1169
+ return _LevelLife # type: ignore
sonolus/script/sprite.py CHANGED
@@ -267,7 +267,7 @@ def sprite(name: str) -> Any:
267
267
  return SkinSprite(name)
268
268
 
269
269
 
270
- type Skin = NewType("Skin", Any)
270
+ type Skin = NewType("Skin", Any) # type: ignore
271
271
 
272
272
 
273
273
  class RenderMode(StrEnum):
sonolus/script/stream.py CHANGED
@@ -13,6 +13,7 @@ from sonolus.script.internal.introspection import get_field_specifiers
13
13
  from sonolus.script.internal.native import native_function
14
14
  from sonolus.script.internal.value import BackingValue, Value
15
15
  from sonolus.script.iterator import SonolusIterator
16
+ from sonolus.script.maybe import Maybe, Nothing, Some
16
17
  from sonolus.script.num import Num
17
18
  from sonolus.script.record import Record
18
19
  from sonolus.script.runtime import prev_time, time
@@ -48,7 +49,7 @@ class _StreamDataField(SonolusDescriptor):
48
49
 
49
50
  def __get__(self, instance, owner):
50
51
  _check_can_read_or_write_stream()
51
- return self._get()._get_()
52
+ return self._get()._get_readonly_()
52
53
 
53
54
  def __set__(self, instance, value):
54
55
  _check_can_write_stream()
@@ -80,12 +81,12 @@ def streams[T](cls: type[T]) -> T:
80
81
  ```python
81
82
  @streams
82
83
  class Streams:
83
- stream_1: Stream[Num] # A stream of Num values
84
+ stream_1: Stream[int] # A stream of int values
84
85
  stream_2: Stream[Vec2] # A stream of Vec2 values
85
- group_1: StreamGroup[Num, 10] # A group of 10 Num streams
86
+ group_1: StreamGroup[int, 10] # A group of 10 int streams
86
87
  group_2: StreamGroup[Vec2, 5] # A group of 5 Vec2 streams
87
88
 
88
- data_field_1: Num # A data field of type Num
89
+ data_field_1: int # A data field of type int
89
90
  data_field_2: Vec2 # A data field of type Vec2
90
91
  ```
91
92
  """
@@ -136,7 +137,7 @@ class _StreamBacking(BackingValue):
136
137
  id: Num
137
138
  index: Num
138
139
 
139
- def __init__(self, stream_id: int, index: Num):
140
+ def __init__(self, stream_id: int, index: Num | int | float):
140
141
  super().__init__()
141
142
  self.id = Num._accept_(stream_id)
142
143
  self.index = Num._accept_(index)
@@ -240,10 +241,10 @@ class Stream[T](Record):
240
241
  # We still need to store something to preserve the key, so this is a special case.
241
242
  _stream_set(self.offset, key, 0)
242
243
  else:
243
- for i, v in enumerate(value._to_list_()):
244
+ for i, v in enumerate(cast(Value, value)._to_list_()):
244
245
  _stream_set(self.offset + i, key, Num(v))
245
246
 
246
- def next_key(self, key: int | float) -> int:
247
+ def next_key(self, key: int | float) -> int | float:
247
248
  """Get the next key, or the key unchanged if it is the last key or the stream is empty.
248
249
 
249
250
  If the key is in the stream and there is a next key, returns the next key.
@@ -251,13 +252,13 @@ class Stream[T](Record):
251
252
  _check_can_read_stream()
252
253
  return _stream_get_next_key(self.offset, key)
253
254
 
254
- def next_key_or_default(self, key: int | float, default: int | float) -> int:
255
+ def next_key_or_default(self, key: int | float, default: int | float) -> int | float:
255
256
  """Get the next key, or the default value if there is no next key."""
256
257
  _check_can_read_stream()
257
258
  next_key = self.next_key(key)
258
259
  return next_key if next_key > key else default
259
260
 
260
- def previous_key(self, key: int | float) -> int:
261
+ def previous_key(self, key: int | float) -> int | float:
261
262
  """Get the previous key, or the key unchanged if it is the first key or the stream is empty.
262
263
 
263
264
  If the key is in the stream and there is a previous key, returns the previous key.
@@ -265,7 +266,7 @@ class Stream[T](Record):
265
266
  _check_can_read_stream()
266
267
  return _stream_get_previous_key(self.offset, key)
267
268
 
268
- def previous_key_or_default(self, key: int | float, default: int | float) -> int:
269
+ def previous_key_or_default(self, key: int | float, default: int | float) -> int | float:
269
270
  """Get the previous key, or the default value if there is no previous key."""
270
271
  _check_can_read_stream()
271
272
  previous_key = self.previous_key(key)
@@ -283,12 +284,12 @@ class Stream[T](Record):
283
284
  previous_key = self.previous_key(key)
284
285
  return previous_key < key
285
286
 
286
- def next_key_inclusive(self, key: int | float) -> int:
287
+ def next_key_inclusive(self, key: int | float) -> int | float:
287
288
  """Like `next_key`, but returns the key itself if it is in the stream."""
288
289
  _check_can_read_stream()
289
290
  return key if key in self else self.next_key(key)
290
291
 
291
- def previous_key_inclusive(self, key: int | float) -> int:
292
+ def previous_key_inclusive(self, key: int | float) -> int | float:
292
293
  """Like `previous_key`, but returns the key itself if it is in the stream."""
293
294
  _check_can_read_stream()
294
295
  return key if key in self else self.previous_key(key)
@@ -508,21 +509,20 @@ class StreamGroup[T, Size](Record):
508
509
  _check_can_read_or_write_stream()
509
510
  assert index in self
510
511
  # Size 0 elements still need 1 stream to preserve the key.
511
- return Stream[self.type_var_value(T)](max(1, sizeof(self.element_type())) * index + self.offset)
512
+ t = self.type_var_value(T)
513
+ return Stream[t](max(1, sizeof(self.element_type())) * index + self.offset)
512
514
 
513
515
 
514
516
  class _StreamAscIterator[T](Record, SonolusIterator[tuple[int | float, T]]):
515
517
  stream: Stream[T]
516
518
  current_key: int | float
517
519
 
518
- def has_next(self) -> bool:
519
- return self.current_key in self.stream
520
-
521
- def get(self) -> tuple[int | float, T]:
522
- return self.current_key, self.stream[self.current_key]
523
-
524
- def advance(self):
525
- self.current_key = self.stream.next_key_or_default(self.current_key, inf)
520
+ def next(self) -> Maybe[tuple[int | float, T]]:
521
+ if self.current_key in self.stream:
522
+ result = (self.current_key, self.stream[self.current_key])
523
+ self.current_key = self.stream.next_key_or_default(self.current_key, inf)
524
+ return Some(result)
525
+ return Nothing
526
526
 
527
527
 
528
528
  class _StreamBoundedAscIterator[T](Record, SonolusIterator[tuple[int | float, T]]):
@@ -530,42 +530,36 @@ class _StreamBoundedAscIterator[T](Record, SonolusIterator[tuple[int | float, T]
530
530
  current_key: int | float
531
531
  end_key: int | float
532
532
 
533
- def has_next(self) -> bool:
534
- return self.current_key in self.stream and self.current_key <= self.end_key
535
-
536
- def get(self) -> tuple[int | float, T]:
537
- return self.current_key, self.stream[self.current_key]
538
-
539
- def advance(self):
540
- self.current_key = self.stream.next_key_or_default(self.current_key, inf)
533
+ def next(self) -> Maybe[tuple[int | float, T]]:
534
+ if self.current_key in self.stream and self.current_key <= self.end_key:
535
+ result = (self.current_key, self.stream[self.current_key])
536
+ self.current_key = self.stream.next_key_or_default(self.current_key, inf)
537
+ return Some(result)
538
+ return Nothing
541
539
 
542
540
 
543
541
  class _StreamDescIterator[T](Record, SonolusIterator[tuple[int | float, T]]):
544
542
  stream: Stream[T]
545
543
  current_key: int | float
546
544
 
547
- def has_next(self) -> bool:
548
- return self.current_key in self.stream
549
-
550
- def get(self) -> tuple[int | float, T]:
551
- return self.current_key, self.stream[self.current_key]
552
-
553
- def advance(self):
554
- self.current_key = self.stream.previous_key_or_default(self.current_key, -inf)
545
+ def next(self) -> Maybe[tuple[int | float, T]]:
546
+ if self.current_key in self.stream:
547
+ result = (self.current_key, self.stream[self.current_key])
548
+ self.current_key = self.stream.previous_key_or_default(self.current_key, -inf)
549
+ return Some(result)
550
+ return Nothing
555
551
 
556
552
 
557
553
  class _StreamAscKeyIterator[T](Record, SonolusIterator[int | float]):
558
554
  stream: Stream[T]
559
555
  current_key: int | float
560
556
 
561
- def has_next(self) -> bool:
562
- return self.current_key in self.stream
563
-
564
- def get(self) -> int | float:
565
- return self.current_key
566
-
567
- def advance(self):
568
- self.current_key = self.stream.next_key_or_default(self.current_key, inf)
557
+ def next(self) -> Maybe[int | float]:
558
+ if self.current_key in self.stream:
559
+ result = self.current_key
560
+ self.current_key = self.stream.next_key_or_default(self.current_key, inf)
561
+ return Some(result)
562
+ return Nothing
569
563
 
570
564
 
571
565
  class _StreamBoundedAscKeyIterator[T](Record, SonolusIterator[int | float]):
@@ -573,42 +567,36 @@ class _StreamBoundedAscKeyIterator[T](Record, SonolusIterator[int | float]):
573
567
  current_key: int | float
574
568
  end_key: int | float
575
569
 
576
- def has_next(self) -> bool:
577
- return self.current_key in self.stream and self.current_key <= self.end_key
578
-
579
- def get(self) -> int | float:
580
- return self.current_key
581
-
582
- def advance(self):
583
- self.current_key = self.stream.next_key_or_default(self.current_key, inf)
570
+ def next(self) -> Maybe[int | float]:
571
+ if self.current_key in self.stream and self.current_key <= self.end_key:
572
+ result = self.current_key
573
+ self.current_key = self.stream.next_key_or_default(self.current_key, inf)
574
+ return Some(result)
575
+ return Nothing
584
576
 
585
577
 
586
578
  class _StreamDescKeyIterator[T](Record, SonolusIterator[int | float]):
587
579
  stream: Stream[T]
588
580
  current_key: int | float
589
581
 
590
- def has_next(self) -> bool:
591
- return self.current_key in self.stream
592
-
593
- def get(self) -> int | float:
594
- return self.current_key
595
-
596
- def advance(self):
597
- self.current_key = self.stream.previous_key_or_default(self.current_key, -inf)
582
+ def next(self) -> Maybe[int | float]:
583
+ if self.current_key in self.stream:
584
+ result = self.current_key
585
+ self.current_key = self.stream.previous_key_or_default(self.current_key, -inf)
586
+ return Some(result)
587
+ return Nothing
598
588
 
599
589
 
600
590
  class _StreamAscValueIterator[T](Record, SonolusIterator[T]):
601
591
  stream: Stream[T]
602
592
  current_key: int | float
603
593
 
604
- def has_next(self) -> bool:
605
- return self.current_key in self.stream
606
-
607
- def get(self) -> T:
608
- return self.stream[self.current_key]
609
-
610
- def advance(self):
611
- self.current_key = self.stream.next_key_or_default(self.current_key, inf)
594
+ def next(self) -> Maybe[T]:
595
+ if self.current_key in self.stream:
596
+ result = self.stream[self.current_key]
597
+ self.current_key = self.stream.next_key_or_default(self.current_key, inf)
598
+ return Some(result)
599
+ return Nothing
612
600
 
613
601
 
614
602
  class _StreamBoundedAscValueIterator[T](Record, SonolusIterator[T]):
@@ -616,28 +604,24 @@ class _StreamBoundedAscValueIterator[T](Record, SonolusIterator[T]):
616
604
  current_key: int | float
617
605
  end_key: int | float
618
606
 
619
- def has_next(self) -> bool:
620
- return self.current_key in self.stream and self.current_key <= self.end_key
621
-
622
- def get(self) -> T:
623
- return self.stream[self.current_key]
624
-
625
- def advance(self):
626
- self.current_key = self.stream.next_key_or_default(self.current_key, inf)
607
+ def next(self) -> Maybe[T]:
608
+ if self.current_key in self.stream and self.current_key <= self.end_key:
609
+ result = self.stream[self.current_key]
610
+ self.current_key = self.stream.next_key_or_default(self.current_key, inf)
611
+ return Some(result)
612
+ return Nothing
627
613
 
628
614
 
629
615
  class _StreamDescValueIterator[T](Record, SonolusIterator[T]):
630
616
  stream: Stream[T]
631
617
  current_key: int | float
632
618
 
633
- def has_next(self) -> bool:
634
- return self.current_key in self.stream
635
-
636
- def get(self) -> T:
637
- return self.stream[self.current_key]
638
-
639
- def advance(self):
640
- self.current_key = self.stream.previous_key_or_default(self.current_key, -inf)
619
+ def next(self) -> Maybe[T]:
620
+ if self.current_key in self.stream:
621
+ result = self.stream[self.current_key]
622
+ self.current_key = self.stream.previous_key_or_default(self.current_key, -inf)
623
+ return Some(result)
624
+ return Nothing
641
625
 
642
626
 
643
627
  @native_function(Op.StreamGetNextKey)