Files
hakorune/docs/private/roadmap/phases/phase-20-python-integration/parser-integration/implementation-plan.md

11 KiB
Raw Blame History

PythonParserBox実装計画統合版

更新日: 2025-08-27

🎯 エキスパートからの統合フィードバック

最重要ポイント(両エキスパートが一致)

  1. 関数単位のフォールバック戦略

    • ファイル全体でなく関数レベルでコンパイル/フォールバックを切り替え
    • 未対応機能を含む関数はCPython exec、対応済み関数はNyash MIR/JIT
  2. Python 3.11固定

    • AST安定性の確保3.8 Constant統一、3.10 match/case、3.12位置情報)
    • py_versionast_formatをJSON IRに埋め込む
  3. 意味論の正確な実装が最優先

    • 最適化より先にPython互換性を確保
    • 特に: イテレータプロトコル、真偽値判定、スコーピング規則LEGB
  4. GIL管理の最小化

    • Python側でJSON生成ast.NodeVisitor + json.dumps
    • Rust側で解析GIL外で実行
    • py.allow_threads(|| { ... })で重い処理をGIL外実行
  5. テレメトリー重視

    • 未対応ノードの記録(support_levelフィールド)
    • フォールバック率の計測
    • ソース位置情報の保持(lineno/col_offset/end_*

Differential Testing戦略

  • 世界中のPythonコードがNyashのテストケース
  • CPythonを「オラクル」として使用
  • 出力、戻り値、例外を比較
  • Grammar-based fuzzingHypothesis活用

技術的実装方針

1. CPythonパーサー統合pyo3使用

// Cargo.toml
[dependencies]
pyo3 = { version = "0.22", features = ["auto-initialize"] }
pyo3-numpy = "0.22"  // NumPy連携用

// 初期化(一度だけ)
pyo3::prepare_freethreaded_python();

// Python側ヘルパーembedded
const PYTHON_HELPER: &str = r#"
import ast
import json
import sys

def parse_to_json(code, filename="<string>", mode="exec"):
    tree = ast.parse(code, filename, mode)
    return json.dumps(ast_to_dict(tree))

def ast_to_dict(node):
    result = {}
    
    # 必須フィールド
    result['node_type'] = node.__class__.__name__
    result['py_version'] = f"{sys.version_info.major}.{sys.version_info.minor}"
    
    # 位置情報(エラー診断用)
    if hasattr(node, 'lineno'):
        result['lineno'] = node.lineno
        result['col_offset'] = node.col_offset
        if hasattr(node, 'end_lineno'):  # Python 3.8+
            result['end_lineno'] = node.end_lineno
            result['end_col_offset'] = node.end_col_offset
    
    # サポートレベルNyash側で設定
    result['support_level'] = 'unknown'
    
    # ASTフィールド
    if isinstance(node, ast.AST):
        for field in node._fields:
            value = getattr(node, field)
            result[field] = ast_to_dict(value)
    elif isinstance(node, list):
        return [ast_to_dict(x) for x in node]
    else:
        return node
        
    return result
"#;

2. 最小実装セットPhase 1: Must-Have

Phase 1 意味論の必須要素Codex先生強調:
- LEGB + locals/freevarsスコーピング
- デフォルト引数の評価タイミング(定義時)
- イテレータベースのfor文
- for/else + while/elsePython独特
- Python真偽値判定__bool__ → __len__
- 短絡評価and/or

Phase 1 AST構造:
├─ Module (py_version, ast_format)
├─ FunctionDef (name, args, body, decorator_list=[])
│   └─ arguments (args, defaults, kwonlyargs=[], kw_defaults=[])
├─ Return (value)
├─ Assign (targets, value) 
├─ AugAssign (target, op, value)  # +=, -=等
└─ Expr (value)

Phase 1 式:
├─ BinOp (left, op, right)
│   └─ ops: Add, Sub, Mult, Div, FloorDiv, Mod, Pow
├─ Compare (left, ops, comparators)
│   └─ ops: Eq, NotEq, Lt, LtE, Gt, GtE, Is, IsNot
├─ BoolOp (op, values)  # and/or
├─ UnaryOp (op, operand)  # not, -, +
├─ Call (func, args, keywords=[])
├─ Name (id, ctx=Load/Store/Del)
├─ Constant (value)  # Python 3.8+統一
└─ IfExp (test, body, orelse)  # 三項演算子

Phase 1 制御フロー:
├─ If (test, body, orelse)
├─ While (test, body, orelse)  # else節対応必須
├─ For (target, iter, body, orelse)  # else節対応必須
├─ Break
└─ Continue

3. 関数単位フォールバック戦略

// 関数単位のコンパイル判定
pub struct FunctionCompiler {
    supported_nodes: HashSet<&'static str>,
    telemetry: CompilationTelemetry,
}

impl FunctionCompiler {
    pub fn can_compile(&self, func_def: &PythonAst) -> CompileResult {
        let mut visitor = SupportChecker::new(&self.supported_nodes);
        visitor.visit(func_def);
        
        if visitor.has_unsupported() {
            // CPython execへフォールバック
            CompileResult::Fallback {
                reason: visitor.unsupported_nodes(),
                location: func_def.location(),
            }
        } else {
            // Nyash MIR/JITへコンパイル
            CompileResult::Compile
        }
    }
    
    pub fn compile_module(&mut self, module: &PythonAst) -> ModuleUnit {
        let mut units = vec![];
        
        // モジュールトップレベルはPythonで実行globals設定
        units.push(ExecutionUnit::PythonExec(module.top_level));
        
        // 各関数を判定
        for func in module.functions() {
            match self.can_compile(func) {
                CompileResult::Compile => {
                    let mir = self.compile_to_mir(func);
                    units.push(ExecutionUnit::NyashFunction(mir));
                    self.telemetry.record_compiled(func.name);
                }
                CompileResult::Fallback { reason, location } => {
                    units.push(ExecutionUnit::PythonThunk(func));
                    self.telemetry.record_fallback(func.name, reason, location);
                }
            }
        }
        
        ModuleUnit { units }
    }
}

4. データ共有戦略

// NdArrayBox定義
pub struct NdArrayBox {
    base: BoxBase,
    py_array: Py<PyArray<f64, Dim<[usize; 2]>>>,  // Python側の参照保持
    // 操作時のみGIL取得してArrayViewを取る
}

impl NdArrayBox {
    pub fn to_view(&self) -> PyResult<ArrayView2<f64>> {
        Python::with_gil(|py| {
            let array = self.py_array.as_ref(py);
            Ok(array.readonly())
        })
    }
}

4. 実装ロードマップ

Phase 1: パーサー統合1-2週間

  • pyo3セットアップとPythonParserBox骨格
  • Python側parse_to_jsonヘルパー実装
  • JSON→Nyash AST最小変換
  • run()メソッドCPython exec委譲
  • 例外変換PyErr → NyashError

Phase 2: MIR変換2-4週間

  • AST→MIR変換器最小セット
  • 数値演算プリミティブ実装
  • スコープ解決(関数ローカル/グローバル)
  • 基本的な制御フローIf/While

Phase 3: NumPy統合並行可能

  • pyo3-numpy統合
  • NdArrayBox実装
  • ゼロコピーベンチマーク
  • バッファプロトコル対応

Phase 4: 最適化と拡張

  • 型特化とガード最適化
  • 例外処理try/except
  • クラス/メソッド対応
  • import統合

性能目標(現実的な見積もり)

コードタイプ 期待される高速化 備考
純Pythonループ 2-10倍 型安定なホットパス
関数呼び出し多 1.5-3倍 インライン化効果
NumPy処理中心 1.0-1.2倍 既に最適化済み
動的特性多用 1.2-3倍 ガード頻発で限定的

実装上の注意点(エキスパート推奨)

意味論の重要な違いPhase 1で対応必須

  1. 制御フロー

    • for文: イテレータプロトコル必須(__iter__/__next__
    • for/else, while/else: breakしなかった場合のelse実行
    • 短絡評価: andは左がFalseなら右を評価しない
  2. スコープ規則LEGB

    # Local → Enclosing → Global → Builtins
    global_var = 1
    
    def outer():
        enclosing_var = 2
    
        def inner():
            local_var = 3
            nonlocal enclosing_var  # 明示的な宣言
            global global_var       # 明示的な宣言
    
  3. 数値演算の違い

    • /: Python 3では常にfloattrue division
    • //: floor division整数除算
    • 大整数: デフォルトで無限精度
    • is vs ==: オブジェクト同一性 vs 値の等価性
  4. 関数定義の罠

    def f(x, y=[]): # デフォルト引数は定義時に1度だけ評価
        y.append(x) # 全呼び出しで同じリストを共有
        return y
    

GIL管理のベストプラクティス

// ❌ 悪い例: GILを長時間保持
let result = Python::with_gil(|py| {
    let ast = parse_python(py, code)?;
    let json = convert_to_json(py, ast)?;  // ここまでGIL必要
    let nyash_ast = parse_json(&json)?;    // GIL不要なのに保持
    compile_to_mir(nyash_ast)?             // GIL不要なのに保持
});

// ✅ 良い例: GILを最小限に
let json = Python::with_gil(|py| {
    let ast = parse_python(py, code)?;
    convert_to_json(py, ast)  // JSON生成まで
})?;

// GIL外で重い処理
let nyash_ast = parse_json(&json)?;
let mir = compile_to_mir(nyash_ast)?;

// 必要時のみ再取得
Python::with_gil(|py| {
    py.allow_threads(|| {
        // 時間のかかるRust処理
        optimize_mir(mir)
    })
})

テレメトリーとデバッグ

// 環境変数で制御
NYASH_PYTHONPARSER_TELEMETRY=1      # 基本統計
NYASH_PYTHONPARSER_TELEMETRY=2      # 詳細ログ
NYASH_PYTHONPARSER_STRICT=1         # フォールバック時にパニック(CI用

// 出力例
[PythonParser] Module: example.py
  Functions: 10 total
  Compiled: 7 (70%)
  Fallback: 3 (30%)
    - async_function: unsupported node 'AsyncFunctionDef' at line 23
    - generator_func: unsupported node 'Yield' at line 45  
    - class_method: unsupported node 'ClassDef' at line 67

次のステップ

即座に開始すべきこと

  1. Python 3.11環境固定

    pyenv install 3.11.9
    pyenv local 3.11.9
    
  2. 最小動作確認

    # test_minimal.py
    def add(x, y):
        return x + y
    
    result = add(10, 5)
    print(f"Result: {result}")  # → Nyashで15が出力されれば成功
    
  3. テレメトリー基盤構築

    • 未対応ノードの記録システム
    • フォールバック率の可視化
    • ソース位置情報の保持
  4. Differential Testingの準備

    • CPythonとの出力比較フレームワーク
    • 標準出力、戻り値、例外のキャプチャ
    • テストコーパスの選定

成功の測定基準

フェーズ 目標 測定指標
Phase 1 基本動作 簡単な数値計算の70%がコンパイル可能
Phase 2 実用性 scikit-learnの基本アルゴリズムが動作
Phase 3 性能 純Pythonループで5倍以上の高速化
Phase 4 成熟度 PyPIトップ100の30%が基本動作

まとめ

このPythonParserBox実装は、単なる機能追加ではなく、Nyash言語の成熟度を飛躍的に高める戦略的プロジェクト。 エキスパートの指摘を踏まえ、関数単位のフォールバック、Python 3.11固定、意味論の正確な実装、 GIL最小化、テレメトリー重視で着実に実装を進める。