onec-interactive-runtime-core 0.1.19__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 (87) hide show
  1. onec_interactive_runtime_core-0.1.19.dist-info/METADATA +12 -0
  2. onec_interactive_runtime_core-0.1.19.dist-info/RECORD +87 -0
  3. onec_interactive_runtime_core-0.1.19.dist-info/WHEEL +4 -0
  4. onec_interactive_runtime_core-0.1.19.dist-info/licenses/COPYRIGHT +9 -0
  5. onec_interactive_runtime_core-0.1.19.dist-info/licenses/LICENSE +232 -0
  6. onec_interactive_runtime_core-0.1.19.dist-info/licenses/THIRD_PARTY_NOTICES.txt +32 -0
  7. onec_runtime/AGENTS.md +7 -0
  8. onec_runtime/__init__.py +3 -0
  9. onec_runtime/artifacts.py +59 -0
  10. onec_runtime/bootstrap.py +310 -0
  11. onec_runtime/breakpoint_workspace.py +249 -0
  12. onec_runtime/bsl/__init__.py +139 -0
  13. onec_runtime/bsl/diagnostics.py +1046 -0
  14. onec_runtime/bsl/full_ast_worker_projection.py +522 -0
  15. onec_runtime/bsl/generated_semantic_parser.py +6322 -0
  16. onec_runtime/bsl/lexer.py +164 -0
  17. onec_runtime/bsl/module_catalog.py +420 -0
  18. onec_runtime/bsl/module_delta.py +1079 -0
  19. onec_runtime/bsl/module_universe.py +1877 -0
  20. onec_runtime/bsl/notebook_cells.py +232 -0
  21. onec_runtime/bsl/notebook_method_globals.py +74 -0
  22. onec_runtime/bsl/notebook_methods.py +175 -0
  23. onec_runtime/bsl/parser_artifact_identity.py +119 -0
  24. onec_runtime/bsl/parser_target.py +388 -0
  25. onec_runtime/bsl/preprocessor.py +16 -0
  26. onec_runtime/bsl/semantic_lowering.py +1539 -0
  27. onec_runtime/bsl/source_maps.py +2411 -0
  28. onec_runtime/bsl/worker_dependency_resolver.py +289 -0
  29. onec_runtime/bsl/worker_preprocessor.py +247 -0
  30. onec_runtime/bsl/worker_projection_model.py +137 -0
  31. onec_runtime/bsl/worker_reload_source_map.py +1697 -0
  32. onec_runtime/capture.py +113 -0
  33. onec_runtime/capture_source.py +453 -0
  34. onec_runtime/compact_table.py +376 -0
  35. onec_runtime/compact_table_backend.py +500 -0
  36. onec_runtime/config.py +198 -0
  37. onec_runtime/configurator_agent.py +520 -0
  38. onec_runtime/controller_worker.py +400 -0
  39. onec_runtime/credentials.py +18 -0
  40. onec_runtime/epf_container.py +196 -0
  41. onec_runtime/errors.py +205 -0
  42. onec_runtime/experiment.py +130 -0
  43. onec_runtime/extension_bundle.py +944 -0
  44. onec_runtime/extension_lifecycle.py +402 -0
  45. onec_runtime/extension_state.py +222 -0
  46. onec_runtime/fault_injection.py +30 -0
  47. onec_runtime/kernel.py +179 -0
  48. onec_runtime/lease.py +123 -0
  49. onec_runtime/observation.py +287 -0
  50. onec_runtime/performance_profile.py +245 -0
  51. onec_runtime/privacy.py +263 -0
  52. onec_runtime/processes.py +265 -0
  53. onec_runtime/prototype_runtime.py +2644 -0
  54. onec_runtime/rdbg/__init__.py +1 -0
  55. onec_runtime/rdbg/models.py +130 -0
  56. onec_runtime/rdbg/reconnect.py +59 -0
  57. onec_runtime/rdbg/session.py +913 -0
  58. onec_runtime/rdbg/transport.py +138 -0
  59. onec_runtime/rdbg/xml_codec.py +980 -0
  60. onec_runtime/recovery.py +106 -0
  61. onec_runtime/recovery_journal.py +79 -0
  62. onec_runtime/resources/extension/OnecInteractiveRuntime.cfe +0 -0
  63. onec_runtime/resources/extension/extension-manifest.json +108 -0
  64. onec_runtime/runtime_api.py +5272 -0
  65. onec_runtime/runtime_contracts.py +196 -0
  66. onec_runtime/server_worker.py +2034 -0
  67. onec_runtime/session.py +2123 -0
  68. onec_runtime/startup_diagnostics.py +45 -0
  69. onec_runtime/stop_routing.py +65 -0
  70. onec_runtime/supervised_processes.py +232 -0
  71. onec_runtime/supervised_runtime.py +512 -0
  72. onec_runtime/supervisor.py +363 -0
  73. onec_runtime/supervisor_model.py +148 -0
  74. onec_runtime/supervisor_protocol.py +77 -0
  75. onec_runtime/table_materialization.py +471 -0
  76. onec_runtime/table_transfer_backend.py +203 -0
  77. onec_runtime/table_value.py +311 -0
  78. onec_runtime/toolchain.py +730 -0
  79. onec_runtime/value_materialization.py +432 -0
  80. onec_runtime/value_transfer_backend.py +199 -0
  81. onec_runtime/worker_breakpoints.py +1431 -0
  82. onec_runtime/worker_epf.py +313 -0
  83. onec_runtime/worker_epf_template.py +53 -0
  84. onec_runtime/worker_stage_protocol.py +516 -0
  85. onec_runtime/worker_universe.py +3772 -0
  86. onec_runtime_build/__init__.py +1 -0
  87. onec_runtime_build/parser_target_development.py +116 -0
@@ -0,0 +1,522 @@
1
+ """Project the generated full BSL AST into the Worker-facing module model."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from bisect import bisect_left
6
+ from collections.abc import Iterable, Mapping
7
+ from dataclasses import dataclass, fields
8
+
9
+ from onec_runtime.bsl import generated_semantic_parser as generated
10
+ from onec_runtime.bsl.lexer import Token, tokenize
11
+ from onec_runtime.bsl.parser_target import BslParseError, PythonParserTarget
12
+ from onec_runtime.bsl.source_maps import SourceSpan, source_sha256
13
+ from onec_runtime.bsl.worker_preprocessor import select_server_effective_tokens
14
+ from onec_runtime.bsl.worker_projection_model import (
15
+ BareName,
16
+ BareNameKind,
17
+ ParsedMethodModel,
18
+ ParsedModuleModel,
19
+ )
20
+ from onec_runtime.performance_profile import PhaseRecorder
21
+
22
+
23
+ _IGNORED = 0
24
+ _ONE = 1
25
+ _MANY = 2
26
+ _POSTFIX = 3
27
+ _HOT = 4
28
+ _CARRIER = 5
29
+
30
+ # Every generated dataclass field is explicit: typed semantic child edge,
31
+ # direct hot-path responsibility, linked carrier, or intentionally ignored
32
+ # scalar/non-fact field. Field order mirrors the generated dataclasses.
33
+ _NODE_FIELD_SCHEMA: dict[str, tuple[tuple[str, int], ...]] = {
34
+ "Module": (("Declarations", _MANY), ("Elements", _ONE), ("span", _IGNORED)),
35
+ "ModuleElements": (("Item", _CARRIER), ("Rest", _CARRIER), ("span", _IGNORED)),
36
+ "NotebookCell": (("Elements", _ONE), ("span", _IGNORED)),
37
+ "ModuleVariableDeclaration": (("Variables", _MANY), ("span", _IGNORED)),
38
+ "ModuleVariable": (("Name", _IGNORED), ("Export", _IGNORED), ("span", _IGNORED)),
39
+ "Method": (
40
+ ("Decorations", _MANY), ("Async", _IGNORED),
41
+ ("Declaration", _ONE), ("span", _IGNORED),
42
+ ),
43
+ "ProcedureDeclaration": (
44
+ ("Name", _IGNORED), ("Parameters", _ONE), ("Export", _IGNORED),
45
+ ("Body", _ONE), ("span", _IGNORED),
46
+ ),
47
+ "FunctionDeclaration": (
48
+ ("Name", _IGNORED), ("Parameters", _ONE), ("Export", _IGNORED),
49
+ ("Body", _ONE), ("span", _IGNORED),
50
+ ),
51
+ "MethodBody": (("LocalDeclarations", _MANY), ("Code", _ONE), ("span", _IGNORED)),
52
+ "LocalVariableDeclaration": (("Names", _HOT), ("span", _IGNORED)),
53
+ "ParameterList": (("Items", _MANY), ("span", _IGNORED)),
54
+ "Parameter": (
55
+ ("Decorations", _HOT), ("ByValue", _IGNORED), ("Name", _HOT),
56
+ ("Default", _HOT), ("span", _IGNORED),
57
+ ),
58
+ "SignedConstant": (("Sign", _IGNORED), ("Value", _IGNORED), ("span", _IGNORED)),
59
+ "Decoration": (("Name", _IGNORED), ("Parameters", _ONE), ("span", _IGNORED)),
60
+ "AnnotationParameters": (("Items", _MANY), ("span", _IGNORED)),
61
+ "NamedAnnotationParameter": (("Name", _IGNORED), ("Value", _ONE), ("span", _IGNORED)),
62
+ "PositionalAnnotationParameter": (("Value", _ONE), ("span", _IGNORED)),
63
+ "CodeBlock": (("First", _CARRIER), ("Rest", _CARRIER), ("span", _IGNORED)),
64
+ "LabeledStatement": (("Label", _IGNORED), ("Statement", _ONE), ("span", _IGNORED)),
65
+ "Label": (("Name", _IGNORED), ("span", _IGNORED)),
66
+ "ContinueStatement": (("span", _IGNORED),),
67
+ "BreakStatement": (("span", _IGNORED),),
68
+ "GotoStatement": (("Label", _IGNORED), ("span", _IGNORED)),
69
+ "IfStatement": (
70
+ ("Condition", _ONE), ("Then", _ONE), ("ElseIf", _MANY),
71
+ ("Else", _ONE), ("span", _IGNORED),
72
+ ),
73
+ "ElseIfClause": (("Condition", _ONE), ("Body", _ONE), ("span", _IGNORED)),
74
+ "ElseClause": (("Body", _ONE), ("span", _IGNORED)),
75
+ "WhileStatement": (("Condition", _ONE), ("Body", _ONE), ("span", _IGNORED)),
76
+ "ForEachStatement": (
77
+ ("Variable", _HOT), ("Iterable", _HOT), ("Body", _HOT),
78
+ ("span", _IGNORED),
79
+ ),
80
+ "ForRangeStatement": (
81
+ ("Variable", _HOT), ("Start", _HOT), ("End", _HOT),
82
+ ("Body", _HOT), ("span", _IGNORED),
83
+ ),
84
+ "TryStatement": (("TryBody", _ONE), ("ExceptBody", _ONE), ("span", _IGNORED)),
85
+ "ReturnStatement": (("Value", _ONE), ("span", _IGNORED)),
86
+ "RaiseStatement": (("Parameters", _ONE), ("span", _IGNORED)),
87
+ "RaiseCallParameters": (
88
+ ("Arguments", _ONE), ("Postfix", _POSTFIX), ("Tail", _ONE),
89
+ ("span", _IGNORED),
90
+ ),
91
+ "ContinuedExpression": (
92
+ ("PrefixOperators", _IGNORED), ("Primary", _ONE),
93
+ ("Tail", _ONE), ("span", _IGNORED),
94
+ ),
95
+ "PostfixedPrimary": (("Base", _ONE), ("Postfix", _POSTFIX), ("span", _IGNORED)),
96
+ "ExpressionTail": (
97
+ ("MultiplicativeOperators", _IGNORED),
98
+ ("MultiplicativeOperands", _MANY),
99
+ ("AdditiveOperators", _IGNORED), ("AdditiveOperands", _MANY),
100
+ ("ComparisonOperators", _IGNORED), ("ComparisonOperands", _MANY),
101
+ ("AndOperands", _MANY), ("OrOperands", _MANY),
102
+ ("span", _IGNORED),
103
+ ),
104
+ "ExecuteStatement": (("Value", _ONE), ("span", _IGNORED)),
105
+ "AddHandlerStatement": (("Event", _ONE), ("Handler", _ONE), ("span", _IGNORED)),
106
+ "RemoveHandlerStatement": (("Event", _ONE), ("Handler", _ONE), ("span", _IGNORED)),
107
+ "AwaitStatement": (("Value", _ONE), ("span", _IGNORED)),
108
+ "SimpleStatement": (("Target", _HOT), ("Value", _HOT), ("span", _IGNORED)),
109
+ "OrExpression": (("First", _HOT), ("Operands", _HOT), ("span", _IGNORED)),
110
+ "AndExpression": (("First", _HOT), ("Operands", _HOT), ("span", _IGNORED)),
111
+ "ComparisonExpression": (
112
+ ("First", _HOT), ("Operators", _IGNORED), ("Operands", _HOT),
113
+ ("span", _IGNORED),
114
+ ),
115
+ "AdditiveExpression": (
116
+ ("First", _HOT), ("Operators", _IGNORED), ("Operands", _HOT),
117
+ ("span", _IGNORED),
118
+ ),
119
+ "MultiplicativeExpression": (
120
+ ("First", _HOT), ("Operators", _IGNORED), ("Operands", _HOT),
121
+ ("span", _IGNORED),
122
+ ),
123
+ "UnaryExpression": (("Operators", _IGNORED), ("Value", _HOT), ("span", _IGNORED)),
124
+ "ParenthesizedExpression": (("Value", _ONE), ("Postfix", _POSTFIX), ("span", _IGNORED)),
125
+ "AccessChain": (("Root", _HOT), ("Arguments", _HOT), ("Postfix", _HOT), ("span", _IGNORED)),
126
+ "MemberAccess": (("Name", _IGNORED), ("Arguments", _ONE), ("span", _IGNORED)),
127
+ "IndexAccess": (("Index", _ONE), ("span", _IGNORED)),
128
+ "CallArguments": (("Items", _ONE), ("span", _IGNORED)),
129
+ "ArgumentList": (("Items", _MANY), ("span", _IGNORED)),
130
+ "OmittedArgument": (("span", _IGNORED),),
131
+ "NewExpression": (("Type", _IGNORED), ("Arguments", _ONE), ("span", _IGNORED)),
132
+ "TernaryExpression": (("Condition", _ONE), ("Then", _ONE), ("Else", _ONE), ("span", _IGNORED)),
133
+ "ConstantLiteral": (("Value", _IGNORED), ("span", _IGNORED)),
134
+ "RegionDirective": (("Name", _IGNORED), ("span", _IGNORED)),
135
+ "EndRegionDirective": (("span", _IGNORED),),
136
+ "PreprocessorIfDirective": (("Condition", _ONE), ("span", _IGNORED)),
137
+ "PreprocessorElseIfDirective": (("Condition", _ONE), ("span", _IGNORED)),
138
+ "PreprocessorElseDirective": (("span", _IGNORED),),
139
+ "PreprocessorEndIfDirective": (("span", _IGNORED),),
140
+ "UseDirective": (("Library", _ONE), ("span", _IGNORED)),
141
+ "NativeDirective": (("span", _IGNORED),),
142
+ "StackDirective": (("span", _IGNORED),),
143
+ "UsedLibrary": (("Value", _IGNORED), ("span", _IGNORED)),
144
+ "PreprocessorOr": (("First", _ONE), ("Operands", _MANY), ("span", _IGNORED)),
145
+ "PreprocessorAnd": (("First", _ONE), ("Operands", _MANY), ("span", _IGNORED)),
146
+ "PreprocessorNot": (("Value", _ONE), ("span", _IGNORED)),
147
+ "PreprocessorIdentifier": (("Name", _IGNORED), ("span", _IGNORED)),
148
+ }
149
+
150
+
151
+ def _validate_node_field_schema(
152
+ ast_classes: Mapping[str, type[object]],
153
+ ) -> None:
154
+ actual_types = frozenset(ast_classes)
155
+ classified_types = frozenset(_NODE_FIELD_SCHEMA)
156
+ if actual_types != classified_types:
157
+ raise ValueError(
158
+ "generated AST type schema mismatch: "
159
+ f"generated={sorted(actual_types)!r}, "
160
+ f"classified={sorted(classified_types)!r}"
161
+ )
162
+ valid_kinds = {_IGNORED, _ONE, _MANY, _POSTFIX, _HOT, _CARRIER}
163
+ for node_name, node_type in ast_classes.items():
164
+ actual_fields = tuple(field.name for field in fields(node_type))
165
+ classified = _NODE_FIELD_SCHEMA[node_name]
166
+ classified_fields = tuple(field_name for field_name, _kind in classified)
167
+ if actual_fields != classified_fields:
168
+ raise ValueError(
169
+ f"generated AST field schema mismatch for {node_name}: "
170
+ f"generated={actual_fields!r}, classified={classified_fields!r}"
171
+ )
172
+ if any(kind not in valid_kinds for _field_name, kind in classified):
173
+ raise ValueError(f"invalid AST field classification for {node_name}")
174
+
175
+
176
+ _validate_node_field_schema(generated.AST_CLASSES)
177
+ _TYPED_CHILD_EDGES: dict[type[object], tuple[tuple[str, int], ...]] = {
178
+ generated.AST_CLASSES[node_name]: tuple(
179
+ (field_name, kind)
180
+ for field_name, kind in field_schema
181
+ if kind in {_ONE, _MANY, _POSTFIX}
182
+ )
183
+ for node_name, field_schema in _NODE_FIELD_SCHEMA.items()
184
+ }
185
+
186
+
187
+ @dataclass(frozen=True, slots=True)
188
+ class _ScopeFacts:
189
+ declared_names: tuple[str, ...]
190
+ bare_names: tuple[BareName, ...]
191
+
192
+
193
+ def _linked_items(value: object) -> tuple[object, ...]:
194
+ """Flatten generated linked sequence nodes without recursive Python calls."""
195
+ result: list[object] = []
196
+ current = value
197
+ while isinstance(current, generated.ModuleElements):
198
+ if current.Item is not None:
199
+ result.append(current.Item)
200
+ current = current.Rest
201
+ if current is not None:
202
+ if isinstance(current, tuple):
203
+ result.extend(current)
204
+ else:
205
+ result.append(current)
206
+ return tuple(result)
207
+
208
+
209
+ def _collect_scope_facts(roots: Iterable[object]) -> _ScopeFacts:
210
+ declared_names: set[str] = set()
211
+ # normalized -> [first span start, source-order tie-breaker, spelling, flags]
212
+ names: dict[str, list[object]] = {}
213
+ sequence = 0
214
+ read_kind = int(BareNameKind.READ)
215
+ write_kind = int(BareNameKind.BARE_WRITE)
216
+ stack = list(reversed(tuple(roots)))
217
+
218
+ while stack:
219
+ value = stack.pop()
220
+ value_type = type(value)
221
+
222
+ # These six precedence wrappers are over 60% of a real ZUP AST. Keep
223
+ # their typed edges at the front of the dispatch chain.
224
+ if (
225
+ value_type is generated.OrExpression
226
+ or value_type is generated.AndExpression
227
+ or value_type is generated.ComparisonExpression
228
+ or value_type is generated.AdditiveExpression
229
+ or value_type is generated.MultiplicativeExpression
230
+ ):
231
+ if value.Operands:
232
+ stack.extend(reversed(value.Operands))
233
+ if value.First is not None:
234
+ stack.append(value.First)
235
+ continue
236
+ if value_type is generated.UnaryExpression:
237
+ if value.Value is not None:
238
+ stack.append(value.Value)
239
+ continue
240
+ if (
241
+ value_type is generated.SimpleStatement
242
+ or value_type is generated.AccessChain
243
+ ):
244
+ if value_type is generated.SimpleStatement:
245
+ target = value.Target
246
+ if type(target) is not generated.AccessChain:
247
+ raise TypeError(
248
+ "generated SimpleStatement has an unsupported target"
249
+ )
250
+ if value.Value is None:
251
+ has_call = target.Arguments is not None or any(
252
+ type(marker) is generated.MemberAccess
253
+ and marker.Arguments is not None
254
+ for marker in target.Postfix
255
+ )
256
+ if not has_call:
257
+ raise BslParseError(
258
+ "bare access-chain statement is forbidden at "
259
+ f"{target.span.start}",
260
+ span=target.span,
261
+ code="bare_access_chain_statement",
262
+ )
263
+ else:
264
+ stack.append(value.Value)
265
+ direct_write = bool(
266
+ value.Value is not None
267
+ and target.Root
268
+ and target.Arguments is None
269
+ and not target.Postfix
270
+ )
271
+ else:
272
+ target = value
273
+ direct_write = False
274
+
275
+ name = str(target.Root)
276
+ normalized = name.casefold()
277
+ fact_kind = write_kind if direct_write else read_kind
278
+ fact = names.get(normalized)
279
+ if fact is None:
280
+ names[normalized] = [
281
+ target.span.start, sequence, name, fact_kind,
282
+ ]
283
+ else:
284
+ fact[3] = int(fact[3]) | fact_kind
285
+ if (target.span.start, sequence) < (int(fact[0]), int(fact[1])):
286
+ fact[0] = target.span.start
287
+ fact[1] = sequence
288
+ fact[2] = name
289
+ sequence += 1
290
+
291
+ if not direct_write:
292
+ for marker in reversed(target.Postfix):
293
+ if type(marker) is generated.MemberAccess:
294
+ if marker.Arguments is not None:
295
+ stack.append(marker.Arguments)
296
+ elif type(marker) is generated.IndexAccess:
297
+ stack.append(marker.Index)
298
+ else:
299
+ raise TypeError(
300
+ "generated AccessChain has an unsupported postfix"
301
+ )
302
+ if target.Arguments is not None:
303
+ stack.append(target.Arguments)
304
+ continue
305
+
306
+ if value_type is generated.Parameter:
307
+ declared_names.add(str(value.Name).casefold())
308
+ if value.Default is not None:
309
+ stack.append(value.Default)
310
+ if value.Decorations:
311
+ stack.extend(reversed(value.Decorations))
312
+ elif value_type is generated.LocalVariableDeclaration:
313
+ declared_names.update(str(name).casefold() for name in value.Names)
314
+ elif value_type is generated.ForEachStatement:
315
+ declared_names.add(str(value.Variable).casefold())
316
+ if value.Body is not None:
317
+ stack.append(value.Body)
318
+ if value.Iterable is not None:
319
+ stack.append(value.Iterable)
320
+ elif value_type is generated.ForRangeStatement:
321
+ declared_names.add(str(value.Variable).casefold())
322
+ if value.Body is not None:
323
+ stack.append(value.Body)
324
+ if value.End is not None:
325
+ stack.append(value.End)
326
+ if value.Start is not None:
327
+ stack.append(value.Start)
328
+ elif value_type is generated.CodeBlock:
329
+ items: list[object] = []
330
+ current: object = value
331
+ while isinstance(current, generated.CodeBlock):
332
+ if current.First is not None:
333
+ items.append(current.First)
334
+ current = current.Rest
335
+ if current is not None:
336
+ items.append(current)
337
+ if items:
338
+ stack.extend(reversed(items))
339
+ elif value_type is generated.ModuleElements:
340
+ items = list(_linked_items(value))
341
+ if items:
342
+ stack.extend(reversed(items))
343
+ else:
344
+ child_edges = _TYPED_CHILD_EDGES.get(value_type)
345
+ if child_edges is None:
346
+ raise TypeError(
347
+ f"unclassified generated AST node: {value_type.__name__}"
348
+ )
349
+ for field_name, edge_kind in reversed(child_edges):
350
+ child = getattr(value, field_name)
351
+ if child is None:
352
+ continue
353
+ if edge_kind == _ONE:
354
+ stack.append(child)
355
+ elif edge_kind == _MANY:
356
+ if child:
357
+ stack.extend(reversed(child))
358
+ else:
359
+ for marker in reversed(child):
360
+ if type(marker) is generated.MemberAccess:
361
+ if marker.Arguments is not None:
362
+ stack.append(marker.Arguments)
363
+ elif type(marker) is generated.IndexAccess:
364
+ stack.append(marker.Index)
365
+ else:
366
+ raise TypeError(
367
+ "generated expression has an unsupported postfix"
368
+ )
369
+
370
+ ordered_names = sorted(names.items(), key=lambda item: (item[1][0], item[1][1]))
371
+ result = tuple(
372
+ BareName(str(fact[2]), normalized, BareNameKind(int(fact[3])))
373
+ for normalized, fact in ordered_names
374
+ )
375
+ return _ScopeFacts(tuple(sorted(declared_names)), tuple(result))
376
+
377
+
378
+ def _initializer_offset(
379
+ body: generated.MethodBody,
380
+ tokens: tuple[Token, ...],
381
+ token_starts: tuple[int, ...],
382
+ ) -> int:
383
+ declarations = body.LocalDeclarations
384
+ if not declarations:
385
+ return body.span.start
386
+ declaration_end = declarations[-1].span.end
387
+ token_index = bisect_left(token_starts, declaration_end)
388
+ if token_index >= len(tokens) or tokens[token_index].type != ";":
389
+ raise BslParseError(
390
+ f"local declaration terminator is missing at {declaration_end}",
391
+ span=SourceSpan(declaration_end, declaration_end),
392
+ code="projection_invariant",
393
+ )
394
+ return tokens[token_index].end
395
+
396
+
397
+ def _project_method(
398
+ method: generated.Method,
399
+ tokens: tuple[Token, ...],
400
+ token_starts: tuple[int, ...],
401
+ ) -> ParsedMethodModel:
402
+ declaration = method.Declaration
403
+ if not isinstance(
404
+ declaration,
405
+ (generated.ProcedureDeclaration, generated.FunctionDeclaration),
406
+ ):
407
+ raise TypeError("generated Method has an unsupported declaration")
408
+ body = declaration.Body
409
+ if not isinstance(body, generated.MethodBody):
410
+ raise TypeError("generated method declaration has an unsupported body")
411
+ name = str(declaration.Name)
412
+ scope = _collect_scope_facts((declaration,))
413
+ return ParsedMethodModel(
414
+ name=name,
415
+ normalized_name=name.casefold(),
416
+ exported=declaration.Export is not None,
417
+ declaration_span=method.span,
418
+ alias_declaration_offset=body.span.start,
419
+ alias_initializer_offset=_initializer_offset(body, tokens, token_starts),
420
+ declared_names=scope.declared_names,
421
+ bare_names=scope.bare_names,
422
+ )
423
+
424
+
425
+ def project_full_ast_module(
426
+ source: str,
427
+ root: object,
428
+ tokens: tuple[Token, ...],
429
+ *,
430
+ parser_identity: tuple[str, str],
431
+ ) -> ParsedModuleModel:
432
+ """Build the exact immutable Worker model without retaining AST or tokens."""
433
+ if not isinstance(root, generated.Module):
434
+ raise TypeError("root must be a generated Module")
435
+ if type(tokens) is not tuple:
436
+ raise TypeError("tokens must be an immutable tuple")
437
+
438
+ module_variables = {
439
+ str(variable.Name).casefold()
440
+ for declaration in root.Declarations
441
+ for variable in declaration.Variables
442
+ }
443
+ module_roots: list[object] = []
444
+ methods: list[generated.Method] = []
445
+ for item in _linked_items(root.Elements):
446
+ if isinstance(item, generated.Method):
447
+ methods.append(item)
448
+ else:
449
+ module_roots.append(item)
450
+
451
+ token_starts = tuple(token.start for token in tokens)
452
+ module_scope = _collect_scope_facts(module_roots)
453
+ module_variables.update(module_scope.declared_names)
454
+ return ParsedModuleModel(
455
+ source_sha256=source_sha256(source),
456
+ module_variables=tuple(sorted(module_variables)),
457
+ module_bare_names=module_scope.bare_names,
458
+ methods=tuple(
459
+ _project_method(method, tokens, token_starts) for method in methods
460
+ ),
461
+ parser_identity=parser_identity,
462
+ )
463
+
464
+
465
+ def _target_parser_identity(target: PythonParserTarget) -> tuple[str, str]:
466
+ package_sha256 = target.metadata.parsergen_package_sha256
467
+ if package_sha256 is None:
468
+ raise RuntimeError("full AST parser package identity is unavailable")
469
+ return target.metadata.parser_identity_sha256, package_sha256
470
+
471
+
472
+ def full_ast_parser_identity() -> tuple[str, str]:
473
+ """Return provenance embedded in the committed full-AST parser artifact."""
474
+ return _target_parser_identity(PythonParserTarget.from_generated())
475
+
476
+
477
+ def parse_full_ast_module(
478
+ source: str,
479
+ *,
480
+ profiler: PhaseRecorder | None = None,
481
+ ) -> ParsedModuleModel:
482
+ """Tokenize and parse once, then retain only the hot-reload module model."""
483
+ tokens = tuple(tokenize(source))
484
+ effective = select_server_effective_tokens(source, tokens)
485
+ target = PythonParserTarget.from_generated()
486
+ parser_identity = _target_parser_identity(target)
487
+
488
+ def parse_root() -> object:
489
+ return target.parse_tokens_ast(effective, "Модуль")
490
+
491
+ root = (
492
+ parse_root()
493
+ if profiler is None
494
+ else profiler.measure_parser("full_module_parses", parse_root)
495
+ )
496
+
497
+ def extract() -> ParsedModuleModel:
498
+ return project_full_ast_module(
499
+ source,
500
+ root,
501
+ effective,
502
+ parser_identity=parser_identity,
503
+ )
504
+
505
+ return extract() if profiler is None else profiler.measure("ast_model_extract", extract)
506
+
507
+
508
+ def parse_full_ast_projected_module(
509
+ source: str,
510
+ *,
511
+ profiler: PhaseRecorder | None = None,
512
+ ) -> ParsedModuleModel:
513
+ """Compatibility name for benchmarks that compare full AST with Worker."""
514
+ return parse_full_ast_module(source, profiler=profiler)
515
+
516
+
517
+ __all__ = [
518
+ "full_ast_parser_identity",
519
+ "parse_full_ast_module",
520
+ "parse_full_ast_projected_module",
521
+ "project_full_ast_module",
522
+ ]