sonolus.py 0.9.3__py3-none-any.whl → 0.10.1__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/script/bucket.py CHANGED
@@ -156,7 +156,7 @@ class Bucket(Record):
156
156
  """The judgment window of the bucket."""
157
157
  if not ctx():
158
158
  raise RuntimeError("Bucket window access outside of compilation")
159
- match ctx().global_state.mode:
159
+ match ctx().mode_state.mode:
160
160
  case Mode.PLAY:
161
161
  return _deref(ctx().blocks.LevelBucket, self.id * JudgmentWindow._size_(), JudgmentWindow)
162
162
  case Mode.WATCH:
@@ -2,7 +2,6 @@ from __future__ import annotations
2
2
 
3
3
  from typing import Self
4
4
 
5
- from sonolus.backend.visitor import compile_and_call
6
5
  from sonolus.script.array import Array
7
6
  from sonolus.script.array_like import ArrayLike, get_positive_index
8
7
  from sonolus.script.debug import error
@@ -144,11 +143,11 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
144
143
  assert p == Pair(5, 6) # The value of p has changed
145
144
  ```
146
145
  """
147
- return self._array[get_positive_index(item, len(self))]
146
+ return self._array.get_unchecked(get_positive_index(item, self._size))
148
147
 
149
148
  def __setitem__(self, key: int, value: T):
150
149
  """Update the element at the given index."""
151
- self._array[get_positive_index(key, len(self))] = value
150
+ self._array.set_unchecked(get_positive_index(key, self._size), value)
152
151
 
153
152
  def __delitem__(self, key: int):
154
153
  """Remove the element at the given index."""
@@ -160,8 +159,8 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
160
159
  Args:
161
160
  value: The value to append.
162
161
  """
163
- assert self._size < len(self._array)
164
- self._array[self._size] = value
162
+ assert self._size < len(self._array), "Array is full"
163
+ self._array.set_unchecked(self._size, value)
165
164
  self._size += 1
166
165
 
167
166
  def append_unchecked(self, value: T):
@@ -172,7 +171,7 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
172
171
  Args:
173
172
  value: The value to append.
174
173
  """
175
- self._array[self._size] = value
174
+ self._array.set_unchecked(self._size, value)
176
175
  self._size += 1
177
176
 
178
177
  def extend(self, values: ArrayLike[T]):
@@ -181,8 +180,12 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
181
180
  Args:
182
181
  values: The values to append.
183
182
  """
184
- for value in values:
185
- self.append(value)
183
+ assert self._size + len(values) <= len(self._array), "Array is full"
184
+ i = 0
185
+ while i < len(values):
186
+ self._array.set_unchecked(self._size + i, values[i])
187
+ i += 1
188
+ self._size += len(values)
186
189
 
187
190
  def pop(self, index: int | None = None) -> T:
188
191
  """Remove and return a copy of the value at the given index.
@@ -194,13 +197,12 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
194
197
  """
195
198
  if index is None:
196
199
  index = self._size - 1
197
- index = get_positive_index(index, len(self))
198
- assert 0 <= index < self._size
199
- value = copy(self._array[index])
200
+ index = get_positive_index(index, self._size)
201
+ value = copy(self._array.get_unchecked(index))
200
202
  self._size -= 1
201
203
  if index < self._size:
202
204
  for i in range(index, self._size):
203
- self._array[i] = self._array[i + 1]
205
+ self._array.set_unchecked(i, self._array.get_unchecked(i + 1))
204
206
  return value
205
207
 
206
208
  def insert(self, index: int, value: T):
@@ -212,12 +214,12 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
212
214
  index: The index at which to insert the value. Must be in the range [0, size].
213
215
  value: The value to insert.
214
216
  """
215
- index = clamp(get_positive_index(index, len(self)), 0, self._size)
216
- assert self._size < len(self._array)
217
+ index = clamp(get_positive_index(index, self._size, include_end=True), 0, self._size)
218
+ assert self._size < len(self._array), "Array is full"
217
219
  self._size += 1
218
220
  for i in range(self._size - 1, index, -1):
219
- self._array[i] = self._array[i - 1]
220
- self._array[index] = value
221
+ self._array.set_unchecked(i, self._array.get_unchecked(i - 1))
222
+ self._array.set_unchecked(index, value)
221
223
 
222
224
  def remove(self, value: T) -> bool:
223
225
  """Remove the first occurrence of the given value, returning whether the value was removed.
@@ -277,7 +279,7 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
277
279
  if index < 0:
278
280
  return False
279
281
  if index < self._size - 1:
280
- self._array[index] = self._array[self._size - 1]
282
+ self._array.set_unchecked(index, self._array.get_unchecked(self._size - 1))
281
283
  self._size -= 1
282
284
  return True
283
285
 
@@ -293,7 +295,7 @@ class VarArray[T, Capacity](Record, ArrayLike[T]):
293
295
  return False
294
296
  i = 0
295
297
  while i < len(self):
296
- if self[i] != other[i]:
298
+ if self.get_unchecked(i) != other.get_unchecked(i):
297
299
  return False
298
300
  i += 1
299
301
  return True
@@ -329,12 +331,8 @@ class ArrayPointer[T](Record, ArrayLike[T]):
329
331
  """Return the type of the elements in the array."""
330
332
  return cls.type_var_value(T)
331
333
 
332
- def _check_index(self, index: int):
333
- assert 0 <= index < self.size
334
-
335
334
  @meta_fn
336
335
  def _get_item(self, item: int) -> T:
337
- item = get_positive_index(item, self.size)
338
336
  if not ctx():
339
337
  raise TypeError("ArrayPointer values cannot be accessed outside of a context")
340
338
  return _deref(
@@ -344,14 +342,18 @@ class ArrayPointer[T](Record, ArrayLike[T]):
344
342
  self.element_type(),
345
343
  )
346
344
 
345
+ def __getitem__(self, item) -> T:
346
+ return self.get_unchecked(get_positive_index(item, self.size))
347
+
348
+ def __setitem__(self, key: int, value: T):
349
+ self.set_unchecked(get_positive_index(key, self.size), value)
350
+
347
351
  @meta_fn
348
- def __getitem__(self, item: int) -> T:
349
- compile_and_call(self._check_index, item)
352
+ def get_unchecked(self, item: int) -> T:
350
353
  return self._get_item(item)._get_()
351
354
 
352
355
  @meta_fn
353
- def __setitem__(self, key: int, value: T):
354
- compile_and_call(self._check_index, key)
356
+ def set_unchecked(self, key: int, value: T):
355
357
  dst = self._get_item(key)
356
358
  if self.element_type()._is_value_type_():
357
359
  dst._set_(value)
@@ -519,7 +521,7 @@ class ArrayMap[K, V, Capacity](Record):
519
521
  ```
520
522
  """
521
523
  for i in range(self._size):
522
- entry = self._array[i]
524
+ entry = self._array.get_unchecked(i)
523
525
  if entry.key == key:
524
526
  return entry.value
525
527
  error()
@@ -535,12 +537,12 @@ class ArrayMap[K, V, Capacity](Record):
535
537
  value: The value to associate with the key
536
538
  """
537
539
  for i in range(self._size):
538
- entry = self._array[i]
540
+ entry = self._array.get_unchecked(i)
539
541
  if entry.key == key:
540
542
  entry.value = value
541
543
  return
542
- assert self._size < self.capacity()
543
- self._array[self._size] = _ArrayMapEntry(key, value)
544
+ assert self._size < self.capacity(), "Map is full"
545
+ self._array.set_unchecked(self._size, _ArrayMapEntry(key, value))
544
546
  self._size += 1
545
547
 
546
548
  def __delitem__(self, key: K):
@@ -552,11 +554,11 @@ class ArrayMap[K, V, Capacity](Record):
552
554
  key: The key to remove
553
555
  """
554
556
  for i in range(self._size):
555
- entry = self._array[i]
557
+ entry = self._array.get_unchecked(i)
556
558
  if entry.key == key:
557
559
  self._size -= 1
558
560
  if i < self._size:
559
- self._array[i] = self._array[self._size]
561
+ self._array.set_unchecked(i, self._array.get_unchecked(self._size))
560
562
  return
561
563
  error()
562
564
 
@@ -570,7 +572,7 @@ class ArrayMap[K, V, Capacity](Record):
570
572
  True if the key is present, False otherwise.
571
573
  """
572
574
  for i in range(self._size): # noqa: SIM110
573
- if self._array[i].key == key:
575
+ if self._array.get_unchecked(i).key == key:
574
576
  return True
575
577
  return False
576
578
 
@@ -586,12 +588,12 @@ class ArrayMap[K, V, Capacity](Record):
586
588
  The value associated with the key
587
589
  """
588
590
  for i in range(self._size):
589
- entry = self._array[i]
591
+ entry = self._array.get_unchecked(i)
590
592
  if entry.key == key:
591
593
  value = copy(entry.value)
592
594
  self._size -= 1
593
595
  if i < self._size:
594
- self._array[i] = self._array[self._size]
596
+ self._array.set_unchecked(i, self._array.get_unchecked(self._size))
595
597
  return value
596
598
  error()
597
599
 
@@ -606,7 +608,7 @@ class _ArrayMapKeyIterator[K, V, Capacity](Record, SonolusIterator):
606
608
 
607
609
  def next(self) -> Maybe[K]:
608
610
  if self._index < len(self._map):
609
- key = self._map._array[self._index].key
611
+ key = self._map._array.get_unchecked(self._index).key
610
612
  self._index += 1
611
613
  return Some(key)
612
614
  return Nothing
@@ -618,7 +620,7 @@ class _ArrayMapValueIterator[K, V, Capacity](Record, SonolusIterator):
618
620
 
619
621
  def next(self) -> Maybe[V]:
620
622
  if self._index < len(self._map):
621
- value = self._map._array[self._index].value
623
+ value = self._map._array.get_unchecked(self._index).value
622
624
  self._index += 1
623
625
  return Some(value)
624
626
  return Nothing
@@ -630,7 +632,7 @@ class _ArrayMapEntryIterator[K, V, Capacity](Record, SonolusIterator):
630
632
 
631
633
  def next(self) -> Maybe[tuple[K, V]]:
632
634
  if self._index < len(self._map):
633
- entry = self._map._array[self._index]
635
+ entry = self._map._array.get_unchecked(self._index)
634
636
  result = (entry.key, entry.value)
635
637
  self._index += 1
636
638
  return Some(result)
sonolus/script/debug.py CHANGED
@@ -7,7 +7,7 @@ from sonolus.backend.ops import Op
7
7
  from sonolus.backend.optimize.flow import cfg_to_mermaid
8
8
  from sonolus.backend.optimize.passes import CompilerPass, OptimizerConfig, run_passes
9
9
  from sonolus.backend.optimize.simplify import RenumberVars
10
- from sonolus.script.internal.context import GlobalContextState, ReadOnlyMemory, ctx, set_ctx
10
+ from sonolus.script.internal.context import ModeContextState, ProjectContextState, ctx, set_ctx
11
11
  from sonolus.script.internal.impl import meta_fn, validate_value
12
12
  from sonolus.script.internal.native import native_function
13
13
  from sonolus.script.internal.simulation_context import SimulationContext
@@ -18,17 +18,23 @@ debug_log_callback = ContextVar[Callable[[Num], None]]("debug_log_callback")
18
18
 
19
19
  @meta_fn
20
20
  def error(message: str | None = None) -> Never: # type: ignore
21
- """Raise an error.
21
+ """Raise an error, and if in a dev build, log a message and pause the game.
22
22
 
23
23
  This function is used to raise an error during runtime.
24
24
  When this happens, the game will pause in debug mode. The current callback will also immediately return 0.
25
+
26
+ In non-dev builds, this function will terminate the current callback silently.
27
+
28
+ Args:
29
+ message: The message to log.
25
30
  """
26
31
  message = validate_value(message)._as_py_() or "Error" # type: ignore
27
32
  if not isinstance(message, str):
28
33
  raise ValueError("Expected a string")
29
34
  if ctx():
30
- debug_log(ctx().map_constant(message))
31
- debug_pause()
35
+ if ctx().project_state.dev:
36
+ debug_log(ctx().map_debug_message(message))
37
+ debug_pause()
32
38
  terminate()
33
39
  else:
34
40
  raise RuntimeError(message)
@@ -40,6 +46,9 @@ def static_error(message: str | None = None) -> Never:
40
46
 
41
47
  This function is used to raise an error during compile-time if the compiler cannot guarantee that
42
48
  this function will not be called during runtime.
49
+
50
+ Args:
51
+ message: The message to log.
43
52
  """
44
53
  message = validate_value(message)._as_py_() or "Error" # type: ignore
45
54
  if not isinstance(message, str):
@@ -69,7 +78,39 @@ def debug_pause():
69
78
 
70
79
 
71
80
  @meta_fn
72
- def assert_true(value: int | float | bool, message: str | None = None):
81
+ def notify(message: str):
82
+ """Log a code that can be decoded by the dev server and pause the game if in debug mode and in a dev build.
83
+
84
+ Does nothing if not a dev build.
85
+
86
+ Args:
87
+ message: The message to log.
88
+ """
89
+ message = validate_value(message)._as_py_() # type: ignore
90
+ if not isinstance(message, str):
91
+ raise ValueError("Expected a string")
92
+ if ctx():
93
+ if ctx().project_state.dev:
94
+ debug_log(ctx().map_debug_message(message))
95
+ debug_pause()
96
+ else:
97
+ print(f"[NOTIFY] {message}")
98
+
99
+
100
+ @meta_fn
101
+ def require(value: int | float | bool, message: str | None = None):
102
+ """Require a condition to be true, or raise an error.
103
+
104
+ Similar to assert, but does not get stripped in non-dev builds.
105
+
106
+ If in a dev build, this function will log a message and pause the game if the condition is false.
107
+
108
+ In non-dev builds, this function will terminate the current callback silently if the condition is false.
109
+
110
+ Args:
111
+ value: The condition to check.
112
+ message: The message to log if the condition is false.
113
+ """
73
114
  if not ctx():
74
115
  if not value:
75
116
  raise AssertionError(message if message is not None else "Assertion failed")
@@ -91,6 +132,13 @@ def assert_true(value: int | float | bool, message: str | None = None):
91
132
  set_ctx(t_branch)
92
133
 
93
134
 
135
+ @meta_fn
136
+ def assert_true(value: int | float | bool, message: str | None = None):
137
+ if ctx() and not ctx().project_state.dev:
138
+ return
139
+ require(value, message)
140
+
141
+
94
142
  def assert_false(value: int | float | bool, message: str | None = None):
95
143
  assert_true(not value, message)
96
144
 
@@ -156,13 +204,13 @@ def visualize_cfg(
156
204
  RenumberVars(),
157
205
  ]
158
206
 
159
- global_state = GlobalContextState(
207
+ project_state = ProjectContextState()
208
+ mode_state = ModeContextState(
160
209
  mode,
161
210
  {a: i for i, a in enumerate(archetypes)} if archetypes is not None else None,
162
- ReadOnlyMemory(),
163
211
  )
164
212
 
165
- cfg = callback_to_cfg(global_state, fn, callback, archetype=archetype) # type: ignore
213
+ cfg = callback_to_cfg(project_state, mode_state, fn, callback, archetype=archetype) # type: ignore
166
214
  cfg = run_passes(cfg, passes, OptimizerConfig(mode=mode))
167
215
  return cfg_to_mermaid(cfg)
168
216
 
sonolus/script/effect.py CHANGED
@@ -5,7 +5,7 @@ from dataclasses import dataclass
5
5
  from typing import Annotated, Any, NewType, dataclass_transform, get_origin
6
6
 
7
7
  from sonolus.backend.ops import Op
8
- from sonolus.script.array_like import ArrayLike
8
+ from sonolus.script.array_like import ArrayLike, check_positive_index
9
9
  from sonolus.script.debug import static_error
10
10
  from sonolus.script.internal.introspection import get_field_specifiers
11
11
  from sonolus.script.internal.native import native_function
@@ -111,7 +111,10 @@ class EffectGroup(Record, ArrayLike[Effect]):
111
111
  return self.size
112
112
 
113
113
  def __getitem__(self, index: int) -> Effect:
114
- assert 0 <= index < self.size
114
+ check_positive_index(index, self.size)
115
+ return Effect(self.start_id + index)
116
+
117
+ def get_unchecked(self, index: int) -> Effect:
115
118
  return Effect(self.start_id + index)
116
119
 
117
120
  def __setitem__(self, index: int, value: Effect) -> None:
@@ -3,7 +3,7 @@ from typing import Never, assert_never
3
3
  from sonolus.backend.ops import Op
4
4
  from sonolus.script.array import Array
5
5
  from sonolus.script.array_like import ArrayLike
6
- from sonolus.script.debug import error
6
+ from sonolus.script.debug import error, require
7
7
  from sonolus.script.internal.context import ctx
8
8
  from sonolus.script.internal.dict_impl import DictImpl
9
9
  from sonolus.script.internal.impl import meta_fn, validate_value
@@ -192,7 +192,7 @@ def _max_num_iterator(iterable, default, key):
192
192
  iterator = iterable.__iter__() # noqa: PLC2801
193
193
  initial = iterator.next()
194
194
  if initial.is_nothing:
195
- assert default is not None
195
+ require(default is not None, "default must be provided if the iterator is empty")
196
196
  return default
197
197
  if key is not None:
198
198
  result = initial.get_unsafe()
@@ -285,7 +285,7 @@ def _min_num_iterator(iterable, default, key):
285
285
  iterator = iterable.__iter__() # noqa: PLC2801
286
286
  initial = iterator.next()
287
287
  if initial.is_nothing:
288
- assert default is not None
288
+ require(default is not None, "default must be provided if the iterator is empty")
289
289
  return default
290
290
  if key is not None:
291
291
  result = initial.get_unsafe()
@@ -371,7 +371,7 @@ def _sum(iterable, /, start=0):
371
371
 
372
372
 
373
373
  def _next(iterator):
374
- assert isinstance(iterator, SonolusIterator)
374
+ require(isinstance(iterator, SonolusIterator), "Only subclasses of SonolusIterator are supported as iterators")
375
375
  value = iterator.next()
376
376
  if value.is_some:
377
377
  return value.get_unsafe()
@@ -39,19 +39,38 @@ _disabled_debug_config = DebugConfig(
39
39
  debug_var = ContextVar("debug_var", default=_disabled_debug_config)
40
40
 
41
41
 
42
- class GlobalContextState:
42
+ class ProjectContextState:
43
+ rom: ReadOnlyMemory
44
+ const_mappings: dict[Any, int]
45
+ debug_str_mappings: dict[str, int]
46
+ lock: Lock
47
+ dev: bool
48
+
49
+ def __init__(
50
+ self,
51
+ rom: ReadOnlyMemory | None = None,
52
+ const_mappings: dict[Any, int] | None = None,
53
+ debug_str_mappings: dict[str, int] | None = None,
54
+ dev: bool = False,
55
+ ):
56
+ self.rom = ReadOnlyMemory() if rom is None else rom
57
+ self.const_mappings = {} if const_mappings is None else const_mappings
58
+ self.debug_str_mappings = {} if debug_str_mappings is None else debug_str_mappings
59
+ self.lock = Lock()
60
+ self.dev = dev
61
+
62
+
63
+ class ModeContextState:
43
64
  archetypes: dict[type, int]
44
65
  archetypes_by_name: dict[str, type]
45
66
  keys_by_archetype_id: Sequence[int]
46
67
  is_scored_by_archetype_id: Sequence[bool]
47
- rom: ReadOnlyMemory
48
- const_mappings: dict[Any, int]
49
68
  environment_mappings: dict[_GlobalInfo, int]
50
69
  environment_offsets: dict[Block, int]
51
70
  mode: Mode
52
71
  lock: Lock
53
72
 
54
- def __init__(self, mode: Mode, archetypes: dict[type, int] | None = None, rom: ReadOnlyMemory | None = None):
73
+ def __init__(self, mode: Mode, archetypes: dict[type, int] | None = None):
55
74
  from sonolus.script.array import Array
56
75
 
57
76
  self.archetypes = archetypes or {}
@@ -65,8 +84,6 @@ class GlobalContextState:
65
84
  if archetypes
66
85
  else Array[bool, Literal[0]]()
67
86
  )
68
- self.rom = ReadOnlyMemory() if rom is None else rom
69
- self.const_mappings = {}
70
87
  self.environment_mappings = {}
71
88
  self.environment_offsets = {}
72
89
  self.mode = mode
@@ -76,16 +93,19 @@ class GlobalContextState:
76
93
  class CallbackContextState:
77
94
  callback: str
78
95
  used_names: dict[str, int]
96
+ debug_stack: list[str]
79
97
  no_eval: bool
80
98
 
81
99
  def __init__(self, callback: str, no_eval: bool = False):
82
100
  self.callback = callback
83
101
  self.used_names = {}
102
+ self.debug_stack = []
84
103
  self.no_eval = no_eval
85
104
 
86
105
 
87
106
  class Context:
88
- global_state: GlobalContextState
107
+ project_state: ProjectContextState
108
+ mode_state: ModeContextState
89
109
  callback_state: CallbackContextState
90
110
  statements: list[IRStmt]
91
111
  test: IRExpr
@@ -96,13 +116,15 @@ class Context:
96
116
 
97
117
  def __init__(
98
118
  self,
99
- global_state: GlobalContextState,
119
+ project_state: ProjectContextState,
120
+ mode_state: ModeContextState,
100
121
  callback_state: CallbackContextState,
101
122
  scope: Scope | None = None,
102
123
  live: bool = True,
103
124
  foldable_constants: dict[TempBlock, list[float | None]] | None = None,
104
125
  ):
105
- self.global_state = global_state
126
+ self.project_state = project_state
127
+ self.mode_state = mode_state
106
128
  self.callback_state = callback_state
107
129
  self.statements = []
108
130
  self.test = IRConst(0)
@@ -114,11 +136,11 @@ class Context:
114
136
 
115
137
  @property
116
138
  def rom(self) -> ReadOnlyMemory:
117
- return self.global_state.rom
139
+ return self.project_state.rom
118
140
 
119
141
  @property
120
142
  def blocks(self) -> type[Block]:
121
- return self.global_state.mode.blocks
143
+ return self.mode_state.mode.blocks
122
144
 
123
145
  @property
124
146
  def callback(self) -> str:
@@ -182,7 +204,8 @@ class Context:
182
204
 
183
205
  def copy_with_scope(self, scope: Scope) -> Context:
184
206
  return Context(
185
- global_state=self.global_state,
207
+ project_state=self.project_state,
208
+ mode_state=self.mode_state,
186
209
  callback_state=self.callback_state,
187
210
  scope=scope,
188
211
  live=self.live,
@@ -267,25 +290,33 @@ class Context:
267
290
  )
268
291
 
269
292
  def map_constant(self, value: Any) -> int:
270
- with self.global_state.lock:
271
- const_mappings = self.global_state.const_mappings
293
+ with self.project_state.lock:
294
+ const_mappings = self.project_state.const_mappings
272
295
  if value not in const_mappings:
273
296
  const_mappings[value] = len(const_mappings)
274
297
  return const_mappings[value]
275
298
 
299
+ def map_debug_message(self, message: str) -> int:
300
+ with self.project_state.lock:
301
+ message_with_trace = "\n".join([*self.callback_state.debug_stack, message])
302
+ debug_str_mappings = self.project_state.debug_str_mappings
303
+ if message_with_trace not in debug_str_mappings:
304
+ debug_str_mappings[message_with_trace] = len(debug_str_mappings) + 1
305
+ return debug_str_mappings[message_with_trace]
306
+
276
307
  def get_global_base(self, value: _GlobalInfo | _GlobalPlaceholder) -> BlockPlace:
277
- with self.global_state.lock:
278
- block = value.blocks.get(self.global_state.mode)
308
+ with self.mode_state.lock:
309
+ block = value.blocks.get(self.mode_state.mode)
279
310
  if block is None:
280
- raise RuntimeError(f"Global {value} is not available in '{self.global_state.mode.name}' mode")
281
- if value not in self.global_state.environment_mappings:
311
+ raise RuntimeError(f"Global {value} is not available in '{self.mode_state.mode.name}' mode")
312
+ if value not in self.mode_state.environment_mappings:
282
313
  if value.offset is None:
283
- offset = self.global_state.environment_offsets.get(block, 0)
284
- self.global_state.environment_mappings[value] = offset
285
- self.global_state.environment_offsets[block] = offset + value.size
314
+ offset = self.mode_state.environment_offsets.get(block, 0)
315
+ self.mode_state.environment_mappings[value] = offset
316
+ self.mode_state.environment_offsets[block] = offset + value.size
286
317
  else:
287
- self.global_state.environment_mappings[value] = value.offset
288
- return BlockPlace(block, self.global_state.environment_mappings[value])
318
+ self.mode_state.environment_mappings[value] = value.offset
319
+ return BlockPlace(block, self.mode_state.environment_mappings[value])
289
320
 
290
321
  @classmethod
291
322
  def meet(cls, contexts: list[Context]) -> Context:
@@ -303,10 +334,10 @@ class Context:
303
334
  return target
304
335
 
305
336
  def register_archetype(self, type_: type) -> int:
306
- with self.global_state.lock:
307
- if type_ not in self.global_state.archetypes:
308
- self.global_state.archetypes[type_] = len(self.global_state.archetypes)
309
- return self.global_state.archetypes[type_]
337
+ with self.mode_state.lock:
338
+ if type_ not in self.mode_state.archetypes:
339
+ self.mode_state.archetypes[type_] = len(self.mode_state.archetypes)
340
+ return self.mode_state.archetypes[type_]
310
341
 
311
342
 
312
343
  def ctx() -> Context | Any: # Using Any to silence type checker warnings if it's None