diff --git a/jaclang/jac/passes/blue/sym_tab_build_pass.py b/jaclang/jac/passes/blue/sym_tab_build_pass.py index 57e673a2c..88237a0e9 100644 --- a/jaclang/jac/passes/blue/sym_tab_build_pass.py +++ b/jaclang/jac/passes/blue/sym_tab_build_pass.py @@ -1,7 +1,7 @@ """Ast build pass for Jaseci Ast.""" import jaclang.jac.absyntree as ast from jaclang.jac.passes import Pass -from jaclang.jac.symtable import Symbol, SymbolTable +from jaclang.jac.symtable import SymbolHitType, SymbolTable class SymTabBuildPass(Pass): @@ -11,14 +11,20 @@ def before_pass(self) -> None: """Before pass.""" self.cur_sym_tab = SymbolTable() - def register_unique_name(self, name: str, typ: str, node: ast.AstNode) -> None: - """Register unique name.""" - if not self.cur_sym_tab.insert(Symbol(name, node), fresh_only=True): - original = self.cur_sym_tab.lookup(name) - self.error( - f"Name used for {typ} '{name}' already declared " - f"on line {original.decl.line if original else self.ice()}", - ) + def already_declared_err(self, name: str, typ: str, original: ast.AstNode) -> None: + """Already declared error.""" + self.error( + f"Name used for {typ} '{name}' already declared on line {original.line}" + ) + + def resolve_ability_symtab_name(self, node: ast.Ability) -> str: + """Resolve ability name in symbol table.""" + return ( + f"{node.arch_attached.parent.name.value}.{node.py_resolve_name()}" + if node.arch_attached + and isinstance(node.arch_attached.parent, ast.Architype) + else node.py_resolve_name() + ) def enter_module(self, node: ast.Module) -> None: """Sub objects. @@ -75,7 +81,13 @@ def enter_test(self, node: ast.Test) -> None: body: CodeBlock, sym_tab: Optional[SymbolTable], """ - self.register_unique_name(node.name.value, "test", node) + if collide := self.cur_sym_tab.insert( + name=node.name.value, + sym_hit=SymbolHitType.DECL, + node=node, + single=True, + ): + self.already_declared_err(node.name.value, "test", collide) self.cur_sym_tab = self.cur_sym_tab.push_scope() node.sym_tab = self.cur_sym_tab @@ -151,7 +163,13 @@ def enter_architype(self, node: ast.Architype) -> None: body: Optional[ArchBlock], sym_tab: Optional[SymbolTable], """ - self.register_unique_name(node.name.value, "architype", node) + if collide := self.cur_sym_tab.insert( + name=node.name.value, + sym_hit=SymbolHitType.DECL, + node=node, + single=True, + ): + self.already_declared_err(node.name.value, "architype", collide) self.cur_sym_tab = self.cur_sym_tab.push_scope() node.sym_tab = self.cur_sym_tab @@ -211,7 +229,14 @@ def enter_ability(self, node: ast.Ability) -> None: sym_tab: Optional[SymbolTable], arch_attached: Optional[ArchBlock], """ - self.register_unique_name(node.py_resolve_name(), "ability", node) + ability_name = self.resolve_ability_symtab_name(node) + if collide := self.cur_sym_tab.insert( + name=ability_name, + sym_hit=SymbolHitType.DECL, + node=node, + single=True, + ): + self.already_declared_err(ability_name, "ability", collide) self.cur_sym_tab = self.cur_sym_tab.push_scope() node.sym_tab = self.cur_sym_tab @@ -242,6 +267,22 @@ def enter_ability_def(self, node: ast.AbilityDef) -> None: body: CodeBlock, sym_tab: Optional[SymbolTable], """ + ability_name = node.ability.py_resolve_name() + if node.target: + owner = node.target.names[-1] + if not isinstance(owner, ast.ArchRef): + self.error("Expected reference to Architype!") + owner = "" + else: + owner = owner.py_resolve_name() + ability_name = f"{owner}.{ability_name}" + if collide := self.cur_sym_tab.insert( + name=ability_name, + sym_hit=SymbolHitType.DEFN, + node=node, + single=True, + ): + self.already_declared_err(ability_name, "ability def", collide) node.sym_tab = self.cur_sym_tab def enter_event_signature(self, node: ast.EventSignature) -> None: @@ -301,7 +342,13 @@ def enter_enum(self, node: ast.Enum) -> None: body: Optional['EnumBlock'], sym_tab: Optional[SymbolTable], """ - self.register_unique_name(node.name.value, "enum", node) + if collide := self.cur_sym_tab.insert( + name=node.name.value, + sym_hit=SymbolHitType.DECL, + node=node, + single=True, + ): + self.already_declared_err(node.name.value, "enum", collide) self.cur_sym_tab = self.cur_sym_tab.push_scope() node.sym_tab = self.cur_sym_tab diff --git a/jaclang/jac/symtable.py b/jaclang/jac/symtable.py index e5be78b67..28eb667b3 100644 --- a/jaclang/jac/symtable.py +++ b/jaclang/jac/symtable.py @@ -1,20 +1,29 @@ """Jac Symbol Table.""" from __future__ import annotations +from enum import Enum from typing import Optional, TYPE_CHECKING if TYPE_CHECKING: import jaclang.jac.absyntree as ast +class SymbolHitType(Enum): + """Symbol types.""" + + DECL = "decl" + DEFN = "defn" + USE = "use" + + class Symbol: """Symbol.""" def __init__( self, name: str, - decl: ast.AstNode, typ: Optional[type] = None, + decl: Optional[ast.AstNode] = None, defn: Optional[list[ast.AstNode]] = None, uses: Optional[list[ast.AstNode]] = None, ) -> None: @@ -42,12 +51,44 @@ def lookup(self, name: str, deep: bool = True) -> Optional[Symbol]: return self.parent.lookup(name, deep) return None - def insert(self, sym: Symbol, fresh_only: bool = False) -> bool: - """Set a variable in the symbol table.""" - if fresh_only and sym.name in self.tab: - return False - self.tab[sym.name] = sym - return True + def insert( + self, + name: str, + sym_hit: SymbolHitType, + node: ast.AstNode, + single: bool = False, + ) -> Optional[ast.AstNode]: + """Set a variable in the symbol table. + + Returns original symbol single check fails. + """ + if single: + if ( + sym_hit == SymbolHitType.DECL + and name in self.tab + and self.tab[name].decl + ): + return self.tab[name].decl + elif ( + sym_hit == SymbolHitType.DEFN + and name in self.tab + and len(self.tab[name].defn) + ): + return self.tab[name].defn[-1] + elif ( + sym_hit == SymbolHitType.USE + and name in self.tab + and len(self.tab[name].uses) + ): + return self.tab[name].uses[-1] + if name not in self.tab: + self.tab[name] = Symbol(name=name) + if sym_hit == SymbolHitType.DECL: + self.tab[name].decl = node + elif sym_hit == SymbolHitType.DEFN: + self.tab[name].defn.append(node) + elif sym_hit == SymbolHitType.USE: + self.tab[name].uses.append(node) def push_scope(self) -> SymbolTable: """Push a new scope onto the symbol table."""