diff --git a/src/llvm_py/builders/entry.py b/src/llvm_py/builders/entry.py index dca95630..2d750463 100644 --- a/src/llvm_py/builders/entry.py +++ b/src/llvm_py/builders/entry.py @@ -1,5 +1,9 @@ from typing import Optional import os +import sys +# NamingBox SSOT: Add parent directory to path for naming_helper import +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) +from naming_helper import encode_static_method def ensure_ny_main(builder) -> None: """Ensure ny_main wrapper exists by delegating to Main.main/1 or main(). @@ -8,8 +12,10 @@ def ensure_ny_main(builder) -> None: has_ny_main = any(f.name == 'ny_main' for f in builder.module.functions) fn_main_box = None fn_main_plain = None + # NamingBox SSOT: Use encode_static_method for name comparison + main_box_name = encode_static_method("Main", "main", 1) for f in builder.module.functions: - if f.name == 'Main.main/1': + if f.name == main_box_name: fn_main_box = f elif f.name == 'main': fn_main_plain = f diff --git a/src/llvm_py/instructions/boxcall.py b/src/llvm_py/instructions/boxcall.py index dca6b1ff..45f6f34b 100644 --- a/src/llvm_py/instructions/boxcall.py +++ b/src/llvm_py/instructions/boxcall.py @@ -6,6 +6,7 @@ Core of Nyash's "Everything is Box" philosophy import llvmlite.ir as ir from typing import Dict, List, Optional, Any from instructions.safepoint import insert_automatic_safepoint +from naming_helper import encode_static_method def _declare(module: ir.Module, name: str, ret, args): for f in module.functions: @@ -431,9 +432,9 @@ def lower_boxcall( except Exception: pass if is_me and cur_fn_name.startswith('Main.'): - # Build target function name with arity + # NamingBox SSOT: Build target function name with arity arity = len(args) - target = f"Main.{method_name}/{arity}" + target = encode_static_method("Main", method_name, arity) # If module already has such function, prefer direct call callee = None for f in module.functions: diff --git a/src/llvm_py/instructions/call.py b/src/llvm_py/instructions/call.py index 5773350d..b2ac4ff6 100644 --- a/src/llvm_py/instructions/call.py +++ b/src/llvm_py/instructions/call.py @@ -7,6 +7,7 @@ import llvmlite.ir as ir from typing import Dict, List, Optional, Any from trace import debug as trace_debug from instructions.safepoint import insert_automatic_safepoint +from naming_helper import encode_static_method def lower_call( builder: ir.IRBuilder, @@ -165,8 +166,13 @@ def lower_call( # Make the call result = builder.call(func, call_args, name=f"call_{func_name}") - # Optional trace for final debugging - if isinstance(actual_name, str) and actual_name in ("Main.node_json/3", "Main.esc_json/1", "main"): + # NamingBox SSOT: Optional trace for final debugging + traced_names = ( + encode_static_method("Main", "node_json", 3), + encode_static_method("Main", "esc_json", 1), + "main" + ) + if isinstance(actual_name, str) and actual_name in traced_names: trace_debug(f"[TRACE] call {actual_name} args={len(call_args)}") # Store result if needed diff --git a/src/llvm_py/naming_helper.py b/src/llvm_py/naming_helper.py new file mode 100644 index 00000000..29886552 --- /dev/null +++ b/src/llvm_py/naming_helper.py @@ -0,0 +1,88 @@ +""" +MIR NamingHelper — Python mirror of Rust src/mir/naming.rs + +Responsibility: +- Encode/decode static box methods to MIR function names. +- Centralize naming rules (e.g., Main._nop/0) for Builder/PyVM consistency. +- Minimal: handle `main.*` → `Main.*` cases safely. + +Non-responsibility: +- Dynamic dispatch or BoxFactory name resolution. +- Entry point selection policy (NYASH_ENTRY). +""" + + +def encode_static_method(box_name: str, method: str, arity: int) -> str: + """ + Encode a static box method into a MIR function name: `BoxName.method/arity`. + + Args: + box_name: Raw box name (e.g., "main", "Main", "Calculator") + method: Method name (e.g., "main", "_nop", "add") + arity: Number of parameters (e.g., 0, 1, 3) + + Returns: + Canonical MIR function name (e.g., "Main.main/1", "Main._nop/0") + + Examples: + >>> encode_static_method("main", "main", 1) + 'Main.main/1' + >>> encode_static_method("Main", "_nop", 0) + 'Main._nop/0' + >>> encode_static_method("Calculator", "add", 2) + 'Calculator.add/2' + """ + return f"{canonical_box_name(box_name)}.{method}/{arity}" + + +def canonical_box_name(raw: str) -> str: + """ + Canonicalize a static box name for MIR-level usage. + + Current rules: + - "main" → "Main" (minimal correction) + - Others: return as-is (avoid wide-scope spec changes) + + Args: + raw: Raw box name (e.g., "main", "Main", "Calculator") + + Returns: + Canonical box name (e.g., "Main", "Calculator") + + Examples: + >>> canonical_box_name("main") + 'Main' + >>> canonical_box_name("Main") + 'Main' + >>> canonical_box_name("Calculator") + 'Calculator' + """ + return "Main" if raw == "main" else raw + + +def normalize_static_global_name(func_name: str) -> str: + """ + If `func_name` looks like a static box method like `main._nop/0`, + normalize the box part (`main` → `Main`) and return canonical form. + + Args: + func_name: MIR function name (e.g., "main._nop/0", "Main.main/1", "print") + + Returns: + Normalized function name (e.g., "Main._nop/0", "Main.main/1", "print") + + Examples: + >>> normalize_static_global_name("main._nop/0") + 'Main._nop/0' + >>> normalize_static_global_name("Main._nop/0") + 'Main._nop/0' + >>> normalize_static_global_name("print") + 'print' + """ + if '.' in func_name: + box_part, rest = func_name.split('.', 1) + # rest contains "method/arity" + canon = canonical_box_name(box_part) + if canon != box_part: + return f"{canon}.{rest}" + return func_name diff --git a/src/llvm_py/pyvm/intrinsic.py b/src/llvm_py/pyvm/intrinsic.py index 77068869..959d1e10 100644 --- a/src/llvm_py/pyvm/intrinsic.py +++ b/src/llvm_py/pyvm/intrinsic.py @@ -5,11 +5,16 @@ from __future__ import annotations from typing import Any, List, Tuple import os +import sys +# NamingBox SSOT: Add parent directory to path for naming_helper import +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) +from naming_helper import encode_static_method def try_intrinsic(name: str, args: List[Any]) -> Tuple[bool, Any]: try: - if name == "Main.esc_json/1": + # NamingBox SSOT: Use encode_static_method for name comparison + if name == encode_static_method("Main", "esc_json", 1): s = "" if not args else ("" if args[0] is None else str(args[0])) out = [] for ch in s: @@ -41,7 +46,8 @@ def try_intrinsic(name: str, args: List[Any]) -> Tuple[bool, Any]: start = idx + len(key) ok, digits = try_intrinsic("MiniVm.read_digits/2", [js, start]) return True, digits - if name == "Main.dirname/1": + # NamingBox SSOT: Use encode_static_method for name comparison + if name == encode_static_method("Main", "dirname", 1): p = "" if not args else ("" if args[0] is None else str(args[0])) d = os.path.dirname(p) if d == "":