Files
hakorune/docs/private/roadmap/phases/phase-20-python-integration/parser-integration/builtin-box-flow.md

17 KiB
Raw Blame History

PythonParserBox ビルトインBox実装フローエキスパート統合版

CPythonパーサー統合とPhase 1実装の具体的な流れ 更新日: 2025-08-27

🎯 全体の実装フロー

Step 0: Python 3.11固定(エキスパート推奨)

- Python 3.11.9を使用AST安定性確保
- pyenvまたはpython3.11コマンドで固定
- py_versionとast_formatをJSON IRに必ず含める

Step 1: ビルトインBoxとしての基盤作成

1. src/boxes/python_parser_box/mod.rs を作成
2. BoxBase + BoxCore統一アーキテクチャに準拠
3. PythonParserBoxの基本構造を定義
4. src/boxes/mod.rs に登録
5. テレメトリー基盤を初期から組み込む

Step 2: pyo3統合とCPythonパーサー接続

1. Cargo.tomlに pyo3依存関係追加
2. pyo3::prepare_freethreaded_python()で一度だけ初期化
3. ast.parse()へのFFIブリッジ実装
4. JSON中間表現への変換Python側でJSON生成
5. GILは最小限に、py.allow_threads()でRust処理

Step 3: Phase 1機能の実装必須意味論要素

必須要素Codex先生強調:
- LEGBスコーピング + locals/freevars
- デフォルト引数の定義時評価
- イテレータプロトコルfor文
- for/else + while/else
- Python真偽値判定
- 短絡評価and/or

実装手順:
1. 関数単位フォールバック戦略の実装
2. 基本的なAST変換def, if, for, while, return
3. 式の変換(算術/比較/論理演算子、関数呼び出し)
4. Nyash ASTへの意味論を保ったマッピング
5. Differential Testingフレームワーク

📝 具体的な実装コード

1. ビルトインBox定義src/boxes/python_parser_box/mod.rs

use crate::core::{BoxBase, BoxCore, NyashBox};
use crate::ast;
use pyo3::prelude::*;
use std::sync::{Arc, Mutex};

pub struct PythonParserBox {
    base: BoxBase,
    py_helper: Arc<Mutex<PyHelper>>,  // Python実行環境
}

impl BoxCore for PythonParserBox {
    fn box_id(&self) -> u64 {
        self.base.box_id()
    }
    
    fn parent_type_id(&self) -> Option<std::any::TypeId> {
        self.base.parent_type_id()
    }
    
    fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "PythonParserBox#{}", self.box_id())
    }
}

impl NyashBox for PythonParserBox {
    fn type_name(&self) -> &'static str {
        "PythonParserBox"
    }
    
    fn clone_box(&self) -> Box<dyn NyashBox> {
        Box::new(PythonParserBox {
            base: BoxBase::new(),
            py_helper: self.py_helper.clone(),
        })
    }
    
    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
    
    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
        self
    }
}

2. メソッド実装Phase 1対応

// テレメトリー用構造体
#[derive(Default)]
pub struct CompilationTelemetry {
    compiled_functions: Vec<String>,
    fallback_functions: Vec<(String, String, usize)>, // (name, reason, lineno)
    unsupported_nodes: HashMap<String, usize>,  // node_type -> count
}

impl PythonParserBox {
    // コンストラクタ
    pub fn new() -> Result<Self, String> {
        // 一度だけ初期化
        static INIT: std::sync::Once = std::sync::Once::new();
        INIT.call_once(|| {
            pyo3::prepare_freethreaded_python();
        });
        
        // Python環境の初期化
        Python::with_gil(|py| {
            // Python 3.11確認
            let version = py.version_info();
            if version.major != 3 || version.minor != 11 {
                return Err(format!("Python 3.11 required, got {}.{}", 
                    version.major, version.minor));
            }
            
            let helper = PyHelper::new(py)?;
            Ok(PythonParserBox {
                base: BoxBase::new(),
                py_helper: Arc::new(Mutex::new(helper)),
            })
        })
    }
    
    // Python code → JSON AST
    pub fn parse_to_json(&self, code: &str) -> Result<String, String> {
        let helper = self.py_helper.lock().unwrap();
        Python::with_gil(|py| {
            helper.parse_to_json(py, code)
        })
    }
    
    // JSON AST → Nyash ASTPhase 1機能のみ
    pub fn json_to_nyash_ast(&self, json: &str) -> Result<ast::Program, String> {
        let py_ast: Phase1PythonAst = serde_json::from_str(json)
            .map_err(|e| format!("JSON parse error: {}", e))?;
        
        let converter = Phase1Converter::new();
        converter.convert(py_ast)
    }
    
    // 直接実行(関数単位フォールバック)
    pub fn run(&self, code: &str) -> Result<Box<dyn NyashBox>, String> {
        // まずJSON ASTを取得
        let json_ast = self.parse_to_json(code)?;
        let py_ast: serde_json::Value = serde_json::from_str(&json_ast)?;
        
        // モジュール内の各関数をチェック
        let compiler = FunctionCompiler::new();
        let module_result = compiler.compile_module(&py_ast)?;
        
        // テレメトリー出力(環境変数で制御)
        if std::env::var("NYASH_PYTHONPARSER_TELEMETRY").is_ok() {
            compiler.print_telemetry();
        }
        
        // 実行コンパイル済み関数はMIR、他はCPython
        module_result.execute()
    }
}

3. Python側ヘルパー実装

// Pythonコードを文字列として埋め込み
const PYTHON_HELPER_CODE: &str = r#"
import ast
import json
import sys

# Python 3.11固定チェック
assert sys.version_info[:2] == (3, 11), f"Python 3.11 required, got {sys.version}"

def ast_to_dict(node):
    """Phase 1: 基本的なAST要素のみ変換エキスパート推奨JSON IR"""
    
    result = {
        "node_type": node.__class__.__name__,
        "py_version": "3.11",
        "ast_format": "v1"
    }
    
    # 位置情報(エラー診断用)
    if hasattr(node, 'lineno'):
        result['lineno'] = node.lineno
        result['col_offset'] = node.col_offset
        if hasattr(node, 'end_lineno'):
            result['end_lineno'] = node.end_lineno
            result['end_col_offset'] = node.end_col_offset
    if isinstance(node, ast.Module):
        return {
            "type": "Module",
            "body": [ast_to_dict(stmt) for stmt in node.body]
        }
    elif isinstance(node, ast.FunctionDef):
        # 意味論上重要:デフォルト引数情報を保存
        args_info = {
            "args": [arg.arg for arg in node.args.args],
            "defaults": [ast_to_dict(default) for default in node.args.defaults],
            "kwonlyargs": [arg.arg for arg in node.args.kwonlyargs],
            "kw_defaults": [ast_to_dict(d) if d else None for d in node.args.kw_defaults]
        }
        result.update({
            "name": node.name,
            "args": args_info,
            "body": [ast_to_dict(stmt) for stmt in node.body],
            "decorator_list": [],  # Phase 1では未対応
            "support_level": "full"  # コンパイル可能
        })
        return result
    elif isinstance(node, ast.Return):
        return {
            "type": "Return",
            "value": ast_to_dict(node.value) if node.value else None
        }
    elif isinstance(node, ast.BinOp):
        return {
            "type": "BinOp",
            "op": node.op.__class__.__name__,
            "left": ast_to_dict(node.left),
            "right": ast_to_dict(node.right)
        }
    elif isinstance(node, ast.Call):
        return {
            "type": "Call",
            "func": ast_to_dict(node.func),
            "args": [ast_to_dict(arg) for arg in node.args]
        }
    elif isinstance(node, ast.Name):
        return {
            "type": "Name",
            "id": node.id
        }
    elif isinstance(node, ast.Constant):
        return {
            "type": "Constant",
            "value": node.value
        }
    elif isinstance(node, ast.If):
        return {
            "type": "If",
            "test": ast_to_dict(node.test),
            "body": [ast_to_dict(stmt) for stmt in node.body],
            "orelse": [ast_to_dict(stmt) for stmt in node.orelse]
        }
    elif isinstance(node, ast.For):
        # 意味論上重要for/else構文
        result.update({
            "target": ast_to_dict(node.target),
            "iter": ast_to_dict(node.iter),
            "body": [ast_to_dict(stmt) for stmt in node.body],
            "orelse": [ast_to_dict(stmt) for stmt in node.orelse],  # else節
            "support_level": "full"
        })
        return result
    
    elif isinstance(node, ast.While):
        # 意味論上重要while/else構文
        result.update({
            "test": ast_to_dict(node.test),
            "body": [ast_to_dict(stmt) for stmt in node.body],
            "orelse": [ast_to_dict(stmt) for stmt in node.orelse],  # else節
            "support_level": "full"
        })
        return result
    
    elif isinstance(node, ast.BoolOp):
        # 意味論上重要:短絡評価
        result.update({
            "op": node.op.__class__.__name__,  # And, Or
            "values": [ast_to_dict(v) for v in node.values],
            "support_level": "full"
        })
        return result
    else:
        # Phase 1では未対応テレメトリー用
        return {
            "node_type": "Unsupported", 
            "original_type": type(node).__name__,
            "support_level": "fallback",
            "lineno": getattr(node, 'lineno', -1)
        }

def parse_to_json(code):
    try:
        tree = ast.parse(code)
        return json.dumps(ast_to_dict(tree))
    except Exception as e:
        return json.dumps({"type": "Error", "message": str(e)})
"#;

struct PyHelper {
    // Python側のヘルパー関数への参照を保持
    parse_func: PyObject,
}

impl PyHelper {
    fn new(py: Python) -> PyResult<Self> {
        // ヘルパーコードをPythonで実行
        let helpers = PyModule::from_code(py, PYTHON_HELPER_CODE, "helper.py", "helper")?;
        let parse_func = helpers.getattr("parse_to_json")?.to_object(py);
        
        Ok(PyHelper { parse_func })
    }
    
    fn parse_to_json(&self, py: Python, code: &str) -> Result<String, String> {
        match self.parse_func.call1(py, (code,)) {
            Ok(result) => result.extract::<String>(py)
                .map_err(|e| format!("Extract error: {}", e)),
            Err(e) => Err(format!("Parse error: {}", e))
        }
    }
}

4. Phase 1 AST変換器

struct Phase1Converter;

impl Phase1Converter {
    fn new() -> Self {
        Phase1Converter
    }
    
    fn convert(&self, py_ast: Phase1PythonAst) -> Result<ast::Program, String> {
        match py_ast {
            Phase1PythonAst::Module { body } => {
                let mut items = vec![];
                for stmt in body {
                    match self.convert_statement(stmt)? {
                        Some(item) => items.push(item),
                        None => {} // 未対応要素はスキップ
                    }
                }
                Ok(ast::Program { items })
            }
            _ => Err("Expected Module at top level".into())
        }
    }
    
    fn convert_statement(&self, stmt: Phase1PythonAst) -> Result<Option<ast::ProgramItem>, String> {
        match stmt {
            Phase1PythonAst::FunctionDef { name, args, body } => {
                // Python def → Nyash function
                let params = args.into_iter()
                    .map(|arg| ast::Parameter { name: arg, ty: None })
                    .collect();
                
                let nyash_body = self.convert_body(body)?;
                
                Ok(Some(ast::ProgramItem::Function(ast::FunctionDef {
                    name,
                    params,
                    body: nyash_body,
                    return_type: None,
                })))
            }
            Phase1PythonAst::Return { value } => {
                let expr = value
                    .map(|v| self.convert_expression(v))
                    .transpose()?;
                    
                Ok(Some(ast::ProgramItem::Statement(ast::Statement::Return(expr))))
            }
            // 他の文も同様に変換
            _ => Ok(None) // Phase 1では未対応
        }
    }
    
    fn convert_expression(&self, expr: Phase1PythonAst) -> Result<ast::Expression, String> {
        match expr {
            Phase1PythonAst::BinOp { op, left, right } => {
                let left = Box::new(self.convert_expression(*left)?);
                let right = Box::new(self.convert_expression(*right)?);
                
                let op = match op.as_str() {
                    "Add" => ast::BinaryOp::Add,
                    "Sub" => ast::BinaryOp::Sub,
                    "Mul" => ast::BinaryOp::Mul,
                    "Div" => ast::BinaryOp::Div,
                    _ => return Err(format!("Unsupported operator: {}", op))
                };
                
                Ok(ast::Expression::BinaryOp { op, left, right })
            }
            Phase1PythonAst::Constant { value } => {
                // Python定数 → Nyashリテラル
                match value {
                    serde_json::Value::Number(n) => {
                        if let Some(i) = n.as_i64() {
                            Ok(ast::Expression::Integer(i))
                        } else if let Some(f) = n.as_f64() {
                            Ok(ast::Expression::Float(f))
                        } else {
                            Err("Unsupported number type".into())
                        }
                    }
                    serde_json::Value::String(s) => {
                        Ok(ast::Expression::String(s))
                    }
                    serde_json::Value::Bool(b) => {
                        Ok(ast::Expression::Bool(b))
                    }
                    _ => Err("Unsupported constant type".into())
                }
            }
            // 他の式も同様
            _ => Err("Unsupported expression in Phase 1".into())
        }
    }
}

5. インタープリター統合src/interpreter/builtins.rs

// ビルトインBox登録に追加
pub fn register_builtin_boxes(env: &mut Environment) {
    // 既存のBox登録...
    
    // PythonParserBox追加
    env.register_builtin_box("PythonParserBox", || {
        match PythonParserBox::new() {
            Ok(parser) => Arc::new(parser) as Arc<dyn NyashBox>,
            Err(e) => panic!("Failed to initialize PythonParserBox: {}", e)
        }
    });
}

6. 使用例とテストケース

// test_python_parser_phase1.nyash
local py = new PythonParserBox()

// Phase 1: 基本的な関数定義と演算
local code = """
def add(x, y):
    return x + y

def multiply(x, y):
    return x * y

def calculate(a, b):
    sum_val = add(a, b)
    prod_val = multiply(a, b)
    return sum_val + prod_val
"""

// パースしてJSON ASTを確認
local json_ast = py.parse_to_json(code)
print("JSON AST: " + json_ast)

// Nyash ASTに変換
local nyash_ast = py.json_to_nyash_ast(json_ast)
print("Conversion successful!")

// 実行最初はCPython経由
local result = py.run(code + "\nprint(calculate(10, 5))")

📊 段階的な実装計画

Week 1: 基盤構築

  • PythonParserBoxの基本構造
  • pyo3統合とPython環境初期化
  • parse_to_json基本実装
  • エラーハンドリング

Week 2: Phase 1変換器

  • Phase1PythonAstの定義
  • Phase1Converterの実装
  • 基本的な文と式の変換
  • テストケース作成

Week 3: 統合とテスト

  • インタープリター統合
  • CPython exec経由の実行
  • ベンチマーク準備
  • ドキュメント整備

🚀 期待される成果

Phase 1完了時点で実現できること

  1. 基本的なPythonコードの実行

    • 関数定義、算術演算、条件分岐、ループ
  2. Nyash ASTへの変換

    • 将来のMIR/JIT最適化への道筋
  3. 統合開発環境

    • PythonコードとNyashコードの混在実行
  4. 性能測定基盤

    • CPython実行 vs Nyash実行の比較

📡 テレメトリー出力例

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

# 実行例
./target/release/nyash test_python_parser.nyash

# 出力
[PythonParser] Module: test.py (Python 3.11)
  Functions: 10 total
  Compiled: 7 (70%) → Nyash MIR/JIT
  Fallback: 3 (30%) → CPython exec
    - async_function: unsupported node 'AsyncFunctionDef' at line 23
    - generator_func: unsupported node 'Yield' at line 45
    - decorator_func: unsupported node 'decorator_list' at line 67
  
  Unsupported Nodes Summary:
    AsyncFunctionDef: 1
    Yield: 2
    ClassDef: 1

📊 Differential Testingフレームワーク

// CPythonとNyashの出力比較
pub fn differential_test(code: &str) -> TestResult {
    // CPythonで実行オラクル
    let python_result = Python::with_gil(|py| {
        capture_python_execution(py, code)
    })?;
    
    // Nyashで実行
    let nyash_result = execute_with_pythonparser(code)?;
    
    // 結果比較
    compare_results(python_result, nyash_result)
}

作成日: 2025-08-27 Phase 1実装の具体的な手順とエキスパートフィードバック統合