Implement Phase 10.7 control-flow hooks in CraneliftBuilder: block vectors, switch/seal, br_if/jump; adjust begin_function and emit sites to honor current block. (Feature-gated)

This commit is contained in:
Moe Charm
2025-08-27 17:14:37 +09:00
parent 7dac50d139
commit 1b55233365
2 changed files with 496 additions and 12 deletions

View File

@ -0,0 +1,410 @@
# PythonParserBox ビルトインBox実装フロー
CPythonパーサー統合とPhase 1実装の具体的な流れ
## 🎯 全体の実装フロー
### Step 1: ビルトインBoxとしての基盤作成
```
1. src/boxes/python_parser_box/mod.rs を作成
2. BoxBase + BoxCore統一アーキテクチャに準拠
3. PythonParserBoxの基本構造を定義
4. src/boxes/mod.rs に登録
```
### Step 2: pyo3統合とCPythonパーサー接続
```
1. Cargo.tomlに pyo3依存関係追加
2. Python環境の自動初期化設定
3. ast.parse()へのFFIブリッジ実装
4. JSON中間表現への変換
```
### Step 3: Phase 1機能の実装
```
1. 最小限のAST変換def, if, for, return
2. 基本的な式の変換(演算子、関数呼び出し)
3. Nyash ASTへのマッピング
4. テストケースの作成
```
## 📝 具体的な実装コード
### 1. ビルトインBox定義src/boxes/python_parser_box/mod.rs
```rust
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対応
```rust
impl PythonParserBox {
// コンストラクタ
pub fn new() -> Result<Self, String> {
// Python環境の初期化
Python::with_gil(|py| {
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)
}
// 直接実行最初はCPython exec、段階的にMIR実行へ
pub fn run(&self, code: &str) -> Result<Box<dyn NyashBox>, String> {
// Phase 1: CPython exec経由
let helper = self.py_helper.lock().unwrap();
Python::with_gil(|py| {
helper.exec_code(py, code)
})
}
}
```
### 3. Python側ヘルパー実装
```rust
// Pythonコードを文字列として埋め込み
const PYTHON_HELPER_CODE: &str = r#"
import ast
import json
def ast_to_dict(node):
"""Phase 1: 基本的なAST要素のみ変換"""
if isinstance(node, ast.Module):
return {
"type": "Module",
"body": [ast_to_dict(stmt) for stmt in node.body]
}
elif isinstance(node, ast.FunctionDef):
return {
"type": "FunctionDef",
"name": node.name,
"args": [arg.arg for arg in node.args.args],
"body": [ast_to_dict(stmt) for stmt in node.body]
}
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):
return {
"type": "For",
"target": ast_to_dict(node.target),
"iter": ast_to_dict(node.iter),
"body": [ast_to_dict(stmt) for stmt in node.body]
}
elif isinstance(node, ast.While):
return {
"type": "While",
"test": ast_to_dict(node.test),
"body": [ast_to_dict(stmt) for stmt in node.body]
}
else:
# Phase 1では未対応
return {"type": "Unsupported", "node": str(type(node))}
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変換器
```rust
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
```rust
// ビルトイン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. 使用例とテストケース
```nyash
// 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実行の比較
---
作成日: 2025-08-27
Phase 1実装の具体的な手順とコード例

View File

@ -73,6 +73,9 @@ pub struct CraneliftBuilder {
current_name: Option<String>, current_name: Option<String>,
value_stack: Vec<cranelift_codegen::ir::Value>, value_stack: Vec<cranelift_codegen::ir::Value>,
entry_block: Option<cranelift_codegen::ir::Block>, entry_block: Option<cranelift_codegen::ir::Block>,
// Phase 10.7: basic block wiring state
blocks: Vec<cranelift_codegen::ir::Block>,
current_block_index: Option<usize>,
// Finalized function pointer (if any) // Finalized function pointer (if any)
compiled_closure: Option<std::sync::Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>>, compiled_closure: Option<std::sync::Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>>,
// Desired simple ABI (Phase 10_c minimal): i64 params count and i64 return // Desired simple ABI (Phase 10_c minimal): i64 params count and i64 return
@ -184,7 +187,7 @@ impl IRBuilder for CraneliftBuilder {
self.current_name = Some(name.to_string()); self.current_name = Some(name.to_string());
self.value_stack.clear(); self.value_stack.clear();
self.entry_block = None; // Keep any pre-created blocks (from prepare_blocks)
// Minimal signature: (i64 x argc) -> i64? (Core-1 integer path) // Minimal signature: (i64 x argc) -> i64? (Core-1 integer path)
let call_conv = self.module.isa().default_call_conv(); let call_conv = self.module.isa().default_call_conv();
@ -195,12 +198,18 @@ impl IRBuilder for CraneliftBuilder {
self.ctx.func.name = cranelift_codegen::ir::UserFuncName::user(0, 0); self.ctx.func.name = cranelift_codegen::ir::UserFuncName::user(0, 0);
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
let block = fb.create_block(); // Prepare entry block: use pre-created block[0] if present, otherwise create
fb.append_block_params_for_function_params(block); if self.blocks.is_empty() {
fb.switch_to_block(block); let block = fb.create_block();
fb.seal_block(block); self.blocks.push(block);
self.entry_block = Some(block); }
// Store builder back (drop at end_function) let entry = self.blocks[0];
fb.append_block_params_for_function_params(entry);
fb.switch_to_block(entry);
// Entry block can be sealed immediately
fb.seal_block(entry);
self.entry_block = Some(entry);
self.current_block_index = Some(0);
fb.finalize(); fb.finalize();
} }
@ -243,7 +252,8 @@ impl IRBuilder for CraneliftBuilder {
use cranelift_frontend::FunctionBuilder; use cranelift_frontend::FunctionBuilder;
// Recreate FunctionBuilder each emit (lightweight wrapper around ctx+fbc) // Recreate FunctionBuilder each emit (lightweight wrapper around ctx+fbc)
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
if let Some(b) = self.entry_block { fb.switch_to_block(b); } if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
let v = fb.ins().iconst(types::I64, val); let v = fb.ins().iconst(types::I64, val);
self.value_stack.push(v); self.value_stack.push(v);
self.stats.0 += 1; self.stats.0 += 1;
@ -258,7 +268,8 @@ impl IRBuilder for CraneliftBuilder {
let rhs = self.value_stack.pop().unwrap(); let rhs = self.value_stack.pop().unwrap();
let lhs = self.value_stack.pop().unwrap(); let lhs = self.value_stack.pop().unwrap();
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
if let Some(b) = self.entry_block { fb.switch_to_block(b); } if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
let res = match op { let res = match op {
BinOpKind::Add => fb.ins().iadd(lhs, rhs), BinOpKind::Add => fb.ins().iadd(lhs, rhs),
BinOpKind::Sub => fb.ins().isub(lhs, rhs), BinOpKind::Sub => fb.ins().isub(lhs, rhs),
@ -278,7 +289,8 @@ impl IRBuilder for CraneliftBuilder {
let rhs = self.value_stack.pop().unwrap(); let rhs = self.value_stack.pop().unwrap();
let lhs = self.value_stack.pop().unwrap(); let lhs = self.value_stack.pop().unwrap();
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
if let Some(b) = self.entry_block { fb.switch_to_block(b); } if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
let cc = match op { let cc = match op {
CmpKind::Eq => IntCC::Equal, CmpKind::Eq => IntCC::Equal,
CmpKind::Ne => IntCC::NotEqual, CmpKind::Ne => IntCC::NotEqual,
@ -300,7 +312,8 @@ impl IRBuilder for CraneliftBuilder {
use cranelift_frontend::FunctionBuilder; use cranelift_frontend::FunctionBuilder;
self.stats.4 += 1; self.stats.4 += 1;
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
if let Some(b) = self.entry_block { fb.switch_to_block(b); } if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
if let Some(v) = self.value_stack.pop() { if let Some(v) = self.value_stack.pop() {
fb.ins().return_(&[v]); fb.ins().return_(&[v]);
} else { } else {
@ -334,7 +347,8 @@ impl IRBuilder for CraneliftBuilder {
.expect("declare import failed"); .expect("declare import failed");
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
if let Some(b) = self.entry_block { fb.switch_to_block(b); } if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
let fref = self.module.declare_func_in_func(func_id, fb.func); let fref = self.module.declare_func_in_func(func_id, fb.func);
let call_inst = fb.ins().call(fref, &args); let call_inst = fb.ins().call(fref, &args);
if has_ret { if has_ret {
@ -345,6 +359,64 @@ impl IRBuilder for CraneliftBuilder {
} }
fb.finalize(); fb.finalize();
} }
// ==== Phase 10.7 block APIs ====
fn prepare_blocks(&mut self, count: usize) {
use cranelift_frontend::FunctionBuilder;
if count == 0 { return; }
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
// Only create if not already created
if self.blocks.len() < count {
let to_create = count - self.blocks.len();
for _ in 0..to_create { self.blocks.push(fb.create_block()); }
}
fb.finalize();
}
fn switch_to_block(&mut self, index: usize) {
use cranelift_frontend::FunctionBuilder;
if index >= self.blocks.len() { return; }
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
fb.switch_to_block(self.blocks[index]);
self.current_block_index = Some(index);
fb.finalize();
}
fn seal_block(&mut self, index: usize) {
use cranelift_frontend::FunctionBuilder;
if index >= self.blocks.len() { return; }
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
fb.seal_block(self.blocks[index]);
fb.finalize();
}
fn br_if_top_is_true(&mut self, then_index: usize, else_index: usize) {
use cranelift_codegen::ir::{types, condcodes::IntCC};
use cranelift_frontend::FunctionBuilder;
if then_index >= self.blocks.len() || else_index >= self.blocks.len() { return; }
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
// Ensure we are in a block
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
// Take top-of-stack as cond; if it's i64, normalize to b1 via icmp_imm != 0
let cond_b1 = if let Some(v) = self.value_stack.pop() {
fb.ins().icmp_imm(IntCC::NotEqual, v, 0)
} else {
let zero = fb.ins().iconst(types::I64, 0);
fb.ins().icmp_imm(IntCC::NotEqual, zero, 0)
};
fb.ins().brif(cond_b1, self.blocks[then_index], &[]);
fb.ins().jump(self.blocks[else_index], &[]);
self.stats.3 += 1;
fb.finalize();
}
fn jump_to(&mut self, target_index: usize) {
use cranelift_frontend::FunctionBuilder;
if target_index >= self.blocks.len() { return; }
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
if let Some(idx) = self.current_block_index { fb.switch_to_block(self.blocks[idx]); }
else if let Some(b) = self.entry_block { fb.switch_to_block(b); }
fb.ins().jump(self.blocks[target_index], &[]);
self.stats.3 += 1;
fb.finalize();
}
} }
#[cfg(feature = "cranelift-jit")] #[cfg(feature = "cranelift-jit")]
@ -397,6 +469,8 @@ impl CraneliftBuilder {
current_name: None, current_name: None,
value_stack: Vec::new(), value_stack: Vec::new(),
entry_block: None, entry_block: None,
blocks: Vec::new(),
current_block_index: None,
compiled_closure: None, compiled_closure: None,
desired_argc: 0, desired_argc: 0,
desired_has_ret: true, desired_has_ret: true,