@pineforge/codegen-pyodide 0.9.0 → 0.10.1

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.
@@ -24,7 +24,7 @@ from __future__ import annotations
24
24
  from dataclasses import dataclass, field
25
25
  from typing import Any
26
26
 
27
- from ..symbols import TypeSpec
27
+ from ..symbols import PineType, TypeSpec
28
28
 
29
29
 
30
30
  @dataclass
@@ -88,11 +88,12 @@ class FuncInfo:
88
88
  # ``pivot hi`` parameter emits as ``pivot hi`` (not ``double hi``) and an
89
89
  # untyped ``s`` used as a string emits as ``std::string s``.
90
90
  param_type_specs: list = field(default_factory=list)
91
- # ``TypeSpec`` of the function's return value when it is a collection the
92
- # coarse ``return_type`` (PineType) cannot represent — today this covers
93
- # array-returning functions (``buildPDLevels() => array.from(...)`` ->
94
- # ``std::vector<double>``). UDT / drawing-handle returns use
95
- # ``udt_return_type``; tuple returns use ``returns_tuple``.
91
+ # Exact ``TypeSpec`` of the function's return value when downstream
92
+ # expression inference needs more than the coarse ``return_type`` slot.
93
+ # This primarily carries arrays/maps and also direct-terminal primitive
94
+ # array-element reads, whose cached spec prevents inference from re-visiting
95
+ # a stateful UDF call. UDT / drawing-handle returns use ``udt_return_type``;
96
+ # tuple returns use ``returns_tuple``.
96
97
  return_type_spec: Any = None
97
98
 
98
99
 
@@ -137,6 +138,12 @@ class SecurityCallInfo:
137
138
  expression: Any
138
139
  returns_tuple: bool
139
140
  tuple_size: int
141
+ # Exact helper/direct tuple element types. Empty for scalar calls and for
142
+ # legacy/TA tuple records whose runtime result struct already defines its
143
+ # field types. User-defined request.security helpers use this to select
144
+ # numeric-double versus homogeneous-bool aggregate storage without
145
+ # guessing in codegen.
146
+ tuple_element_types: tuple[PineType, ...] = ()
140
147
  gaps: Any = None
141
148
  lookahead: Any = None
142
149
  ta_range: Any = None
@@ -181,6 +188,22 @@ class AnalyzerContext:
181
188
  symbols: Any # SymbolTable
182
189
  ta_call_sites: list = field(default_factory=list)
183
190
  series_vars: set = field(default_factory=set)
191
+ # Exact emitted member identities for persistent ``var``/``varip``
192
+ # bindings that are history-referenced. Unlike ``series_vars`` this set
193
+ # preserves sibling-block renames such as ``x__blk1``.
194
+ series_var_members: set = field(default_factory=set)
195
+ # Exact VarDecl node ids whose binding is history-referenced. This lets
196
+ # codegen distinguish a scalar local shadow from a same-named global
197
+ # Series without relying on the raw-name union above.
198
+ series_decl_nodes: set = field(default_factory=set)
199
+ # Exact declaration binding keys ``(node_id, raw_name)``. TupleAssign has
200
+ # one AST node for several independently scoped names, so node identity
201
+ # alone cannot say which destructured element owns history storage.
202
+ series_decl_bindings: set = field(default_factory=set)
203
+ # Raw names of exact declaration-bound, non-persistent Series storage.
204
+ # This excludes history parameters and global reads observed from inside a
205
+ # callable, both of which also appear in legacy raw series inventories.
206
+ nonpersistent_series_decl_names: set = field(default_factory=set)
184
207
  series_bar_fields: set = field(default_factory=set)
185
208
  var_members: list = field(default_factory=list) # [(name, PineType, init_expr_str)]
186
209
  func_infos: list = field(default_factory=list)
@@ -198,11 +221,43 @@ class AnalyzerContext:
198
221
  filename: str = "<stdin>"
199
222
  global_var_decls: list = field(default_factory=list) # [(name, PineType)] non-var global scope vars
200
223
  global_expr_map: dict = field(default_factory=dict) # name -> defining AST expr (global, non-var)
224
+ # Exact lexical binding scope for each visited Identifier AST node. Raw
225
+ # name lookup is not enough once a UDF parameter, block local, or loop
226
+ # iterator shadows a same-spelled global binding.
227
+ identifier_binding_scopes: dict = field(default_factory=dict) # id(Identifier) -> scope name | None
201
228
  var_member_init_exprs: dict = field(default_factory=dict) # var-member name -> init AST expr
229
+ # id(VarDecl) -> (node, emitted member name, PineType, rendered initializer,
230
+ # is function/method scoped)
231
+ var_member_metadata_by_node: dict = field(default_factory=dict)
232
+ # id(VarDecl) -> exact structured declaration type. Name-keyed type
233
+ # registries cannot distinguish sibling/callable persistent bindings.
234
+ var_member_type_specs_by_node: dict = field(default_factory=dict)
235
+ # id(VarDecl) -> owning callable key (``FuncInfo.name``), or None for
236
+ # top-level/on-bar persistent bindings.
237
+ var_member_owners_by_node: dict = field(default_factory=dict)
202
238
  # Per-call-site TA member cloning for user functions:
203
239
  func_ta_ranges: dict = field(default_factory=dict) # func_name -> (start_idx, end_idx)
240
+ # Exact TA indices belonging to each callable path. ``func_ta_ranges`` is
241
+ # retained as the legacy stateful-callable marker, but a widened min/max
242
+ # range can contain unrelated sites allocated between two borrowed sites.
243
+ func_ta_indices: dict = field(default_factory=dict) # func_name -> [site_idx, ...]
204
244
  func_call_cs_map: dict = field(default_factory=dict) # call_node_id -> (func_name, call_site_index)
245
+ # Removed lexical nested-call mappings inherit their natural parent's
246
+ # active cs index. Fresh context-sensitive parents have no such index, so
247
+ # codegen uses this exact node -> callee identity to compose dispatch.
248
+ func_inherited_call_names: dict = field(default_factory=dict)
205
249
  func_call_site_counts: dict = field(default_factory=dict) # func_name -> int
250
+ # Exact primitive parameter/return types for each emitted written-callsite
251
+ # variant. Untyped Pine parameters are independently specialized at every
252
+ # written call; collapsing these onto FuncInfo's first observed type makes
253
+ # generated semantics depend on source order (for example one ``src[1]``
254
+ # helper called with both ``bar_index`` and ``close``).
255
+ func_callsite_param_types: dict = field(default_factory=dict)
256
+ func_callsite_return_types: dict = field(default_factory=dict)
257
+ # Declaration-time parameter specs, before any call-site inference fills
258
+ # untyped slots on the legacy FuncInfo record. Codegen uses this to apply
259
+ # variant overrides only to genuinely untyped parameters.
260
+ func_declared_param_type_specs: dict = field(default_factory=dict)
206
261
  # Functions that need per-call-site body cloning PURELY because they
207
262
  # contain a request.security(...) whose timeframe is a UDF parameter
208
263
  # called with >= 2 distinct literal timeframes (the security-tf-
@@ -225,15 +280,57 @@ class AnalyzerContext:
225
280
  # request.security calls (see SecurityCallInfo):
226
281
  security_calls: list = field(default_factory=list)
227
282
  global_mutable_infos: dict = field(default_factory=dict) # name -> MutableGlobalInfo
283
+ # AST-authoritative top-level non-var binding names. Unlike
284
+ # ``global_var_decls``, this is not filtered through legacy raw Series
285
+ # tracking and therefore survives same-named callable history state.
286
+ ordinary_global_binding_names: set = field(default_factory=set)
287
+ # Exact subset whose own top-level declaration is history-bearing. A raw
288
+ # callable Series name does not promote a same-named scalar/map global.
289
+ ordinary_global_series_names: set = field(default_factory=set)
228
290
  # Per-function var_members + series_vars (used when emitting per-function call-site variants):
229
291
  func_var_members: dict = field(default_factory=dict)
292
+ # Ordinary FuncDef persistent locals keep their Pine spelling in
293
+ # ``func_var_members`` for lexical/type analysis. When its storage would
294
+ # collide with another persistent lexical declaration, this owner-scoped
295
+ # overlay carries the collision-safe class-member identity instead:
296
+ # ``func_name -> {raw_name: emitted_member_name}``.
297
+ #
298
+ # Keeping lexical and storage identity separate is important. Clone maps
299
+ # operate on emitted members, while a function body must not see the new
300
+ # member until its declaration has executed (so an earlier same-named
301
+ # global read remains lexical Pine, not the future local).
302
+ func_var_storage_names: dict = field(default_factory=dict)
230
303
  func_series_vars: dict = field(default_factory=dict)
231
- # Per-function array-return TypeSpec (see FuncInfo.return_type_spec).
304
+ # FuncDef owner -> declaration-bound non-persistent history-local names.
305
+ # Parameters are deliberately excluded so a same-named qualified
306
+ # persistent member does not manufacture a bogus raw/clone Series.
307
+ func_nonpersistent_series_vars: dict = field(default_factory=dict)
308
+ # Per-function exact return TypeSpec (see FuncInfo.return_type_spec).
232
309
  func_return_type_specs: dict = field(default_factory=dict)
233
310
  # var_name -> UDT type name for variables instantiated via TypeName.new(...)
234
311
  udt_var_types: dict[str, str] = field(default_factory=dict)
235
312
  # var_name -> structured collection/UDT type metadata
236
313
  collection_types: dict[str, TypeSpec] = field(default_factory=dict)
314
+ # Stable callable key -> raw local name -> collection TypeSpec. Function
315
+ # and method locals are lexical: two callables may both declare ``values``
316
+ # with unrelated collection kinds/element types, so they cannot share the
317
+ # top-level raw-name registry above.
318
+ func_collection_types: dict[str, dict[str, TypeSpec]] = field(default_factory=dict)
319
+ # id(immediate branch/loop owner) -> raw local name -> collection TypeSpec.
320
+ # Sibling lexical blocks may reuse one raw name with unrelated kinds, so
321
+ # their bindings cannot be collapsed into ``func_collection_types``.
322
+ block_collection_types: dict[int, dict[str, TypeSpec | None]] = field(default_factory=dict)
323
+ # Block id -> owning FuncInfo key. Used for dead-callable header filtering
324
+ # and exact persistent-member TypeSpec lookup outside callable emission.
325
+ block_collection_owners: dict[int, str] = field(default_factory=dict)
326
+ # id(callable-local VarDecl) -> exact collection TypeSpec, or None for a
327
+ # non-collection declaration that lexically tombstones an outer binding.
328
+ callable_collection_bindings: dict[int, TypeSpec | None] = field(default_factory=dict)
329
+ # Declaration node id -> owning callable key. Persistent-member emission
330
+ # happens outside the analyzer's lexical scope, so raw names alone cannot
331
+ # distinguish a real persistent collection from a same-named ordinary
332
+ # local in another callable.
333
+ callable_collection_binding_owners: dict[int, str] = field(default_factory=dict)
237
334
  # UDT name -> field_name -> structured type metadata
238
335
  udt_field_type_specs: dict[str, dict[str, TypeSpec]] = field(default_factory=dict)
239
336
  # id(block_node) -> {raw_var_name: scope_unique_member_name} for block-scoped