Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions pineforge_codegen/analyzer/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ def __init__(self, ast: Program, filename: str = "<stdin>") -> None:
self._block_var_owner: dict[str, int] = {}
self._block_var_renames: dict[int, dict[str, str]] = {}
self._block_var_seq = 0
# Identifier node identity -> the lexical Symbol.scope resolved while
# that node's source scope is live. Codegen uses this narrow
# provenance to distinguish true global aliases from same-named local
# shadows after the analyzer's scope stack has unwound.
self._identifier_binding_scopes: dict[int, str | None] = {}
self._ta_counter = 0
self._fixnan_counter = 0
# All fixnan member names minted so far (base + clones), for O(1)
Expand Down Expand Up @@ -520,6 +525,7 @@ def analyze(self) -> AnalyzerContext:
filename=self._filename,
global_var_decls=self._global_var_decls,
global_expr_map=pure_global_expr_map,
identifier_binding_scopes=dict(self._identifier_binding_scopes),
var_member_init_exprs=self._var_member_init_exprs,
var_member_metadata_by_node=self._var_member_metadata_by_node,
var_member_type_specs_by_node=self._var_member_type_specs_by_node,
Expand Down Expand Up @@ -4762,6 +4768,9 @@ def _visit_Identifier(self, node: Identifier) -> PineType:
return PineType.VOID

sym = self._symbols.resolve(node.name)
self._identifier_binding_scopes[id(node)] = (
getattr(sym, "scope", None) if sym is not None else None
)
if sym is not None:
return sym.pine_type

Expand Down
4 changes: 4 additions & 0 deletions pineforge_codegen/analyzer/contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ class AnalyzerContext:
filename: str = "<stdin>"
global_var_decls: list = field(default_factory=list) # [(name, PineType)] non-var global scope vars
global_expr_map: dict = field(default_factory=dict) # name -> defining AST expr (global, non-var)
# Exact lexical binding scope for each visited Identifier AST node. Raw
# name lookup is not enough once a UDF parameter, block local, or loop
# iterator shadows a same-spelled global binding.
identifier_binding_scopes: dict = field(default_factory=dict) # id(Identifier) -> scope name | None
var_member_init_exprs: dict = field(default_factory=dict) # var-member name -> init AST expr
# id(VarDecl) -> (node, emitted member name, PineType, rendered initializer,
# is function/method scoped)
Expand Down
Loading
Loading