feat(naming): Python NamingHelper実装 - Rust NamingBoxのミラー完成
Phase 25.4 メンテナンス: Python LLVM側のNamingBox SSOT統一 ## 📦 実装内容 ### 1. Python NamingHelper作成 - 新規作成: `src/llvm_py/naming_helper.py` - Rust `src/mir/naming.rs` と完全同一の意味論を実装 - 3つの関数: - `encode_static_method(box_name, method, arity)` → "BoxName.method/arity" - `canonical_box_name(raw)` → "main" → "Main" - `normalize_static_global_name(func_name)` → "main._nop/0" → "Main._nop/0" - doctest 9個全てPASS ✅ ### 2. Python LLVM側の統一修正 - `instructions/boxcall.py:437` - f"Main.{method_name}/{arity}" → encode_static_method() - `instructions/call.py:170-173` - traced_names タプル生成をNamingHelper経由に変更 - `pyvm/intrinsic.py:17, 50` - "Main.esc_json/1", "Main.dirname/1" → encode_static_method() - `builders/entry.py:16` - 'Main.main/1' → encode_static_method("Main", "main", 1) ## 🎯 技術的成果 - **意味論一致**: Rust ↔ Python で完全同一の命名規則 - **保守性向上**: ハードコード4箇所 → NamingHelper一元管理 - **テスト完備**: doctest 9個でRust NamingBoxと同一動作を保証 ## テスト結果 ✅ python3 -m py_compile: 全ファイル構文OK ✅ python3 -m doctest naming_helper.py: 9 tests passed ## 参考 - Phase 25.4-A (Rust側):fa9cea51,bceb20ed- Rust NamingBox SSOT: src/mir/naming.rs 🎉 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -1,5 +1,9 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
import os
|
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:
|
def ensure_ny_main(builder) -> None:
|
||||||
"""Ensure ny_main wrapper exists by delegating to Main.main/1 or main().
|
"""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)
|
has_ny_main = any(f.name == 'ny_main' for f in builder.module.functions)
|
||||||
fn_main_box = None
|
fn_main_box = None
|
||||||
fn_main_plain = 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:
|
for f in builder.module.functions:
|
||||||
if f.name == 'Main.main/1':
|
if f.name == main_box_name:
|
||||||
fn_main_box = f
|
fn_main_box = f
|
||||||
elif f.name == 'main':
|
elif f.name == 'main':
|
||||||
fn_main_plain = f
|
fn_main_plain = f
|
||||||
|
|||||||
@ -6,6 +6,7 @@ Core of Nyash's "Everything is Box" philosophy
|
|||||||
import llvmlite.ir as ir
|
import llvmlite.ir as ir
|
||||||
from typing import Dict, List, Optional, Any
|
from typing import Dict, List, Optional, Any
|
||||||
from instructions.safepoint import insert_automatic_safepoint
|
from instructions.safepoint import insert_automatic_safepoint
|
||||||
|
from naming_helper import encode_static_method
|
||||||
|
|
||||||
def _declare(module: ir.Module, name: str, ret, args):
|
def _declare(module: ir.Module, name: str, ret, args):
|
||||||
for f in module.functions:
|
for f in module.functions:
|
||||||
@ -431,9 +432,9 @@ def lower_boxcall(
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
if is_me and cur_fn_name.startswith('Main.'):
|
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)
|
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
|
# If module already has such function, prefer direct call
|
||||||
callee = None
|
callee = None
|
||||||
for f in module.functions:
|
for f in module.functions:
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import llvmlite.ir as ir
|
|||||||
from typing import Dict, List, Optional, Any
|
from typing import Dict, List, Optional, Any
|
||||||
from trace import debug as trace_debug
|
from trace import debug as trace_debug
|
||||||
from instructions.safepoint import insert_automatic_safepoint
|
from instructions.safepoint import insert_automatic_safepoint
|
||||||
|
from naming_helper import encode_static_method
|
||||||
|
|
||||||
def lower_call(
|
def lower_call(
|
||||||
builder: ir.IRBuilder,
|
builder: ir.IRBuilder,
|
||||||
@ -165,8 +166,13 @@ def lower_call(
|
|||||||
|
|
||||||
# Make the call
|
# Make the call
|
||||||
result = builder.call(func, call_args, name=f"call_{func_name}")
|
result = builder.call(func, call_args, name=f"call_{func_name}")
|
||||||
# Optional trace for final debugging
|
# NamingBox SSOT: Optional trace for final debugging
|
||||||
if isinstance(actual_name, str) and actual_name in ("Main.node_json/3", "Main.esc_json/1", "main"):
|
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)}")
|
trace_debug(f"[TRACE] call {actual_name} args={len(call_args)}")
|
||||||
|
|
||||||
# Store result if needed
|
# Store result if needed
|
||||||
|
|||||||
88
src/llvm_py/naming_helper.py
Normal file
88
src/llvm_py/naming_helper.py
Normal file
@ -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
|
||||||
@ -5,11 +5,16 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Any, List, Tuple
|
from typing import Any, List, Tuple
|
||||||
import os
|
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]:
|
def try_intrinsic(name: str, args: List[Any]) -> Tuple[bool, Any]:
|
||||||
try:
|
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]))
|
s = "" if not args else ("" if args[0] is None else str(args[0]))
|
||||||
out = []
|
out = []
|
||||||
for ch in s:
|
for ch in s:
|
||||||
@ -41,7 +46,8 @@ def try_intrinsic(name: str, args: List[Any]) -> Tuple[bool, Any]:
|
|||||||
start = idx + len(key)
|
start = idx + len(key)
|
||||||
ok, digits = try_intrinsic("MiniVm.read_digits/2", [js, start])
|
ok, digits = try_intrinsic("MiniVm.read_digits/2", [js, start])
|
||||||
return True, digits
|
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]))
|
p = "" if not args else ("" if args[0] is None else str(args[0]))
|
||||||
d = os.path.dirname(p)
|
d = os.path.dirname(p)
|
||||||
if d == "":
|
if d == "":
|
||||||
|
|||||||
Reference in New Issue
Block a user