tnfr 4.5.1__py3-none-any.whl → 6.0.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.
Files changed (170) hide show
  1. tnfr/__init__.py +270 -90
  2. tnfr/__init__.pyi +40 -0
  3. tnfr/_compat.py +11 -0
  4. tnfr/_version.py +7 -0
  5. tnfr/_version.pyi +7 -0
  6. tnfr/alias.py +631 -0
  7. tnfr/alias.pyi +140 -0
  8. tnfr/cache.py +732 -0
  9. tnfr/cache.pyi +232 -0
  10. tnfr/callback_utils.py +381 -0
  11. tnfr/callback_utils.pyi +105 -0
  12. tnfr/cli/__init__.py +89 -0
  13. tnfr/cli/__init__.pyi +47 -0
  14. tnfr/cli/arguments.py +199 -0
  15. tnfr/cli/arguments.pyi +33 -0
  16. tnfr/cli/execution.py +322 -0
  17. tnfr/cli/execution.pyi +80 -0
  18. tnfr/cli/utils.py +34 -0
  19. tnfr/cli/utils.pyi +8 -0
  20. tnfr/config/__init__.py +12 -0
  21. tnfr/config/__init__.pyi +8 -0
  22. tnfr/config/constants.py +104 -0
  23. tnfr/config/constants.pyi +12 -0
  24. tnfr/config/init.py +36 -0
  25. tnfr/config/init.pyi +8 -0
  26. tnfr/config/operator_names.py +106 -0
  27. tnfr/config/operator_names.pyi +28 -0
  28. tnfr/config/presets.py +104 -0
  29. tnfr/config/presets.pyi +7 -0
  30. tnfr/constants/__init__.py +228 -0
  31. tnfr/constants/__init__.pyi +104 -0
  32. tnfr/constants/core.py +158 -0
  33. tnfr/constants/core.pyi +17 -0
  34. tnfr/constants/init.py +31 -0
  35. tnfr/constants/init.pyi +12 -0
  36. tnfr/constants/metric.py +102 -0
  37. tnfr/constants/metric.pyi +19 -0
  38. tnfr/constants_glyphs.py +16 -0
  39. tnfr/constants_glyphs.pyi +12 -0
  40. tnfr/dynamics/__init__.py +136 -0
  41. tnfr/dynamics/__init__.pyi +83 -0
  42. tnfr/dynamics/adaptation.py +201 -0
  43. tnfr/dynamics/aliases.py +22 -0
  44. tnfr/dynamics/coordination.py +343 -0
  45. tnfr/dynamics/dnfr.py +2315 -0
  46. tnfr/dynamics/dnfr.pyi +33 -0
  47. tnfr/dynamics/integrators.py +561 -0
  48. tnfr/dynamics/integrators.pyi +35 -0
  49. tnfr/dynamics/runtime.py +521 -0
  50. tnfr/dynamics/sampling.py +34 -0
  51. tnfr/dynamics/sampling.pyi +7 -0
  52. tnfr/dynamics/selectors.py +680 -0
  53. tnfr/execution.py +216 -0
  54. tnfr/execution.pyi +65 -0
  55. tnfr/flatten.py +283 -0
  56. tnfr/flatten.pyi +28 -0
  57. tnfr/gamma.py +320 -89
  58. tnfr/gamma.pyi +40 -0
  59. tnfr/glyph_history.py +337 -0
  60. tnfr/glyph_history.pyi +53 -0
  61. tnfr/grammar.py +23 -153
  62. tnfr/grammar.pyi +13 -0
  63. tnfr/helpers/__init__.py +151 -0
  64. tnfr/helpers/__init__.pyi +66 -0
  65. tnfr/helpers/numeric.py +88 -0
  66. tnfr/helpers/numeric.pyi +12 -0
  67. tnfr/immutable.py +214 -0
  68. tnfr/immutable.pyi +37 -0
  69. tnfr/initialization.py +199 -0
  70. tnfr/initialization.pyi +73 -0
  71. tnfr/io.py +311 -0
  72. tnfr/io.pyi +11 -0
  73. tnfr/locking.py +37 -0
  74. tnfr/locking.pyi +7 -0
  75. tnfr/metrics/__init__.py +41 -0
  76. tnfr/metrics/__init__.pyi +20 -0
  77. tnfr/metrics/coherence.py +1469 -0
  78. tnfr/metrics/common.py +149 -0
  79. tnfr/metrics/common.pyi +15 -0
  80. tnfr/metrics/core.py +259 -0
  81. tnfr/metrics/core.pyi +13 -0
  82. tnfr/metrics/diagnosis.py +840 -0
  83. tnfr/metrics/diagnosis.pyi +89 -0
  84. tnfr/metrics/export.py +151 -0
  85. tnfr/metrics/glyph_timing.py +369 -0
  86. tnfr/metrics/reporting.py +152 -0
  87. tnfr/metrics/reporting.pyi +12 -0
  88. tnfr/metrics/sense_index.py +294 -0
  89. tnfr/metrics/sense_index.pyi +9 -0
  90. tnfr/metrics/trig.py +216 -0
  91. tnfr/metrics/trig.pyi +12 -0
  92. tnfr/metrics/trig_cache.py +105 -0
  93. tnfr/metrics/trig_cache.pyi +10 -0
  94. tnfr/node.py +255 -177
  95. tnfr/node.pyi +161 -0
  96. tnfr/observers.py +154 -150
  97. tnfr/observers.pyi +46 -0
  98. tnfr/ontosim.py +135 -134
  99. tnfr/ontosim.pyi +33 -0
  100. tnfr/operators/__init__.py +452 -0
  101. tnfr/operators/__init__.pyi +31 -0
  102. tnfr/operators/definitions.py +181 -0
  103. tnfr/operators/definitions.pyi +92 -0
  104. tnfr/operators/jitter.py +266 -0
  105. tnfr/operators/jitter.pyi +11 -0
  106. tnfr/operators/registry.py +80 -0
  107. tnfr/operators/registry.pyi +15 -0
  108. tnfr/operators/remesh.py +569 -0
  109. tnfr/presets.py +10 -23
  110. tnfr/presets.pyi +7 -0
  111. tnfr/py.typed +0 -0
  112. tnfr/rng.py +440 -0
  113. tnfr/rng.pyi +14 -0
  114. tnfr/selector.py +217 -0
  115. tnfr/selector.pyi +19 -0
  116. tnfr/sense.py +307 -142
  117. tnfr/sense.pyi +30 -0
  118. tnfr/structural.py +69 -164
  119. tnfr/structural.pyi +46 -0
  120. tnfr/telemetry/__init__.py +13 -0
  121. tnfr/telemetry/verbosity.py +37 -0
  122. tnfr/tokens.py +61 -0
  123. tnfr/tokens.pyi +41 -0
  124. tnfr/trace.py +520 -95
  125. tnfr/trace.pyi +68 -0
  126. tnfr/types.py +382 -17
  127. tnfr/types.pyi +145 -0
  128. tnfr/utils/__init__.py +158 -0
  129. tnfr/utils/__init__.pyi +133 -0
  130. tnfr/utils/cache.py +755 -0
  131. tnfr/utils/cache.pyi +156 -0
  132. tnfr/utils/data.py +267 -0
  133. tnfr/utils/data.pyi +73 -0
  134. tnfr/utils/graph.py +87 -0
  135. tnfr/utils/graph.pyi +10 -0
  136. tnfr/utils/init.py +746 -0
  137. tnfr/utils/init.pyi +85 -0
  138. tnfr/utils/io.py +157 -0
  139. tnfr/utils/io.pyi +10 -0
  140. tnfr/utils/validators.py +130 -0
  141. tnfr/utils/validators.pyi +19 -0
  142. tnfr/validation/__init__.py +25 -0
  143. tnfr/validation/__init__.pyi +17 -0
  144. tnfr/validation/compatibility.py +59 -0
  145. tnfr/validation/compatibility.pyi +8 -0
  146. tnfr/validation/grammar.py +149 -0
  147. tnfr/validation/grammar.pyi +11 -0
  148. tnfr/validation/rules.py +194 -0
  149. tnfr/validation/rules.pyi +18 -0
  150. tnfr/validation/syntax.py +151 -0
  151. tnfr/validation/syntax.pyi +7 -0
  152. tnfr-6.0.0.dist-info/METADATA +135 -0
  153. tnfr-6.0.0.dist-info/RECORD +157 -0
  154. tnfr/cli.py +0 -322
  155. tnfr/config.py +0 -41
  156. tnfr/constants.py +0 -277
  157. tnfr/dynamics.py +0 -814
  158. tnfr/helpers.py +0 -264
  159. tnfr/main.py +0 -47
  160. tnfr/metrics.py +0 -597
  161. tnfr/operators.py +0 -525
  162. tnfr/program.py +0 -176
  163. tnfr/scenarios.py +0 -34
  164. tnfr/validators.py +0 -38
  165. tnfr-4.5.1.dist-info/METADATA +0 -221
  166. tnfr-4.5.1.dist-info/RECORD +0 -28
  167. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/WHEEL +0 -0
  168. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/entry_points.txt +0 -0
  169. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/licenses/LICENSE.md +0 -0
  170. {tnfr-4.5.1.dist-info → tnfr-6.0.0.dist-info}/top_level.txt +0 -0
tnfr/structural.py CHANGED
@@ -1,201 +1,106 @@
1
+ """Structural analysis."""
2
+
1
3
  from __future__ import annotations
2
- """API de operadores estructurales y secuencias TNFR.
3
-
4
- Este módulo ofrece:
5
- - Factoría `create_nfr` para inicializar redes/nodos TNFR.
6
- - Clases de operador (`Operador` y derivados) con interfaz común.
7
- - Registro de operadores `OPERADORES`.
8
- - Utilidades `validate_sequence` y `run_sequence` para ejecutar
9
- secuencias canónicas de operadores.
10
- """
11
- from typing import Iterable, Tuple, List
4
+
5
+ from typing import Iterable
6
+
12
7
  import networkx as nx
13
8
 
9
+ from .constants import EPI_PRIMARY, VF_PRIMARY, THETA_PRIMARY
14
10
  from .dynamics import (
15
11
  set_delta_nfr_hook,
16
- update_epi_via_nodal_equation,
17
12
  dnfr_epi_vf_mixed,
18
13
  )
19
- from .operators import aplicar_glifo
20
- from .constants import ALIAS_EPI, ALIAS_VF, ALIAS_THETA
14
+ from .types import DeltaNFRHook, NodeId, TNFRGraph
15
+ from .operators.definitions import (
16
+ Operator,
17
+ Emission,
18
+ Reception,
19
+ Coherence,
20
+ Dissonance,
21
+ Coupling,
22
+ Resonance,
23
+ Silence,
24
+ Expansion,
25
+ Contraction,
26
+ SelfOrganization,
27
+ Mutation,
28
+ Transition,
29
+ Recursivity,
30
+ )
31
+ from .operators.registry import OPERATORS
32
+ from .validation import validate_sequence
21
33
 
22
34
 
23
35
  # ---------------------------------------------------------------------------
24
- # 1) Factoría NFR
36
+ # 1) NFR factory
25
37
  # ---------------------------------------------------------------------------
26
38
 
39
+
27
40
  def create_nfr(
28
41
  name: str,
29
42
  *,
30
43
  epi: float = 0.0,
31
44
  vf: float = 1.0,
32
45
  theta: float = 0.0,
33
- graph: nx.Graph | None = None,
34
- dnfr_hook=dnfr_epi_vf_mixed,
35
- ) -> Tuple[nx.Graph, str]:
36
- """Crea una red (graph) con un nodo NFR inicializado.
46
+ graph: TNFRGraph | None = None,
47
+ dnfr_hook: DeltaNFRHook = dnfr_epi_vf_mixed,
48
+ ) -> tuple[TNFRGraph, str]:
49
+ """Create a graph with an initialised NFR node.
37
50
 
38
- Devuelve la tupla ``(G, name)`` para conveniencia.
51
+ Returns the tuple ``(G, name)`` for convenience.
39
52
  """
40
- G = graph or nx.Graph()
53
+ G = graph if graph is not None else nx.Graph()
41
54
  G.add_node(
42
55
  name,
43
56
  **{
44
- ALIAS_EPI[0]: float(epi),
45
- ALIAS_VF[0]: float(vf),
46
- ALIAS_THETA[0]: float(theta),
47
- }
57
+ EPI_PRIMARY: float(epi),
58
+ VF_PRIMARY: float(vf),
59
+ THETA_PRIMARY: float(theta),
60
+ },
48
61
  )
49
62
  set_delta_nfr_hook(G, dnfr_hook)
50
63
  return G, name
51
64
 
52
65
 
53
- # ---------------------------------------------------------------------------
54
- # 2) Operadores estructurales como API de primer orden
55
- # ---------------------------------------------------------------------------
56
-
57
-
58
- class Operador:
59
- """Base para operadores TNFR.
60
-
61
- Cada operador define ``name`` (identificador ASCII) y ``glyph`` (glifo
62
- canónico). La llamada ejecuta el glifo correspondiente sobre el nodo.
63
- """
64
-
65
- name = "operador"
66
- glyph = None # tipo: str
67
-
68
- def __call__(self, G: nx.Graph, node, **kw) -> None:
69
- if self.glyph is None:
70
- raise NotImplementedError("Operador sin glifo asignado")
71
- aplicar_glifo(G, node, self.glyph, **kw)
72
-
73
-
74
- # Derivados concretos -------------------------------------------------------
75
- class Emision(Operador):
76
- name = "emision"
77
- glyph = "A’L"
78
-
79
-
80
- class Recepcion(Operador):
81
- name = "recepcion"
82
- glyph = "E’N"
83
-
84
-
85
- class Coherencia(Operador):
86
- name = "coherencia"
87
- glyph = "I’L"
88
-
89
-
90
- class Disonancia(Operador):
91
- name = "disonancia"
92
- glyph = "O’Z"
93
-
94
-
95
- class Acoplamiento(Operador):
96
- name = "acoplamiento"
97
- glyph = "U’M"
98
-
99
-
100
- class Resonancia(Operador):
101
- name = "resonancia"
102
- glyph = "R’A"
103
-
104
-
105
- class Silencio(Operador):
106
- name = "silencio"
107
- glyph = "SH’A"
108
-
109
-
110
- class Expansion(Operador):
111
- name = "expansion"
112
- glyph = "VA’L"
113
-
114
-
115
- class Contraccion(Operador):
116
- name = "contraccion"
117
- glyph = "NU’L"
118
-
119
-
120
- class Autoorganizacion(Operador):
121
- name = "autoorganizacion"
122
- glyph = "T’HOL"
123
-
124
-
125
- class Mutacion(Operador):
126
- name = "mutacion"
127
- glyph = "Z’HIR"
128
-
129
-
130
- class Transicion(Operador):
131
- name = "transicion"
132
- glyph = "NA’V"
133
-
134
-
135
- class Recursividad(Operador):
136
- name = "recursividad"
137
- glyph = "RE’MESH"
138
-
139
-
140
- OPERADORES = {
141
- op().name: op
142
- for op in [
143
- Emision,
144
- Recepcion,
145
- Coherencia,
146
- Disonancia,
147
- Acoplamiento,
148
- Resonancia,
149
- Silencio,
150
- Expansion,
151
- Contraccion,
152
- Autoorganizacion,
153
- Mutacion,
154
- Transicion,
155
- Recursividad,
156
- ]
157
- }
158
-
159
-
160
- # ---------------------------------------------------------------------------
161
- # 3) Motor de secuencias + validador sintáctico
162
- # ---------------------------------------------------------------------------
163
-
164
-
165
- _INICIO_VALIDOS = {"emision", "recursividad"}
166
- _TRAMO_INTERMEDIO = {"disonancia", "acoplamiento", "resonancia"}
167
- _CIERRE_VALIDO = {"silencio", "transicion", "recursividad"}
168
-
66
+ __all__ = (
67
+ "create_nfr",
68
+ "Operator",
69
+ "Emission",
70
+ "Reception",
71
+ "Coherence",
72
+ "Dissonance",
73
+ "Coupling",
74
+ "Resonance",
75
+ "Silence",
76
+ "Expansion",
77
+ "Contraction",
78
+ "SelfOrganization",
79
+ "Mutation",
80
+ "Transition",
81
+ "Recursivity",
82
+ "OPERATORS",
83
+ "validate_sequence",
84
+ "run_sequence",
85
+ )
169
86
 
170
- def validate_sequence(nombres: List[str]) -> Tuple[bool, str]:
171
- """Valida reglas mínimas de la sintaxis TNFR."""
172
- if not nombres:
173
- return False, "secuencia vacía"
174
- if nombres[0] not in _INICIO_VALIDOS:
175
- return False, "debe iniciar en emisión o recursividad"
176
- try:
177
- i_rec = nombres.index("recepcion")
178
- i_coh = nombres.index("coherencia", i_rec + 1)
179
- except ValueError:
180
- return False, "falta tramo entrada→coherencia"
181
- if not any(n in _TRAMO_INTERMEDIO for n in nombres[i_coh + 1 :]):
182
- return False, "falta tramo de tensión/acoplamiento/resonancia"
183
- if not any(n in _CIERRE_VALIDO for n in nombres[-2:]):
184
- return False, "falta cierre (silencio/transición/recursividad)"
185
- return True, "ok"
186
87
 
88
+ def run_sequence(G: TNFRGraph, node: NodeId, ops: Iterable[Operator]) -> None:
89
+ """Execute a sequence of operators on ``node`` after validation."""
187
90
 
188
- def run_sequence(G: nx.Graph, node, ops: Iterable[Operador]) -> None:
189
- """Ejecuta una secuencia validada de operadores sobre el nodo dado."""
91
+ compute = G.graph.get("compute_delta_nfr")
190
92
  ops_list = list(ops)
191
- nombres = [op.name for op in ops_list]
192
- ok, msg = validate_sequence(nombres)
93
+ names = [op.name for op in ops_list]
94
+
95
+ ok, msg = validate_sequence(names)
193
96
  if not ok:
194
- raise ValueError(f"Secuencia no válida: {msg}")
97
+ raise ValueError(f"Invalid sequence: {msg}")
98
+
195
99
  for op in ops_list:
196
100
  op(G, node)
197
- compute = G.graph.get("compute_delta_nfr")
198
101
  if callable(compute):
199
102
  compute(G)
200
- update_epi_via_nodal_equation(G)
201
-
103
+ # ``update_epi_via_nodal_equation`` was previously invoked here to
104
+ # recalculate the EPI value after each operator. The responsibility for
105
+ # updating EPI now lies with the dynamics hook configured in
106
+ # ``compute_delta_nfr`` or with external callers.
tnfr/structural.pyi ADDED
@@ -0,0 +1,46 @@
1
+ from typing import TYPE_CHECKING, Any, Callable, Hashable, Iterable
2
+
3
+ from .operators.definitions import (
4
+ Operator,
5
+ Emission,
6
+ Reception,
7
+ Coherence,
8
+ Dissonance,
9
+ Coupling,
10
+ Resonance,
11
+ Silence,
12
+ Expansion,
13
+ Contraction,
14
+ SelfOrganization,
15
+ Mutation,
16
+ Transition,
17
+ Recursivity,
18
+ )
19
+
20
+ if TYPE_CHECKING:
21
+ import networkx as nx
22
+
23
+ __all__: tuple[str, ...]
24
+
25
+
26
+ def __getattr__(name: str) -> Any: ...
27
+
28
+
29
+ def create_nfr(
30
+ name: str,
31
+ *,
32
+ epi: float = ...,
33
+ vf: float = ...,
34
+ theta: float = ...,
35
+ graph: "nx.Graph" | None = ...,
36
+ dnfr_hook: Callable[..., None] = ...,
37
+ ) -> tuple["nx.Graph", str]: ...
38
+
39
+
40
+ OPERATORS: dict[str, Operator]
41
+
42
+
43
+ def validate_sequence(names: Iterable[str]) -> tuple[bool, str]: ...
44
+
45
+
46
+ def run_sequence(G: "nx.Graph", node: Hashable, ops: Iterable[Operator]) -> None: ...
@@ -0,0 +1,13 @@
1
+ """Telemetry helpers for shared observability settings."""
2
+
3
+ from .verbosity import (
4
+ TelemetryVerbosity,
5
+ TELEMETRY_VERBOSITY_DEFAULT,
6
+ TELEMETRY_VERBOSITY_LEVELS,
7
+ )
8
+
9
+ __all__ = [
10
+ "TelemetryVerbosity",
11
+ "TELEMETRY_VERBOSITY_DEFAULT",
12
+ "TELEMETRY_VERBOSITY_LEVELS",
13
+ ]
@@ -0,0 +1,37 @@
1
+ """Canonical telemetry verbosity presets for TNFR structures.
2
+
3
+ Each level expresses how much structural context is exported in traces and
4
+ metrics:
5
+
6
+ * ``basic`` preserves lightweight coherence checks for quick health probes.
7
+ * ``detailed`` adds phase alignment and coupling diagnostics to map resonance.
8
+ * ``debug`` captures the full glyph narrative for deep structural forensics.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from enum import Enum
14
+
15
+
16
+ class TelemetryVerbosity(str, Enum):
17
+ """Enumerated verbosity tiers shared by trace and metrics pipelines."""
18
+
19
+ BASIC = "basic"
20
+ DETAILED = "detailed"
21
+ DEBUG = "debug"
22
+
23
+
24
+ TELEMETRY_VERBOSITY_LEVELS: tuple[str, ...] = tuple(
25
+ level.value for level in TelemetryVerbosity
26
+ )
27
+ """Ordered tuple of canonical telemetry verbosity identifiers."""
28
+
29
+ TELEMETRY_VERBOSITY_DEFAULT: str = TelemetryVerbosity.DEBUG.value
30
+ """Default telemetry verbosity preserving complete structural capture."""
31
+
32
+
33
+ __all__ = [
34
+ "TelemetryVerbosity",
35
+ "TELEMETRY_VERBOSITY_LEVELS",
36
+ "TELEMETRY_VERBOSITY_DEFAULT",
37
+ ]
tnfr/tokens.py ADDED
@@ -0,0 +1,61 @@
1
+ """Token primitives for the TNFR DSL."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from enum import Enum, auto
7
+ from typing import Any, Iterable, Optional, Sequence, Union
8
+
9
+ from .types import Glyph, NodeId
10
+
11
+ Node = NodeId
12
+ #: Alias maintained for backwards compatibility with historical token helpers.
13
+
14
+
15
+ @dataclass(slots=True)
16
+ class WAIT:
17
+ """Wait a number of steps without applying glyphs."""
18
+
19
+ steps: int = 1
20
+
21
+
22
+ @dataclass(slots=True)
23
+ class TARGET:
24
+ """Select the subset of nodes for subsequent glyphs."""
25
+
26
+ nodes: Optional[Iterable[Node]] = None # ``None`` targets all nodes
27
+
28
+
29
+ @dataclass(slots=True)
30
+ class THOL:
31
+ """THOL block that opens self-organisation."""
32
+
33
+ body: Sequence[Any]
34
+ repeat: int = 1
35
+ force_close: Optional[Glyph] = None
36
+
37
+
38
+ Token = Union[Glyph, WAIT, TARGET, THOL, str]
39
+
40
+ # Sentinel used internally to mark the boundaries of a THOL block during flattening
41
+ THOL_SENTINEL = object()
42
+
43
+
44
+ class OpTag(Enum):
45
+ """Operation tags emitted by the flattening step."""
46
+
47
+ TARGET = auto()
48
+ WAIT = auto()
49
+ GLYPH = auto()
50
+ THOL = auto()
51
+
52
+
53
+ __all__ = [
54
+ "Node",
55
+ "WAIT",
56
+ "TARGET",
57
+ "THOL",
58
+ "Token",
59
+ "THOL_SENTINEL",
60
+ "OpTag",
61
+ ]
tnfr/tokens.pyi ADDED
@@ -0,0 +1,41 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from enum import Enum
5
+ from typing import Iterable, Optional, Sequence
6
+
7
+ from .types import Glyph, NodeId
8
+ from ._compat import TypeAlias
9
+
10
+ __all__: tuple[str, ...]
11
+
12
+ Node: TypeAlias = NodeId
13
+
14
+
15
+ @dataclass(slots=True)
16
+ class WAIT:
17
+ steps: int = 1
18
+
19
+
20
+ @dataclass(slots=True)
21
+ class TARGET:
22
+ nodes: Optional[Iterable[Node]] = None
23
+
24
+
25
+ @dataclass(slots=True)
26
+ class THOL:
27
+ body: Sequence["Token"]
28
+ repeat: int = 1
29
+ force_close: Optional[Glyph] = None
30
+
31
+
32
+ Token: TypeAlias = Glyph | WAIT | TARGET | THOL | str
33
+
34
+ THOL_SENTINEL: object
35
+
36
+
37
+ class OpTag(Enum):
38
+ TARGET = ...
39
+ WAIT = ...
40
+ GLYPH = ...
41
+ THOL = ...