📚 Phase 12: Nyashスクリプトプラグインシステム設計と埋め込みVM構想

## 主な成果
- Nyashスクリプトでプラグイン作成可能という革命的発見
- C ABI制約の分析と埋め込みVMによる解決策
- MIR/VM/JIT層での箱引数サポートの詳細分析

## ドキュメント作成
- Phase 12基本構想(README.md)
- Gemini/Codex先生の技術分析
- C ABIとの整合性問題と解決策
- 埋め込みVM実装ロードマップ
- 箱引数サポートの技術詳細

## 重要な洞察
- 制約は「リンク時にC ABI必要」のみ
- 埋め込みVMでMIRバイトコード実行により解決可能
- Nyashスクリプト→C ABIプラグイン変換が実現可能

Everything is Box → Everything is Plugin → Everything is Possible!
This commit is contained in:
Moe Charm
2025-08-30 22:52:16 +09:00
parent 7a0f9bd432
commit c13d9c045e
82 changed files with 5842 additions and 138 deletions

44
CURRENT_TASK.md Normal file
View File

@ -0,0 +1,44 @@
# CURRENT TASK (Phase 10.5c)
目的: Handle-First + by-name を軸に、Python統合PyRuntimeBox/PyObjectBoxを汎用・安全に実装する。最適化は後段。
ステータス2025-08-30 更新)
- フェーズ: 10.5c 汎用Handle/TLV実装の拡張Python統合開始
- 方針: 「綺麗に作って動かす」= ハードコーディング排除・Handle/TLV統一・最適化は後回し
10.5b 完了項目(橋渡し済み)
- by-name シムgetattr/callを実装JIT/AOTし、Lowerer から a0 を `nyash.handle.of` で確実にハンドル化して呼び出し
- 引数 a1/a2 はハンドル優先/なければレガシー参照から TLV 構築String/Integer はプリミティブ化)
- 汎用 birth シムを追加
- `nyash.box.birth_h(type_id:i64)->i64`JIT/AOT
- `nyash.box.birth_i64(type_id:i64, argc:i64, a1:i64, a2:i64)->i64`JIT/AOT
- Lowerer: NewBox引数無しは birth_h に統一。引数ありは安全なケースInteger const引数が既にハンドルだけ birth_i64 に段階導入
- AOT: examples/aot_py_math_sqrt_min.nyash で Strict でも .o 生成を確認target/aot_objects/main.o
- ログ
- AOT: NYASH_CLI_VERBOSE=1 で birth_h の可視化
- JIT: events で by-name/birth の観測(必要十分の最小限)
10.5c 着手項目(進行中)
- Lowerer: PluginInvoketype_id/method_id & by-nameの Handle-First 配線を統一a0を常にnyash.handle.of
- JIT/AOT: birth_h/_i64と by-name シムでTLV生成を汎用化String/Integerはプリミティブ化、他はHandle
- Strict時のJIT実行停止コンパイル専用でVM=仕様の原則を徹底
非対応(後回し・最適化)
- StringBox 専用の known_string/再利用最適化
- 汎用的な定数プールbirth の可変長 TLV 一括最適化
次の作業10.5c 続き)
1) FFI仕様の短文化a0/a1/a2=Handle優先→TLV、レガシー抑止フラグ、戻りTLVのdecodeポリシー
2) birth引数の一般化メモ可変長TLV、例外時ハンドリング
3) Python統合の最小チェーンimport→getattr→callのAOT/VM双方での実装確認サンプル追加
4) ドキュメント更新10.5c README/INDEX、FFIガイド
合意済みルール
- まず汎用・安全に動かす(最適化は内部に隠し、後段)
- StringBox 等の個別特化は入れない。Handle/TLV で統一し、Box 追加を阻害しない
- Strict/FailFast を維持fallback で隠さない)

BIN
app

Binary file not shown.

View File

@ -11,10 +11,18 @@ pub extern "C" fn nyash_plugin_invoke3_i64(
a2: i64, a2: i64,
) -> i64 { ) -> i64 {
use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2; use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
// Resolve receiver instance from legacy VM args (param index) // Resolve receiver instance from handle first; fallback to legacy VM args (param index)
let mut instance_id: u32 = 0; let mut instance_id: u32 = 0;
let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None; let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None;
if a0 >= 0 { if a0 > 0 {
if let Some(obj) = nyash_rust::jit::rt::handles::get(a0 as u64) {
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
instance_id = p.instance_id();
invoke = Some(p.inner.invoke_fn);
}
}
}
if invoke.is_none() && a0 >= 0 && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") {
nyash_rust::jit::rt::with_legacy_vm_args(|args| { nyash_rust::jit::rt::with_legacy_vm_args(|args| {
let idx = a0 as usize; let idx = a0 as usize;
if let Some(nyash_rust::backend::vm::VMValue::BoxRef(b)) = args.get(idx) { if let Some(nyash_rust::backend::vm::VMValue::BoxRef(b)) = args.get(idx) {
@ -39,18 +47,23 @@ pub extern "C" fn nyash_plugin_invoke3_i64(
VMValue::Float(f) => nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, *f), VMValue::Float(f) => nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, *f),
VMValue::Bool(b) => nyash_rust::runtime::plugin_ffi_common::encode::bool(&mut buf, *b), VMValue::Bool(b) => nyash_rust::runtime::plugin_ffi_common::encode::bool(&mut buf, *b),
VMValue::BoxRef(b) => { VMValue::BoxRef(b) => {
// BufferBox → TLV bytes
if let Some(bufbox) = b.as_any().downcast_ref::<nyash_rust::boxes::buffer::BufferBox>() {
nyash_rust::runtime::plugin_ffi_common::encode::bytes(&mut buf, &bufbox.to_vec());
return;
}
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() { if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() {
// Prefer StringBox/IntegerBox primitives when possible // Prefer StringBox/IntegerBox primitives when possible
let host = nyash_rust::runtime::get_global_plugin_host(); let host = nyash_rust::runtime::get_global_plugin_host();
if let Ok(hg) = host.read() { if let Ok(hg) = host.read() {
if p.box_type() == "StringBox" { if p.box_type == "StringBox" {
if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) { if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) {
if let Some(s) = sb.as_any().downcast_ref::<nyash_rust::box_trait::StringBox>() { if let Some(s) = sb.as_any().downcast_ref::<nyash_rust::box_trait::StringBox>() {
nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value); nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value);
return; return;
} }
} }
} else if p.box_type() == "IntegerBox" { } else if p.box_type == "IntegerBox" {
if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) { if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) {
if let Some(i) = ibx.as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() { if let Some(i) = ibx.as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() {
nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value); nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value);
@ -77,17 +90,22 @@ pub extern "C" fn nyash_plugin_invoke3_i64(
// Try handle first // Try handle first
if val > 0 { if val > 0 {
if let Some(obj) = handles::get(val as u64) { if let Some(obj) = handles::get(val as u64) {
// BufferBox handle → TLV bytes
if let Some(bufbox) = obj.as_any().downcast_ref::<nyash_rust::boxes::buffer::BufferBox>() {
nyash_rust::runtime::plugin_ffi_common::encode::bytes(&mut buf, &bufbox.to_vec());
appended = true; return;
}
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() { if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
let host = nyash_rust::runtime::get_global_plugin_host(); let host = nyash_rust::runtime::get_global_plugin_host();
if let Ok(hg) = host.read() { if let Ok(hg) = host.read() {
if p.box_type() == "StringBox" { if p.box_type == "StringBox" {
if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) { if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) {
if let Some(s) = sb.as_any().downcast_ref::<nyash_rust::box_trait::StringBox>() { if let Some(s) = sb.as_any().downcast_ref::<nyash_rust::box_trait::StringBox>() {
nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value); appended = true; nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value); appended = true;
return; return;
} }
} }
} else if p.box_type() == "IntegerBox" { } else if p.box_type == "IntegerBox" {
if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) { if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) {
if let Some(i) = ibx.as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() { if let Some(i) = ibx.as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() {
nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value); appended = true; nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value); appended = true;
@ -111,6 +129,10 @@ pub extern "C" fn nyash_plugin_invoke3_i64(
}; };
if nargs >= 1 { encode_arg(a1, 1); } if nargs >= 1 { encode_arg(a1, 1); }
if nargs >= 2 { encode_arg(a2, 2); } if nargs >= 2 { encode_arg(a2, 2); }
// Extra args from legacy VM args (positions 3..nargs)
if nargs > 2 && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") {
for pos in 3..=nargs { encode_from_legacy(pos); }
}
// Prepare output buffer (dynamic growth on short buffer) // Prepare output buffer (dynamic growth on short buffer)
let mut cap: usize = 256; let mut cap: usize = 256;
let (mut tag_ret, mut sz_ret, mut payload_ret): (u8, usize, Vec<u8>) = (0, 0, Vec::new()); let (mut tag_ret, mut sz_ret, mut payload_ret): (u8, usize, Vec<u8>) = (0, 0, Vec::new());
@ -136,6 +158,25 @@ pub extern "C" fn nyash_plugin_invoke3_i64(
if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) { return v as i64; } if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) { return v as i64; }
if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); }
} }
8 => { // Handle(tag=8) -> register and return handle id (i64)
if sz == 8 {
let mut t = [0u8;4]; t.copy_from_slice(&payload[0..4]);
let mut i = [0u8;4]; i.copy_from_slice(&payload[4..8]);
let r_type = u32::from_le_bytes(t);
let r_inst = u32::from_le_bytes(i);
// Build PluginBoxV2 and register into handle-registry
let box_type_name = nyash_rust::runtime::plugin_loader_unified::get_global_plugin_host()
.read()
.ok()
.and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone()))
.and_then(|m| m.into_iter().find(|(_k,v)| *v == r_type).map(|(k,_v)| k))
.unwrap_or_else(|| "PluginBox".to_string());
let pb = nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2(box_type_name, r_type, r_inst, invoke.unwrap());
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::new(pb);
let h = nyash_rust::jit::rt::handles::to_handle(arc);
return h as i64;
}
}
1 => { // Bool 1 => { // Bool
return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 };
} }
@ -217,17 +258,21 @@ pub extern "C" fn nyash_plugin_invoke3_f64(
VMValue::Float(f) => nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, *f), VMValue::Float(f) => nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, *f),
VMValue::Bool(b) => nyash_rust::runtime::plugin_ffi_common::encode::bool(&mut buf, *b), VMValue::Bool(b) => nyash_rust::runtime::plugin_ffi_common::encode::bool(&mut buf, *b),
VMValue::BoxRef(b) => { VMValue::BoxRef(b) => {
if let Some(bufbox) = b.as_any().downcast_ref::<nyash_rust::boxes::buffer::BufferBox>() {
nyash_rust::runtime::plugin_ffi_common::encode::bytes(&mut buf, &bufbox.to_vec());
return;
}
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() { if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() {
let host = nyash_rust::runtime::get_global_plugin_host(); let host = nyash_rust::runtime::get_global_plugin_host();
if let Ok(hg) = host.read() { if let Ok(hg) = host.read() {
if p.box_type() == "StringBox" { if p.box_type == "StringBox" {
if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) { if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) {
if let Some(s) = sb.as_any().downcast_ref::<nyash_rust::box_trait::StringBox>() { if let Some(s) = sb.as_any().downcast_ref::<nyash_rust::box_trait::StringBox>() {
nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value); nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value);
return; return;
} }
} }
} else if p.box_type() == "IntegerBox" { } else if p.box_type == "IntegerBox" {
if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) { if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) {
if let Some(i) = ibx.as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() { if let Some(i) = ibx.as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() {
nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value); nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value);
@ -251,17 +296,21 @@ pub extern "C" fn nyash_plugin_invoke3_f64(
let mut appended = false; let mut appended = false;
if val > 0 { if val > 0 {
if let Some(obj) = handles::get(val as u64) { if let Some(obj) = handles::get(val as u64) {
if let Some(bufbox) = obj.as_any().downcast_ref::<nyash_rust::boxes::buffer::BufferBox>() {
nyash_rust::runtime::plugin_ffi_common::encode::bytes(&mut buf, &bufbox.to_vec());
appended = true; return;
}
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() { if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
let host = nyash_rust::runtime::get_global_plugin_host(); let host = nyash_rust::runtime::get_global_plugin_host();
if let Ok(hg) = host.read() { if let Ok(hg) = host.read() {
if p.box_type() == "StringBox" { if p.box_type == "StringBox" {
if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) { if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) {
if let Some(s) = sb.as_any().downcast_ref::<nyash_rust::box_trait::StringBox>() { if let Some(s) = sb.as_any().downcast_ref::<nyash_rust::box_trait::StringBox>() {
nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value); appended = true; nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value); appended = true;
return; return;
} }
} }
} else if p.box_type() == "IntegerBox" { } else if p.box_type == "IntegerBox" {
if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) { if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) {
if let Some(i) = ibx.as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() { if let Some(i) = ibx.as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() {
nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value); appended = true; nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value); appended = true;
@ -282,6 +331,9 @@ pub extern "C" fn nyash_plugin_invoke3_f64(
}; };
if nargs >= 1 { encode_arg(a1, 1); } if nargs >= 1 { encode_arg(a1, 1); }
if nargs >= 2 { encode_arg(a2, 2); } if nargs >= 2 { encode_arg(a2, 2); }
if nargs > 2 && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") {
for pos in 3..=nargs { encode_from_legacy(pos); }
}
// Prepare output buffer (dynamic growth on short buffer) // Prepare output buffer (dynamic growth on short buffer)
let mut cap: usize = 256; let mut cap: usize = 256;
let (mut tag_ret, mut sz_ret, mut payload_ret): (u8, usize, Vec<u8>) = (0, 0, Vec::new()); let (mut tag_ret, mut sz_ret, mut payload_ret): (u8, usize, Vec<u8>) = (0, 0, Vec::new());
@ -386,6 +438,9 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64
V::Float(f) => nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, *f), V::Float(f) => nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, *f),
V::Bool(b) => nyash_rust::runtime::plugin_ffi_common::encode::bool(&mut buf, *b), V::Bool(b) => nyash_rust::runtime::plugin_ffi_common::encode::bool(&mut buf, *b),
V::BoxRef(b) => { V::BoxRef(b) => {
if let Some(bufbox) = b.as_any().downcast_ref::<nyash_rust::boxes::buffer::BufferBox>() {
nyash_rust::runtime::plugin_ffi_common::encode::bytes(&mut buf, &bufbox.to_vec()); return;
}
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() { if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() {
let host = nyash_rust::runtime::get_global_plugin_host(); let host = nyash_rust::runtime::get_global_plugin_host();
if let Ok(hg) = host.read() { if let Ok(hg) = host.read() {
@ -416,6 +471,7 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64
}; };
if argc >= 2 { add_from_legacy(1); } if argc >= 2 { add_from_legacy(1); }
if argc >= 3 { add_from_legacy(2); } if argc >= 3 { add_from_legacy(2); }
if argc > 3 { for pos in 3..(argc as usize) { add_from_legacy(pos); } }
let mut out = vec![0u8; 4096]; let mut out_len: usize = out.len(); let mut out = vec![0u8; 4096]; let mut out_len: usize = out.len();
let rc = unsafe { invoke.unwrap()(type_id as u32, method_id, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) }; let rc = unsafe { invoke.unwrap()(type_id as u32, method_id, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) };
if rc != 0 { return 0; } if rc != 0 { return 0; }
@ -434,6 +490,168 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64
// ---- Handle-based birth shims for AOT/JIT object linkage ---- // ---- Handle-based birth shims for AOT/JIT object linkage ----
// These resolve symbols like "nyash.string.birth_h" referenced by ObjectModule. // These resolve symbols like "nyash.string.birth_h" referenced by ObjectModule.
// Generic birth by type_id -> handle (no args). Exported as nyash.box.birth_h
#[export_name = "nyash.box.birth_h"]
pub extern "C" fn nyash_box_birth_h_export(type_id: i64) -> i64 {
if type_id <= 0 { return 0; }
let tid = type_id as u32;
// Map type_id back to type name
let name_opt = nyash_rust::runtime::plugin_loader_unified::get_global_plugin_host()
.read()
.ok()
.and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone()))
.and_then(|m| m.into_iter().find(|(_k,v)| *v == tid).map(|(k,_v)| k));
if let Some(box_type) = name_opt {
if let Ok(host_g) = nyash_rust::runtime::get_global_plugin_host().read() {
if let Ok(b) = host_g.create_box(&box_type, &[]) {
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::from(b);
let h = nyash_rust::jit::rt::handles::to_handle(arc);
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
println!("nyrt: birth_h {} (type_id={}) -> handle={}", box_type, tid, h);
}
return h as i64;
} else if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
eprintln!("nyrt: birth_h {} (type_id={}) FAILED: create_box", box_type, tid);
}
}
} else if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
eprintln!("nyrt: birth_h (type_id={}) FAILED: type map not found", tid);
}
0
}
// Generic birth with args: (type_id, argc, a1, a2) -> handle
// Export name: nyash.box.birth_i64
#[export_name = "nyash.box.birth_i64"]
pub extern "C" fn nyash_box_birth_i64_export(type_id: i64, argc: i64, a1: i64, a2: i64) -> i64 {
use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
if type_id <= 0 { return 0; }
let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None;
// Resolve invoke_fn via temporary instance
let box_type_name = nyash_rust::runtime::plugin_loader_unified::get_global_plugin_host()
.read().ok()
.and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone()))
.and_then(|m| m.into_iter().find(|(_k,v)| *v == (type_id as u32)).map(|(k,_v)| k))
.unwrap_or_else(|| "PluginBox".to_string());
if let Ok(host_g) = nyash_rust::runtime::get_global_plugin_host().read() {
if let Ok(b) = host_g.create_box(&box_type_name, &[]) {
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() { invoke = Some(p.inner.invoke_fn); }
}
}
if invoke.is_none() {
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { eprintln!("nyrt: birth_i64 (type_id={}) FAILED: no invoke", type_id); }
return 0;
}
let method_id: u32 = 0; // birth
let instance_id: u32 = 0; // static
// Build TLV args
use nyash_rust::jit::rt::handles;
let nargs = argc.max(0) as usize;
let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
let mut encode_handle = |h: i64| {
if h > 0 {
if let Some(obj) = handles::get(h as u64) {
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
let host = nyash_rust::runtime::get_global_plugin_host();
if let Ok(hg) = host.read() {
if p.box_type == "StringBox" {
if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) {
if let Some(s) = sb.as_any().downcast_ref::<nyash_rust::box_trait::StringBox>() { nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value); return; }
}
} else if p.box_type == "IntegerBox" {
if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) {
if let Some(i) = ibx.as_any().downcast_ref::<nyash_rust::box_trait::IntegerBox>() { nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value); return; }
}
}
}
nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle(&mut buf, p.inner.type_id, p.instance_id());
return;
}
}
}
nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, h);
};
if nargs >= 1 { encode_handle(a1); }
if nargs >= 2 { encode_handle(a2); }
// Extra birth args from legacy VM when present
if nargs > 2 && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") {
for pos in 3..=nargs {
nyash_rust::jit::rt::with_legacy_vm_args(|args| {
if let Some(v) = args.get(pos) {
use nyash_rust::backend::vm::VMValue as V;
match v {
V::String(s) => nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, s),
V::Integer(i) => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, *i),
V::Float(f) => nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, *f),
V::Bool(b) => nyash_rust::runtime::plugin_ffi_common::encode::bool(&mut buf, *b),
V::BoxRef(bx) => {
if let Some(pb) = bx.as_any().downcast_ref::<PluginBoxV2>() {
if let Some(bufbox) = bx.as_any().downcast_ref::<nyash_rust::boxes::buffer::BufferBox>() {
nyash_rust::runtime::plugin_ffi_common::encode::bytes(&mut buf, &bufbox.to_vec());
} else {
nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle(&mut buf, pb.inner.type_id, pb.instance_id());
}
} else {
let s = bx.to_string_box().value; nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s)
}
}
_ => {}
}
}
});
}
}
let mut out = vec![0u8; 1024]; let mut out_len: usize = out.len();
let rc = unsafe { invoke.unwrap()(type_id as u32, method_id, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) };
if rc != 0 { if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { eprintln!("nyrt: birth_i64 (type_id={}) FAILED: invoke rc={}", type_id, rc); } return 0; }
if let Some((tag, _sz, payload)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
if tag == 8 && payload.len() == 8 {
let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]);
let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]);
let r_type = u32::from_le_bytes(t); let r_inst = u32::from_le_bytes(i);
let pb = nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2(box_type_name, r_type, r_inst, invoke.unwrap());
let arc: std::sync::Arc<dyn nyash_rust::box_trait::NyashBox> = std::sync::Arc::new(pb);
let h = nyash_rust::jit::rt::handles::to_handle(arc);
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
println!("nyrt: birth_i64 {} (type_id={}) argc={} -> handle={}", box_type_name, type_id, nargs, h);
}
return h as i64;
}
}
if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { eprintln!("nyrt: birth_i64 (type_id={}) FAILED: decode", type_id); }
0
}
// Convert a VM argument (param index or existing handle) into a runtime handle
// Exported as: nyash.handle.of
#[export_name = "nyash.handle.of"]
pub extern "C" fn nyash_handle_of_export(v: i64) -> i64 {
use nyash_rust::jit::rt::{handles, with_legacy_vm_args};
use nyash_rust::box_trait::NyashBox;
use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2;
// If already a positive handle, pass through
if v > 0 {
return v;
}
// Otherwise treat as legacy param index and box-ref → handleize
if v >= 0 {
let idx = v as usize;
let mut out: i64 = 0;
with_legacy_vm_args(|args| {
if let Some(nyash_rust::backend::vm::VMValue::BoxRef(b)) = args.get(idx) {
// If it's a PluginBoxV2 or any NyashBox, register into handle registry
// Note: store as NyashBox for uniform access
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::from(b.clone());
out = handles::to_handle(arc) as i64;
} else if let Some(nyash_rust::backend::vm::VMValue::BoxRef(b)) = args.get(idx) {
let arc: std::sync::Arc<dyn NyashBox> = std::sync::Arc::from(b.clone());
out = handles::to_handle(arc) as i64;
}
});
return out;
}
0
}
#[export_name = "nyash.string.birth_h"] #[export_name = "nyash.string.birth_h"]
pub extern "C" fn nyash_string_birth_h_export() -> i64 { pub extern "C" fn nyash_string_birth_h_export() -> i64 {
// Create a new StringBox via unified plugin host; return runtime handle as i64 // Create a new StringBox via unified plugin host; return runtime handle as i64
@ -476,6 +694,42 @@ pub extern "C" fn nyash_console_birth_h_export() -> i64 {
pub extern "C" fn main() -> i32 { pub extern "C" fn main() -> i32 {
// Initialize plugin host: prefer nyash.toml next to the executable; fallback to CWD // Initialize plugin host: prefer nyash.toml next to the executable; fallback to CWD
let exe_dir = std::env::current_exe().ok().and_then(|p| p.parent().map(|d| d.to_path_buf())); let exe_dir = std::env::current_exe().ok().and_then(|p| p.parent().map(|d| d.to_path_buf()));
// Windows: assist DLL/plugin discovery by extending PATH and normalizing PYTHONHOME
#[cfg(target_os = "windows")]
if let Some(dir) = &exe_dir {
use std::path::PathBuf;
// Extend PATH with exe_dir and exe_dir\plugins if not already present
let mut path_val = std::env::var("PATH").unwrap_or_default();
let add_path = |pv: &mut String, p: &PathBuf| {
let ps = p.display().to_string();
if !pv.split(';').any(|seg| seg.eq_ignore_ascii_case(&ps)) {
if !pv.is_empty() { pv.push(';'); }
pv.push_str(&ps);
}
};
add_path(&mut path_val, dir);
let plug = dir.join("plugins"); if plug.is_dir() { add_path(&mut path_val, &plug); }
std::env::set_var("PATH", &path_val);
// Normalize PYTHONHOME: if unset, point to exe_dir\python when present.
match std::env::var("PYTHONHOME") {
Ok(v) => {
// If relative, make absolute under exe_dir
let pb = PathBuf::from(&v);
if pb.is_relative() {
let abs = dir.join(pb);
std::env::set_var("PYTHONHOME", abs.display().to_string());
}
}
Err(_) => {
let cand = dir.join("python");
if cand.is_dir() {
std::env::set_var("PYTHONHOME", cand.display().to_string());
}
}
}
}
let mut inited = false; let mut inited = false;
if let Some(dir) = &exe_dir { if let Some(dir) = &exe_dir {
let candidate = dir.join("nyash.toml"); let candidate = dir.join("nyash.toml");

View File

@ -3,12 +3,11 @@
- プラグインBox引数の最小対応を追加TLV: BoxRef - プラグインBox引数の最小対応を追加TLV: BoxRef
- TLVタグ: 1=Bool, 2=I32, 3=I64, 4=F32, 5=F64, 6=String, 7=Bytes, 8=Handle(BoxRef) - TLVタグ: 1=Bool, 2=I32, 3=I64, 4=F32, 5=F64, 6=String, 7=Bytes, 8=Handle(BoxRef)
- BoxRefはプラグインBox参照type_id:u32, instance_id:u32を8バイトでエンコード - BoxRefはプラグインBox参照type_id:u32, instance_id:u32を8バイトでエンコード
- ユーザー定義Box・複雑なビルトインは当面非対応toStringフォールバック - ユーザー定義/複雑なBoxは当面一部非対応toStringフォールバック。標準Boxはプラグイン経由で統一
現状のルーティング: 現状のルーティングPlugin-First:
- User-defined: MIR関数{Box}.{method}/{N}) にCall化関数存在時。それ以外はBoxCall。 - User-defined: MIR関数{Box}.{method}/{N}) にCall化関数存在時。それ以外はBoxCall。
- Builtin: BoxCall → VM内の簡易ディスパッチ - Plugin: BoxCall → PluginInvokemethod_idが解決可能→ それ以外は名前解決で PluginHost.invoke_instance_method
- Plugin: BoxCall → PluginLoaderV2.invoke_instance_method。
今後のタスク: 今後のタスク:
- VM側のfrom Parent.method対応Builder/VM両対応 - VM側のfrom Parent.method対応Builder/VM両対応
@ -74,7 +73,7 @@ NYASH_VM_PIC_DEBUG=1 # PICヒットのしきい値通過時にログ
``` ```
今後の拡張: 今後の拡張:
- 一般`method_id`(ユーザー/ビルトイン/プラグインに対するvtableスロット→thunk直呼び。 - 一般`method_id`(ユーザー/プラグインに対するvtableスロット→thunk直呼び。
- PICのキャッシュ無効化型versionと多相PICへの拡張Phase 10 - PICのキャッシュ無効化型versionと多相PICへの拡張Phase 10
- SocketBoxVM - SocketBoxVM
- 基本API: `bind/listen/accept/connect/read/write/close/isServer/isConnected` - 基本API: `bind/listen/accept/connect/read/write/close/isServer/isConnected`

View File

@ -80,6 +80,55 @@
6) ドキュメント/サンプル更新 6) ドキュメント/サンプル更新
- Handle-First のガイドと最小AOT手順の追記。 - Handle-First のガイドと最小AOT手順の追記。
### 10.5c ドキュメント/サンプル 追加(本スナップショット)
- FFI最小仕様a0/a1/a2, 戻りTLVを短文化: `docs/reference/abi/ffi_calling_convention_min.md`
- birth引数の一般化メモ可変長TLV/例外伝搬): `docs/ideas/new-features/2025-08-30-birth-args-tlv-generalization.md`
- Python最小チェーンの追加:
- VM: `examples/py_min_chain_vm.nyash`
- AOT: `examples/aot_py_min_chain.nyash`
### 10.5d AOT統合 仕上げ(今回)
- ガイド追加: `docs/guides/build/aot_quickstart.md`CLI/スクリプト/内部フロー/FAQ
- by-nameシム整理: nyrtの `nyash_plugin_invoke_name_{getattr,call}_i64` をFFI要約へ反映
- スモーク更新: `tools/smoke_aot_vs_vm.sh` に Python最小チェーンを追加VM側は `NYASH_PY_AUTODECODE=1`
- 今後: nyrtシムのTLV拡充bytes/N引数、Windowsのプラグイン探索微調整
### 10.5e 小仕上げ(今回)
- nyrtシム TLV拡充: BufferBox→bytes(tag=7) 自動エンコード、3引数目以降はレガシー引数からTLVに詰める暫定N引数対応
- Windows探索調整: EXE起動時に PATHへexe/`plugins/`を追加、`PYTHONHOME` 未設定時は `exe\python` を自動採用存在時。相対PYTHONHOMEはexe基準に正規化
### 次フェーズ: 10.6Thread-Safety / Scheduler
- 計画: docs/development/roadmap/phases/phase-10.6/PLAN.txt新規
- 10.6a 監査: Array/Map/Buffer/NyashRuntime/Scheduler の共有戦略Arc+RwLock/Mutexを確認し、未整備箇所をTODO化
- 10.6b スケジューラ: SingleThreadScheduler を Safepoint で `poll()` 連携(観測: `NYASH_SCHED_DEMO/TRACE/POLL_BUDGET`
- 10.6c 並列GC設計: per-thread roots / safepoint協調 / カードマーキングの段階導入メモ確定
### 橋渡し: 10.7 Python Nativeトランスパイル / All-or-Nothing
- 方針と計画: docs/development/roadmap/phases/phase-10.7/PLAN.txt新規
- 二本立て明確化: 実行系現行PyRuntimeBoxと トランスパイル系Python→Nyash→MIR→AOTを併走。自動フォールバック無し
- サブフェーズ: C1 Parser1週→ C2 Compiler Core2週→ C3 CLI配線3日→ C4 テスト(並行)
- 既存導線の活用: 生成Nyashは既存 `--compile-native` でAOT化Strict
### Nyash-only パイプライン(作業場 / 最小導線)
- 目的: すべてNyashで書き、即実行・即修正できる足場を先に用意
- 追加ファイル: tools/pyc/
- PythonParserNy.nyashPyRuntimeBox経由で ast.parse/dump。NYASH_PY_CODE を参照)
- PyIR.nyashIR最小ヘルパ/ PyCompiler.nyashNyash側コンパイラ骨組み/ pyc.nyashエントリ
- Parser/Compiler Rustプラグインは雛形として併存将来削減。当面はNyash実装を優先
### 次の順番(小粒で進める)
1) Parser JSON→IR 変換の最小実装def/return。tools/pyc/PyCompiler.nyash に追加env NYASH_PY_CODE を Pythonで解析→IR生成
2) IR→Nyash 生成の最小拡張Return定数→Return文字列/数値に対応、If/Assignは後続
3) All-or-NothingのStrictスイッチunsupported_nodes 非空ならErr。開閉はenvで制御
4) CLI隠しフラグ `--pyc/--pyc-native` を追加し、Parser→Compiler→AOT を一本化内部で現行AOTを使用
5) サンプル/回帰: tools/pyc の最小ケースをVM/AOTで回し、差分を記録
### Python AOTサンプルの追加最小
- `examples/aot_py_min_chain.nyash`import→getattr→call
- `examples/aot_py_result_ok.nyash`returns_result: Ok
- `examples/aot_py_result_err.nyash`returns_result: Err
- kwargs暫定ブリッジenv eval + **dict: `examples/aot_py_eval_kwargs_env.nyash`
## 🔧 実行方法(再起動手順) ## 🔧 実行方法(再起動手順)
```bash ```bash
cargo build --release --features cranelift-jit cargo build --release --features cranelift-jit

View File

@ -6,9 +6,9 @@ Purpose: Claude×Copilot×ChatGPT協調開発の総合ロードマップ
## 📍 現在位置 ## 📍 現在位置
- **現在フェーズ**: Phase 8.6 VM性能改善進行中 - 現在フェーズ: Phase 10.5b ネイティブビルド基盤の固めMIR→VM→AOTの足固め
- **次フェーズ**: Phase 9 JIT実装 - 次フェーズ: Phase 10.5c Handle-First PluginInvokePython統合の実装着手
- **詳細タスク**: [CURRENT_TASK.md](../../current/CURRENT_TASK.md) - 備考: 旧10.1系10.1c/dは「PythonをNyashで動かすフェーズ」の設計資料Archived。順番を入れ替え、先にネイティブビルド基盤を完成させています。
## 🗺️ フェーズ概要 ## 🗺️ フェーズ概要

View File

@ -1,5 +1,7 @@
[Archived] 旧10.1系ドキュメントです。最新は ../INDEX.md を参照してください。 [Archived] 旧10.1系ドキュメントです。最新は ../INDEX.md を参照してください。
Note: 本来は「PythonをNyashで動かすフェーズパーサー統合」の位置づけでしたが、現在は順番を変更し、先に 10.5bMIR→VM→ネイティブビルド基盤を進めています。
# Phase 10.1c - パーサー統合実装 # Phase 10.1c - パーサー統合実装
## 🎯 このフェーズの目的 ## 🎯 このフェーズの目的

View File

@ -1,5 +1,7 @@
[Archived] 旧10.1系ドキュメントです。最新は ../INDEX.md を参照してください。 [Archived] 旧10.1系ドキュメントです。最新は ../INDEX.md を参照してください。
Note: 本来は「PythonをNyashで動かすフェーズCore実装」の位置づけでしたが、現在は順番を変更し、先に 10.5bMIR→VM→ネイティブビルド基盤を進めています。
# Phase 10.1d - Core実装Phase 1機能 # Phase 10.1d - Core実装Phase 1機能
## 🎯 このフェーズの目的 ## 🎯 このフェーズの目的

View File

@ -6,15 +6,16 @@
- 10.5 README全体像: ./README.md - 10.5 README全体像: ./README.md
- 10.5a Python ABI 設計: ./10.5a-ABI-DESIGN.md - 10.5a Python ABI 設計: ./10.5a-ABI-DESIGN.md
- 10.5b ネイティブビルド基盤: ./10.5b-native-build-consolidation.md - 10.5b ネイティブビルド基盤: ./10.5b-native-build-consolidation.md
- 10.5c PyRuntimeBox / PyObjectBox 実装(予定) - 現在フォーカス: MIR→VM→ネイティブビルドAOT/EXEを先に堅牢化
- 10.5c Handle-First PluginInvoke / PyRuntimeBox / PyObjectBox次段
- 10.5d JIT/AOT 統合(予定) - 10.5d JIT/AOT 統合(予定)
- 10.5e サンプル / テスト / ドキュメント(予定) - 10.5e サンプル / テスト / ドキュメント(予定)
## Archived旧10.1系・参照用) ## Archived旧10.1系・参照用)
- chatgpt5 統合計画(旧称 Phase 10.1: ./chatgpt5_integrated_plan.md - chatgpt5 統合計画(旧称 Phase 10.1: ./chatgpt5_integrated_plan.md
- 10.1a_planning 10.1g_documentation 各READMEと資料 - 10.1a_planning 10.1g_documentation 各READMEと資料
- 10.1c / 10.1d は「PythonをNyashで動かすフェーズ」の設計・実装メモです順番変更により後段へ
整理方針: 整理方針:
- Active ドキュメントに計画と用語を集約。旧10.1系は背景情報として参照のみ。 - Active ドキュメントに計画と用語を集約。旧10.1系は背景情報として参照のみ。
- 実装の優先は「必要最小の箱PyRuntimeBox / PyObjectBox」→ 後から最適化。 - 実装の優先は「必要最小の箱PyRuntimeBox / PyObjectBox」→ 後から最適化。

View File

@ -56,6 +56,12 @@
- 参照管理: `Py_INCREF`/`Py_DECREF` をBoxライフサイクルfiniに接続 - 参照管理: `Py_INCREF`/`Py_DECREF` をBoxライフサイクルfiniに接続
- プラグイン化: `nyash-python-plugin`cdylib/staticlib`nyplug_python_invoke` を提供(将来の静的同梱に対応) - プラグイン化: `nyash-python-plugin`cdylib/staticlib`nyplug_python_invoke` を提供(将来の静的同梱に対応)
追加方針10.5c Handle-First/TLV 統一)
- Lowerer は Handle-First を徹底a0 は常に `nyash.handle.of(receiver)`)。
- 引数TLVは String/Integer をプリミティブ化、その他は Handle(tag=8) に統一。
- 受け手箱名が未確定な経路には by-name シムを導入(後方安全の回避路)。
- 参考: `docs/reference/abi/ffi_calling_convention_min.md`
### 10.5c 境界の双方向化35日 ### 10.5c 境界の双方向化35日
- Nyash→Python: BoxCall→plugin_invokeでCPython C-APIに橋渡し - Nyash→Python: BoxCall→plugin_invokeでCPython C-APIに橋渡し
- Python→Nyash: `nyashrt`CPython拡張`nyash.call(func, args)` を提供 - Python→Nyash: `nyashrt`CPython拡張`nyash.call(func, args)` を提供
@ -72,6 +78,10 @@
- テスト: GILの再入・参照カウントリーク検知・例外伝搬・多プラットフォーム - テスト: GILの再入・参照カウントリーク検知・例外伝搬・多プラットフォーム
- ドキュメント: 使用例、制約GIL/スレッド、AOT時のリンク・ランタイム要件 - ドキュメント: 使用例、制約GIL/スレッド、AOT時のリンク・ランタイム要件
追加済みサンプル(最小チェーン)
- VM: `examples/py_min_chain_vm.nyash`import→getattr→call→println
- AOT: `examples/aot_py_min_chain.nyash`import→getattr→call→return
## 🎯 DoD定義 ## 🎯 DoD定義
- NyashからPythonコードを評価し、PyObjectをHandleで往復できる - NyashからPythonコードを評価し、PyObjectをHandleで往復できる
- 代表的なプロパティ取得/呼び出しROがJIT/VMで動作 - 代表的なプロパティ取得/呼び出しROがJIT/VMで動作

View File

@ -0,0 +1,87 @@
# Phase 10.6 計画(整理版 / txt
目的: Thread-Safety監査→仕込みと協調スケジューラSafepoint連携を最小構成で通し、次段の並列GC/並列実行に備える。
====================
1) ゴールDoD
====================
- 10.6a: Thread-Safety Audit の完了(一次)
- NyashBox/Runtime の共有戦略Arc+RwLock/Mutexを棚卸し、未整備箇所はTODO化
- Plugin-First 前提PluginBoxV2 はクロススレッド未サポ運用を明記)
- 10.6b: 協調スケジューラの足場
- SingleThreadSchedulerqueue + delayed + pollを Safepoint に接続済み
- デモ/トレースNYASH_SCHED_DEMO=1, NYASH_SCHED_TRACE=1, 予算NYASH_SCHED_POLL_BUDGETで観測可
- 10.6c: 並列GC 設計の下準備
- Per-thread roots / Safepoint協調 / カードマーキングの設計メモを確定(段階導入方針)
====================
2) スコープ
====================
- 実装は“最小で通す”に限定(最適化は後続)。
- 既存のVM=仕様、JIT=AOT生成専用という原則は維持。
- Python/Plugin 経路は Plugin-First/Handle-First/TLV 統一の上に載せる10.5で固定済)。
====================
3) サブフェーズとタスク
====================
3.1) 10.6a Thread-Safety Audit12日
- ドキュメント: phase-10/phase_10_6a_thread_safety_audit.md既存に一次棚卸しを反映
- Grepチェック: Arc/Mutex/RwLock/Send/Sync の確認と未整備箇所の列挙
- 確認対象:
- ArrayBox/MapBox/BufferBox の共有戦略Arc<RwLock<...>>
- NyashRuntime メンバのSend+Sync前提Arcで包む
- Scheduler/GC Hooks のSend+Sync前提
- PluginBoxV2: クロススレッド未サポ運用を明記将来設計のTODO
3.2) 10.6b Scheduler Prep12日
- 仕様固定: poll() を MIR Safepoint で呼ぶ(既実装の確認)
- 観測: NYASH_SCHED_DEMO=1 / NYASH_SCHED_TRACE=1 / NYASH_SCHED_POLL_BUDGET で動作確認
- 最小API: spawn/spawn_after/poll/yield_nowSingleThreadScheduler
- ドキュメント: phase-10/phase_10_6b_scheduler_prep.txt 更新Trace/観測例追加済)
3.3) 10.6c Parallel GC Design23日
- 設計メモ: phase-10/phase_10_6c_parallel_gc_design.txt既存を最終化
- 方針:
- Per-thread roots, Safepoint協調、カードマーキングの段階導入
- デフォルトは単一スレッド継続featureで並列ON
- API拡張は後方互換シムを用意barrier引数拡張など
====================
4) 成果物Artifacts
====================
- Docs
- 10.6a 監査メモ: phase-10/phase_10_6a_thread_safety_audit.md一次棚卸し完了
- 10.6b スケジューラ: phase-10/phase_10_6b_scheduler_prep.txtTrace/デモ手順)
- 10.6c 並列GC設計: phase-10/phase_10_6c_parallel_gc_design.txt確定版
- Code
- src/runtime/scheduler.rsSingleThreadScheduler / Send+Sync
- MIR Safepoint → VM Runtime.scheduler.poll()(済)
====================
5) リスクと緩和
====================
- 共有の粒度: 競合を避けるため、Box内部は最小限のロックで運用RwLock優先
- 並列前倒しの誘惑: 10.6では並列化を“設計と足場”に限定、実実装は次フェーズfeature
- Plugin/FFI: クロススレッド呼出しは明示的に非対応(ドキュメントで制約明記)。
====================
6) タイムライン(目安)
====================
- 10.6a: 12日
- 10.6b: 12日
- 10.6c: 23日
====================
7) 依存関係
====================
- 10.5 完了AOT/nyrt/Handle-First/TLV統一/Strict運用を前提
- Docsの最新導線: docs/development/roadmap/phases/phase-10.5/INDEX.md
====================
8) 参照リンク
====================
- phase-10/phase_10_6_thread_safe_revolution.md設計準備
- phase-10/phase_10_6a_thread_safety_audit.md監査
- phase-10/phase_10_6b_scheduler_prep.txtスケジューラ
- phase-10/phase_10_6c_parallel_gc_design.txt並列GC

View File

@ -0,0 +1,79 @@
# Phase 10.6 → Pythonネイティブ接続プラン整理版 / txt
目的: 10.6で整えたThread-Safety/Schedulerの最小足場の上に、PythonプラグインPyRuntimeBox/PyObjectBoxをネイティブEXEAOTまで安定接続する。
====================
1) 到達イメージDoD
====================
- Linux/Windows で以下が安定動作:
- AOT: `examples/aot_py_min_chain.nyash` → `Result: 4`math.sqrt(16)
- VM: `examples/py_min_chain_vm.nyash` → 4.0 表示NYASH_PY_AUTODECODE=1
- returns_result 系サンプルimportR/getattrR/callRで Ok/Err が期待通りに表示
- AOTビルドの配布体験が明確:
- `tools/build_aot.{sh,ps1}` で .o → EXE、`nyash.toml`/plugins 解決、Windowsでは PATH/PYTHONHOME 調整
- 配布ガイド(依存コピー/環境変数/動作確認手順)が `docs/guides/build/aot_quickstart.md` に追記済み
====================
2) 方針(変えない原則)
====================
- Plugin-First / Handle-First / TLV 統一String/Integerはプリミティブ、Bufferはbytes(tag=7)、他はHandle(tag=8))。
- Strict: 実行はVM、JITはAOT生成専用フォールバック禁止
- by-name経路: 受け手箱名未確定時は `nyash_plugin_invoke_name_{getattr,call}_i64` で実行時解決。
====================
3) 実装タスクM1→M5
====================
M1: AOT最小ルートの安定完了確認/微修正)
- nyrt シムBufferBox→bytes(tag=7) 対応、3引数目以降をレガシーVM引数からTLV追補暫定N引数
- Windows探索EXE起動時に PATH へ exe/`plugins/` を追加、`PYTHONHOME` が未設定なら `exe\python` を採用相対はexe基準に正規化
- ドキュメントAOTクイックスタートにWindows注意点を追記
M2: TLV/FFIカバレッジ拡大最小
- kwargs/辞書のパスcallKwRをbytes(tag=7) or string(tag=6, JSON)で暫定対応(将来 BID-1拡張
- N引数の一般化invoke3→invokeN 設計、実装は段階導入。先行はレガシー補完で可)
- returns_result の統一処理VM既存→AOTでも同等に扱う
M3: Lowerer/Policy の整流
- Handle-First統一の確認Python特化の型伝搬は撤去済み戻りはHandle or プリミティブのみ参照)
- birth引数の一般化TLVメモに沿い、Integer/Stringはプリミティブ、他はHandle
- by-name の適用範囲と導線(`getattr`/`call` はby-name固定、importはmethod_id直参照
M4: 配布導線とサンプル
- スクリプト整備:`tools/build_python_aot.sh` の統合(`build_aot.sh` に一本化 or ラッパー)
- サンプル最小化returns_resultOk/Err、例外伝搬Error文字列、bytes引数、context共有per-runtime globals
- ガイド整理:`docs/guides/build/aot_quickstart.md` に「Pythonネイティブ」節を追加動作要件・環境変数
M5: 観測/CI軽量
- スモークVMチェーン / AOTチェーンの比較Result行を `tools/smoke_aot_vs_vm.sh` に追加Python系は最小のみ
- ログ:`NYASH_JIT_EVENTS*`/`NYASH_JIT_NATIVE_F64`/`NYASH_PY_AUTODECODE` による差分観測
====================
4) リスク/制約
====================
- CPython依存の配布WindowsのDLL探索PATH/PYTHONHOMEは最小整備。完全同梱embeddedは後段で検討。
- KW/辞書のTLV表現暫定はbytes/stringでブリッジ。正式BIDタグは将来導入後方互換のためJSON連携を許容
- ネイティブN引数v0はレガシーVM引数からのTLV補完でしのぎ、invokeNは次期導入。
====================
5) タイムライン(目安)
====================
- M1安定: 0.5日(確認/微修正)
- M2TLV拡充: 12日kwargsは暫定・bytes/JSON
- M3Lowerer整流: 0.51日
- M4配布/サンプル): 1日
- M5観測/CI: 0.5日
====================
6) 成果物Artifacts
====================
- 例: `examples/aot_py_min_chain.nyash`, `examples/py_min_chain_vm.nyash`(既存)
- ツール: `tools/build_aot.{sh,ps1}`Python節、`tools/smoke_aot_vs_vm.sh`Python最小
- Docs: `docs/guides/build/aot_quickstart.md`Python節、`docs/reference/abi/ffi_calling_convention_min.md`bytes/N引数注記
====================
7) 参照
====================
- 10.5c: Handle-First/PluginInvoke 設計by-name シム)
- 10.5d/e: AOT統合/最終仕上げnyrt/Windows探索/TLV拡張
- 10.6: Thread-Safety/Scheduler並列化前の足場

View File

@ -0,0 +1,67 @@
# Phase 10.7 Python Native 再スタート計画(合意版 / txt
目的: 現行の Plugin-FirstPyRuntimeBox/PyObjectBox, Handle-First/TLVを維持しつつ、トランスパイル路線Python→Nyashを“All or Nothing”原則で段階導入。10.6の足場Thread-Safety/Scheduler上で、AOT配布体験に直結する最短ラインを構築する。
====================
A) 方針(判断)
====================
- 二本立てを明確化:
1) 実行系(現行): PyRuntimeBox 経由VM=仕様、JIT=AOT生成のみ。配布/運用の実用ライン。
2) トランスパイル系10.7: Python→Nyash→MIR→AOT。コンパイル成功/失敗の二択(フォールバック自動無し)。
- 役割分担未対応Pythonはユーザーが明示的に PyRuntimeBox を使う。トランスパイルはコンパイル成功率を段階的に拡大。
- Plugin-Firstは維持Parser/CompilerもプラグインBox化。CLI/VMから統一呼び出し。
====================
B) 最小成功像Phase 1 / DoD
====================
- サンプルpyPhase 1 範囲内)を `pythonc`(仮)で Nyashスクリプトへ生成 → `--compile-native` で EXE 生成 → 実行。
- 機能カバレッジPhase 1: def/if/for/while/return/bool/算術/比較/関数呼び出し/LEGB/デフォルト引数/for-else。
- Differential限定: Phase 1 サンプル群で CPython と一致(出力/戻り/例外の有無)。
====================
C) サブフェーズとタスク
====================
C1) Parser Plugin1週
- `plugins/nyash-python-parser-plugin`: Python→ASTpyo3
- AST→CorePy IRJSON: 構文の正規化with→try/finally などはPhase 2
- Telemetry: ノード統計/未対応ノードを列挙。
C2) Compiler Core2週
- IR→Nyash AST 生成Box化/クロージャ/LEGB/デフォ引数の再現)。
- peephole最小定数畳み込み
- 生成NyashのPretty-print + 簡易ソースマップ。
C3) 配線/CLI/実行3日
- `nyash --pyc file.py -o out.ny`Nyash出力/ `--pyc-native file.py -o app`EXE直行を追加内部で既存 `--compile-native` を利用)。
- 生成Nyashは既存コンパイラ経由で MIR→AOT を通すStrict
C4) テスト/観測1週並行
- `phase-10.7/testing-plan.md` の Phase 1 部分を小粒に実装。
- VM vs AOT の「Result:」比較ラインを流用(既存スモークベース)。
====================
D) インターフェース / 成果物
====================
- ParserBox: `parse(code: String) -> AstBox/CorePyBox`内部JSON保持 or to_json
- CompilerBox: `compile(ir: CorePyBox) -> Result<NyashSourceBox, ErrorBox>`
- CLI: `--pyc/--pyc-native`(初期は隠しフラグでも可)
- Docs: README/implementation/testing-plan を PLAN に沿って更新。
====================
E) リスク/緩和
====================
- 範囲膨張: Phase 1 の構文/意味論を固定し、Beyondは即Err。PyRuntimeBoxは明示利用。
- 例外/with/comp/async: Phase 2/3の対象。IR設計時に将来ードを予約後方互換
- Windows配布: 10.5で整えた PATH/PYTHONHOME 補助はPyRuntime向け。トランスパイル後はCPython依存なし。
====================
F) タイムライン(目安)
====================
- C1: 1週 / C2: 2週 / C3: 3日 / C4: 1週並行
====================
G) 現行との接続
====================
- 10.6の足場Thread-Safety/Schedulerは維持。トランスパイル系は単一スレッド/VM基準で十分。
- 10.5のAOT導線/nyrtシムはそのまま活用生成Nyashに対して適用

View File

@ -0,0 +1,148 @@
# Phase 10.7 - True Python Native via Plugin Boxes
## 🎯 概要
PythonコードをNyashで**本当にネイティブ実行**する。CPythonへの依存なしに、Pythonコードが完全にNyash MIR/JIT経由で機械語として実行される。
### 現状 vs 理想
**現状Phase 10.6**: PyRuntimeBox → libpython呼び出し
**理想Phase 10.7**: Python → Nyashスクリプト → MIR → ネイティブ
## 🏗️ アーキテクチャ:トランスパイル方式
```
Python AST → CorePy IR → Nyash AST → Nyashスクリプト
```
### なぜトランスパイル?
1. **透明性**: 生成コードが見える・デバッグできる・手を加えられる
2. **既存資産活用**: Nyashコンパイラの最適化を自動享受
3. **教育的価値**: PythonとNyashの対応が学習価値を持つ
4. **段階的改善**: 生成コードの品質を徐々に向上
### プラグインBox群
- **PythonParserBox**: Python → AST変換
- **PythonCompilerBox**: AST → Nyashスクリプト生成
- **py_runtime.ny**: Pythonセマンティクス保持ライブラリ
## ⚠️ All or Nothing設計フォールバックなし
**コンパイルできる or できない の2択のみ**
```nyash
compiler = new PythonCompilerBox()
result = compiler.compile(ast)
if result.isOk() {
// 100%コンパイル成功 → ネイティブ実行
print("Success! Native execution ready.")
} else {
// 未対応機能あり → 完全拒否
print("Cannot compile: " + result.getError())
print("Use PyRuntimeBox instead.")
}
```
理由:開発時と本番時で挙動が変わるのは最悪の設計
## 📋 実装フェーズ
### Phase 10.7a - Parser Plugin1週間
- PythonParserBoxの実装
- Python AST → ASTBox変換
- テレメトリー基盤
### Phase 10.7b - Compiler Core2週間
**Phase 1機能必須**
- 関数定義、条件分岐、ループ
- 演算子、関数呼び出し
- Python固有LEGB、デフォルト引数、for/else
### Phase 10.7c - Validation & Testing1週間
- コンパイル可能性の事前検証
- Differential testingCPythonと比較
- 明確なエラーメッセージ
### Phase 10.7d - Coverage拡大3-4週間
**Phase 2**: 例外処理、with文、comprehensions
**Phase 3**: async/await、デコレータ、ジェネレータ
## 🧪 py_runtime設計
```nyash
// Pythonセマンティクスを忠実に再現
box PyRuntime {
py_truthy(x) {
// Python的真偽値判定
if x == null or x == false { return false }
if x.hasMethod("__bool__") { return x.__bool__() }
if x.hasMethod("__len__") { return x.__len__() != 0 }
return true
}
py_getattr(obj, name) {
// ディスクリプタプロトコル、MRO探索
}
py_call(f, args, kwargs) {
// デフォルト引数、*args/**kwargs処理
}
}
```
## 📊 成功指標
### Phase 1完了時
```
Compilable files: 15/100 (15%)
Performance (numeric): 10x faster than CPython
Correctness: 100% (differential testing)
```
### 最終目標Phase 3
```
Coverage: 95%+ of common patterns
Performance: 5-20x faster
Distribution: Single binary, no CPython
```
## 🚀 クイックスタート
```bash
# プラグイン作成
cd plugins/
cargo new nyash-python-parser-plugin --lib
# 最小実装
[dependencies]
pyo3 = { version = "0.22", features = ["auto-initialize"] }
nyash-plugin-sdk = { path = "../../crates/plugin-sdk" }
# テスト実行
cargo build --release
../../target/release/nyash test_parser.nyash
```
## 💡 創造的可能性
### ハイブリッドプログラミング
```python
@nyash.vectorize # PythonデコレータがNyashのSIMD生成
def matrix_multiply(a, b):
return a @ b
```
### 言語の共進化
- Nyashが「Pythonで最も使われるイディオム」から学習
- Pythonに「Nyash-aware」コーディングスタイル誕生
### 教育的インパクト
左にPython、右にリアルタイムNyash変換のPlayground
## 📚 参考資料
- **archive/gemini-analysis-transpile-beauty.md** - 創造性分析
- **archive/codex-analysis-technical-implementation.md** - 技術分析

View File

@ -0,0 +1,252 @@
# Codex (GPT-5)の技術分析Python→Nyashトランスパイルの実装詳細
2025-08-30 - OpenAI Codex v0.25.0による技術的深掘り
## Big Picture
- **Purpose**: Generate Nyash source from Python to leverage Nyash's optimizer, keep debugging simple, and "think in Nyash" early without locking into MIR details.
- **Core idea**: Normalize Python to a small CorePy IR, then lower to idiomatic Nyash with a thin "Py runtime shim" in Nyash that preserves Python's dynamic semantics where required.
## AST Conversion
### Two-stage lowering
```
Python AST → CorePy IR → Nyash AST
```
CorePy should be expression-friendly, desugared, and semantics-explicit (e.g., with→try/finally, for→iterator loop, boolean ops→if/else).
### Visitor + environment
Implement a node visitor that carries scope info (locals/free/globals), evaluation order, and source spans. Use Python's `symtable` to seed symbol kinds; validate against your own pass.
### Semantic shims
Lower Python ops to Nyash intrinsics that preserve semantics:
- `py_truthy(x)`
- `py_getattr(o,"a")`
- `py_setattr(o,"a",v)`
- `py_binop("add", a, b)`
- `py_cmp("lt", a, b)`
- `py_iter(x)`
- `py_call(f, args, kwargs)`
- `py_slice(x, i, j, k)`
### Boxes and cells
Model Python variables and closures with Box/Cell objects. Rule of thumb: locals are unboxed unless captured or aliased; promote to Box when needed. Everything-is-Box in Nyash aligns well with Python's mutability/aliasing.
### Control flow
Normalize to a small set: `if`, `block`, `loop`, `break`, `continue`, `try/catch/finally`, `throw`. Lower `and`/`or` with short-circuit temp; turn comprehensions into explicit loops with dedicated inner scope.
## Transpile Quality
### Readability vs optimize
Offer modes. Default emits idiomatic Nyash constructs and meaningful identifiers, comments with source spans, and simple temporaries. "Optimize" mode switches to `py_*` intrinsics fusion and fewer temps.
### Idiomatic Nyash
Prefer Nyash control constructs over procedural labels. Use native blocks for `if/else`, `match` if Nyash has it; only fall back to runtime calls where semantics demand.
### Stable pretty-printer
Round-trip friendly formatter with consistent whitespace, trailing comma rules, and deterministic temp naming (`_t1`, `_t2…`). Keep def/class emitted in declaration-order.
### Debug info
Attach `span(file, line, col)` to every IR node, carry through to Nyash AST, then emit a sidecar source map. Optionally embed lightweight `#line` directives or inline comments per statement in debug builds.
## Python Feature Mapping
### Default args
Evaluate at def-time; store tuple/dict on the function object. At call-time, fill missing with stored defaults. Beware mutable defaults: do not clone; reuse exact object.
### LEGB scoping
Build symbol table with flags for `global`/`nonlocal`. Emit closure "cells" (Boxes) for free vars; functions capture a vector of cells. Globals map to the module dict; builtins fallback when name miss in global.
### for/else, while/else
Introduce `broken=false`. On `break`, set and exit; after loop, `if !broken { else_block }`.
### Comprehensions
Create inner function/scope per comprehension (Python 3 semantics). Assignment targets exist only in that scope. Preserve evaluation order and late binding behavior.
### With statement
Desugar to try/finally per Python spec: evaluate context expr, call `__enter__`, bind target, run body, always call `__exit__`, and suppress exception only if `__exit__` returns truthy.
### Decorators
Evaluate bottom-up at def-time: `fn = decoN(...(deco1(fn)))` then assign back. Keep evaluation order of decorator expressions.
### Generators
Lower to a state machine object implementing Nyash's iterator protocol, with saved instruction pointer, stack slots, and exception injection (`throw`, `close`). Support `yield from` by delegation trampoline.
### Pattern matching (PEP 634)
If supported by Nyash, map directly; else lower to nested guards and extractor calls in a `py_match` helper library.
### Data model
Attribute access honors descriptors; method binding creates bound objects; arithmetic and comparisons dispatch to `__op__`/`__rop__` and rich comparisons; truthiness via `__bool__`/`__len__`.
## Performance Opportunities
### At transpile-time
- Constant fold literals, f-strings (format plan precomputation), simple arithmetic if types are literal.
- Invariant hoisting for loop-invariant comprehensions and attribute chains when no side-effects (guarded).
- Direct calls to Nyash intrinsics for selected builtins (`len`, `isinstance`, `range`) only if not shadowed (prove via symbol table).
- Peephole: collapse nested `py_truthy(py_truthy(x))`, merge adjacent `if` with literal conditions, drop dead temporaries.
### Defer to Nyash compiler
- Inlining across Nyash functions, register allocation, loop unrolling, vectorization, constant propagation at MIR level.
- DCE/CSE once `py_*` helpers are inlined or annotated as pure/idempotent where legal.
### Types as hints
- Consume Python annotations/`typing` to emit specialized fast paths: e.g., `int` → direct Nyash integer ops, else fallback to `py_binop`. Plumb types through IR as optional metadata for MIR to exploit.
- Profile-guided guards: optional mode emits guards around hot calls to enable Nyash JIT/AOT to speculate and deopt to generic `py_*`.
## Error Handling & Debug
### Source maps
Emit a compact mapping (e.g., VLQ JSON) from Nyash line/col → Python original; include segment mappings per expression for precise stepping.
### Exception rewriting
Wrap Nyash runtime entrypoints to translate stack frames via the source map, hiding frames from helpers (`py_*`) unless verbose mode is on.
### Stage diagnostics
- CorePy dump: toggle to print normalized IR with spans.
- Nyash preview: post-format preview with original Python line hints.
- Trace toggles: selective tracing of `py_call`, `py_getattr`, iteration; throttle to avoid noise.
### Friendly messages
On unsupported nodes or ambiguous semantics, show minimal repro, Python snippet, and link to a doc page. Include symbol table excerpt when scoping fails.
## Architecture & DX
### Pass pipeline
```
Parse Python AST → Symbol table → Normalize to CorePy →
Scope/closure analysis → Type/meta attach → Lower to Nyash AST →
Optimize (peephole/simplify) → Pretty-print + source map
```
### Runtime shim (`nyash/lib/py_runtime.ny`)
Core APIs:
- `py_call(f, pos, kw, star, dstar)`
- `py_truthy(x)`
- `py_getattr/py_setattr`
- `py_binop(op, a, b)`
- `py_cmp(op,a,b)`
- `py_iter(x)`
- `py_next(it)`
- `py_slice(x,i,j,k)`
- `py_with(mgr, body_fn)`
- `py_raise`
- `py_is`
- `py_eq`
Data model support: descriptor get/set, method binding, MRO lookup, exception hierarchy, StopIteration protocol.
Perf annotations: mark pure or inline candidates; keep stable ABI.
### CLI/flags
Modes:
- `--readable`
- `--optimized`
- `--debug`
- `--emit-sourcemap`
- `--dump-corepy`
- `--strict-builtins`
Caching: hash of Python AST + flags to cache Nyash output, source map, and diagnostics.
Watch/incremental: re-transpile changed modules, preserve source map continuity.
### Tests
- Golden tests: Python snippet → Nyash output diff, with normalization.
- Differential: run under CPython vs Nyash runtime for functional parity on a corpus (unit/property tests).
- Conformance: edge cases (scoping, descriptors, generators, exceptions) and evaluation order tests.
## Pitfalls & Remedies
### Evaluation order
Python's left-to-right arg eval, starred/unpacking, and kw conflict checks. Enforce by sequencing temps precisely before `py_call`.
### Shadowing builtins/globals
Only specialize when proven not shadowed in any reachable scope. Provide `--strict-builtins` to disable specialization unless guaranteed.
### Identity vs equality
`is` is reference identity; avoid folding or substituting.
### Integer semantics
Python's bigints; ensure Nyash numeric layer matches or route to bigints in `py_*`.
## Future Extensibility
### Plugins
Pass manager with hooks (`before_lower`, `after_lower`, `on_node_<Type>`). Allow project-local rewrites and macros, with access to symbol/type info.
### Custom rules
DSL for pattern→rewrite with predicates (types, purity), e.g., rewrite `dataclass` patterns to Nyash records.
### Multi-language
Keeping the Nyash script as a stable contract invites other frontends (e.g., a subset of JS/TypeScript or Lua) to target Nyash; keep `py_*` separate from language-agnostic intrinsics to avoid contamination.
### Gradual migration
As Nyash grows Pythonic libraries, progressively replace `py_*` with native Nyash idioms; keep a compatibility layer for mixed projects.
## Concrete Translation Sketches
### Attribute
```python
a.b
```
```nyash
py_getattr(a, "b")
```
### Call
```python
f(x, y=1, *as, **kw)
```
```nyash
py_call(f, [x], {"y":1}, as, kw)
```
### If
```python
if a and b:
```
```nyash
let _t=py_truthy(a); if _t { if py_truthy(b) { ... } }
```
### For/else
```python
for x in xs:
if cond:
break
else:
else_block
```
```nyash
let _it = py_iter(xs);
let _broken=false;
loop {
let _n = py_next(_it) catch StopIteration { break };
x = _n;
...
if cond { _broken=true; break }
}
if !_broken { else_block }
```
### With
Evaluate mgr, call `__enter__`, bind val; try body; on exception, call `__exit__(type,e,tb)` and suppress if returns true; finally call `__exit__(None,None,None)` when no exception.
### Decorators
```nyash
let f = <def>;
f = decoN(...(deco1(f)));
name = f
```
## Why Nyash Script First
- **Debuggability**: Human-readable Nyash is easier to inspect, diff, and map errors to; source maps stay small and precise.
- **Optimization leverage**: Nyash compiler/MIR can keep improving independently; your Python frontend benefits automatically.
- **Ecosystem fit**: Generates idiomatic Nyash that other tools (formatters, linters, analyzers) can understand; fosters a consistent DX.

View File

@ -0,0 +1,72 @@
# Gemini先生の分析Python→Nyashトランスパイルの「面白さ」と「可能性」
2025-08-30 - Geminiによる深い洞察
## 1. 創造的な活用方法 - 「ハイブリッドプログラミング」の新しい形
このアプローチの最大の面白さは、**PythonとNyashの境界を曖昧にし、両者の長所を自在に組み合わせられる「ハイブリッドプログラミング」環境**が生まれる点にあります。
### パフォーマンスの「目利き」チューニング
開発者はまず、書き慣れたPythonで迅速にプロトタイプを構築します。パフォーマンスが問題になる箇所だけ、トランスパイルされたNyashコードを覗き見ます。「なるほど、このリスト内包表記はこういうループになるのか。ここをNyashの並列処理機能に書き換えれば速くなりそうだ」といった具合に、**生成されたコードを"最適化のためのヒント"として活用**し、手動で高性能なNyashコードに置き換えることができます。これは、Pythonの手軽さとネイティブの速度を、開発者が主体的にコントロールできる、非常に楽しいプロセスです。
### Nyashを操るためのDSLドメイン固有言語としてPythonを使う
Pythonの強力なメタプログラミング能力デコレータ、AST操作などを使い、「Nyashコードジェネレータ」をPythonで記述できます。例えば、特定の計算パターンを最適化するNyashコードを生成するPythonデコレータを作るのはどうでしょう。
```python
# このデコレータがNyashのSIMD命令を使うコードを生成する
@nyash.vectorize
def python_function(a, b):
return a + b
```
このように、Pythonの簡潔な記述から、裏では非常に複雑で高性能なNyashコードが生成される。これはまさに、**Pythonを「Nyashのための高級マクロ言語」として使う**創造的なハックです。
### 動的なコード生成と実行
実行時にPythonコード文字列を生成し、それをNyashにトランスパイルして即座にネイティブ実行する、といった芸当も可能です。これにより、設定ファイルやユーザー入力に基づいて動的に高性能な処理ロジックを組み立てるような、柔軟なアプリケーションが実現できます。
## 2. 言語進化への影響 - 共進化するエコシステム
この方式は、一方的な変換ではなく、両言語が互いに影響を与え合う「共進化」の触媒となります。
### Nyashへのフィードバック
Pythonの標準的なイディオム例: `with`文、ジェネレータ、リスト内包表記をNyashに変換する際、「どうもしっくりくる表現がNyashにない」「もっとエレガントに書ける構文が欲しい」という課題が必ず見つかります。これは、**Pythonという巨大なユースケースからNyashが学ぶ絶好の機会**です。このフィードバックループが、Nyashをより表現力豊かで実用的な言語へと進化させます。
### Pythonコミュニティへの影響
「この書き方をすれば、Nyashで速くなる」という知見が広まると、Pythonプログラマーの中に**「Nyash-aware」なコーディングスタイル**が生まれる可能性があります。これは、Pythonのサブセットや方言のようなもので、パフォーマンスを意識した新しいPythonの書き方として定着するかもしれません。Pythonのコードが、静的解析や型ヒントだけでなく、「ネイティブ変換効率」という新しい評価軸を持つようになります。
## 3. 実装の優雅さ - 「AST to AST」という美しい設計
トランスパイラの内部実装そのものにも、開発者を楽しませるエレガントなパターンが存在します。
### AST抽象構文木レベルでの変換
最もクリーンな実装は、Pythonの`ast`モジュールでソースコードをASTに変換し、そのASTの各ードを再帰的に辿りながら、対応するNyashのASTードを構築していく方法です。これは、文字列置換のような場当たり的な方法とは異なり、非常に構造的で堅牢です。Pythonの`for`ループのASTードが、Nyashの`for`ループのASTードに1対1で対応付けられる様は、パズルを解くような知的な面白さがあります。
### 変換パターンの分離と合成
Pythonの各構文要素関数定義、if文、クラスなどに対応する変換ロジックを、それぞれ独立した小さな関数やクラスとして実装します。これにより、トランスパイラ全体の見通しが良くなり、テストも容易になります。新しい構文への対応も、新しい変換パターンを追加するだけで済みます。このモジュール性は、大規模なソフトウェアを設計する上での美しさそのものです。
## 4. 教育的インパクト - 生きた「コンパイラ入門」
このツールは、プログラミング学習者にとって最高の教材となり得ます。
### 概念の可視化
「高級言語の裏側では、実際にはもっと低レベルな処理が行われている」というコンピュータサイエンスの基本概念を、これ以上なく明確に示せます。Pythonのたった1行が、Nyashの数行のコードに展開されるのを見ることで、抽象化のコストとメリットを直感的に理解できます。
### 言語間の「ロゼッタストーン」
PythonとNyashという2つの言語の思考様式の違いを学ぶための、動的な「ロゼッタストーン」になります。「Pythonの辞書は、Nyashではハッシュマップとしてこう表現されるのか」「Pythonの動的型付けは、Nyashの型推論によってこう解決されるのか」といった発見は、学習者にとって大きな喜びとなるでしょう。
### インタラクティブな学習環境
Web上で、左にPython、右にリアルタイムで変換されたNyashコードが表示されるような「Nyash Playground」を作れば、非常に人気の出る学習ツールになるはずです。
## 5. 長期的な展望 - 5年後、10年後の可能性
このアプローチは、短期的な実装の容易さだけでなく、長期的に大きな可能性を秘めています。
### 5年後: Pythonエコシステムのシームレスな高速化
トランスパイラの完成度が高まり、主要なPythonライブラリNumPy, Pandasの一部などをNyashに変換できるようになっているかもしれません。`pip install`するだけで、内部的にNyashへ変換・コンパイルされ、ユーザーは意識することなくPythonコードのままネイティブの速度を手に入れる、という未来が考えられます。Pythonの型ヒントが、単なる静的解析のためだけでなく、**Nyashへの最適化コンパイルのための重要なヒント**として活用されているでしょう。
### 10年後: 「ハイブリッド言語」としての地位確立
PythonとNyashの関係は、TypeScriptとJavaScriptの関係に似たものになっているかもしれません。開発者は、プロジェクトの大部分をPythonで書き、パフォーマンスクリティカルな部分はNyashで書く、あるいはPythonで書いたものをトランスパイルして微調整する、という開発スタイルが当たり前になっている可能性があります。Nyashは「Pythonをネイティブ速度で動かすための最高のパートナー言語」としての地位を確立し、両言語は互いに補完し合う強力なエコシステムを形成しているでしょう。最終的には、**Pythonの書きやすさと、ネイティブコードの実行性能を両立させた、究極のスクリプト環境**が実現しているかもしれません。
## まとめ
B案トランスパイル方式は、単に技術的に堅実なだけでなく、開発者の知的好奇心を刺激し、言語コミュニティ全体を巻き込んで成長していく「面白さ」と「可能性」に満ちた選択です。生成されたNyashコードが「ブラックボックス」ではなく「ホワイトボックス」であることが、デバッグ、最適化、学習、そして未来の創造的なハックへと繋がる鍵となります。この選択は、Nyashプロジェクトの成功に大きく貢献する戦略的な一手だと確信します。

View File

@ -0,0 +1,295 @@
# Python Native実装例
## 🎯 実装イメージ
### 使用例1: 基本的な関数のネイティブ化
```nyash
// example1_basic.nyash
// Pythonコードをネイティブコンパイル
// Step 1: Pythonコードを用意
code = """
def fibonacci(n):
if n <= 1:
return n
return fibonacci(n-1) + fibonacci(n-2)
def factorial(n):
if n == 0:
return 1
return n * factorial(n-1)
"""
// Step 2: パース
parser = new PythonParserBox()
ast = parser.parse(code)
print("Parsed functions: " + parser.getStats().get("functions"))
// Step 3: コンパイル
compiler = new PythonCompilerBox()
mir_module = compiler.compile(ast)
// Step 4: 実行
if mir_module.isOk() {
// ネイティブ実行!
module = mir_module.get()
// 関数を取得して実行
fib = module.getFunction("fibonacci")
result = fib.call(10)
print("fibonacci(10) = " + result) // 55
fact = module.getFunction("factorial")
result = fact.call(5)
print("factorial(5) = " + result) // 120
} else {
print("Compilation failed: " + mir_module.getError())
}
```
### 使用例2: コンパイル可否の明確な判定
```nyash
// example2_clear_separation.nyash
// コンパイルできるかどうか事前に判定
// Phase 1対応のコード
code_phase1 = """
def compute_sum(n):
total = 0
for i in range(n):
total += i * i
return total
def factorial(n):
if n == 0:
return 1
return n * factorial(n-1)
"""
// Phase 1未対応のコード
code_unsupported = """
def fibonacci_generator(n):
a, b = 0, 1
for _ in range(n):
yield a
a, b = b, a + b
"""
// コンパイラーで判定
parser = new PythonParserBox()
compiler = new PythonCompilerBox()
// Phase 1対応コードのチェック
ast1 = parser.parse(code_phase1)
result1 = compiler.compile(ast1)
if result1.isOk() {
print("✅ Phase 1 code compiled successfully!")
module = result1.get()
print("compute_sum(100) = " + module.call("compute_sum", 100))
} else {
print("❌ Compilation failed: " + result1.getError())
}
// 未対応コードのチェック
ast2 = parser.parse(code_unsupported)
result2 = compiler.compile(ast2)
if result2.isOk() {
print("✅ Compiled successfully!")
} else {
print("❌ Cannot compile: " + result2.getError())
print(" Reason: yield expression not supported in Phase 1")
print(" Please use PyRuntimeBox instead")
}
```
### 使用例3: プログレッシブ最適化
```nyash
// example3_progressive.nyash
// 実行しながら徐々に最適化
// 型推論付きコンパイラー
compiler = new PythonCompilerBox()
compiler.enableTypeInference(true)
compiler.enableProfiling(true)
// 初回実行(型情報収集)
code = """
def matrix_multiply(A, B):
# 最初は型が不明
result = []
for i in range(len(A)):
row = []
for j in range(len(B[0])):
sum = 0
for k in range(len(B)):
sum += A[i][k] * B[k][j]
row.append(sum)
result.append(row)
return result
"""
// プロファイル付き実行
for i in range(5) {
mir = compiler.compile(parser.parse(code))
// 実行してプロファイル収集
module = mir.get()
A = [[1, 2], [3, 4]]
B = [[5, 6], [7, 8]]
result = module.call("matrix_multiply", A, B)
// 型情報が蓄積される
print("Iteration " + i + ": ")
print(" Type confidence: " + compiler.getTypeConfidence())
print(" Optimization level: " + compiler.getOptimizationLevel())
}
// 5回実行後、完全に最適化されたコードが生成される
```
### 使用例4: 言語間相互運用
```nyash
// example4_interop.nyash
// PythonコードとNyashコードのシームレスな連携
// Pythonで数値計算関数を定義
python_math = """
import math
def distance(x1, y1, x2, y2):
return math.sqrt((x2-x1)**2 + (y2-y1)**2)
def normalize(vector):
magnitude = math.sqrt(sum(x**2 for x in vector))
return [x/magnitude for x in vector]
"""
// コンパイルしてNyashから使用
module = compile_python(python_math)
// Nyash側のゲームロジック
box GameObject {
init { x, y, vx, vy }
update(dt) {
// Python関数をネイティブ速度で呼び出し
me.x = me.x + me.vx * dt
me.y = me.y + me.vy * dt
// 正規化Pythonの関数を使用
local normalized = module.normalize([me.vx, me.vy])
me.vx = normalized[0]
me.vy = normalized[1]
}
distanceTo(other) {
// Pythonの距離計算関数を使用
return module.distance(me.x, me.y, other.x, other.y)
}
}
// 完全にネイティブコードとして実行される!
```
### 使用例5: デバッグとプロファイリング
```nyash
// example5_debug.nyash
// 開発時のデバッグ支援
// デバッグモード有効
parser = new PythonParserBox()
parser.enableDebug(true)
compiler = new PythonCompilerBox()
compiler.enableDebug(true)
compiler.enableSourceMap(true) // 元のPythonコードへのマッピング
problematic_code = """
def buggy_function(items):
total = 0
for item in items:
# バグ: itemが文字列の場合エラー
total += item * 2
return total / len(items)
"""
// コンパイル試行
result = compiler.compile(parser.parse(problematic_code))
if result.isErr() {
// 詳細なエラー情報
diag = compiler.getDiagnostics()
print("Compilation failed at line " + diag.line)
print("Issue: " + diag.message)
print("Suggestion: " + diag.suggestion)
// フォールバックで実行してランタイムエラーを確認
runtime = new PythonRuntimeBox()
try {
runtime.exec(problematic_code)
runtime.call("buggy_function", ["a", "b", "c"])
} catch (e) {
print("Runtime error: " + e.message)
print("This would have been caught at compile time!")
}
}
// プロファイリング情報
profiler = new PythonProfiler()
profiler.attach(module)
profiler.run()
print("Hot spots:")
print(profiler.getHotSpots())
print("Type instability:")
print(profiler.getTypeInstability())
```
## 🎯 実装の進化
### Phase 1現在
```python
# これらがネイティブ化可能
def add(x, y): return x + y
def factorial(n): ...
def fibonacci(n): ...
```
### Phase 2予定
```python
# 特殊メソッド対応
class Vector:
def __add__(self, other): ...
def __len__(self): ...
# 内包表記
squares = [x**2 for x in range(10)]
```
### Phase 3将来
```python
# 完全な言語機能
async def fetch_data(): ...
@decorator
def enhanced_function(): ...
yield from generator
```
## 🚀 パフォーマンス期待値
```
Benchmark: Fibonacci(30)
CPython: 1.234s
PyPy: 0.123s
Nyash Native: 0.012s (100x faster!)
Benchmark: Matrix Multiplication (100x100)
CPython: 5.678s
NumPy: 0.234s
Nyash Native: 0.198s (NumPyに匹敵!)
```

View File

@ -0,0 +1,249 @@
# Phase 10.7 実装詳細
## 🛠️ 技術アーキテクチャ
### 2段階変換パイプライン
```
Python AST → CorePy IR → Nyash AST → Nyashスクリプト
```
**CorePy IR**の役割:
- Pythonの複雑な構文を正規化
- セマンティクスを明示的にwith→try/finally等
- 最適化しやすい中間表現
### 実装構造
```rust
// plugins/nyash-python-parser-plugin/src/lib.rs
#[plugin_box]
pub struct PythonParserBox {
base: BoxBase,
}
#[plugin_methods]
impl PythonParserBox {
pub fn parse(&self, code: &str) -> Result<Box<dyn NyashBox>> {
Python::with_gil(|py| {
let ast_mod = py.import("ast")?;
let tree = ast_mod.call_method1("parse", (code,))?;
Ok(self.convert_ast(tree)?)
})
}
}
```
## 📐 Python固有機能の実装戦略
### 1. デフォルト引数の罠
```python
# Python: 定義時に一度だけ評価
def bad_default(lst=[]):
lst.append(1)
return lst
```
```nyash
// 生成されるNyash
box GeneratedModule {
init { _default_lst }
constructor() {
me._default_lst = new ArrayBox() // 定義時に一度だけ
}
bad_default(lst) {
if lst == null {
lst = me._default_lst // 同じインスタンスを再利用!
}
lst.append(1)
return lst
}
}
```
### 2. LEGB スコーピング
```python
# Local → Enclosing → Global → Builtin
global_var = 1
def outer():
enclosing_var = 2
def inner():
local_var = 3
```
実装:
- シンボルテーブルでスコープ管理
- クロージャはBox/Cellで実装
- global/nonlocalフラグを追跡
### 3. for/else, while/else
```python
for i in range(10):
if i == 5:
break
else:
print("No break")
```
```nyash
// 生成されるNyash
local _broken = false
local _iter = py_iter(range(10))
loop(true) {
local _next = py_next(_iter)
if _next.isStopIteration() { break }
local i = _next.value
if i == 5 {
_broken = true
break
}
}
if not _broken {
print("No break")
}
```
## 🔧 パスパイプライン
```
Parse Python AST
Symbol table analysis
Normalize to CorePy IR
Scope/closure analysis
Type metadata attachment
Lower to Nyash AST
Peephole optimization
Pretty-print + source map
```
## 📊 最適化戦略
### トランスパイル時の最適化
- 定数畳み込み
- ループ不変式の巻き上げ
- ビルトイン関数の直接呼び出し(シャドウイングなし時)
- 冗長な`py_truthy()`の除去
### Nyashコンパイラに委ねる最適化
- インライン展開
- レジスタ割り当て
- ループアンローリング
- ベクトル化
### 型情報の活用
```python
def add(x: int, y: int) -> int:
return x + y
```
→ 型ヒントがあれば`py_binop`ではなく直接整数演算
## 🐛 エラー処理とデバッグ
### ソースマップ
```json
{
"version": 3,
"sources": ["example.py"],
"mappings": "AAAA,IAAM,CAAC,GAAG...",
"names": ["add", "x", "y"]
}
```
### デバッグモード
```bash
nyash-transpile --debug example.py
# 出力:
# - CorePy IRダンプ
# - Nyashプレビュー元のPython行ヒント付き
# - 変換トレース
```
### エラーメッセージ
```
ERROR: Cannot compile function 'async_func' at line 10
Reason: async/await not supported in Phase 1
AST Node: AsyncFunctionDef
Suggestion: Use PyRuntimeBox or wait for Phase 3
```
## ⚡ パフォーマンス最適化
### ホットパス識別
```nyash
// プロファイル情報を活用
if compiler.isHotPath(func) {
// 積極的な最適化
result = compiler.optimizeAggressive(func)
} else {
// 標準的な変換
result = compiler.compile(func)
}
```
### JIT連携
```nyash
// 型特化コード生成
@jit_specialize(int, int)
def add(x, y):
return x + y
```
## 🔌 プラグインAPI
### 変換フック
```rust
trait TransformHook {
fn before_lower(&mut self, node: &CorePyNode);
fn after_lower(&mut self, node: &NyashNode);
fn on_function(&mut self, func: &FunctionDef);
}
```
### カスタムルール
```yaml
# custom_rules.yaml
rules:
- pattern: "dataclass"
action: "convert_to_nyash_box"
- pattern: "numpy.array"
action: "use_native_array"
```
## 📋 実装チェックリスト
### Phase 1必須
- [ ] 関数定義def
- [ ] 条件分岐if/elif/else
- [ ] ループfor/while with else
- [ ] 基本演算子
- [ ] 関数呼び出し
- [ ] return/break/continue
- [ ] LEGB スコーピング
- [ ] デフォルト引数
### Phase 2拡張
- [ ] 例外処理try/except/finally
- [ ] with文
- [ ] list/dict/set comprehensions
- [ ] lambda式
- [ ] *args, **kwargs
### Phase 3高度
- [ ] async/await
- [ ] yield/yield from
- [ ] デコレータ
- [ ] クラス定義(基本)
- [ ] import文

View File

@ -0,0 +1,285 @@
# Python Native Testing Plan
## 🎯 テスト戦略の全体像
「世界中のPythonコードがNyashのテストケース」という思想のもと、CPythonをオラクルとして使用する包括的なテスト戦略。
## 🧪 テストレベル
### 1. プラグインレベルテスト
#### PythonParserBox Tests
```rust
// plugins/nyash-python-parser-plugin/tests/parser_tests.rs
#[test]
fn test_parse_simple_function() {
let parser = create_parser_box();
let code = "def add(x, y): return x + y";
let ast = parser.parse(create_string_box(code));
assert_eq!(ast.get_type().to_string(), "Module");
let functions = ast.get_children();
assert_eq!(functions.length(), 1);
}
#[test]
fn test_parse_with_telemetry() {
let parser = create_parser_box();
parser.enable_telemetry(true);
let code = r#"
def supported(): return 1
async def unsupported(): await foo()
"#;
parser.parse(create_string_box(code));
let stats = parser.get_stats();
assert_eq!(stats.get("total_functions"), 2);
assert_eq!(stats.get("supported_functions"), 1);
}
```
#### PythonCompilerBox Tests
```rust
#[test]
fn test_compile_arithmetic() {
let compiler = create_compiler_box();
let ast = /* ... */;
let mir = compiler.compile(ast);
assert!(mir.is_ok());
// MIR検証
let module = mir.unwrap();
assert!(module.has_function("add"));
}
```
### 2. Differential Testing Framework
```nyash
// tests/differential/framework.nyash
box DifferentialTester {
init { oracle, implementation, results }
constructor() {
me.oracle = new PythonRuntimeBox() // CPython
me.implementation = new NativeEngine()
me.results = new ArrayBox()
}
test(code) {
local oracle_result, impl_result
// CPythonで実行
oracle_result = me.oracle.eval(code)
// Native実装で実行
impl_result = me.implementation.exec(code)
// 結果比較
return me.compare(oracle_result, impl_result)
}
compare(expected, actual) {
// 出力、戻り値、例外を比較
local match = new MapBox()
match.set("output", expected.output == actual.output)
match.set("return", expected.return == actual.return)
match.set("exception", expected.exception == actual.exception)
return match
}
}
```
### 3. テストケース生成
#### 基本テストスイート
```python
# tests/suites/phase1_tests.py
# 算術演算
def test_arithmetic():
assert add(2, 3) == 5
assert multiply(4, 5) == 20
assert divide(10, 2) == 5.0 # true division
# 制御フロー
def test_control_flow():
# if/else
result = conditional_logic(True, 10, 20)
assert result == 10
# for/else
found = search_with_else([1, 2, 3], 5)
assert found == "not found" # else節実行
# デフォルト引数の罠
def test_default_args():
list1 = append_to_default(1)
list2 = append_to_default(2)
assert list1 is list2 # 同じリスト!
```
#### Fuzzing with Hypothesis
```python
# tests/fuzzing/property_tests.py
from hypothesis import given, strategies as st
@given(st.integers(), st.integers())
def test_arithmetic_properties(x, y):
"""算術演算の性質をテスト"""
# Commutativity
assert add(x, y) == add(y, x)
# Identity
assert add(x, 0) == x
# Differential testing
native_result = native_add(x, y)
cpython_result = x + y
assert native_result == cpython_result
```
### 4. ベンチマークスイート
```nyash
// benchmarks/numeric_suite.nyash
box NumericBenchmark {
run() {
local suite = new BenchmarkSuite()
// Fibonacci
suite.add("fibonacci", {
"cpython": { return me.runCPython("fib.py") },
"native": { return me.runNative("fib.py") }
})
// Matrix multiplication
suite.add("matrix_mult", {
"cpython": { return me.runCPython("matrix.py") },
"native": { return me.runNative("matrix.py") }
})
return suite.execute()
}
}
// 実行結果例
// fibonacci:
// CPython: 1.234s
// Native: 0.123s (10.0x faster)
// matrix_mult:
// CPython: 5.678s
// Native: 0.456s (12.4x faster)
```
### 5. 回帰テスト
```yaml
# .github/workflows/python-native-tests.yml
name: Python Native Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Differential Tests
run: |
cargo test --package nyash-python-parser-plugin
cargo test --package nyash-python-compiler-plugin
- name: Coverage Report
run: |
./tools/measure_compilation_coverage.sh
# Expected output:
# Phase 1 compatible files: 15%
# Phase 2 functions: 40% compilable
# Phase 3 functions: 10% compilable
```
## 📊 メトリクス収集
### コンパイル成功率
```nyash
// 自動計測ツール
box CoverageAnalyzer {
analyze(directory) {
local parser = new PythonParserBox()
local compiler = new PythonCompilerBox()
local stats = new MapBox()
for file in directory.glob("*.py") {
local ast = parser.parseFile(file)
local result = compiler.compile(ast)
stats.increment("total")
if result.isOk() {
stats.increment("success")
} else {
stats.increment("not_compilable")
stats.record("unsupported", result.getError())
}
}
return stats
}
}
```
### パフォーマンス追跡
```sql
-- メトリクスDB
CREATE TABLE benchmark_results (
id SERIAL PRIMARY KEY,
test_name VARCHAR(255),
implementation VARCHAR(50), -- 'cpython' or 'native'
execution_time FLOAT,
memory_usage BIGINT,
timestamp TIMESTAMP,
git_hash VARCHAR(40)
);
```
## 🚨 失敗時の診断
### デバッグ情報収集
```nyash
// コンパイル失敗時の詳細情報
compiler.enableDebug(true)
result = compiler.compile(ast)
if result.isErr() {
local diag = compiler.getDiagnostics()
print("Failed at: " + diag.get("location"))
print("Reason: " + diag.get("reason"))
print("AST node: " + diag.get("node_type"))
print("Suggestion: " + diag.get("suggestion"))
}
```
### トレース機能
```
NYASH_PYTHON_TRACE=1 ./target/release/nyash test.py
[Parser] Parsing function 'compute' at line 5
[Compiler] Compiling BinOp: Add at line 7
[Compiler] Unsupported: YieldFrom at line 15
[Error] Cannot compile function 'generator_func' - yield not supported
```
## ✅ 受け入れ基準
### Phase 1完了
- [ ] 基本テストスイート100%パス
- [ ] Differential testing 100%一致
- [ ] Phase 1対応コードの100%コンパイル成功
- [ ] 10x性能向上数値計算ベンチマーク
### 各PR必須
- [ ] 新機能の単体テスト
- [ ] Differential testケース追加
- [ ] ベンチマーク結果(該当する場合)
- [ ] カバレッジ低下なし

View File

@ -0,0 +1,13 @@
# DECISIONS (Phase 10.7)
## 2025-08-30 — 二本立て運用(決定)
- 決定: 現行の実行系PyRuntimeBox, Plugin-Firstは維持し、トランスパイル系Python→Nyashは All-or-Nothing で併走。
- 代替案: トランスパイルの部分フォールバック実行時にPyRuntimeへ落とす
- 理由: 実行時の不一致/隠れ分岐を避ける。デプロイ時の挙動を単純に保つ。
- 影響: 生成Nyashの品質責任はトランスパイラ側。利用者は明示的に系を選択。
## 2025-08-30 — Parser/CompilerもプラグインBox決定
- 決定: PythonParserBox/PythonCompilerBox としてプラグイン化し、CLIから呼び出す。
- 代替案: コア組込み。
- 理由: Plugin-First原則、配布容易性、差し替え性、隔離テスト。
- 影響: plugins/ 以下に新規プラグインを追加。SDKの最小拡張が必要になる場合あり。

View File

@ -0,0 +1,39 @@
# CorePy IR 最小スキーマC2草案
目的: Phase 1 の End-to-End を最短で通すための暫定IR。将来は構造化・拡張with/try/comp/async等
## JSON 形式(暫定)
```json
{
"module": {
"functions": [
{
"name": "main", // 省略可(既定: "main"
"return_value": 0, // 省略可bodyと排他
"body": [ // 省略可return_valueと排他
{ "Return": { "value": 0 } }
]
}
]
}
}
```
ショートカット(デバッグ/ブリッジ用)
```json
{ "nyash_source": "static box Generated { main() { return 0 } }" }
```
## 変換規則(最小)
- module.functions[0] だけを見る(複数関数は将来対応)
- name があれば `static box Generated { <name>() { ... } }`
- return_value が数値/文字列なら `return <value>` を生成
- body があれば先頭の Return.value を探し、`return <value>` を生成
- 上記が無ければ `return 0`
## 将来(予約)
- statements: If/While/For/Assign/Expr などの節を追加
- expressions: BinOp/Call/Name/Constant などを構造化
- functions配列の複数対応、クロージャは別Box化の方針を検討
注意: All-or-Nothing 原則のもと、未対応ードはCompiler側で明示的にエラーにする現段階では未実装のため、return 0にフォールバックするが、C2終盤でStrict化する

View File

@ -0,0 +1,21 @@
# Phase 10.7 Workbench
このフォルダは Python Nativeトランスパイル路線, All-or-Nothing専用の作業台です。仕様・決定・スパイク・タスクをここに集約し、雑多にならないようにします。
構成
- TODO.md: 直近の作業キュー(小粒で管理)
- DECISIONS.md: 決定事項(理由/代替案/影響)
- SPIKES/: 検証スパイクの成果小さなPoCやプロト
- notes-YYYYMMDD.md: 打合せ/検討メモ(必要に応じて)
関連
- 計画: ../PLAN.txt
- 実装: ../implementation.md
- テスト: ../testing-plan.md
- 背景: ../README.md
運用ルール(最小)
- 一度に大きくしない5〜30分単位の成果で刻む
- 決定は DECISIONS.md に残す(誰でも後から辿れる)
- スパイクは SPIKES に隔離(本流に混ぜない)

View File

@ -0,0 +1,13 @@
# TODO (Phase 10.7 Workbench)
短期C1〜C3に向けた小粒タスク
- [ ] C1: Parser plugin 雛形スケルトンを作るpyo3, parse(code)->AstBox/to_json
- [ ] C1: Telemetry最小node種別カウント, 未対応ノード列挙)
- [ ] C2: CorePy IR最小スキーマJSONを commitwith/async系は予約
- [ ] C2: IR→Nyash ASTの最小変換def/if/for/while/return/算術/比較/呼出し)
- [ ] C3: CLI隠しフラグ prototyping--pyc/--pyc-native
- [ ] Docs: PLANとimplementationの差分同期週次
メモ
- All-or-Nothing原則未対応は即Err自動フォールバックなし
- 生成Nyashは現行AOT導線で配布可能Strict

View File

@ -0,0 +1,46 @@
# Phase 10.6a — Thread-Safety Audit (Checklist)
目的: NyashBox/ランタイムのスレッド安全性を棚卸しし、将来の並列化10.6b/c以降に備える。
## 方針
- 既定は単一スレッド実行VM/Interpreter。並列化は opt-in。
- 共有状態は `Arc<...>``RwLock/Mutex` により内的可変を確保。
- クロススレッド境界に出る型は `Send + Sync` を満たす(必要に応じてラッパで担保)。
## チェックリスト
- Box実装src/boxes/*
- [ ] 共有内部状態を持つ型は `Arc<RwLock<_>>` のようにラップされているか
- [ ] `to_string_box()` が重い処理やグローバル可変に依存しないか
- [ ] FFI/プラグイン橋渡し時に非同期イベント/コールバックを保持しないか(保持する場合は送受戦略を文書化)
- ランタイムsrc/runtime/*
- [ ] `NyashRuntime` のメンバは `Send + Sync` 要件を満たす(`Arc<...>`
- [ ] `GcHooks` 実装は `Send + Sync`CountingGc/NullGc はOK
- [ ] Scheduler 実装は `Send + Sync`SingleThreadSchedulerはOK
- VM/Interpreter
- [ ] MIR `Safepoint``runtime.scheduler.poll()` を呼ぶ(協調スケジューラの結合点)
- [ ] Grep: `rg -n "Safepoint" src` で配置確認
## Grep支援
```bash
rg -n "Arc<|Mutex<|RwLock<|Send|Sync" src/boxes src/runtime
```
## 既知の注意点
- Python/外部DLLとの橋渡しはGIL/PATH管理で単一スレッド優先AOT時はPATH/PYTHONHOME調整済
- BufferBox は共有化のために `Arc<RwLock<Vec<u8>>>` を採用済み。
## クイック監査(第一次)
- ArrayBox: `Arc<RwLock<Vec<Box<dyn NyashBox>>>>` → OK共有内的可変
- MapBox: `Arc<RwLock<HashMap<String, Box<dyn NyashBox>>>>` → OK
- BufferBox: `Arc<RwLock<Vec<u8>>>` → OK
- NyashRuntime: `box_registry: Arc<Mutex<_>>`, `box_declarations: Arc<RwLock<_>>`, `gc: Arc<dyn GcHooks>`, `scheduler: Option<Arc<dyn Scheduler>>` → OK
- Scheduler: `SingleThreadScheduler` 内部に `Arc<Mutex<VecDeque<...>>>` → OK
- GC Hooks: `NullGc/CountingGc``Send+Sync` 実装方針 → OK
未確認/注意:
- プラグインBoxPluginBoxV2の内部FFIハンドルはVM/EXE側で共有参照のみ実体はFFI側。クロススレッド呼出しは未サポート運用明記要
- 一部のBoxで外部資源ファイル/ネットを扱う場合、スレッド越境のI/O同期設計は別途Phase 10.6d+)。
## 次の一手(提案)
- マーカーTraits例: `ThreadSafeBox`)の導入は保留(破壊的)。現時点は監査+ドキュメントで運用。
- 並列スケジューラM:Nの実装は `feature` フラグで段階導入。

View File

@ -5,6 +5,7 @@ Whats added
- Queue + delayed tasks (spawn/spawn_after) and `poll()` to run work. - Queue + delayed tasks (spawn/spawn_after) and `poll()` to run work.
- VM calls `scheduler.poll()` at MIR `Safepoint` to integrate cooperative scheduling. - VM calls `scheduler.poll()` at MIR `Safepoint` to integrate cooperative scheduling.
- Poll budget via env `NYASH_SCHED_POLL_BUDGET` (default: 1) - Poll budget via env `NYASH_SCHED_POLL_BUDGET` (default: 1)
- Trace via `NYASH_SCHED_TRACE=1` (diagnostic)
How to use (dev) How to use (dev)
- Build runtime with default SingleThreadScheduler (already default via builder), or inject custom via: - Build runtime with default SingleThreadScheduler (already default via builder), or inject custom via:

View File

@ -0,0 +1,142 @@
# 埋め込みVMでの箱引数サポート
## 🎯 結論:完全にサポート可能
現在のMIR/VM/JIT/プラグインすべてで**箱は箱を引数にできる**仕組みが確立されており、埋め込みVMでも同じパターンで実装可能です。
## 📊 各層での箱引数の扱い
### 1. Nyashスクリプトレベル
```nyash
// 箱を引数に取るメソッド
box Processor {
process(inputBox, configBox) {
// inputBoxもconfigBoxも箱インスタンス
local data = inputBox.getData()
local settings = configBox.getSettings()
return me.transform(data, settings)
}
}
```
### 2. MIRレベル
```
; box1.process(box2, box3)
%4 = BoxCall %1.process(%2, %3)
```
### 3. 埋め込みVMでの実装
#### バイトコード表現
```c
// BoxCall命令: [op][dst][recv][method_id:2][argc][args...]
// 例: box1.process(box2, box3)
0x20 // OP_BOXCALL
0x04 // dst: %4
0x01 // recv: %1 (box1)
0x00 0x10 // method_id: 16 (process)
0x02 // argc: 2
0x02 // arg[0]: %2 (box2)
0x03 // arg[1]: %3 (box3)
```
#### 実行時処理
```c
// 埋め込みVMでの箱引数処理
int nyvm_execute_boxcall(NyashEmbeddedVM* vm, ...) {
// レシーバー取得
NyVMValue* recv = &vm->values[recv_idx];
// 引数をTLVエンコード
for (int i = 0; i < argc; i++) {
NyVMValue* arg = &vm->values[args[i]];
if (arg->type == NYVM_TYPE_HANDLE) {
// 箱引数はハンドルとしてエンコード
uint32_t type_id = get_type_id_from_handle(arg->value.h);
uint32_t inst_id = get_instance_id_from_handle(arg->value.h);
encode_handle(&tlv, type_id, inst_id);
} else {
// プリミティブ値
encode_primitive(&tlv, arg);
}
}
// C ABIプラグイン呼び出し
return nyash_plugin_invoke(...);
}
```
## 🔄 ハンドル管理の詳細
### 1. ハンドルレジストリ
```c
// グローバルハンドルテーブル
typedef struct {
uint32_t type_id; // Box型ID
uint32_t instance_id; // インスタンスID
uint8_t flags; // GC/所有権フラグ
} HandleEntry;
static HandleEntry g_handles[MAX_HANDLES];
// 新しい箱インスタンスの登録
uint64_t register_box_handle(uint32_t type_id, uint32_t instance_id) {
uint64_t handle = allocate_handle();
g_handles[handle] = (HandleEntry){
.type_id = type_id,
.instance_id = instance_id,
.flags = HANDLE_OWNED
};
return handle;
}
```
### 2. プラグイン間の箱共有
```c
// プラグインAが箱を返す
int plugin_a_create_box(uint8_t* result, size_t* result_len) {
// 新しい箱を作成
uint32_t type_id = 100; // CustomBox
uint32_t inst_id = create_instance();
// TLVエンコード
encode_handle(result, type_id, inst_id);
return 0;
}
// プラグインBが箱を受け取る
int plugin_b_process(const uint8_t* args, size_t args_len) {
// TLVデコード
uint32_t type_id, inst_id;
decode_handle(args, &type_id, &inst_id);
// 箱を使用
process_box(type_id, inst_id);
return 0;
}
```
## 💡 重要なポイント
### 1. 型安全性
- ハンドルには型情報type_idが含まれる
- 実行時の型チェックが可能
### 2. 所有権管理
- ハンドルは参照カウント or GC管理
- プラグイン間で安全に共有
### 3. 相互運用性
- ネイティブBox ↔ スクリプトBox間で透過的
- 同じハンドル機構を使用
## 🎯 結論
埋め込みVMでも
1. **箱は箱を引数に取れる**(ハンドル経由)
2. **型情報を保持**type_id
3. **プラグイン間で共有可能**instance_id
4. **C ABIと完全互換**TLVエンコード
これにより、Nyashスクリプトで書いた高度なBoxコンポジションも、C ABIプラグインとして動作します

View File

@ -0,0 +1,106 @@
# 🚨 重大な設計問題スクリプトプラグインとMIR/EXEの非互換性
## 問題の本質
**MIR/JIT/AOTEXEは、C ABIの関数しか呼び出せない。**
```
現実のフロー:
MIR → JIT → C関数呼び出し固定アドレス→ ネイティブコード実行
MIR → AOT → EXE内のC関数静的リンク→ ネイティブコード実行
不可能なフロー:
MIR → ??? → Nyashスクリプト実行インタープリター必要
```
## なぜスクリプトプラグインは動作しないのか
### 1. インタープリター依存
```nyash
// スクリプトプラグイン
export box CustomMath {
sin(x) { return me._math.sin(x) }
}
```
**実行にはNyash VMが必要** → EXEに埋め込めない
### 2. 動的型システム
- C ABI: 静的型i32, i64, double等
- Nyashスクリプト: 動的型Box
- 型変換にはランタイムが必要
### 3. メモリ管理
- C ABI: 手動管理またはシンプルなGC
- Nyashスクリプト: Arc<Mutex<dyn NyashBox>>
- GC/参照カウント管理にランタイムが必要
## 実例なぜFileBoxはC ABIなのか
```rust
// FileBoxプラグインネイティブ
#[no_mangle]
pub extern "C" fn nyash_plugin_invoke(...) -> i32 {
// 直接システムコール可能
// VMなしで動作
// EXEに静的リンク可能
}
```
対して、Nyashスクリプトは
- VM必須
- 動的評価
- EXEに埋め込み不可
## 結論Phase 12の方向転換が必要
### ❌ 不可能なこと
- スクリプトプラグインをJIT/AOTから直接呼び出し
- スクリプトプラグインをEXEに埋め込み
- ネイティブプラグインと完全に透過的な利用
### ✅ 可能なこと
1. **インタープリターモード限定**
- スクリプトプラグインはインタープリター実行時のみ
- 開発/プロトタイピング用途
2. **トランスパイル方式**
- Nyashスクリプト → C/Rust → ネイティブプラグイン
- ビルドステップが必要
3. **ハイブリッド実行**
- 開発時: スクリプトプラグイン(高速イテレーション)
- 本番時: ネイティブプラグイン(高性能)
## 修正された価値提案
### 開発フローの改善
```
1. アイデア → Nyashスクリプトでプロトタイプ
2. 動作確認 → インタープリターでテスト
3. 性能要求 → Rust/Cで再実装
4. 配布 → ネイティブプラグインとして
```
### 制限事項の明確化
- **JIT/AOT**: ネイティブプラグインのみ
- **インタープリター**: スクリプトプラグインも可
- **EXE生成**: ネイティブプラグインのみ含む
## 推奨アクション
1. **Phase 12の再定義**
- 「開発支援ツール」として位置づけ
- JIT/AOT統合は諦める
2. **ドキュメント修正**
- 制限事項を明確に記載
- 誤解を招く「透過的利用」を削除
3. **代替案の検討**
- Nyash→Rustトランスパイラー
- プラグインテンプレート生成ツール
---
**重要な教訓C ABIの制約は、システムプログラミングの本質的な制約である。**

View File

@ -0,0 +1,207 @@
# 埋め込みVMでのBox処理設計
## 🎯 核心MIRレベルでのBox処理を再現
### 現在のMIR/VMでのBox処理フロー
```
1. MIR: BoxCall/PluginInvoke命令
2. VM: ValueId → VMValue変換
3. VMValue → Box<dyn NyashBox> or TLVエンコード
4. メソッド実行
5. 結果をVMValueに戻す
```
## 📊 埋め込みVMの設計
### 1. 軽量VMValue定義
```c
// 埋め込みVM用の値表現
typedef enum {
NYVM_TYPE_INT,
NYVM_TYPE_FLOAT,
NYVM_TYPE_BOOL,
NYVM_TYPE_STRING,
NYVM_TYPE_HANDLE, // Box参照ハンドル
NYVM_TYPE_VOID
} NyVMType;
typedef struct {
NyVMType type;
union {
int64_t i;
double f;
uint8_t b;
struct { const char* data; size_t len; } s;
uint64_t h; // ハンドルBox参照
} value;
} NyVMValue;
```
### 2. MIRバイトコード形式
```c
// BoxCall命令のエンコード
enum {
OP_BOXCALL = 0x20,
OP_PLUGIN_INVOKE = 0x21,
// ...
};
// BoxCall: [op:1] [dst:1] [box_val:1] [method_id:2] [argc:1] [args...]
// 例: BoxCall %2 = %1.toString()
// → 0x20 0x02 0x01 0x00 0x00 0x00
```
### 3. 埋め込みVMでのBoxCall実行
```c
int nyvm_execute_boxcall(
NyashEmbeddedVM* vm,
uint8_t dst,
uint8_t box_val,
uint16_t method_id,
uint8_t argc,
uint8_t* args
) {
// 1. レシーバー取得
NyVMValue* recv = &vm->values[box_val];
// 2. プリミティブ型の場合
if (recv->type != NYVM_TYPE_HANDLE) {
// プリミティブ→TLV変換
uint8_t tlv_buf[256];
size_t tlv_len = encode_primitive_to_tlv(recv, tlv_buf);
// 組み込み実装を呼び出し
return call_builtin_method(recv->type, method_id, tlv_buf, tlv_len);
}
// 3. Boxハンドルの場合
uint64_t handle = recv->value.h;
// 引数をTLVエンコード
uint8_t args_tlv[1024];
size_t args_len = 0;
for (int i = 0; i < argc; i++) {
NyVMValue* arg = &vm->values[args[i]];
args_len += encode_value_to_tlv(arg, &args_tlv[args_len]);
}
// 4. プラグイン呼び出しC ABI
uint8_t result[4096];
size_t result_len = sizeof(result);
int rc = nyash_plugin_invoke(
get_type_id_from_handle(handle),
method_id,
get_instance_id_from_handle(handle),
args_tlv, args_len,
result, &result_len
);
// 5. 結果をVMValueに変換
if (rc == 0) {
decode_tlv_to_value(result, result_len, &vm->values[dst]);
}
return rc;
}
```
## 🔄 Box引数の処理
### 現在のVMRust
```rust
// VMValue → Box<dyn NyashBox>変換
let val = self.get_value(*arg)?;
Ok(val.to_nyash_box())
```
### 埋め込みVMC
```c
// NyVMValue → TLVエンコード
switch (value->type) {
case NYVM_TYPE_INT:
encode_i64(tlv, value->value.i);
break;
case NYVM_TYPE_HANDLE:
encode_handle(tlv,
get_type_id_from_handle(value->value.h),
get_instance_id_from_handle(value->value.h)
);
break;
// ...
}
```
## 💡 実装のポイント
### 1. ハンドル管理
```c
// グローバルハンドルテーブル
typedef struct {
uint32_t type_id;
uint32_t instance_id;
void* native_ptr; // 実際のBoxポインタ必要な場合
} HandleEntry;
static HandleEntry g_handles[MAX_HANDLES];
static uint64_t g_next_handle = 1;
uint64_t register_handle(uint32_t type_id, uint32_t instance_id) {
uint64_t h = g_next_handle++;
g_handles[h].type_id = type_id;
g_handles[h].instance_id = instance_id;
return h;
}
```
### 2. 組み込みメソッド
```c
// 頻出メソッドは埋め込みVMに直接実装
int call_builtin_method(NyVMType type, uint16_t method_id, ...) {
switch (type) {
case NYVM_TYPE_INT:
if (method_id == 0) { // toString
// 整数→文字列変換
}
break;
// ...
}
}
```
### 3. プラグインとの統合
```c
// 生成されるCコード
extern "C" int32_t nyplug_mybox_invoke(...) {
// MIRバイトコード実行
NyashEmbeddedVM vm;
nyvm_init(&vm, BYTECODE, sizeof(BYTECODE));
// 引数をVMスタックに設定
nyvm_decode_args(&vm, args, args_len);
// メソッド実行
nyvm_execute_method(&vm, method_id);
// 結果をTLVエンコード
return nyvm_encode_result(&vm, result, result_len);
}
```
## 🎯 結論
埋め込みVMは
1. **MIRのBoxCall/PluginInvoke命令を忠実に実装**
2. **TLVエンコード/デコードでC ABIと通信**
3. **ハンドルでBox参照を管理**
4. **頻出処理は最適化実装**
これにより、Nyashスクリプトで書いたプラグインも、ネイティブプラグインと同じC ABIで動作します

View File

@ -0,0 +1,195 @@
# 埋め込みVM実装ロードマップ
## 🎯 目標スクリプトプラグインのC ABI化
**Nyashスクリプト → C ABIプラグイン変換の完全自動化**
## 📊 技術スタック
```
[Nyashスクリプト]
↓ パース・型チェック
[MIR (中間表現)]
↓ 最適化・定数畳み込み
[MIRバイトコード]
↓ 埋め込み
[Cソースコード] ← nyash-to-c ツール
↓ コンパイル (cc/clang/gcc)
[.so/.dll/.a] ← 通常のプラグイン!
```
## 🚀 実装フェーズ
### Phase 12.1: 最小埋め込みVM2-3週間
#### 1. MIRバイトコード設計
```rust
// mir_bytecode.rs
pub enum CompactInstruction {
// 1バイト命令頻出
LoadLocal(u8), // 0x00-0x7F
StoreLocal(u8), // 0x80-0xFF
// 2バイト命令
LoadConst(u8), // 0x01 XX
Call(u8), // 0x02 XX
// 可変長
LoadString, // 0x03 [len:u16] [data]
Jump, // 0x04 [offset:i16]
}
```
#### 2. 埋め込みVMコア
```c
// nyash_embedded_vm.h
typedef struct {
const uint8_t* bytecode;
size_t bytecode_len;
// 実行時状態(最小)
void* stack[256];
int sp;
void* locals[16];
} NyashEmbeddedVM;
int32_t nyash_embedded_execute(
const uint8_t* bytecode,
size_t bytecode_len,
uint32_t method_id,
const uint8_t* args,
size_t args_len,
uint8_t* result,
size_t* result_len
);
```
### Phase 12.2: Nyash→Cトランスパイラー3-4週間
#### 1. 基本変換
```bash
$ nyash-to-c math_plugin.ny -o math_plugin.c
Generating C plugin from Nyash script...
- Parsing... OK
- Type checking... OK
- MIR generation... OK
- Bytecode emission... OK
- C code generation... OK
Output: math_plugin.c (2.3KB)
```
#### 2. 生成コード例
```c
// Generated from: math_plugin.ny
#include <nyash_embedded.h>
// MIRバイトコード最適化済み
static const uint8_t BYTECODE[] = {
0x01, 0x00, // Version 1.0
0x01, 0x00, // 1 function
// Function: cached_sin
0x00, 0x08, // Function header
0x80, 0x00, // StoreLocal 0 (x)
0x02, 0x10, // Call sin
0x90, // Return
};
// プラグインエントリポイント
extern "C" int32_t nyplug_math_plugin_invoke(
uint32_t type_id,
uint32_t method_id,
uint32_t instance_id,
const uint8_t* args,
size_t args_len,
uint8_t* result,
size_t* result_len
) {
return nyash_embedded_execute(
BYTECODE, sizeof(BYTECODE),
method_id,
args, args_len,
result, result_len
);
}
```
### Phase 12.3: 最適化とツールチェーン4-6週間
#### 1. ビルドシステム統合
```toml
# nyash.toml
[[plugins]]
name = "math_plugin"
source = "plugins/math_plugin.ny" # Nyashソース
type = "script" # 自動的にC変換
[[plugins]]
name = "file_plugin"
source = "plugins/file_plugin/Cargo.toml"
type = "native" # 従来のRustプラグイン
```
#### 2. 自動ビルドパイプライン
```bash
$ nyash build --plugins
Building plugins...
[1/2] math_plugin (script)
- Transpiling to C... OK
- Compiling... OK
- Output: target/plugins/libmath_plugin.so
[2/2] file_plugin (native)
- Building with cargo... OK
- Output: target/plugins/libfile_plugin.so
Done!
```
## 📈 パフォーマンス目標
| 操作 | ネイティブ | 埋め込みVM | 目標比率 |
|------|-----------|------------|----------|
| 単純計算 | 10ns | 50ns | 5x |
| メソッド呼び出し | 20ns | 100ns | 5x |
| 文字列操作 | 100ns | 200ns | 2x |
| I/O操作 | 10μs | 10.1μs | 1.01x |
## 🔧 開発ツール
### 1. デバッガ
```bash
$ nyash-debug math_plugin.ny --method cached_sin --args "[3.14]"
Executing cached_sin(3.14)...
[PC:0000] LoadLocal 0 ; x = 3.14
[PC:0002] Call sin ; sin(3.14)
[PC:0004] Return ; 0.0015926...
Result: 0.0015926
```
### 2. プロファイラ
```bash
$ nyash-profile math_plugin.so
Method statistics:
- cached_sin: 1000 calls, avg 120ns
- cached_cos: 500 calls, avg 115ns
Bottlenecks: None detected
```
## 🎉 最終形
```bash
# 開発者の体験
$ cat my_plugin.ny
export box MyPlugin {
init { cache = new MapBox() }
process(x) { return x * 2 }
}
$ nyash build my_plugin.ny
✓ Generated: my_plugin.so
$ nyash run --plugin my_plugin.so test.ny
✓ Plugin loaded (C ABI)
✓ Result: 42
```
**Nyashで書いて、どこでも動く**

View File

@ -0,0 +1,115 @@
# Phase 12: Nyashスクリプトプラグインシステム革命
## 🚀 概要
Nyashスクリプト自体でプラグインを作成できる革命的発見ビルド不要で、既存のネイティブプラグインを組み合わせて新機能を作成可能。
## 💡 発見の経緯
include/export仕様の検討中に、以下の重要な気づきが
```nyash
# custom_math_plugin.ny
export box CustomMathPlugin {
init {
_math = new MathBox() # 既存プラグイン活用
_cache = new MapBox() # 結果キャッシュ
}
// カスタム拡張
cached_sin(x) {
local key = x.toString()
if me._cache.has(key) {
return me._cache.get(key)
}
local result = me._math.sin(x)
me._cache.set(key, result)
return result
}
}
```
これにより、Rust/C++のビルドなしでプラグイン開発が可能に!
## 🎯 統一Box ABI設計
### 基本インターフェース
```rust
// Rust側の統一インターフェース
trait BoxInterface {
fn invoke(&self, method_id: u32, args: NyashValue) -> NyashValue;
fn get_methods(&self) -> Vec<MethodInfo>;
fn init(&mut self, ctx: Context);
fn drop(&mut self);
}
```
### Nyashスクリプトプラグインの要件
```nyash
export box MyPlugin {
// 必須:初期化
init { ... }
// 推奨FFI互換インターフェース
invoke(method_id, args) {
// method_idに基づいてディスパッチ
}
// オプション:メソッド情報
get_methods() {
return [
{ name: "method1", id: 1 },
{ name: "method2", id: 2 }
]
}
}
```
## 📊 エコシステムへの影響
### 開発の民主化
- **参入障壁の劇的低下**: Rust/C++環境不要
- **即座の開発**: ビルド待ち時間ゼロ
- **コミュニティ拡大**: より多くの開発者が参加可能
### 新しい開発パターン
1. **プラグインの合成**: 複数のネイティブプラグインを組み合わせ
2. **ラピッドプロトタイピング**: アイデアを即座に実装
3. **ホットリロード**: 実行中の更新が可能
## 🛣️ 実装ロードマップ
### Phase 12.1: 基盤構築
- [ ] Box ABI仕様の最終決定
- [ ] export box構文のパーサー実装
- [ ] 基本的なPluginRegistry実装
### Phase 12.2: 統一インターフェース
- [ ] FFIプラグインのBoxInterface対応
- [ ] NyashスクリプトのBoxInterface実装
- [ ] 相互運用テスト
### Phase 12.3: 動的機能
- [ ] 動的ロード/アンロード機能
- [ ] ホットリロード対応
- [ ] プラグイン間依存関係管理
### Phase 12.4: セキュリティと最適化
- [ ] サンドボックス実装
- [ ] ケイパビリティベース権限
- [ ] パフォーマンス最適化
## 📚 関連ドキュメント
- [Gemini先生の分析](./gemini-analysis-script-plugins.md)
- [Codex先生の技術提案](./codex-technical-proposal.md)
- [統合分析まとめ](./synthesis-script-plugin-revolution.md)
## 🎯 次のアクション
1. Box ABI仕様書の作成
2. export box構文の実装開始
3. 既存FFIプラグイン1つを統一インターフェースに移行
---
*Everything is Box - そしてプラグインもBoxになる*

View File

@ -0,0 +1,147 @@
# Phase 12改訂版Nyashスクリプトプラグイン - 開発支援ツールとして
## 🎯 現実的な位置づけ
**スクリプトプラグインは、JIT/AOT/EXEとは独立した開発支援機能として実装する。**
## 📊 制約と可能性の整理
### ❌ できないこと(技術的制約)
- MIR/VM/JIT/AOTからのスクリプトプラグイン呼び出し
- スクリプトプラグインのEXE埋め込み
- ネイティブプラグインとの完全な相互運用性
### ✅ できること(現実的な価値)
- インタープリターモードでの高速プロトタイピング
- 既存プラグインの組み合わせによる新機能開発
- ビルド不要な機能拡張(開発時のみ)
## 🔄 修正された開発フロー
```
┌─────────────────┐
│ アイデア/要件 │
└────────┬────────┘
┌─────────────────┐
│ Nyashスクリプト │ ← 高速イテレーション
│ プラグイン作成 │ ビルド不要
└────────┬────────┘
┌─────────────────┐
│ インタープリター│
│ でテスト/検証 │
└────────┬────────┘
性能要求?
↙ ↘
No Yes
↓ ↓
そのまま Rust/Cで
使用 再実装
ネイティブ
プラグイン
JIT/AOT/EXE
```
## 📝 実装方針
### 1. インタープリター専用機能として実装
```nyash
// script_plugin.ny
export box CustomLogic {
init {
_math = new MathBox() // ネイティブプラグイン利用
_cache = new MapBox()
}
process(data) {
// 複雑なビジネスロジック
// インタープリターでのみ実行
}
}
```
### 2. 明確な使用場面の区別
```nyash
// development.ny開発時
local plugin = include("custom_logic.ny") // ✅ OK
// production.ny本番時
local plugin = new CustomLogicBox() // ネイティブ版を使用
```
### 3. トランスパイル支援ツール(将来)
```bash
# Nyashスクリプト → Rustテンプレート生成
nyash-to-rust custom_logic.ny > custom_logic_plugin/src/lib.rs
```
## 🎯 価値提案(修正版)
### 開発者にとっての価値
1. **探索的プログラミング** - アイデアを即座に試せる
2. **プロトタイピング** - ビルドなしで機能検証
3. **学習曲線の緩和** - Rust/C知識不要で拡張開発
### エコシステムへの貢献
1. **アイデアの具現化** - スクリプトで検証→ネイティブで実装
2. **コミュニティ参加** - より多くの開発者が貢献可能
3. **ベストプラクティス** - 成功パターンの蓄積
## 🚀 実装計画(現実的版)
### Phase 12.1: 基盤構築2週間
- [ ] export box構文インタープリター専用
- [ ] include()関数の拡張
- [ ] 基本的なプラグインレジストリ
### Phase 12.2: 開発体験向上3週間
- [ ] ホットリロード(開発モード)
- [ ] エラーメッセージ改善
- [ ] デバッグ支援機能
### Phase 12.3: 移行支援4週間
- [ ] パフォーマンス分析ツール
- [ ] Rust変換テンプレート
- [ ] 移行ガイドライン
## 📚 ドキュメント戦略
### 明確な制約の説明
```markdown
# Nyashスクリプトプラグイン
⚠️ **重要な制約**
- インタープリターモードでのみ動作
- JIT/AOT/EXEでは使用不可
- 本番環境ではネイティブプラグインへの移行推奨
```
### 使用例の充実
- プロトタイピング例
- ネイティブ移行例
- パフォーマンス比較
## 🎉 期待される成果(現実的版)
### 短期3ヶ月
- 開発効率の向上プロトタイピング時間80%削減)
- 新規開発者の参入Rust不要
- アイデア検証の高速化
### 中期1年
- 成功パターンの確立
- ネイティブプラグインの品質向上
- コミュニティ主導の機能提案増加
## 結論
**スクリプトプラグインは、C ABIの制約を認識した上で、開発支援ツールとして大きな価値を提供できる。**
「Everything is Box」の哲学は、実行時の制約はあれど、開発時の自由度として実現される。

View File

@ -0,0 +1,164 @@
# 解決策埋め込みVMによるスクリプトプラグイン実現
## 💡 発想の転換
**制約は「リンク時にC ABIが必要」だけ。つまり、C ABI関数の中でVMを動かせばいい**
## 🎯 アーキテクチャ
```c
// C ABI関数静的リンク可能
extern "C" int32_t nyplug_custom_math_invoke(
uint32_t method_id,
const uint8_t* args,
size_t args_len,
uint8_t* result,
size_t* result_len
) {
// 埋め込みVM起動
static NyashVM* embedded_vm = NULL;
if (!embedded_vm) {
embedded_vm = nyash_vm_create_minimal();
nyash_vm_load_script(embedded_vm, EMBEDDED_SCRIPT);
}
// スクリプト実行
return nyash_vm_invoke(embedded_vm, method_id, args, args_len, result, result_len);
}
// スクリプトは文字列リテラルとして埋め込み
static const char* EMBEDDED_SCRIPT = R"(
export box CustomMath {
cached_sin(x) {
// Nyashコード
}
}
)";
```
## 🔄 実現方法
### 1. Nyash→C トランスパイラー
```bash
# Nyashスクリプト → C関数
nyash-to-c custom_math.ny > custom_math_plugin.c
# 生成されるC
// custom_math_plugin.c
#include "nyash_embedded_vm.h"
static const char* SCRIPT = "..."; // Nyashコード埋め込み
extern "C" int32_t nyplug_custom_math_invoke(...) {
return nyash_embedded_invoke(SCRIPT, method_id, ...);
}
```
### 2. 最小VM実装
```rust
// crates/nyash-embedded-vm
pub struct EmbeddedVM {
// 最小限の実行環境
values: Vec<VMValue>,
// スクリプトはプリコンパイル済みMIR
mir: MirModule,
}
#[no_mangle]
pub extern "C" fn nyash_embedded_invoke(
script: *const c_char,
method_id: u32,
// ... TLV args/result
) -> i32 {
// MIR実行インタープリター
}
```
## 📊 利点と制約
### ✅ 可能になること
- **スクリプトプラグインがEXEに埋め込み可能**
- **JIT/AOTから呼び出し可能**C ABI経由
- **既存のプラグインシステムと完全互換**
### ⚠️ 制約
- **パフォーマンス**: 埋め込みVMのオーバーヘッド
- **サイズ**: 最小VMランタイムが必要~500KB?
- **機能制限**: フルVMの一部機能のみ
## 🚀 段階的実装
### Phase 1: 最小埋め込みVM
```rust
// 必要最小限の機能
- MIR実行(インタープリター)
- 基本型(Integer, String, Bool
- メソッド呼び出し
- TLVエンコード/デコード
```
### Phase 2: Nyash→Cトランスパイラー
```nyash
// input: custom_math.ny
export box CustomMath {
sin(x) { ... }
}
// output: custom_math_plugin.c
extern "C" int32_t nyplug_custom_math_invoke(...) {
static const uint8_t MIR_BYTECODE[] = { ... };
return nyash_embedded_execute(MIR_BYTECODE, ...);
}
```
### Phase 3: 最適化
- MIRプリコンパイル
- 頻出パスのネイティブ化
- 選択的JITコンパイル
## 💡 実装例
```c
// 生成されたプラグイン
#include <nyash_embedded.h>
// MIRバイトコード事前コンパイル
static const uint8_t CUSTOM_MATH_MIR[] = {
0x01, 0x00, // version
0x10, 0x00, // function count
// ... MIR instructions
};
extern "C" int32_t nyplug_custom_math_abi_version() {
return 1;
}
extern "C" int32_t nyplug_custom_math_invoke(
uint32_t method_id,
const uint8_t* args,
size_t args_len,
uint8_t* result,
size_t* result_len
) {
// 埋め込みVM実行
return nyash_mir_execute(
CUSTOM_MATH_MIR,
sizeof(CUSTOM_MATH_MIR),
method_id,
args, args_len,
result, result_len
);
}
```
## 🎯 結論
**「リンク時にC ABI」という制約は、埋め込みVMで解決可能**
- Nyashスクリプト → MIR → Cコード → ネイティブプラグイン
- 開発の容易さNyashと配布の利便性C ABIを両立
- 既存のプラグインエコシステムと完全互換
これで「Everything is Box」が真に実現する

View File

@ -0,0 +1,175 @@
# C ABIとの整合性Phase 12スクリプトプラグインシステム
## 🚨 重要な発見
Phase 10.1で既に**C ABI v0**が定義されており、これとPhase 12の提案を整合させる必要があります。
## 📊 現状のC ABIPhase 10.1
### 既存のBID-FFIプラグイン用
```c
// 現在のプラグインFFITLVベース
extern "C" fn nyash_plugin_invoke(
type_id: u32,
method_id: u32,
instance_id: u32,
args: *const u8, // TLVエンコード
args_len: usize,
result: *mut u8, // TLVエンコード
result_len: *mut usize,
) -> i32
```
### 新しいNyRT C ABI v0
```c
// コア関数
int32_t nyrt_abi_version(void);
NyBox nyrt_box_new(uint64_t typeid, uint64_t size);
void nyrt_box_free(NyBox b);
// プラグイン関数Array
int32_t nyplug_array_abi_version(void);
NyBox nyplug_array_new(void);
int32_t nyplug_array_get(NyBox arr, uint64_t i, NyBox* out);
```
## 🎯 Phase 12の修正案
### 問題点
- Gemini/Codexの提案した`BoxInterface`トレイトは**Rust専用**
- C ABIとの相互運用性が考慮されていない
- TLVエンコーディングとの整合性が不明
### 解決策C ABIラッパー戦略
```rust
// ❌ 元の提案Rust専用
trait BoxInterface {
fn invoke(&self, method_id: u32, args: NyashValue) -> NyashValue;
}
// ✅ 修正案C ABI互換
pub struct ScriptPluginWrapper {
// Nyashスクリプトインスタンス
script_box: NyashValue,
// C ABI互換性のためのFFI関数
ffi_invoke: extern "C" fn(
type_id: u32,
method_id: u32,
instance_id: u32,
args: *const u8,
args_len: usize,
result: *mut u8,
result_len: *mut usize,
) -> i32,
}
impl ScriptPluginWrapper {
// 既存のBID-FFIと完全互換
pub extern "C" fn invoke_ffi(
&self,
type_id: u32,
method_id: u32,
instance_id: u32,
args: *const u8,
args_len: usize,
result: *mut u8,
result_len: *mut usize,
) -> i32 {
// 1. TLVデコード
let nyash_args = decode_tlv(args, args_len);
// 2. Nyashスクリプト呼び出し
let result_value = self.script_box.invoke(method_id, nyash_args);
// 3. TLVエンコード
encode_tlv(result_value, result, result_len)
}
}
```
## 🔄 統合アーキテクチャ
```
[JIT/AOT] ---> C ABI (nyrt_*/nyplug_*) --+--> [ネイティブプラグイン]
|
+--> [ScriptPluginWrapper] --> [Nyashスクリプト]
```
### 利点
1. **完全な後方互換性** - 既存のプラグインがそのまま動作
2. **統一されたFFI** - JIT/AOT/プラグインすべて同じC ABI
3. **透過的な利用** - 呼び出し側はネイティブ/スクリプトを区別しない
## 📝 実装修正案
### Phase 12.1(修正版)
1. **ScriptPluginWrapperの実装**
- BID-FFI互換のC関数エクスポート
- TLVエンコード/デコード処理
- Nyashスクリプトへの橋渡し
2. **プラグインレジストリ拡張**
```rust
pub struct PluginRegistry {
// 既存のネイティブプラグインC ABI
native_plugins: HashMap<u32, PluginHandle>,
// スクリプトプラグインC ABIラッパー経由
script_plugins: HashMap<u32, ScriptPluginWrapper>,
}
```
3. **export box構文の実装**
```nyash
export box CustomMathPlugin {
// BID-FFI互換のためのメタ情報
__type_id__ = 100 // 動的割り当てor設定ファイル
__methods__ = {
"cached_sin": 1,
"cached_cos": 2
}
// 通常のNyashコード
init { ... }
cached_sin(x) { ... }
}
```
## 🚀 移行パス
### 段階1既存プラグインの動作確認
- FileBox、NetBox等がC ABI経由で正常動作
- パフォーマンステスト
### 段階2簡単なスクリプトプラグイン
- MathBoxの一部機能をNyashで再実装
- C ABIラッパー経由での動作確認
### 段階3高度な統合
- ネイティブとスクリプトの混在
- 動的ロード/アンロード
## ⚡ パフォーマンス影響
```
呼び出しチェーン:
1. JIT → C ABI関数呼び出し既存
2. C ABI → ScriptPluginWrapper追加
3. Wrapper → TLVデコード追加
4. Wrapper → Nyashスクリプト実行追加
5. Wrapper → TLVエンコード追加
予想オーバーヘッド: 100-500ns/呼び出し
```
## 🎯 結論
Phase 12のスクリプトプラグインシステムは、**C ABIを尊重**しつつ実装可能です。
- BoxInterfaceトレイトは内部実装詳細に留める
- 外部インターフェースは既存のC ABIBID-FFIを維持
- ScriptPluginWrapperがブリッジとして機能
これにより、**「Everything is Plugin」**の哲学を保ちながら、スクリプトプラグインを実現できます。

View File

@ -0,0 +1,334 @@
# Codex先生の技術提案Nyashスクリプトプラグインシステム実装
## エグゼクティブサマリー
Nyashスクリプトをプラグインとして使用する提案は、技術的に極めて実現可能であり、Nyashエコシステムに革命的な価値をもたらします。「Everything is Box」哲学の究極の実現として、実装言語に依存しない統一インターフェースを提供することで、開発の民主化とエコシステムの爆発的成長が期待できます。
## 技術アーキテクチャ提案
### 1. 統一Box ABIの詳細設計
```rust
// コアインターフェース定義
pub trait UnifiedBoxInterface: Send + Sync {
// 基本メソッド
fn invoke(&self, ctx: &mut Context, method_id: u32, args: &[NyashValue]) -> Result<NyashValue, BoxError>;
fn get_metadata(&self) -> BoxMetadata;
// ライフサイクル管理
fn initialize(&mut self, config: &BoxConfig) -> Result<(), BoxError>;
fn shutdown(&mut self) -> Result<(), BoxError>;
// 動的機能(オプション)
fn hot_reload(&mut self, new_code: &str) -> Result<(), BoxError> {
Err(BoxError::NotSupported)
}
}
// メタデータ構造
pub struct BoxMetadata {
pub name: String,
pub version: String,
pub methods: Vec<MethodInfo>,
pub capabilities: Vec<Capability>,
pub dependencies: Vec<Dependency>,
}
```
### 2. プラグインレジストリアーキテクチャ
```rust
pub struct PluginRegistry {
// ネイティブプラグイン
native_plugins: HashMap<u32, Arc<dyn UnifiedBoxInterface>>,
// スクリプトプラグイン
script_plugins: HashMap<u32, ScriptPlugin>,
// 動的ID管理
id_allocator: IdAllocator,
// 依存関係グラフ
dependency_graph: DependencyGraph,
}
impl PluginRegistry {
pub fn register_native(&mut self, plugin: impl UnifiedBoxInterface + 'static) -> u32 {
let id = self.id_allocator.allocate();
self.native_plugins.insert(id, Arc::new(plugin));
id
}
pub fn register_script(&mut self, source: &str) -> Result<u32, RegistryError> {
let plugin = ScriptPlugin::compile(source)?;
let id = self.id_allocator.allocate();
self.script_plugins.insert(id, plugin);
Ok(id)
}
}
```
### 3. スクリプトプラグインラッパー実装
```rust
pub struct ScriptPlugin {
vm: NyashVM,
box_instance: NyashValue,
method_cache: HashMap<u32, MethodHandle>,
}
impl UnifiedBoxInterface for ScriptPlugin {
fn invoke(&self, ctx: &mut Context, method_id: u32, args: &[NyashValue]) -> Result<NyashValue, BoxError> {
// メソッドキャッシュから高速検索
if let Some(handle) = self.method_cache.get(&method_id) {
return self.vm.call_cached(handle, args);
}
// 動的メソッド解決
let method = self.resolve_method(method_id)?;
self.vm.call_method(&self.box_instance, &method, args)
}
}
```
## 実装戦略
### Phase 1: MVP実装2-3週間
1. **基本インターフェース実装**
- UnifiedBoxInterfaceトレイトの実装
- 既存FFIプラグイン1つを移行MathBox推奨
- ScriptPluginラッパーの基本実装
2. **export box構文の実装**
```nyash
export box MyPlugin {
init { _version = "1.0.0" }
// 必須:プラグインメタデータ
get_metadata() {
return {
name: "MyPlugin",
version: me._version,
methods: ["process", "transform"]
}
}
// ビジネスロジック
process(data) { ... }
transform(input) { ... }
}
```
3. **基本的なレジストリ**
- 静的登録のみ
- 依存関係解決なし
### Phase 2: 動的機能3-4週間
1. **動的ロード/アンロード**
```nyash
local registry = new PluginRegistry()
local id = registry.load_script("path/to/plugin.ny")
registry.unload(id)
```
2. **ホットリロード**
```nyash
registry.enable_hot_reload("path/to/plugin.ny")
// ファイル変更時に自動リロード
```
3. **依存関係管理**
- 循環依存検出
- バージョン互換性チェック
### Phase 3: 最適化とセキュリティ4-6週間
1. **パフォーマンス最適化**
- メソッドキャッシング
- JITコンパイル統合
- プリコンパイルオプション
2. **セキュリティサンドボックス**
```rust
pub struct Sandbox {
memory_limit: usize,
cpu_quota: Duration,
allowed_capabilities: HashSet<Capability>,
}
```
3. **ケイパビリティベースセキュリティ**
- ファイルアクセス制限
- ネットワーク制限
- システムコール制限
## パフォーマンス考察
### ベンチマーク予測
```
操作 | ネイティブ | スクリプト | 比率
--------------------|-----------|-----------|-----
単純メソッド呼び出し | 10ns | 100ns | 10x
複雑な計算1000ops | 1μs | 5μs | 5x
I/O操作 | 100μs | 102μs | 1.02x
```
### 最適化戦略
1. **ホットパスの識別**
- 頻繁に呼ばれるメソッドを自動検出
- JITコンパイル優先度付け
2. **ハイブリッドアプローチ**
- コア機能:ネイティブ実装
- カスタマイズ層:スクリプト実装
## エコシステムへの影響
### 開発者体験の革新
1. **即座のフィードバックループ**
```bash
# 編集
vim my_plugin.ny
# 即座にテスト(ビルド不要)
nyash test_plugin.ny
```
2. **プラグインマーケットプレイス**
- GitHubから直接インストール
- バージョン管理統合
- 自動更新機能
### コミュニティ成長予測
- **現在**: 10-20人のコアコントリビューターRust必須
- **1年後**: 100-500人のプラグイン開発者Nyashのみ
- **3年後**: 1000+のプラグインエコシステム
## リスクと緩和策
### 技術的リスク
1. **パフォーマンス劣化**
- 緩和策:重要部分のネイティブ実装維持
- プロファイリングツール提供
2. **セキュリティ脆弱性**
- 緩和策:デフォルトサンドボックス
- 署名付きプラグイン
### エコシステムリスク
1. **品質のばらつき**
- 緩和策:公式プラグインガイドライン
- 自動品質チェックツール
2. **互換性問題**
- 緩和策:セマンティックバージョニング強制
- 自動互換性テスト
## 結論と推奨事項
### 即時実行すべきアクション
1. **Box ABI仕様書の作成**1週間
2. **export box構文の実装**2週間
3. **MathBoxの統一インターフェース移行**1週間
### 長期ビジョン
Nyashスクリプトプラグインシステムは、単なる機能追加ではなく、Nyashを**プログラミング言語**から**拡張可能なプラットフォーム**へと進化させる革命的な一歩です。
「Everything is Box」の哲学が、実装言語の壁を超えて真に実現される時、Nyashは次世代のプログラミングエコシステムのモデルケースとなるでしょう。
## 付録:実装例
### A. 完全なスクリプトプラグイン例
```nyash
# advanced_math_plugin.ny
export box AdvancedMathPlugin {
init {
_math = new MathBox()
_cache = new MapBox()
_stats = new MapBox()
}
// プラグインメタデータ(必須)
get_metadata() {
return {
name: "AdvancedMathPlugin",
version: "1.0.0",
methods: ["cached_sin", "cached_cos", "fibonacci", "factorial"],
capabilities: ["compute"],
dependencies: [{
name: "MathBox",
version: ">=1.0.0"
}]
}
}
// キャッシュ付き三角関数
cached_sin(x) {
local key = "sin:" + x.toString()
if me._cache.has(key) {
me._update_stats("cache_hit")
return me._cache.get(key)
}
local result = me._math.sin(x)
me._cache.set(key, result)
me._update_stats("cache_miss")
return result
}
// 再帰的フィボナッチ(メモ化)
fibonacci(n) {
if n <= 1 { return n }
local key = "fib:" + n.toString()
if me._cache.has(key) {
return me._cache.get(key)
}
local result = me.fibonacci(n-1) + me.fibonacci(n-2)
me._cache.set(key, result)
return result
}
// 統計情報
get_stats() {
return me._stats
}
// プライベートメソッド
_update_stats(event) {
local count = me._stats.get(event) or 0
me._stats.set(event, count + 1)
}
}
```
### B. ネイティブとスクリプトの透過的利用
```nyash
// 使用側のコード(プラグインの実装言語を意識しない)
local math1 = new MathBox() // ネイティブプラグイン
local math2 = include("advanced_math_plugin.ny") // スクリプトプラグイン
// 同じインターフェースで利用
print(math1.sin(3.14)) // ネイティブ実装
print(math2.cached_sin(3.14)) // スクリプト実装
// 動的に切り替え可能
local math = get_config("use_cached") ? math2 : math1
print(math.sin(1.57))
```
---
*"Write plugins in Nyash, for Nyash, by Nyash!"*

View File

@ -0,0 +1,90 @@
# Gemini先生の分析Nyashスクリプトプラグインシステム
## 技術的妥当性評価
### 結論:極めて実現可能性は高く、技術的にも非常に妥当
このアプローチは、多くのモダンな言語やエンジンLua, JavaScript/Node.js, Pythonなどで採用されている「ネイティブコアとスクリプト拡張」という実績あるモデルを踏襲しています。
### 「Everything is Box」哲学との整合性
このアプローチは、Boxを「外部から観測可能なインターフェースを持つオブジェクト」と定義するならば、その実装がネイティブRust/C++であろうとスクリプトNyashであろうと区別しない、という哲学の究極的な現れです。
## 統一インターフェース設計
### BoxInterface Traitの提案
```rust
// Rust側に、すべてのプラグインが実装すべきtraitを定義
trait BoxInterface {
fn invoke(&self, method_id: u32, args: NyashValue) -> NyashValue;
// その他、初期化やメタデータ取得などの共通メソッド
}
```
### アーキテクチャ
```
[Nyashコード] -> [BoxInterface Trait] --+--> [FFIラッパー] -> [ネイティブコード]
|
+--> [Nyashスクリプトラッパー] -> [Nyash VM実行]
```
これにより、Nyashのコードからプラグインを利用する側は、相手がネイティブかスクリプトかを一切意識する必要がなくなります。
## エコシステムへの影響
### 開発の民主化
- **参入障壁の劇的な低下**: Rust/C++の環境構築やビルドプロセスが不要
- **迅速なプロトタイピング**: アイデアをすぐにNyashスクリプトで形にし、テスト可能
### 新しいプラグインの形態
- **プラグインの合成**: 複数の既存プラグインを組み合わせて新しい機能を持つ「メタプラグイン」
- **アプリケーションの「設定」としてのプラグイン**: ユーザーが自身のアプリケーションの動作をカスタマイズ
### 動的性の向上
アプリケーションの実行中に、Nyashスクリプトプラグインをリロードしたり、新しいものを追加したりすることが容易になります。
## 実装ロードマップ
### フェーズ1コアランタイムの実現MVP
1. `BoxInterface` Traitの設計と実装Rust側
2. 既存FFIの`BoxInterface`への対応
3. Nyashオブジェクトの`BoxInterface`対応
4. `import` / `export` の実装
### フェーズ2動的機能と管理
1. プラグインレジストリの実装
2. 動的ロード/アンロードAPIの提供
3. ID管理の洗練
### フェーズ3セキュリティと堅牢性
1. サンドボックスの導入
2. パフォーマンス分析ツールの提供
### フェーズ4開発者体験DXの向上
1. ドキュメントの整備
2. LSP/静的解析の対応
## 他言語からの学び
### Lua
- C APIが非常にクリーンで、Cの関数をLuaから、Luaの関数をCから呼び出すのが容易
- **学ぶべき点**: ネイティブとスクリプト間の境界APIをいかにシンプルで強力に保つか
### Node.js (JavaScript)
- `require()`システムが、C++で書かれたネイティブアドオンも、JavaScriptで書かれたモジュールも、全く同じように読み込む
- **学ぶべき点**: 統一されたモジュール解決システムとインターフェースの重要性
### Python
- Cで書かれた拡張モジュールと、Pure Pythonで書かれたモジュールが共存
- **学ぶべき点**: パフォーマンスが重要な部分はCで、それ以外は柔軟なPythonで書くという、実用的な使い分け
## 総括
この発見はNyashの方向性を決定づける重要なマイルストーンです。パフォーマンスが最重要視されるコアな機能はRust/C++のFFIプラグインで、それ以外の大部分の機能、ビジネスロジック、UI、ユーザーカスタマイズなどはNyashスクリプトプラグインで開発する、という美しい棲み分けが実現します。
これはNyashを単なるプログラミング言語から、**拡張可能なプラットフォーム**へと昇華させる可能性を秘めています。

View File

@ -0,0 +1,172 @@
# Nyashスクリプトプラグイン革命統合分析まとめ
## 🚀 革命的発見の本質
Nyashスクリプト自体でプラグインを作成できるという発見は、単なる機能追加ではなく、**Nyashの本質的な進化**を意味します。
### Everything is Boxの究極形
```
従来: Box = Rust/C++実装のオブジェクト
革命: Box = 実装言語を問わないインターフェース
```
この発見により、「Everything is Box」哲学が実装レイヤーの制約から解放され、真の意味で実現されます。
## 🎯 両先生の分析の統合
### 共通の評価ポイント
1. **技術的妥当性**: 極めて高い実現可能性
2. **エコシステムへの影響**: 開発者数の爆発的増加が期待
3. **実装アプローチ**: 統一インターフェースによる透過的利用
### 相補的な視点
| 観点 | Gemini先生 | Codex先生 |
|------|-----------|-----------|
| 焦点 | 哲学的整合性・他言語事例 | 具体的実装・パフォーマンス |
| 強み | エコシステム影響分析 | 技術アーキテクチャ設計 |
| 提案 | 段階的ロードマップ | 詳細な実装戦略 |
## 📊 統合実装計画
### 即時着手1-2週間
1. **Box ABI仕様策定**
- Gemini案のBoxInterfaceトレイト
- Codex案のUnifiedBoxInterface
- 両案を統合した最終仕様
2. **export box構文実装**
```nyash
export box PluginName {
// 必須:メタデータ提供
get_metadata() { ... }
// ビジネスロジック
method1() { ... }
method2() { ... }
}
```
3. **プロトタイプ実装**
- MathBoxを統一インターフェースに移行
- 簡単なNyashスクリプトプラグイン作成
### 中期目標1-2ヶ月
1. **動的プラグインシステム**
- ホットリロード機能
- 依存関係管理
- バージョン互換性
2. **開発ツール整備**
- プラグインテンプレート
- デバッグツール
- パフォーマンスプロファイラ
3. **セキュリティ基盤**
- サンドボックス実装
- ケイパビリティベース権限
### 長期ビジョン6ヶ月-1年
1. **プラグインエコシステム**
- マーケットプレイス構築
- 自動品質チェック
- コミュニティガイドライン
2. **高度な最適化**
- JIT統合
- プリコンパイル機能
- ネイティブ/スクリプトハイブリッド
## 🔑 成功の鍵
### 技術的成功要因
1. **シンプルな統一インターフェース**
- 学習コストを最小化
- 既存プラグインの移行を容易に
2. **段階的移行パス**
- 既存のFFIプラグインと共存
- 破壊的変更を避ける
3. **パフォーマンス配慮**
- ホットパスはネイティブ維持
- I/O boundタスクから適用
### エコシステム成功要因
1. **開発体験の劇的改善**
- ビルド不要
- 即座のフィードバック
- 豊富なサンプル
2. **コミュニティ形成**
- 初心者に優しいドキュメント
- アクティブなサポート
- 貢献への明確なパス
## 🎊 期待される成果
### 短期3-6ヶ月
- プラグイン開発者: 10→100人
- プラグイン数: 20→200個
- 開発速度: 10倍向上
### 中期1年
- 主要機能の8割がプラグイン化
- サードパーティエコシステム確立
- 企業採用事例の出現
### 長期3年
- デファクトスタンダード化
- 1000+のプラグイン
- 自立的エコシステム
## 🏁 結論
Nyashスクリプトプラグインシステムは、**技術的に実現可能**であり、**戦略的に必須**の進化です。
「Everything is Box」哲学の真の実現により、Nyashは単なるプログラミング言語から、**次世代の拡張可能プラットフォーム**へと進化します。
### 合言葉
> **"Write plugins in Nyash, for Nyash, by Nyash!"**
この革命により、Nyashコミュニティは爆発的な成長を遂げ、真に民主的なプログラミングエコシステムが誕生するでしょう。
---
## 📎 付録:クイックスタートガイド
### 最初のスクリプトプラグイン5分で作成
```nyash
# my_first_plugin.ny
export box MyFirstPlugin {
init {
_name = "My First Plugin"
_count = 0
}
greet(name) {
me._count = me._count + 1
return "Hello, " + name + "! (call #" + me._count.toString() + ")"
}
}
```
### 使用例
```nyash
# main.ny
local plugin = include("my_first_plugin.ny")
print(plugin.greet("World")) // "Hello, World! (call #1)"
print(plugin.greet("Nyash")) // "Hello, Nyash! (call #2)"
```
**ビルド不要、即実行!** これがNyashスクリプトプラグインの力です。

View File

@ -0,0 +1,40 @@
# Birth 引数の一般化メモ可変長TLV / 例外ハンドリング)
目的10.5c: birth/by-name を汎用化し、String/Integer はプリミティブ、他は Handle で TLV 化。将来的な可変長と例外連携を見据えた最小要件を定義。
## ゴール
- 可変長引数の birth例: `new FileBox(path, mode)` / `new MapBox(initial_pairs...)`)。
- by-name birth: `type_name.birth(args...)` を TLV 経由で統一。
- 失敗は Result/エラー文字列で伝搬し、パニック/例外の越境は行わない。
## TLV レイアウト(案)
- Header: `u16 entry_count`
- Entries: `[tag:u16][len:u16][payload..] * entry_count`
- 許容型: bool(1), i64(3), f64(5), string(6), bytes(7), handle(8)
## 変換規約
- StringBox → TLV string / IntegerBox → TLV i64プリミティブ化
- その他の Box → TLV handle (type_id:u32 + instance_id:u32)
- 即値i64/f64/boolはそのまま TLV 化
## 例
- `new StringBox()` → 引数0
- `new IntegerBox(42)` → [i64:42]
- `new FileBox("/tmp/x", "w")` → [string:"/tmp/x"], [string:"w"]
- `new ArrayBox(1, 2, 3)` → [i64:1], [i64:2], [i64:3]
## 例外/エラーの扱い
- birth 失敗時は、
- 可能なら `returns_result=true` とし、Ok(Handle)/Err(String) の TLV 形状を返すVMは Result で包む)。
- それが未設定の場合は、TLV stringエラーメッセージを返し、呼び手が `try_birth` 等で明示的に扱う(将来)。
- パニック/例外の越境は禁止C-ABI上はコード戻り or TLV に限定)。
## by-name birth
- `nyash_plugin_birth_by_name(box_type_name, args_tlv, out_tlv)`(将来/任意)。
- Lowerer は型名が静的に分かるときは従来の `type_id` 経路、未確定時は by-name を使用可。
## 今後の拡張
- 可変長 >2 の aN 受け: 既存 `*_invoke3_*` シムを拡張(`invokeN`/スタック経由/一時領域)。
- Named 引数: TLV に name ハッシュ or 別エントリで拡張(後段)。
- 既定値: Plugin 側の既定値解決と TLV 省略の整合性を設計。

View File

@ -0,0 +1,66 @@
# Nyash FFI Calling Convention (Handle-First, TLV, v0)
目的: 10.5c の指針に沿って、JIT/AOT/VM 共通の最小・実用的な呼び出し規約を短文化。a0/a1/a2 は Handle-First を前提とし、戻りは TLV で統一。
## 要点TL;DR
- Handle-First: a0 は常に `nyash.handle.of(receiver)`i64。他の引数は TLV でエンコード。
- 引数TLV: String/Integer はプリミティブ化、それ以外は Handle(tag=8)。
- 戻りTLV: i64/f64/bool/string/bytes/handle をサポート。`returns_result` 指定時は Result 形状VMが尊重
- by-name 経路: 型名未確定時は `*_invoke_by_name_*` シムで実行時解決(任意、推奨)。
- Strict 原則: Strict 時は JIT 実行を停止VM=仕様の唯一の基準。AOT 生成のみ許可。
## シム関数(最小)
```c
// 受け手は a0i64: handleで指定。a1/a2 は任意引数のプレースホルダ。
extern "C" long long nyash_plugin_invoke3_i64(
long long type_id,
long long method_id,
long long argc, // 受け手を含む総数(>=1
long long a0, // receiver: nyash.handle.of(...)
long long a1,
long long a2
);
extern "C" double nyash_plugin_invoke3_f64(
long long type_id,
long long method_id,
long long argc,
long long a0,
long long a1,
long long a2
);
// by-name型名未確定時に利用可能最小シム
// 実装済み: getattr/call の i64系
extern "C" long long nyash_plugin_invoke_name_getattr_i64(long long argc, long long a0, long long a1, long long a2);
extern "C" long long nyash_plugin_invoke_name_call_i64(long long argc, long long a0, long long a1, long long a2);
```
## 引数の規約TLV
- a0 は常に受け手のハンドルi64。レシーバは TLV には含めず、直接 a0 から解決。
- a1/a2 は必要に応じて TLV バッファへ詰める最大2引数の最小シム。将来拡張で可変長
- エンコード方針VM/nyrt共通
- StringBox → TLV string(tag=6)
- IntegerBox → TLV i64(tag=3)
- BufferBox → TLV bytes(tag=7)nyrtシムが自動検出
- それ以外の Box → TLV handle(tag=8, payload: type_id:u32 + instance_id:u32)
- i64/f64/bool 等の即値 → 対応する TLV プリミティブ
## 戻り値の規約TLV
- 戻り TLV の先頭タグにより分岐し、必要ならネイティブへデコード:
- tag=3 → i64、tag=5 → f64`*_i64`/`*_f64` 経由)
- tag=6 → string、tag=7 → bytes
- tag=8 → handletype_id/instance_id を登録し BoxRef 構築)
- `returns_result` のメソッドは、VM で Ok/Err を Result に包むAOT も同等方針)。
## N引数サポート暫定
- `*_invoke3_*` は a1/a2 までの即値/ハンドルを直積み、3引数目以降はレガシーVM引数位置3..NからTLVに詰めることで対応開発向け
- ネイティブEXEAOTでのN引数の完全化は将来の `*_invokeN_*` で対応予定。
## Lowerer の前提Handle-First
- `emit_plugin_invoke(type_id, method_id, argc, has_ret)` を一本化。a0 は常に `nyash.handle.of(receiver)` を積む。
- 受け手箱名が未確定な場面は by-name シムを使用可(最適化は後段)。
## メモ
- TLV タグの例: 1=Bool, 3=I64, 5=F64, 6=String, 7=Bytes, 8=Handle。
- 既存詳細は `docs/papers/nyash-unified-lifecycle/technical-details.md` 参照。

View File

@ -0,0 +1,40 @@
Nyash Include/Export Specification (Phase 1)
Overview
- Goal: Simple, box-first module system for code split and reuse.
- Principle: One file exports one module (Box). include(path) returns that Box.
Syntax
- Include expression:
local Math = include "lib/math.nyash"
local r = Math.add(1, 2)
Rules
- One static box per file: The included file must define exactly one static box. Zero or multiple is an error.
- Expression form: include(...) is an expression that evaluates to the included modules Box instance.
- Caching: The same file is evaluated at most once per run (subsequent includes return the cached module).
- Path resolution:
- Relative: include("./x.nyash") resolves relative to the current project root.
- Roots (nyash.toml):
[include.roots]
std = "./stdlib"
lib = "./lib"
Then include("std/string") -> ./stdlib/string.nyash, include("lib/utils") -> ./lib/utils.nyash
- Extension/index: If extension is omitted, .nyash is appended. If the path is a directory, index.nyash is used.
Backends
- Interpreter: include is executed at runtime (implemented via execute_include_expr). Returns the Box instance.
- VM/AOT: include is handled by the MIR builder by reading and parsing the target file and lowering its static box into the same MIR module (no new MIR op added). The include expression lowers into a new Box() for the included static box type.
- No new MIR instruction was added; existing instructions (Const/NewBox/BoxCall/etc.) are used.
Limitations (Phase 1)
- Cycles: Circular includes are not yet reported with a dedicated error path. A follow-up change will add active-load tracking and a clear error message with the cycle path.
- Export box: Reserved for Phase 2. For now, the single static box in the file is the public API.
Examples
- See examples/include_math.nyash and examples/include_main.nyash.
Rationale
- Keeps MIR spec stable and relies on build-time module linking for VM/AOT.
- Aligns with Everything-is-Box: modules are Boxes; methods/fields are the API.

View File

@ -55,3 +55,26 @@ NYASH_JIT_THRESHOLD=1 NYASH_JIT_HOSTCALL=1 \
- DebugConfigBoxevents/stats/dump/dotと GcConfigBox は Box から `apply()` で環境へ反映できます。 - DebugConfigBoxevents/stats/dump/dotと GcConfigBox は Box から `apply()` で環境へ反映できます。
- `--emit-cfg path.dot` または `DebugConfigBox.setPath("jit_dot", path)` でCFGのDOT出力。いずれもdumpを自動有効化。 - `--emit-cfg path.dot` または `DebugConfigBox.setPath("jit_dot", path)` でCFGのDOT出力。いずれもdumpを自動有効化。
- イベントは `phase` フィールドで区別lower/execute`jit_events_path` でJSONL出力先を指定可能。 - イベントは `phase` フィールドで区別lower/execute`jit_events_path` でJSONL出力先を指定可能。
## 5) AOT最小手順--compile-native
- 目的: Craneliftでオブジェクトを生成し、`libnyrt` とリンクしてEXE化。
- 事前: `cargo build --release --features cranelift-jit`
- 実行例String/Integer/Consoleの最小:
```
./target/release/nyash --compile-native examples/aot_min_string_len.nyash -o app && ./app
# 結果は `Result: <val>` として標準出力に表示
```
- Python最小チェーンRO:
```
./target/release/nyash --compile-native examples/aot_py_min_chain.nyash -o app && ./app
```
- スクリプト版(詳細な手順): `tools/build_aot.sh <file> -o <out>`Windowsは `tools/build_aot.ps1`
## 6) SchedulerPhase 10.6b 準備)
- 目的: 協調スケジューラのSafepoint連携を観測
- 実行(デモ):
```
NYASH_SCHED_DEMO=1 NYASH_SCHED_POLL_BUDGET=2 \
./target/release/nyash --backend vm examples/scheduler_demo.nyash
```
- 期待: `[SCHED] immediate task ran at safepoint``[SCHED] delayed task ran at safepoint` が出力

View File

@ -0,0 +1,19 @@
// AOT Python kwargs via env-injected eval code (暫定ブリッジ)
// 目的: TLVでのkwargs未整備の間、evalコードに **dict で渡す
// Build:
// cargo build --release --features cranelift-jit
// Run:
// NYASH_PY_EVAL_CODE="(__import__('builtins').int)(**{'x':'FF','base':16})" \
// ./target/release/nyash --compile-native examples/aot_py_eval_kwargs_env.nyash -o app && \
// NYASH_PY_EVAL_CODE="(__import__('builtins').int)(**{'x':'FF','base':16})" ./app
static box Main {
main() {
local py, r
py = new PyRuntimeBox()
r = py.eval() // code is read from NYASH_PY_EVAL_CODE (host side)
me.console.println(r)
return 0
}
}

View File

@ -0,0 +1,17 @@
// AOT Python minimal chain: import -> getattr -> call
// Build AOT (example):
// cargo build --release --features cranelift-jit
// ./target/release/nyash --compile-native examples/aot_py_min_chain.nyash -o app && ./app
static box Main {
main() {
local py, math, sqrt_fn, x, r
py = new PyRuntimeBox()
math = py.import("math")
sqrt_fn = py.getattr(math, "sqrt")
x = new IntegerBox(16)
r = py.call(sqrt_fn, x)
return r // expects 4.0 (autodecode may convert FloatBox→f64)
}
}

View File

@ -0,0 +1,16 @@
// AOT Python evalR Err demo (returns Result.Err)
// Build:
// cargo build --release --features cranelift-jit
// ./target/release/nyash --compile-native examples/aot_py_result_err.nyash -o app && ./app
static box Main {
main() {
local py, r
py = new PyRuntimeBox()
// Division by zero triggers a Python exception → Err(...)
r = py.evalR(1/0)
me.console.println(r)
return 0
}
}

View File

@ -0,0 +1,16 @@
// AOT Python evalR OK demo (returns Result.Ok)
// Build:
// cargo build --release --features cranelift-jit
// ./target/release/nyash --compile-native examples/aot_py_result_ok.nyash -o app && ./app
static box Main {
main() {
local py, r
py = new PyRuntimeBox()
// evalR returns a Result-like box; VM prints Ok(...), AOT prints numeric when autodecode is enabled
r = py.evalR("1 + 2")
me.console.println(r)
return 0
}
}

View File

@ -0,0 +1,6 @@
static box Main {
main() {
return include "examples/include_math.nyash"
}
}

View File

@ -0,0 +1,7 @@
static box Main {
main() {
local Math = include "examples/include_math.nyash"
local r = Math.add(1, 2)
return r
}
}

View File

@ -0,0 +1,6 @@
static box Math {
add(a, b) {
return a + b
}
}

View File

@ -0,0 +1,19 @@
// Python minimal chain (VM): import -> getattr -> call
// @env NYASH_PLUGIN_ONLY=1
// @env NYASH_PY_AUTODECODE=1
// Run:
// cargo build --release && ./target/release/nyash --backend vm examples/py_min_chain_vm.nyash
static box Main {
main() {
local py, math, sqrt_fn, x, r
py = new PyRuntimeBox()
math = py.import("math")
sqrt_fn = py.getattr(math, "sqrt")
x = new IntegerBox(16)
r = py.call(sqrt_fn, x)
print(r) // expects 4.0
return 0
}
}

View File

@ -0,0 +1,197 @@
// PythonCompilerBox - C2: Python AST → Nyash変換ロジック
// Rust製パーサープラグインC1からのJSONを受け取って処理
box PythonCompilerBox {
init { supportedNodes, corePyIR }
birth() {
// サポートするノードタイプを定義
me.supportedNodes = new MapBox()
me.supportedNodes.set("Module", true)
me.supportedNodes.set("FunctionDef", true)
me.supportedNodes.set("Return", true)
me.supportedNodes.set("Constant", true)
me.supportedNodes.set("If", true)
me.supportedNodes.set("While", true)
me.supportedNodes.set("For", true)
me.supportedNodes.set("BinOp", true)
me.supportedNodes.set("Add", true)
me.supportedNodes.set("Sub", true)
me.supportedNodes.set("Mult", true)
me.supportedNodes.set("Div", true)
}
// メインのコンパイル関数
compile(astJson) {
local console = new ConsoleBox()
// JSONパース現在はスタブ
// TODO: JSONBoxが実装されたら使用
local ast = me.parseJson(astJson)
if ast == null {
return me.error("Failed to parse AST JSON")
}
// All-or-Nothingチェック
local checkResult = me.checkSupported(ast)
if not checkResult.isOk {
return me.error("Unsupported features: " + checkResult.unsupported)
}
// CorePy IR生成
me.corePyIR = me.generateCorePyIR(ast)
// Nyashコード生成
local nyashCode = me.generateNyash(me.corePyIR)
return me.ok(nyashCode)
}
// AST全体のサポートチェック
checkSupported(ast) {
local result = new MapBox()
result.set("isOk", true)
result.set("unsupported", new ArrayBox())
// 再帰的にチェック(簡易版)
if ast.get("counts") {
local counts = ast.get("counts")
if counts.get("unsupported") > 0 {
result.set("isOk", false)
local unsupportedList = ast.get("unsupported")
result.set("unsupported", unsupportedList)
}
}
return result
}
// CorePy IR生成中間表現
generateCorePyIR(ast) {
local ir = new MapBox()
ir.set("version", "0.1")
ir.set("module", new MapBox())
// シンプルな例main関数のみ
local functions = new ArrayBox()
local mainFunc = new MapBox()
mainFunc.set("name", "main")
mainFunc.set("params", new ArrayBox())
mainFunc.set("body", new ArrayBox())
// return 0 を追加
local returnStmt = new MapBox()
returnStmt.set("type", "return")
returnStmt.set("value", 0)
mainFunc.get("body").push(returnStmt)
functions.push(mainFunc)
ir.get("module").set("functions", functions)
return ir
}
// Nyashコード生成
generateNyash(ir) {
local code = new StringBox()
code.append("// Generated from Python by PythonCompilerBox\n")
code.append("// CorePy IR version: " + ir.get("version") + "\n\n")
// static box Main生成
code.append("static box Main {\n")
// 関数生成
local module = ir.get("module")
if module and module.get("functions") {
local functions = module.get("functions")
local i = 0
loop(i < functions.length()) {
local func = functions.get(i)
code.append(me.generateFunction(func))
i = i + 1
}
}
code.append("}\n")
return code.toString()
}
// 個別関数の生成
generateFunction(func) {
local code = new StringBox()
local name = func.get("name")
local params = func.get("params")
local body = func.get("body")
// 関数シグネチャ
code.append(" " + name + "(")
// TODO: パラメータ処理
code.append(") {\n")
// 関数本体
if body {
local i = 0
loop(i < body.length()) {
local stmt = body.get(i)
code.append(me.generateStatement(stmt))
i = i + 1
}
}
code.append(" }\n")
return code.toString()
}
// 文の生成
generateStatement(stmt) {
local type = stmt.get("type")
if type == "return" {
return " return " + stmt.get("value") + "\n"
}
// TODO: 他の文タイプを追加
return " // TODO: " + type + "\n"
}
// ヘルパー関数
ok(value) {
local result = new MapBox()
result.set("success", true)
result.set("value", value)
return result
}
error(message) {
local result = new MapBox()
result.set("success", false)
result.set("error", message)
return result
}
// 仮のJSONパーサースタブ
parseJson(jsonStr) {
// TODO: 実際のJSONパース実装
// 今は固定のテスト用データを返す
local mock = new MapBox()
mock.set("success", true)
mock.set("dump", "Module(...)")
local counts = new MapBox()
counts.set("total_nodes", 5)
counts.set("functions", 1)
counts.set("supported", 5)
counts.set("unsupported", 0)
mock.set("counts", counts)
mock.set("unsupported", new ArrayBox())
return mock
}
}

Binary file not shown.

View File

@ -0,0 +1,35 @@
// PythonCompilerBox 簡易テスト
// 環境変数 NYASH_PY_IR からJSON IRを読み取ってNyashコードを生成
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 超簡易的なコンパイラJSONパースなし
const char* compile_simple(const char* ir) {
// JSONをちゃんとパースせずに、簡単なパターンマッチング
if (strstr(ir, "\"name\":\"main\"") && strstr(ir, "\"return_value\":0")) {
return "// Generated from Python\n"
"static box Main {\n"
" main() {\n"
" return 0\n"
" }\n"
"}\n";
}
return "// Unsupported IR\n";
}
int main() {
const char* ir = getenv("NYASH_PY_IR");
if (!ir) {
ir = "{\"module\":{\"functions\":[{\"name\":\"main\",\"return_value\":0}]}}";
printf("Using default IR: %s\n\n", ir);
} else {
printf("Compiling IR from NYASH_PY_IR: %s\n\n", ir);
}
printf("=== Generated Nyash Code ===\n");
printf("%s", compile_simple(ir));
return 0;
}

View File

@ -0,0 +1,42 @@
// Test Python Parser Plugin
// 環境変数 NYASH_PY_CODE からPythonコードを読み取ってパース
static box Main {
main() {
local console = new ConsoleBox()
// 環境変数からPythonコードを取得
local env_code = ConsoleBox.getEnv("NYASH_PY_CODE")
if env_code == null {
console.log("Usage: NYASH_PY_CODE='def main(): return 0' nyash test_python_parser.nyash")
return 1
}
console.log("=== Python Code ===")
console.log(env_code)
console.log("")
// パーサープラグインを読み込み(仮想的に)
// 実際にはプラグインローダー経由で呼び出す必要がある
console.log("=== Parse Result ===")
console.log("(Parser plugin integration pending)")
// 期待される出力形式を表示
console.log("")
console.log("Expected output format:")
console.log("{")
console.log(' "success": true,')
console.log(' "dump": "Module(body=[FunctionDef(...)])",')
console.log(' "counts": {')
console.log(' "total_nodes": 5,')
console.log(' "functions": 1,')
console.log(' "classes": 0,')
console.log(' "supported": 1,')
console.log(' "unsupported": 0')
console.log(' },')
console.log(' "unsupported": []')
console.log("}")
return 0
}
}

View File

@ -149,6 +149,8 @@ MathBox = 50
TimeBox = 51 TimeBox = 51
PyRuntimeBox= 40 PyRuntimeBox= 40
PyObjectBox = 41 PyObjectBox = 41
PythonParserBox = 60
PythonCompilerBox = 61
# 新スタイルのプラグインルート(併用可・[libraries]は後方互換) # 新スタイルのプラグインルート(併用可・[libraries]は後方互換)
[plugins] [plugins]
@ -162,6 +164,8 @@ PyObjectBox = 41
"libnyash_counter_plugin" = "./plugins/nyash-counter-plugin" "libnyash_counter_plugin" = "./plugins/nyash-counter-plugin"
"libnyash_net_plugin" = "./plugins/nyash-net-plugin" "libnyash_net_plugin" = "./plugins/nyash-net-plugin"
"libnyash_math_plugin" = "./plugins/nyash-math-plugin" "libnyash_math_plugin" = "./plugins/nyash-math-plugin"
"libnyash_python_parser_plugin" = "./plugins/nyash-python-parser-plugin"
"libnyash_python_compiler_plugin" = "./plugins/nyash-python-compiler-plugin"
[libraries."libnyash_array_plugin"] [libraries."libnyash_array_plugin"]
boxes = ["ArrayBox"] boxes = ["ArrayBox"]
path = "./plugins/nyash-array-plugin/target/release/libnyash_array_plugin" path = "./plugins/nyash-array-plugin/target/release/libnyash_array_plugin"

View File

@ -0,0 +1,11 @@
[package]
name = "nyash-python-compiler-plugin"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "staticlib"]
[dependencies]
once_cell = "1.20"
serde_json = "1"

View File

@ -0,0 +1,29 @@
[box]
name = "Nyash Python Compiler Plugin"
version = "0.1.0"
description = "CorePy IR -> Nyash Source (Phase 10.7 C2)"
author = "Nyash Team"
[provides]
boxes = ["PythonCompilerBox"]
[PythonCompilerBox]
type_id = 61
[PythonCompilerBox.lifecycle]
birth = { id = 0 }
fini = { id = 4294967295 }
[PythonCompilerBox.methods.compile]
id = 1
args = [ { name = "ir_json", type = "string" } ]
returns = { type = "string" } # Nyash source (暫定)
[implementation]
ffi_version = 1
thread_safe = false
[artifacts]
windows = "target/x86_64-pc-windows-msvc/release/nyash_python_compiler_plugin.dll"
linux = "target/release/libnyash_python_compiler_plugin.so"
macos = "target/release/libnyash_python_compiler_plugin.dylib"

View File

@ -0,0 +1,103 @@
use once_cell::sync::Lazy;
use std::sync::Mutex;
use serde_json::Value as Json;
const NYB_SUCCESS: i32 = 0;
const NYB_E_INVALID_METHOD: i32 = -3;
const NYB_E_SHORT_BUFFER: i32 = -1;
const TYPE_ID_COMPILER: u32 = 61;
const METHOD_BIRTH: u32 = 0;
const METHOD_COMPILE: u32 = 1;
const METHOD_FINI: u32 = u32::MAX;
static NEXT_ID: Lazy<Mutex<u32>> = Lazy::new(|| Mutex::new(1));
#[no_mangle]
pub extern "C" fn nyash_plugin_abi() -> u32 { 1 }
#[no_mangle]
pub extern "C" fn nyash_plugin_init() -> i32 { NYB_SUCCESS }
#[no_mangle]
pub extern "C" fn nyash_plugin_invoke(
type_id: u32,
method_id: u32,
_instance_id: u32,
args: *const u8,
args_len: usize,
result: *mut u8,
result_len: *mut usize,
) -> i32 {
if type_id != TYPE_ID_COMPILER { return NYB_E_INVALID_METHOD; }
match method_id {
METHOD_BIRTH => {
unsafe {
let mut id_g = NEXT_ID.lock().unwrap();
let id = *id_g; *id_g += 1;
let need = 4usize;
if *result_len < need { *result_len = need; return NYB_E_SHORT_BUFFER; }
let out = std::slice::from_raw_parts_mut(result, *result_len);
out[0..4].copy_from_slice(&(id as u32).to_le_bytes());
*result_len = need;
}
NYB_SUCCESS
}
METHOD_COMPILE => {
// Decode TLV first string arg as JSON IR
let ir = unsafe {
if args.is_null() || args_len < 8 { None } else {
let buf = std::slice::from_raw_parts(args, args_len);
let tag = u16::from_le_bytes([buf[4], buf[5]]);
let len = u16::from_le_bytes([buf[6], buf[7]]) as usize;
if tag == 6 && 8 + len <= buf.len() {
match std::str::from_utf8(&buf[8..8+len]) {
Ok(s) => Some(s.to_string()),
Err(_) => None
}
} else { None }
}
};
let nyash_source = if let Some(s) = ir.or_else(|| std::env::var("NYASH_PY_IR").ok()) {
// Minimal: accept either {"nyash_source": "..."} shortcut, or a tiny IR
match serde_json::from_str::<Json>(&s).ok() {
Some(Json::Object(map)) => {
if let Some(Json::String(src)) = map.get("nyash_source") {
src.clone()
} else if let Some(module) = map.get("module") {
// Try module.functions[0].name and maybe return value
let mut ret_expr = "0".to_string();
if let Some(funcs) = module.get("functions").and_then(|v| v.as_array()) {
if let Some(fun0) = funcs.get(0) {
if let Some(retv) = fun0.get("return_value") {
if retv.is_number() { ret_expr = retv.to_string(); }
else if let Some(s) = retv.as_str() { ret_expr = s.to_string(); }
}
}
}
format!("static box Generated {{\n main() {{\n return {}\n }}\n}}", ret_expr)
} else {
"static box Generated { main() { return 0 } }".to_string()
}
}
_ => "static box Generated { main() { return 0 } }".to_string(),
}
} else {
"static box Generated { main() { return 0 } }".to_string()
};
unsafe {
let bytes = nyash_source.as_bytes();
let need = 4 + bytes.len();
if *result_len < need { *result_len = need; return NYB_E_SHORT_BUFFER; }
let out = std::slice::from_raw_parts_mut(result, *result_len);
out[0..2].copy_from_slice(&6u16.to_le_bytes());
out[2..4].copy_from_slice(&(bytes.len() as u16).to_le_bytes());
out[4..4+bytes.len()].copy_from_slice(bytes);
*result_len = need;
}
NYB_SUCCESS
}
METHOD_FINI => NYB_SUCCESS,
_ => NYB_E_INVALID_METHOD,
}
}

View File

@ -0,0 +1,14 @@
[package]
name = "nyash-python-parser-plugin"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
pyo3 = { version = "0.22", features = ["auto-initialize"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[dev-dependencies]

View File

@ -0,0 +1,29 @@
[box]
name = "Nyash Python Parser Plugin"
version = "0.1.0"
description = "Python -> AST/IR (Phase 10.7 C1)"
author = "Nyash Team"
[provides]
boxes = ["PythonParserBox"]
[PythonParserBox]
type_id = 60
[PythonParserBox.lifecycle]
birth = { id = 0 }
fini = { id = 4294967295 }
[PythonParserBox.methods.parse]
id = 1
args = [ { name = "code", type = "string" } ]
returns = { type = "string" } # JSON AST (暫定)
[implementation]
ffi_version = 1
thread_safe = false
[artifacts]
windows = "target/x86_64-pc-windows-msvc/release/nyash_python_parser_plugin.dll"
linux = "target/release/libnyash_python_parser_plugin.so"
macos = "target/release/libnyash_python_parser_plugin.dylib"

View File

@ -0,0 +1,318 @@
use pyo3::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
struct ParseResult {
success: bool,
dump: Option<String>,
counts: ParseCounts,
unsupported: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug)]
struct ParseCounts {
total_nodes: usize,
functions: usize,
classes: usize,
supported: usize,
unsupported: usize,
}
/// FFI: プラグインABIバージョン
#[no_mangle]
pub extern "C" fn nyash_plugin_abi_version() -> u32 {
1
}
/// FFI: プラグイン初期化
#[no_mangle]
pub extern "C" fn nyash_plugin_init() -> i32 {
// Python初期化は pyo3 の auto-initialize が処理
0
}
/// FFI: プラグインメソッド呼び出しBID形式
#[no_mangle]
pub extern "C" fn nyash_plugin_invoke(
type_id: u32,
method_id: u32,
_instance_id: u32,
args: *const u8,
args_len: usize,
result: *mut u8,
result_len: *mut usize,
) -> i32 {
const TYPE_ID_PARSER: u32 = 60;
const METHOD_BIRTH: u32 = 0;
const METHOD_PARSE: u32 = 1;
const METHOD_FINI: u32 = u32::MAX;
if type_id != TYPE_ID_PARSER {
return -3; // NYB_E_INVALID_METHOD
}
match method_id {
METHOD_BIRTH => {
// インスタンスIDを返す
unsafe {
let instance_id = 1u32; // 簡易実装
if *result_len < 4 {
*result_len = 4;
return -1; // NYB_E_SHORT_BUFFER
}
let out = std::slice::from_raw_parts_mut(result, *result_len);
out[0..4].copy_from_slice(&instance_id.to_le_bytes());
*result_len = 4;
}
0
}
METHOD_PARSE => {
// 引数からコードを取得TLV形式の文字列を期待
let code = unsafe {
if args.is_null() || args_len < 4 {
// 引数なしの場合は環境変数から取得
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
} else {
// TLVデコード簡易版
let buf = std::slice::from_raw_parts(args, args_len);
if args_len >= 8 {
let tag = u16::from_le_bytes([buf[0], buf[1]]);
let len = u16::from_le_bytes([buf[2], buf[3]]) as usize;
if tag == 6 && 4 + len <= args_len {
match std::str::from_utf8(&buf[4..4+len]) {
Ok(s) => s.to_string(),
Err(_) => std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
}
} else {
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
}
} else {
std::env::var("NYASH_PY_CODE").unwrap_or_else(|_| "def main():\n return 0".to_string())
}
}
};
// パース実行
let parse_result = Python::with_gil(|py| {
parse_python_code(py, &code)
});
// JSONにシリアライズ
match serde_json::to_string(&parse_result) {
Ok(json) => {
unsafe {
let bytes = json.as_bytes();
let need = 4 + bytes.len();
if *result_len < need {
*result_len = need;
return -1; // NYB_E_SHORT_BUFFER
}
let out = std::slice::from_raw_parts_mut(result, *result_len);
// TLVエンコードtag=6:string
out[0..2].copy_from_slice(&6u16.to_le_bytes());
out[2..4].copy_from_slice(&(bytes.len() as u16).to_le_bytes());
out[4..4+bytes.len()].copy_from_slice(bytes);
*result_len = need;
}
0
}
Err(_) => -4 // エラー
}
}
METHOD_FINI => 0,
_ => -3 // NYB_E_INVALID_METHOD
}
}
/// FFI: Pythonコードをパース
#[no_mangle]
pub extern "C" fn nyash_python_parse(code: *const std::os::raw::c_char) -> *mut std::os::raw::c_char {
let code = unsafe {
if code.is_null() {
return std::ptr::null_mut();
}
match std::ffi::CStr::from_ptr(code).to_str() {
Ok(s) => s,
Err(_) => return std::ptr::null_mut(),
}
};
let result = Python::with_gil(|py| {
parse_python_code(py, code)
});
match serde_json::to_string(&result) {
Ok(json) => {
let c_str = std::ffi::CString::new(json).unwrap();
c_str.into_raw()
}
Err(_) => std::ptr::null_mut(),
}
}
/// FFI: 文字列解放
#[no_mangle]
pub extern "C" fn nyash_python_free_string(ptr: *mut std::os::raw::c_char) {
if !ptr.is_null() {
unsafe {
let _ = std::ffi::CString::from_raw(ptr);
}
}
}
fn parse_python_code(py: Python, code: &str) -> ParseResult {
let mut result = ParseResult {
success: false,
dump: None,
counts: ParseCounts {
total_nodes: 0,
functions: 0,
classes: 0,
supported: 0,
unsupported: 0,
},
unsupported: Vec::new(),
};
// Pythonのastモジュールをインポート
let ast_module = match py.import_bound("ast") {
Ok(m) => m,
Err(e) => {
result.dump = Some(format!("Failed to import ast module: {}", e));
return result;
}
};
// コードをパース
let tree = match ast_module.call_method1("parse", (code,)) {
Ok(t) => t,
Err(e) => {
result.dump = Some(format!("Parse error: {}", e));
return result;
}
};
// ASTをダンプ文字列表現
if let Ok(dump_str) = ast_module.call_method1("dump", (&tree,)) {
if let Ok(s) = dump_str.extract::<String>() {
result.dump = Some(s);
}
}
// ASTを解析してカウント
analyze_ast(py, &tree, &mut result);
result.success = true;
result
}
fn analyze_ast(_py: Python, node: &Bound<'_, PyAny>, result: &mut ParseResult) {
result.counts.total_nodes += 1;
// ノードタイプを取得 - __class__.__name__ を使用
if let Ok(class_obj) = node.getattr("__class__") {
if let Ok(name_obj) = class_obj.getattr("__name__") {
if let Ok(type_name) = name_obj.extract::<String>() {
match type_name.as_str() {
"FunctionDef" => {
result.counts.functions += 1;
result.counts.supported += 1;
}
"AsyncFunctionDef" => {
result.counts.functions += 1;
result.counts.unsupported += 1;
result.unsupported.push("async function".to_string());
}
"ClassDef" => {
result.counts.classes += 1;
result.counts.unsupported += 1;
result.unsupported.push("class definition".to_string());
}
"For" | "While" | "If" => {
result.counts.supported += 1;
}
"Yield" | "YieldFrom" => {
result.counts.unsupported += 1;
result.unsupported.push("generator".to_string());
}
_ => {}
}
}
}
}
// 子ノードを再帰的に解析
// ast.walk() を使って全ノードを取得
if let Ok(ast_module) = node.py().import_bound("ast") {
if let Ok(walk_iter) = ast_module.call_method1("walk", (node,)) {
if let Ok(nodes) = walk_iter.iter() {
for child_result in nodes {
if let Ok(child) = child_result {
// 自分自身はスキップ(すでにカウント済み)
if !child.is(node) {
// 再帰的に解析(ただし walk は全ノードを返すので、
// 実際には再帰なしでフラットに処理される)
result.counts.total_nodes += 1;
if let Ok(class_obj) = child.getattr("__class__") {
if let Ok(name_obj) = class_obj.getattr("__name__") {
if let Ok(type_name) = name_obj.extract::<String>() {
match type_name.as_str() {
"FunctionDef" => {
result.counts.functions += 1;
result.counts.supported += 1;
}
"AsyncFunctionDef" => {
result.counts.functions += 1;
result.counts.unsupported += 1;
if !result.unsupported.contains(&"async function".to_string()) {
result.unsupported.push("async function".to_string());
}
}
"ClassDef" => {
result.counts.classes += 1;
result.counts.unsupported += 1;
if !result.unsupported.contains(&"class definition".to_string()) {
result.unsupported.push("class definition".to_string());
}
}
"For" | "While" | "If" => {
result.counts.supported += 1;
}
"Yield" | "YieldFrom" => {
result.counts.unsupported += 1;
if !result.unsupported.contains(&"generator".to_string()) {
result.unsupported.push("generator".to_string());
}
}
_ => {}
}
}
}
}
}
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_parse() {
pyo3::prepare_freethreaded_python();
Python::with_gil(|py| {
let code = "def main():\n return 0";
let result = parse_python_code(py, code);
assert!(result.success);
assert_eq!(result.counts.functions, 1);
assert_eq!(result.counts.supported, 1);
});
}
}

Binary file not shown.

View File

@ -0,0 +1,57 @@
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <string.h>
// FFI関数型定義
typedef int (*plugin_init_fn)(void);
typedef char* (*parse_fn)(const char*);
typedef void (*free_fn)(char*);
int main() {
// プラグインをロード
void* handle = dlopen("./target/release/libnyash_python_parser_plugin.so", RTLD_LAZY);
if (!handle) {
fprintf(stderr, "Failed to load plugin: %s\n", dlerror());
return 1;
}
// 関数を取得
plugin_init_fn init_fn = (plugin_init_fn)dlsym(handle, "nyash_plugin_init");
parse_fn parse_func = (parse_fn)dlsym(handle, "nyash_python_parse");
free_fn free_func = (free_fn)dlsym(handle, "nyash_python_free_string");
if (!init_fn || !parse_func || !free_func) {
fprintf(stderr, "Failed to load functions\n");
dlclose(handle);
return 1;
}
// 初期化
if (init_fn() != 0) {
fprintf(stderr, "Plugin init failed\n");
dlclose(handle);
return 1;
}
// 環境変数からコードを取得
const char* code = getenv("NYASH_PY_CODE");
if (!code) {
code = "def main():\n return 0";
printf("Using default code: %s\n", code);
} else {
printf("Parsing code from NYASH_PY_CODE: %s\n", code);
}
// パース実行
char* result = parse_func(code);
if (result) {
printf("\n=== Parse Result ===\n%s\n", result);
free_func(result);
} else {
printf("Parse failed\n");
}
dlclose(handle);
return 0;
}

View File

@ -98,7 +98,7 @@
* *
* ## ⚠️ 注意 * ## ⚠️ 注意
* - キーは自動的に文字列変換される * - キーは自動的に文字列変換される
* - スレッドセーフ (Arc<Mutex>使用) * - スレッドセーフ (Arc<RwLock>使用)
* - 大量データ格納時はメモリ使用量に注意 * - 大量データ格納時はメモリ使用量に注意
* - 存在しないキーの取得は "Key not found" メッセージ返却 * - 存在しないキーの取得は "Key not found" メッセージ返却
*/ */

View File

@ -131,8 +131,8 @@ impl NyashInterpreter {
} }
ASTNode::Include { filename, .. } => { ASTNode::Include { filename, .. } => {
self.execute_include(filename)?; // include式: 最初のstatic boxを返す
Ok(Box::new(VoidBox::new())) self.execute_include_expr(filename)
} }
ASTNode::FromCall { parent, method, arguments, .. } => { ASTNode::FromCall { parent, method, arguments, .. } => {

View File

@ -10,14 +10,53 @@ use super::*;
use crate::parser::NyashParser; use crate::parser::NyashParser;
impl NyashInterpreter { impl NyashInterpreter {
/// Resolve include path using nyash.toml [include.roots]
fn resolve_include_path(&self, filename: &str, caller_dir: Option<&str>) -> String {
// If explicit relative path, resolve relative to caller when provided
if filename.starts_with("./") || filename.starts_with("../") {
return filename.to_string();
}
// Try nyash.toml roots: key/path where key is first segment before '/'
let parts: Vec<&str> = filename.splitn(2, '/').collect();
if parts.len() == 2 {
let root = parts[0];
let rest = parts[1];
let cfg_path = "nyash.toml";
if let Ok(toml_str) = std::fs::read_to_string(cfg_path) {
if let Ok(toml_val) = toml::from_str::<toml::Value>(&toml_str) {
if let Some(include) = toml_val.get("include") {
if let Some(roots) = include.get("roots").and_then(|v| v.as_table()) {
if let Some(root_path_val) = roots.get(root).and_then(|v| v.as_str()) {
let mut base = root_path_val.to_string();
if !base.ends_with('/') && !base.ends_with('\\') { base.push('/'); }
let joined = format!("{}{}", base, rest);
return joined;
}
}
}
}
}
}
// Fallback: if caller_dir provided, join relative
if let Some(dir) = caller_dir {
if !filename.starts_with('/') && !filename.contains(":\\") && !filename.contains(":/") {
return format!("{}/{}", dir.trim_end_matches('/'), filename);
}
}
// Default to ./filename
format!("./{}", filename)
}
/// include文を実行ファイル読み込み・パース・実行 - File inclusion system /// include文を実行ファイル読み込み・パース・実行 - File inclusion system
pub(super) fn execute_include(&mut self, filename: &str) -> Result<(), RuntimeError> { pub(super) fn execute_include(&mut self, filename: &str) -> Result<(), RuntimeError> {
// パス正規化(簡易版 // パス解決nyash.toml include.roots + 相対
let canonical_path = if filename.starts_with("./") || filename.starts_with("../") { let mut canonical_path = self.resolve_include_path(filename, None);
filename.to_string() // 拡張子補完・index対応
} else { if std::path::Path::new(&canonical_path).is_dir() {
format!("./{}", filename) let idx = format!("{}/index.nyash", canonical_path.trim_end_matches('/'));
}; canonical_path = idx;
} else if std::path::Path::new(&canonical_path).extension().is_none() {
canonical_path.push_str(".nyash");
}
// 重複読み込みチェック // 重複読み込みチェック
if self.shared.included_files.lock().unwrap().contains(&canonical_path) { if self.shared.included_files.lock().unwrap().contains(&canonical_path) {
@ -45,6 +84,72 @@ impl NyashInterpreter {
Ok(()) Ok(())
} }
/// include式を実行ファイルを評価し、最初のstatic boxを返す
pub(super) fn execute_include_expr(&mut self, filename: &str) -> Result<Box<dyn NyashBox>, RuntimeError> {
// パス解決nyash.toml include.roots + 相対)
let mut canonical_path = self.resolve_include_path(filename, None);
// 拡張子補完・index対応
if std::path::Path::new(&canonical_path).is_dir() {
let idx = format!("{}/index.nyash", canonical_path.trim_end_matches('/'));
canonical_path = idx;
} else if std::path::Path::new(&canonical_path).extension().is_none() {
canonical_path.push_str(".nyash");
}
// ファイル読み込みstatic box名検出用
let content = std::fs::read_to_string(&canonical_path)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("Failed to read file '{}': {}", filename, e),
})?;
// パースして最初のstatic box名を特定
let ast = NyashParser::parse_from_string(&content)
.map_err(|e| RuntimeError::InvalidOperation {
message: format!("Parse error in '{}': {:?}", filename, e),
})?;
let mut static_names: Vec<String> = Vec::new();
if let crate::ast::ASTNode::Program { statements, .. } = &ast {
for st in statements {
if let crate::ast::ASTNode::BoxDeclaration { name, is_static, .. } = st {
if *is_static { static_names.push(name.clone()); }
}
}
}
if static_names.is_empty() {
return Err(RuntimeError::InvalidOperation { message: format!("include target '{}' does not define a static box", filename) });
}
if static_names.len() > 1 {
return Err(RuntimeError::InvalidOperation { message: format!("include target '{}' defines multiple static boxes; exactly one is required", filename) });
}
let box_name = static_names.remove(0);
// まだ未読なら評価(重複読み込みはスキップ)
let already = {
let set = self.shared.included_files.lock().unwrap();
set.contains(&canonical_path)
};
if !already {
self.shared.included_files.lock().unwrap().insert(canonical_path);
self.execute(ast)?;
}
// static boxを初期化・取得して返す
self.ensure_static_box_initialized(&box_name)?;
// statics名前空間からインスタンスを取り出す
let global_box = self.shared.global_box.lock()
.map_err(|_| RuntimeError::RuntimeFailure { message: "Failed to acquire global box lock".to_string() })?;
let statics = global_box.get_field("statics").ok_or(RuntimeError::TypeError { message: "statics namespace not found in GlobalBox".to_string() })?;
let statics_inst = statics.as_any().downcast_ref::<crate::instance_v2::InstanceBox>()
.ok_or(RuntimeError::TypeError { message: "statics field is not an InstanceBox".to_string() })?;
let value = statics_inst.get_field(&box_name)
.ok_or(RuntimeError::InvalidOperation { message: format!("Static box '{}' not found after include", box_name) })?;
Ok((*value).clone_or_share())
}
/// Arrow演算子を実行: sender >> receiver - Channel communication /// Arrow演算子を実行: sender >> receiver - Channel communication
pub(super) fn execute_arrow(&mut self, sender: &ASTNode, receiver: &ASTNode) pub(super) fn execute_arrow(&mut self, sender: &ASTNode, receiver: &ASTNode)
-> Result<Box<dyn NyashBox>, RuntimeError> { -> Result<Box<dyn NyashBox>, RuntimeError> {

4
src/jit/extern/birth.rs vendored Normal file
View File

@ -0,0 +1,4 @@
//! Generic birth hostcall symbol names
pub const SYM_BOX_BIRTH_H: &str = "nyash.box.birth_h";

4
src/jit/extern/handles.rs vendored Normal file
View File

@ -0,0 +1,4 @@
//! Handle-related extern symbol names
pub const SYM_HANDLE_OF: &str = "nyash.handle.of";

View File

@ -5,4 +5,5 @@
//! these externs once call emission is added. //! these externs once call emission is added.
pub mod collections; pub mod collections;
pub mod handles;
pub mod birth;

View File

@ -262,6 +262,22 @@ extern "C" fn nyash_plugin_invoke3_i64(type_id: i64, method_id: i64, argc: i64,
if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { return v as i64; } if let Some(v) = crate::runtime::plugin_ffi_common::decode::i32(payload) { return v as i64; }
if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); }
} }
8 => { // Handle(tag=8)
if sz == 8 {
let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]);
let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]);
let r_type = u32::from_le_bytes(t); let r_inst = u32::from_le_bytes(i);
let box_type_name = crate::runtime::plugin_loader_unified::get_global_plugin_host()
.read().ok()
.and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone()))
.and_then(|m| m.into_iter().find(|(_k,v)| *v == r_type).map(|(k,_v)| k))
.unwrap_or_else(|| "PluginBox".to_string());
let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2(box_type_name, r_type, r_inst, invoke.unwrap());
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(pb);
let h = crate::jit::rt::handles::to_handle(arc);
return h as i64;
}
}
1 => { // Bool 1 => { // Bool
return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 };
} }
@ -1084,6 +1100,22 @@ impl IRBuilder for CraneliftBuilder {
arg_vals.push(z); arg_vals.push(z);
} }
// Ensure receiver (a0) is a runtime handle via nyash.handle.of (Handle-First)
{
use cranelift_module::Linkage;
use crate::jit::r#extern::handles as h;
let call_conv_h = self.module.isa().default_call_conv();
let mut sig_h = Signature::new(call_conv_h);
sig_h.params.push(AbiParam::new(types::I64));
sig_h.returns.push(AbiParam::new(types::I64));
let func_id_h = self.module
.declare_function(h::SYM_HANDLE_OF, Linkage::Import, &sig_h)
.expect("declare handle.of failed");
let fref_h = self.module.declare_func_in_func(func_id_h, fb.func);
let call_h = fb.ins().call(fref_h, &[arg_vals[0]]);
if let Some(rv) = fb.inst_results(call_h).get(0).copied() { arg_vals[0] = rv; }
}
// Choose f64 shim if allowlisted // Choose f64 shim if allowlisted
let use_f64 = if has_ret { let use_f64 = if has_ret {
if let Ok(list) = std::env::var("NYASH_JIT_PLUGIN_F64") { if let Ok(list) = std::env::var("NYASH_JIT_PLUGIN_F64") {
@ -1136,6 +1168,23 @@ impl IRBuilder for CraneliftBuilder {
arg_vals.push(z); arg_vals.push(z);
} }
// Ensure receiver (a0) is a runtime handle via nyash.handle.of
{
use cranelift_module::Linkage;
use crate::jit::r#extern::handles as h;
let call_conv_h = self.module.isa().default_call_conv();
let mut sig_h = Signature::new(call_conv_h);
sig_h.params.push(AbiParam::new(types::I64));
sig_h.returns.push(AbiParam::new(types::I64));
let func_id_h = self.module
.declare_function(h::SYM_HANDLE_OF, Linkage::Import, &sig_h)
.expect("declare handle.of failed");
let fref_h = self.module.declare_func_in_func(func_id_h, fb.func);
let call_h = fb.ins().call(fref_h, &[arg_vals[0]]);
// Replace a0 with handle result
if let Some(rv) = fb.inst_results(call_h).get(0).copied() { arg_vals[0] = rv; }
}
// Signature: (i64 argc, i64 a0, i64 a1, i64 a2) -> i64 // Signature: (i64 argc, i64 a0, i64 a1, i64 a2) -> i64
let call_conv = self.module.isa().default_call_conv(); let call_conv = self.module.isa().default_call_conv();
let mut sig = Signature::new(call_conv); let mut sig = Signature::new(call_conv);
@ -1400,6 +1449,7 @@ pub struct ObjectBuilder {
desired_ret_is_f64: bool, desired_ret_is_f64: bool,
ret_hint_is_b1: bool, ret_hint_is_b1: bool,
local_slots: std::collections::HashMap<usize, cranelift_codegen::ir::StackSlot>, local_slots: std::collections::HashMap<usize, cranelift_codegen::ir::StackSlot>,
block_param_counts: std::collections::HashMap<usize, usize>,
pub stats: (u64,u64,u64,u64,u64), pub stats: (u64,u64,u64,u64,u64),
pub object_bytes: Option<Vec<u8>>, pub object_bytes: Option<Vec<u8>>,
} }
@ -1435,6 +1485,7 @@ impl ObjectBuilder {
desired_ret_is_f64: false, desired_ret_is_f64: false,
ret_hint_is_b1: false, ret_hint_is_b1: false,
local_slots: std::collections::HashMap::new(), local_slots: std::collections::HashMap::new(),
block_param_counts: std::collections::HashMap::new(),
stats: (0,0,0,0,0), stats: (0,0,0,0,0),
object_bytes: None, object_bytes: None,
} }
@ -1632,9 +1683,57 @@ impl IRBuilder for ObjectBuilder {
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); if self.blocks.len() < count { for _ in 0..(count - self.blocks.len()) { self.blocks.push(fb.create_block()); } } fb.finalize(); } 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); if self.blocks.len() < count { for _ in 0..(count - self.blocks.len()) { 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 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 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, _: usize, _: usize) { /* control-flow omitted for AOT PoC */ } fn br_if_top_is_true(&mut self, then_index: usize, else_index: usize) {
fn ensure_block_params_i64(&mut self, _index: usize, _count: usize) { /* PHI omitted for AOT PoC */ } use cranelift_codegen::ir::{types, condcodes::IntCC};
fn push_block_param_i64_at(&mut self, _pos: usize) { /* omitted */ } 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);
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 cond_b1 = if let Some(v) = self.value_stack.pop() {
let ty = fb.func.dfg.value_type(v);
if ty == types::I64 { fb.ins().icmp_imm(IntCC::NotEqual, v, 0) } else { v }
} else {
let z = fb.ins().iconst(types::I64, 0);
fb.ins().icmp_imm(IntCC::NotEqual, z, 0)
};
fb.ins().brif(cond_b1, self.blocks[then_index], &[], self.blocks[else_index], &[]);
self.stats.3 += 1;
fb.finalize();
}
fn ensure_block_params_i64(&mut self, index: usize, count: usize) {
use cranelift_frontend::FunctionBuilder;
if index >= self.blocks.len() { return; }
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
let have = self.block_param_counts.get(&index).copied().unwrap_or(0);
if count > have {
let b = self.blocks[index];
for _ in have..count { let _ = fb.append_block_param(b, cranelift_codegen::ir::types::I64); }
self.block_param_counts.insert(index, count);
}
fb.finalize();
}
fn push_block_param_i64_at(&mut self, pos: usize) {
use cranelift_frontend::FunctionBuilder;
use cranelift_codegen::ir::types;
let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc);
let b = if let Some(idx) = self.current_block_index { self.blocks[idx] } else if let Some(b) = self.entry_block { b } else { fb.create_block() };
fb.switch_to_block(b);
let params = fb.func.dfg.block_params(b).to_vec();
if let Some(v) = params.get(pos).copied() { self.value_stack.push(v); }
else { let z = fb.ins().iconst(types::I64, 0); self.value_stack.push(z); }
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();
}
fn hint_ret_bool(&mut self, is_b1: bool) { self.ret_hint_is_b1 = is_b1; } fn hint_ret_bool(&mut self, is_b1: bool) { self.ret_hint_is_b1 = is_b1; }
fn ensure_local_i64(&mut self, index: usize) { use cranelift_codegen::ir::{StackSlotData, StackSlotKind}; use cranelift_frontend::FunctionBuilder; if self.local_slots.contains_key(&index) { return; } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); let slot = fb.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8)); self.local_slots.insert(index, slot); fb.finalize(); } fn ensure_local_i64(&mut self, index: usize) { use cranelift_codegen::ir::{StackSlotData, StackSlotKind}; use cranelift_frontend::FunctionBuilder; if self.local_slots.contains_key(&index) { return; } let mut fb = FunctionBuilder::new(&mut self.ctx.func, &mut self.fbc); let slot = fb.create_sized_stack_slot(StackSlotData::new(StackSlotKind::ExplicitSlot, 8)); self.local_slots.insert(index, slot); fb.finalize(); }
fn store_local_i64(&mut self, index: usize) { use cranelift_codegen::ir::{types, condcodes::IntCC}; use cranelift_frontend::FunctionBuilder; if let Some(mut v) = self.value_stack.pop() { if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); } let slot = self.local_slots.get(&index).copied(); 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); } let ty = fb.func.dfg.value_type(v); if ty != types::I64 { if ty == types::F64 { v = fb.ins().fcvt_to_sint(types::I64, v); } else { let one = fb.ins().iconst(types::I64, 1); let zero = fb.ins().iconst(types::I64, 0); let b1 = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); v = fb.ins().select(b1, one, zero); } } if let Some(slot) = slot { fb.ins().stack_store(v, slot, 0); } fb.finalize(); } } fn store_local_i64(&mut self, index: usize) { use cranelift_codegen::ir::{types, condcodes::IntCC}; use cranelift_frontend::FunctionBuilder; if let Some(mut v) = self.value_stack.pop() { if !self.local_slots.contains_key(&index) { self.ensure_local_i64(index); } let slot = self.local_slots.get(&index).copied(); 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); } let ty = fb.func.dfg.value_type(v); if ty != types::I64 { if ty == types::F64 { v = fb.ins().fcvt_to_sint(types::I64, v); } else { let one = fb.ins().iconst(types::I64, 1); let zero = fb.ins().iconst(types::I64, 0); let b1 = fb.ins().icmp_imm(IntCC::NotEqual, v, 0); v = fb.ins().select(b1, one, zero); } } if let Some(slot) = slot { fb.ins().stack_store(v, slot, 0); } fb.finalize(); } }
@ -1653,7 +1752,8 @@ impl CraneliftBuilder {
builder.symbol("nyash.host.stub0", nyash_host_stub0 as *const u8); builder.symbol("nyash.host.stub0", nyash_host_stub0 as *const u8);
{ {
use crate::jit::r#extern::collections as c; use crate::jit::r#extern::collections as c;
use super::extern_thunks::{nyash_plugin_invoke_name_getattr_i64, nyash_plugin_invoke_name_call_i64}; use crate::jit::r#extern::{handles as h, birth as b};
use super::extern_thunks::{nyash_plugin_invoke_name_getattr_i64, nyash_plugin_invoke_name_call_i64, nyash_handle_of, nyash_box_birth_h, nyash_box_birth_i64};
builder.symbol(c::SYM_ARRAY_LEN, nyash_array_len as *const u8); builder.symbol(c::SYM_ARRAY_LEN, nyash_array_len as *const u8);
builder.symbol(c::SYM_ARRAY_GET, nyash_array_get as *const u8); builder.symbol(c::SYM_ARRAY_GET, nyash_array_get as *const u8);
builder.symbol(c::SYM_ARRAY_SET, nyash_array_set as *const u8); builder.symbol(c::SYM_ARRAY_SET, nyash_array_set as *const u8);
@ -1683,6 +1783,10 @@ impl CraneliftBuilder {
builder.symbol(c::SYM_STRING_CHARCODE_AT_H, nyash_string_charcode_at_h as *const u8); builder.symbol(c::SYM_STRING_CHARCODE_AT_H, nyash_string_charcode_at_h as *const u8);
builder.symbol(c::SYM_STRING_BIRTH_H, nyash_string_birth_h as *const u8); builder.symbol(c::SYM_STRING_BIRTH_H, nyash_string_birth_h as *const u8);
builder.symbol(c::SYM_INTEGER_BIRTH_H, nyash_integer_birth_h as *const u8); builder.symbol(c::SYM_INTEGER_BIRTH_H, nyash_integer_birth_h as *const u8);
builder.symbol(b::SYM_BOX_BIRTH_H, nyash_box_birth_h as *const u8);
builder.symbol("nyash.box.birth_i64", nyash_box_birth_i64 as *const u8);
// Handle helpers
builder.symbol(h::SYM_HANDLE_OF, nyash_handle_of as *const u8);
// Plugin invoke shims (i64/f64) // Plugin invoke shims (i64/f64)
builder.symbol("nyash_plugin_invoke3_i64", nyash_plugin_invoke3_i64 as *const u8); builder.symbol("nyash_plugin_invoke3_i64", nyash_plugin_invoke3_i64 as *const u8);
builder.symbol("nyash_plugin_invoke3_f64", nyash_plugin_invoke3_f64 as *const u8); builder.symbol("nyash_plugin_invoke3_f64", nyash_plugin_invoke3_f64 as *const u8);

View File

@ -467,6 +467,12 @@ impl LowerCore {
} }
if let Some(v) = self.known_i64.get(id).copied() { if let Some(v) = self.known_i64.get(id).copied() {
b.emit_const_i64(v); b.emit_const_i64(v);
return;
}
// Load from a local slot if this ValueId was previously materialized (e.g., handle results)
if let Some(slot) = self.local_index.get(id).copied() {
b.load_local_i64(slot);
return;
} }
} }
@ -534,36 +540,61 @@ impl LowerCore {
} }
} }
I::NewBox { dst, box_type, args } => { I::NewBox { dst, box_type, args } => {
// Minimal JIT lowering for builtin pluginized boxes: birth() via handle-based shim // 最適化は後段へ(現状は汎用・安全な実装に徹する)
if std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1") && args.is_empty() { // 通常経路:
let bt = box_type.as_str(); // - 引数なし: 汎用 birth_htype_idのみでハンドル生成
match bt { // - 引数あり: 既存のチェーン(直後の plugin_invoke birth で初期化)を維持(段階的導入)
"StringBox" => { if args.is_empty() {
// Emit host-call to create a new StringBox handle; push as i64 let decision = crate::jit::policy::invoke::decide_box_method(box_type, "birth", 0, true);
b.emit_host_call(crate::jit::r#extern::collections::SYM_STRING_BIRTH_H, 0, true); if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, .. } = decision {
} b.emit_const_i64(type_id as i64);
"IntegerBox" => { b.emit_host_call(crate::jit::r#extern::birth::SYM_BOX_BIRTH_H, 1, true);
b.emit_host_call(crate::jit::r#extern::collections::SYM_INTEGER_BIRTH_H, 0, true); self.handle_values.insert(*dst);
} let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
_ => { b.store_local_i64(slot);
// Any other NewBox (e.g., ArrayBox/MapBox/etc.) is UNSUPPORTED in JIT for now
// Allow plugin boxes to be created at runtime; treat as no-op for lowering
if bt != "PyRuntimeBox" && bt != "StringBox" && bt != "ConsoleBox" { self.unsupported += 1; }
}
}
} else {
// NewBox with args or NYASH_USE_PLUGIN_BUILTINS!=1
// Special-case: IntegerBox(v) → track known i64, but do not treat as unsupported
if box_type == "IntegerBox" {
if let Some(src) = args.get(0) { if let Some(iv) = self.known_i64.get(src).copied() { self.known_i64.insert(*dst, iv); } }
// no-op lowering; avoid marking unsupported
} else if box_type == "PyRuntimeBox" && args.is_empty() {
// Allow PyRuntimeBox creation as no-op in strict AOT path
} else if box_type == "StringBox" || box_type == "ConsoleBox" {
// Allow StringBox creation (with/without arg) as no-op; valueはシム/実行時にTLVへ
} else { } else {
self.unsupported += 1; self.unsupported += 1;
} }
} else {
// 引数あり: 安全なパターンから段階的に birth_i64 に切替
// 1) IntegerBox(const i64)
if box_type == "IntegerBox" && args.len() == 1 {
if let Some(src) = args.get(0) {
if let Some(iv) = self.known_i64.get(src).copied() {
// 汎用 birth_i64(type_id, argc=1, a1=iv)
if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, .. } = crate::jit::policy::invoke::decide_box_method(box_type, "birth", 1, true) {
b.emit_const_i64(type_id as i64);
b.emit_const_i64(1);
b.emit_const_i64(iv);
b.emit_host_call("nyash.box.birth_i64", 3, true);
self.handle_values.insert(*dst);
let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
b.store_local_i64(slot);
// 値伝搬も継続
self.known_i64.insert(*dst, iv);
return Ok(());
}
}
}
}
// 2) 引数がハンドルStringBox等で既に存在する場合最大2引数
if args.len() <= 2 && args.iter().all(|a| self.handle_values.contains(a)) {
if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, .. } = crate::jit::policy::invoke::decide_box_method(box_type, "birth", args.len(), true) {
b.emit_const_i64(type_id as i64);
b.emit_const_i64(args.len() as i64);
// a1, a2 を pushローカルに保存済みのハンドルをロード
for a in args.iter().take(2) { self.push_value_if_known_or_param(b, a); }
b.emit_host_call("nyash.box.birth_i64", 2 + args.len(), true);
self.handle_values.insert(*dst);
let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
b.store_local_i64(slot);
return Ok(());
}
}
// フォールバック: 既存チェーンに委譲(互換)+ 既知値伝搬のみ
if box_type == "IntegerBox" {
if let Some(src) = args.get(0) { if let Some(iv) = self.known_i64.get(src).copied() { self.known_i64.insert(*dst, iv); } }
}
} }
// Track boxed numeric literals to aid signature checks (FloatBox/IntegerBox) // Track boxed numeric literals to aid signature checks (FloatBox/IntegerBox)
if box_type == "FloatBox" { if box_type == "FloatBox" {
@ -603,15 +634,27 @@ impl LowerCore {
let argc = 1 + args.len(); let argc = 1 + args.len();
// push receiver param index (a0) if known // push receiver param index (a0) if known
if let Some(pidx) = self.param_index.get(box_val).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } if let Some(pidx) = self.param_index.get(box_val).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); }
// push primary arguments if availablea1, a2 ...
for a in args.iter() { self.push_value_if_known_or_param(b, a); }
b.emit_plugin_invoke_by_name(m, argc, dst.is_some()); b.emit_plugin_invoke_by_name(m, argc, dst.is_some());
if let Some(d) = dst { self.handle_values.insert(*d); } if let Some(d) = dst {
self.handle_values.insert(*d);
// Store handle result into a local slot so it can be used as argument later
let slot = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
b.store_local_i64(slot);
}
} else if self.handle_values.contains(box_val) && (m == "getattr" || m == "call") { } else if self.handle_values.contains(box_val) && (m == "getattr" || m == "call") {
let argc = 1 + args.len(); let argc = 1 + args.len();
// push receiver handle/param index if possible (here receiver is a handle result previously returned) // push receiver handle/param index if possible (here receiver is a handle result previously returned)
// We cannot reconstruct handle here; pass -1 to allow shim fallback. // We cannot reconstruct handle here; pass -1 to allow shim fallback.
b.emit_const_i64(-1); b.emit_const_i64(-1);
for a in args.iter() { self.push_value_if_known_or_param(b, a); }
b.emit_plugin_invoke_by_name(m, argc, dst.is_some()); b.emit_plugin_invoke_by_name(m, argc, dst.is_some());
if let Some(d) = dst { self.handle_values.insert(*d); } if let Some(d) = dst {
self.handle_values.insert(*d);
let slot = *self.local_index.entry(*d).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
b.store_local_i64(slot);
}
} else if (bt == "PyRuntimeBox" && (m == "birth" || m == "eval")) } else if (bt == "PyRuntimeBox" && (m == "birth" || m == "eval"))
|| (bt == "IntegerBox" && m == "birth") || (bt == "IntegerBox" && m == "birth")
|| (bt == "StringBox" && m == "birth") || (bt == "StringBox" && m == "birth")

View File

@ -11,6 +11,124 @@ use crate::runtime::plugin_loader_unified;
#[cfg(feature = "cranelift-jit")] #[cfg(feature = "cranelift-jit")]
use crate::runtime::plugin_loader_v2::PluginBoxV2; use crate::runtime::plugin_loader_v2::PluginBoxV2;
// ---- Generic Birth (handle) ----
#[cfg(feature = "cranelift-jit")]
pub(super) extern "C" fn nyash_box_birth_h(type_id: i64) -> i64 {
// Map type_id -> type name and create via plugin host; return runtime handle
if type_id <= 0 { return 0; }
let tid = type_id as u32;
let name_opt = crate::runtime::plugin_loader_unified::get_global_plugin_host()
.read().ok()
.and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone()))
.and_then(|m| m.into_iter().find(|(_k,v)| *v == tid).map(|(k,_v)| k));
if let Some(box_type) = name_opt {
if let Ok(host) = crate::runtime::get_global_plugin_host().read() {
if let Ok(b) = host.create_box(&box_type, &[]) {
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::from(b);
let h = crate::jit::rt::handles::to_handle(arc);
events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_h", "box_type": box_type, "type_id": tid, "handle": h}), "hostcall", "<jit>");
return h as i64;
} else {
events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_h", "error": "create_failed", "box_type": box_type, "type_id": tid}), "hostcall", "<jit>");
}
}
} else {
events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_h", "error": "type_map_failed", "type_id": tid}), "hostcall", "<jit>");
}
0
}
// Generic birth with args on JIT side: (type_id, argc, a1, a2) -> handle
#[cfg(feature = "cranelift-jit")]
pub(super) extern "C" fn nyash_box_birth_i64(type_id: i64, argc: i64, a1: i64, a2: i64) -> i64 {
use crate::runtime::plugin_loader_v2::PluginBoxV2;
if type_id <= 0 { return 0; }
// Resolve invoke for the type by creating a temp instance
let mut invoke: Option<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize)->i32> = None;
let mut box_type = String::new();
if let Some(name) = crate::runtime::plugin_loader_unified::get_global_plugin_host()
.read().ok()
.and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone()))
.and_then(|m| m.into_iter().find(|(_k,v)| *v == (type_id as u32)).map(|(k,_v)| k))
{
box_type = name;
if let Ok(host) = crate::runtime::get_global_plugin_host().read() {
if let Ok(b) = host.create_box(&box_type, &[]) {
if let Some(p) = b.as_any().downcast_ref::<PluginBoxV2>() { invoke = Some(p.inner.invoke_fn); }
}
}
}
if invoke.is_none() {
events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_i64", "error": "no_invoke", "type_id": type_id}), "hostcall", "<jit>");
return 0;
}
let method_id: u32 = 0; let instance_id: u32 = 0;
// Build TLV from a1/a2
let nargs = argc.max(0) as usize;
let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16);
let mut encode_val = |h: i64| {
if h > 0 {
if let Some(obj) = crate::jit::rt::handles::get(h as u64) {
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
let host = crate::runtime::get_global_plugin_host();
if let Ok(hg) = host.read() {
if p.box_type == "StringBox" {
if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) {
if let Some(s) = sb.as_any().downcast_ref::<crate::box_trait::StringBox>() { crate::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value); return; }
}
} else if p.box_type == "IntegerBox" {
if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) {
if let Some(i) = ibx.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { crate::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value); return; }
}
}
}
crate::runtime::plugin_ffi_common::encode::plugin_handle(&mut buf, p.inner.type_id, p.instance_id());
return;
}
}
}
crate::runtime::plugin_ffi_common::encode::i64(&mut buf, h);
};
if nargs >= 1 { encode_val(a1); }
if nargs >= 2 { encode_val(a2); }
// Invoke
let mut out = vec![0u8; 1024]; let mut out_len: usize = out.len();
let rc = unsafe { invoke.unwrap()(type_id as u32, method_id, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) };
if rc != 0 { events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_i64", "error": "invoke_failed", "type_id": type_id}), "hostcall", "<jit>"); return 0; }
if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
if tag == 8 && payload.len()==8 {
let mut t=[0u8;4]; t.copy_from_slice(&payload[0..4]); let mut i=[0u8;4]; i.copy_from_slice(&payload[4..8]);
let r_type = u32::from_le_bytes(t); let r_inst = u32::from_le_bytes(i);
let pb = crate::runtime::plugin_loader_v2::make_plugin_box_v2(box_type.clone(), r_type, r_inst, invoke.unwrap());
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(pb);
let h = crate::jit::rt::handles::to_handle(arc);
events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_i64", "box_type": box_type, "type_id": type_id, "argc": nargs, "handle": h}), "hostcall", "<jit>");
return h as i64;
}
}
events::emit_runtime(serde_json::json!({"id": "nyash.box.birth_i64", "error": "decode_failed", "type_id": type_id}), "hostcall", "<jit>");
0
}
// ---- Handle helpers ----
#[cfg(feature = "cranelift-jit")]
pub(super) extern "C" fn nyash_handle_of(v: i64) -> i64 {
// If already a positive handle, pass through
if v > 0 { return v; }
// Otherwise interpret as legacy param index and convert BoxRef -> handle
if v >= 0 {
let idx = v as usize;
let mut out: i64 = 0;
crate::jit::rt::with_legacy_vm_args(|args| {
if let Some(crate::backend::vm::VMValue::BoxRef(b)) = args.get(idx) {
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::from(b.clone());
out = crate::jit::rt::handles::to_handle(arc) as i64;
}
});
return out;
}
0
}
// ---- Math (native f64) ---- // ---- Math (native f64) ----
#[cfg(feature = "cranelift-jit")] #[cfg(feature = "cranelift-jit")]
pub(super) extern "C" fn nyash_math_sin_f64(x: f64) -> f64 { x.sin() } pub(super) extern "C" fn nyash_math_sin_f64(x: f64) -> f64 { x.sin() }
@ -243,7 +361,7 @@ pub(super) extern "C" fn nyash_plugin_invoke_name_call_i64(argc: i64, a0: i64, a
nyash_plugin_invoke_name_common_i64("call", argc, a0, a1, a2) nyash_plugin_invoke_name_common_i64("call", argc, a0, a1, a2)
} }
#[cfg(feature = "cranelift-jit")] #[cfg(feature = "cranelift-jit")]
fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, _a1: i64, _a2: i64) -> i64 { fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64, a2: i64) -> i64 {
// Resolve receiver // Resolve receiver
let mut instance_id: u32 = 0; let mut instance_id: u32 = 0;
let mut type_id: u32 = 0; let mut type_id: u32 = 0;
@ -280,14 +398,36 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, _a1: i6
} }
}); });
} }
if invoke.is_none() { return 0; } if invoke.is_none() { events::emit_runtime(serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "error": "no_invoke"}), "hostcall", "<jit>"); return 0; }
let box_type = box_type.unwrap_or_default(); let box_type = box_type.unwrap_or_default();
// Resolve method_id via PluginHost // Resolve method_id via PluginHost
let mh = if let Ok(host) = plugin_loader_unified::get_global_plugin_host().read() { host.resolve_method(&box_type, method) } else { return 0 }; let mh = if let Ok(host) = plugin_loader_unified::get_global_plugin_host().read() { host.resolve_method(&box_type, method) } else { events::emit_runtime(serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "error": "host_read_failed"}), "hostcall", "<jit>"); return 0 };
let method_id = match mh { Ok(h) => h.method_id, Err(_) => return 0 } as u32; let method_id = match mh { Ok(h) => h.method_id, Err(_) => { events::emit_runtime(serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "error": "resolve_failed"}), "hostcall", "<jit>"); return 0 } } as u32;
// Build TLV args from legacy (skip receiver=pos0) // Build TLV args from a1/a2 preferring handles; fallback to legacy (skip receiver=pos0)
let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header((argc.saturating_sub(1).max(0) as u16)); let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header(argc.saturating_sub(1).max(0) as u16);
let mut add_from_legacy = |pos: usize| { let mut encode_arg = |val: i64, pos: usize| {
let mut appended = false;
if val > 0 {
if let Some(obj) = crate::jit::rt::handles::get(val as u64) {
if let Some(p) = obj.as_any().downcast_ref::<PluginBoxV2>() {
let host = crate::runtime::get_global_plugin_host();
if let Ok(hg) = host.read() {
if p.box_type == "StringBox" {
if let Ok(Some(sb)) = hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) {
if let Some(s) = sb.as_any().downcast_ref::<crate::box_trait::StringBox>() { crate::runtime::plugin_ffi_common::encode::string(&mut buf, &s.value); appended = true; }
}
} else if p.box_type == "IntegerBox" {
if let Ok(Some(ibx)) = hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) {
if let Some(i) = ibx.as_any().downcast_ref::<crate::box_trait::IntegerBox>() { crate::runtime::plugin_ffi_common::encode::i64(&mut buf, i.value); appended = true; }
}
}
}
if !appended { crate::runtime::plugin_ffi_common::encode::plugin_handle(&mut buf, p.inner.type_id, p.instance_id()); appended = true; }
}
}
}
if !appended {
// Fallback: encode from legacy VM args at position
crate::jit::rt::with_legacy_vm_args(|args| { crate::jit::rt::with_legacy_vm_args(|args| {
if let Some(v) = args.get(pos) { if let Some(v) = args.get(pos) {
use crate::backend::vm::VMValue as V; use crate::backend::vm::VMValue as V;
@ -317,23 +457,28 @@ fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, _a1: i6
} }
_ => {} _ => {}
} }
} else {
// No legacy arg: encode raw i64 as last resort
crate::runtime::plugin_ffi_common::encode::i64(&mut buf, val);
} }
}); });
}
}; };
if argc >= 2 { add_from_legacy(1); } if argc >= 2 { encode_arg(a1, 1); }
if argc >= 3 { add_from_legacy(2); } if argc >= 3 { encode_arg(a2, 2); }
let mut out = vec![0u8; 4096]; let mut out_len: usize = out.len(); let mut out = vec![0u8; 4096]; let mut out_len: usize = out.len();
let rc = unsafe { invoke.unwrap()(type_id as u32, method_id, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) }; let rc = unsafe { invoke.unwrap()(type_id as u32, method_id, instance_id, buf.as_ptr(), buf.len(), out.as_mut_ptr(), &mut out_len) };
if rc != 0 { return 0; } if rc != 0 { events::emit_runtime(serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "error": "invoke_failed"}), "hostcall", "<jit>"); return 0; }
let out_slice = &out[..out_len]; let out_slice = &out[..out_len];
if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) { if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(out_slice) {
match tag { match tag {
3 => { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } } 3 => { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); return i64::from_le_bytes(b); } }
1 => { return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; } 1 => { return if crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false) { 1 } else { 0 }; }
5 => { if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref()==Some("1") { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); let f=f64::from_le_bytes(b); return f as i64; } } } 5 => { if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref()==Some("1") { if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); let f=f64::from_le_bytes(b); return f as i64; } } }
_ => {} _ => { events::emit_runtime(serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "warn": "first_tlv_not_primitive_or_handle", "tag": tag}), "hostcall", "<jit>"); }
} }
} }
events::emit_runtime(serde_json::json!({"id": "plugin_invoke_by_name", "method": method, "box_type": box_type, "error": "decode_failed"}), "hostcall", "<jit>");
0 0
} }

View File

@ -137,6 +137,18 @@ impl JitManager {
/// 10_c: execute compiled function if present (stub: empty args). Returns Some(VMValue) if JIT path was taken. /// 10_c: execute compiled function if present (stub: empty args). Returns Some(VMValue) if JIT path was taken.
pub fn execute_compiled(&mut self, func: &str, ret_ty: &crate::mir::MirType, args: &[crate::backend::vm::VMValue]) -> Option<crate::backend::vm::VMValue> { pub fn execute_compiled(&mut self, func: &str, ret_ty: &crate::mir::MirType, args: &[crate::backend::vm::VMValue]) -> Option<crate::backend::vm::VMValue> {
// Strict/FailFastモードではJITは"コンパイル専用"(実行しない)
if std::env::var("NYASH_JIT_STRICT").ok().as_deref() == Some("1") {
// 観測のためイベントだけ出す
crate::jit::events::emit_runtime(
serde_json::json!({
"id": "jit_skip_execute_strict",
"func": func
}),
"jit", func
);
return None;
}
if let Some(h) = self.handle_of(func) { if let Some(h) = self.handle_of(func) {
// Expose args to both legacy VM hostcalls and new JIT ABI TLS // Expose args to both legacy VM hostcalls and new JIT ABI TLS
crate::jit::rt::set_legacy_vm_args(args); crate::jit::rt::set_legacy_vm_args(args);

View File

@ -14,6 +14,36 @@ use super::slot_registry::resolve_slot_by_type_name;
use crate::ast::{ASTNode, LiteralValue, BinaryOperator}; use crate::ast::{ASTNode, LiteralValue, BinaryOperator};
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::fs;
fn resolve_include_path_builder(filename: &str) -> String {
// If relative path provided, keep as is
if filename.starts_with("./") || filename.starts_with("../") {
return filename.to_string();
}
// Try nyash.toml roots: key/rest
let parts: Vec<&str> = filename.splitn(2, '/').collect();
if parts.len() == 2 {
let root = parts[0];
let rest = parts[1];
let cfg_path = "nyash.toml";
if let Ok(toml_str) = fs::read_to_string(cfg_path) {
if let Ok(toml_val) = toml::from_str::<toml::Value>(&toml_str) {
if let Some(include) = toml_val.get("include") {
if let Some(roots) = include.get("roots").and_then(|v| v.as_table()) {
if let Some(root_path) = roots.get(root).and_then(|v| v.as_str()) {
let mut base = root_path.to_string();
if !base.ends_with('/') && !base.ends_with('\\') { base.push('/'); }
return format!("{}{}", base, rest);
}
}
}
}
}
}
// Default to ./filename
format!("./{}", filename)
}
fn builder_debug_enabled() -> bool { fn builder_debug_enabled() -> bool {
std::env::var("NYASH_BUILDER_DEBUG").is_ok() std::env::var("NYASH_BUILDER_DEBUG").is_ok()
@ -469,6 +499,35 @@ impl MirBuilder {
self.build_await_expression(*expression.clone()) self.build_await_expression(*expression.clone())
}, },
ASTNode::Include { filename, .. } => {
// Resolve and read included file
let mut path = resolve_include_path_builder(&filename);
if std::path::Path::new(&path).is_dir() {
path = format!("{}/index.nyash", path.trim_end_matches('/'));
} else if std::path::Path::new(&path).extension().is_none() {
path.push_str(".nyash");
}
let content = fs::read_to_string(&path)
.map_err(|e| format!("Include read error '{}': {}", filename, e))?;
// Parse to AST
let included_ast = crate::parser::NyashParser::parse_from_string(&content)
.map_err(|e| format!("Include parse error '{}': {:?}", filename, e))?;
// Find first static box name
let mut box_name: Option<String> = None;
if let crate::ast::ASTNode::Program { statements, .. } = &included_ast {
for st in statements {
if let crate::ast::ASTNode::BoxDeclaration { name, is_static, .. } = st {
if *is_static { box_name = Some(name.clone()); break; }
}
}
}
let bname = box_name.ok_or_else(|| format!("Include target '{}' has no static box", filename))?;
// Lower included AST into current MIR (register types/methods)
let _ = self.build_expression(included_ast)?;
// Return a new instance of included box (no args)
self.build_new_expression(bname, vec![])
},
_ => { _ => {
Err(format!("Unsupported AST node type: {:?}", ast)) Err(format!("Unsupported AST node type: {:?}", ast))
} }
@ -1140,6 +1199,16 @@ impl MirBuilder {
fn build_new_expression(&mut self, class: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> { fn build_new_expression(&mut self, class: String, arguments: Vec<ASTNode>) -> Result<ValueId, String> {
// Phase 9.78a: Unified Box creation using NewBox instruction // Phase 9.78a: Unified Box creation using NewBox instruction
// Optimization: Primitive wrappers → emit Const directly when possible
if class == "IntegerBox" && arguments.len() == 1 {
if let ASTNode::Literal { value: LiteralValue::Integer(n), .. } = arguments[0].clone() {
let dst = self.value_gen.next();
self.emit_instruction(MirInstruction::Const { dst, value: ConstValue::Integer(n) })?;
self.value_types.insert(dst, super::MirType::Integer);
return Ok(dst);
}
}
// First, evaluate all arguments to get their ValueIds // First, evaluate all arguments to get their ValueIds
let mut arg_values = Vec::new(); let mut arg_values = Vec::new();
for arg in arguments { for arg in arguments {
@ -1169,8 +1238,8 @@ impl MirBuilder {
// Record origin for optimization: dst was created by NewBox of class // Record origin for optimization: dst was created by NewBox of class
self.value_origin_newbox.insert(dst, class.clone()); self.value_origin_newbox.insert(dst, class.clone());
// Immediately call birth(...) on the created instance to run constructor semantics. // For plugin/builtin boxes, call birth(...). For user-defined boxes, skip (InstanceBox already constructed)
// birth typically returns void; we don't capture the result here (dst: None) if !self.user_defined_boxes.contains(&class) {
let birt_mid = resolve_slot_by_type_name(&class, "birth"); let birt_mid = resolve_slot_by_type_name(&class, "birth");
self.emit_box_or_plugin_call( self.emit_box_or_plugin_call(
None, None,
@ -1180,6 +1249,7 @@ impl MirBuilder {
arg_values, arg_values,
EffectMask::READ.add(Effect::ReadHeap), EffectMask::READ.add(Effect::ReadHeap),
)?; )?;
}
Ok(dst) Ok(dst)
} }

View File

@ -286,6 +286,10 @@ impl NyashParser {
/// 基本式をパース: リテラル、変数、括弧、this、new /// 基本式をパース: リテラル、変数、括弧、this、new
fn parse_primary(&mut self) -> Result<ASTNode, ParseError> { fn parse_primary(&mut self) -> Result<ASTNode, ParseError> {
match &self.current_token().token_type { match &self.current_token().token_type {
TokenType::INCLUDE => {
// Allow include as an expression: include "path"
self.parse_include()
}
TokenType::STRING(s) => { TokenType::STRING(s) => {
let value = s.clone(); let value = s.clone();
self.advance(); self.advance();

View File

@ -104,10 +104,79 @@ impl NyashRunner {
/// Collect Box declarations from AST and register into runtime /// Collect Box declarations from AST and register into runtime
pub(crate) fn collect_box_declarations(&self, ast: &ASTNode, runtime: &NyashRuntime) { pub(crate) fn collect_box_declarations(&self, ast: &ASTNode, runtime: &NyashRuntime) {
fn resolve_include_path(filename: &str) -> String {
if filename.starts_with("./") || filename.starts_with("../") { return filename.to_string(); }
let parts: Vec<&str> = filename.splitn(2, '/').collect();
if parts.len() == 2 {
let root = parts[0]; let rest = parts[1];
let cfg_path = "nyash.toml";
if let Ok(toml_str) = std::fs::read_to_string(cfg_path) {
if let Ok(toml_val) = toml::from_str::<toml::Value>(&toml_str) {
if let Some(include) = toml_val.get("include") {
if let Some(roots) = include.get("roots").and_then(|v| v.as_table()) {
if let Some(base) = roots.get(root).and_then(|v| v.as_str()) {
let mut b = base.to_string(); if !b.ends_with('/') && !b.ends_with('\\') { b.push('/'); }
return format!("{}{}", b, rest);
}
}
}
}
}
}
format!("./{}", filename)
}
fn walk(node: &ASTNode, runtime: &NyashRuntime) { fn walk(node: &ASTNode, runtime: &NyashRuntime) {
match node { match node {
ASTNode::Program { statements, .. } => { for st in statements { walk(st, runtime); } } ASTNode::Program { statements, .. } => { for st in statements { walk(st, runtime); } }
ASTNode::FunctionDeclaration { body, .. } => { for st in body { walk(st, runtime); } } ASTNode::FunctionDeclaration { body, .. } => { for st in body { walk(st, runtime); } }
ASTNode::Include { filename, .. } => {
let mut path = resolve_include_path(filename);
if std::path::Path::new(&path).is_dir() {
path = format!("{}/index.nyash", path.trim_end_matches('/'));
} else if std::path::Path::new(&path).extension().is_none() {
path.push_str(".nyash");
}
if let Ok(content) = std::fs::read_to_string(&path) {
if let Ok(inc_ast) = NyashParser::parse_from_string(&content) {
walk(&inc_ast, runtime);
}
}
}
ASTNode::Assignment { target, value, .. } => {
walk(target, runtime); walk(value, runtime);
}
ASTNode::Return { value, .. } => { if let Some(v) = value { walk(v, runtime); } }
ASTNode::Print { expression, .. } => { walk(expression, runtime); }
ASTNode::If { condition, then_body, else_body, .. } => {
walk(condition, runtime);
for st in then_body { walk(st, runtime); }
if let Some(eb) = else_body { for st in eb { walk(st, runtime); } }
}
ASTNode::Loop { condition, body, .. } => {
walk(condition, runtime); for st in body { walk(st, runtime); }
}
ASTNode::TryCatch { try_body, catch_clauses, finally_body, .. } => {
for st in try_body { walk(st, runtime); }
for cc in catch_clauses { for st in &cc.body { walk(st, runtime); } }
if let Some(fb) = finally_body { for st in fb { walk(st, runtime); } }
}
ASTNode::Throw { expression, .. } => { walk(expression, runtime); }
ASTNode::Local { initial_values, .. } => {
for iv in initial_values { if let Some(v) = iv { walk(v, runtime); } }
}
ASTNode::Outbox { initial_values, .. } => {
for iv in initial_values { if let Some(v) = iv { walk(v, runtime); } }
}
ASTNode::FunctionCall { arguments, .. } => { for a in arguments { walk(a, runtime); } }
ASTNode::MethodCall { object, arguments, .. } => { walk(object, runtime); for a in arguments { walk(a, runtime); } }
ASTNode::FieldAccess { object, .. } => { walk(object, runtime); }
ASTNode::New { arguments, .. } => { for a in arguments { walk(a, runtime); } }
ASTNode::BinaryOp { left, right, .. } => { walk(left, runtime); walk(right, runtime); }
ASTNode::UnaryOp { operand, .. } => { walk(operand, runtime); }
ASTNode::AwaitExpression { expression, .. } => { walk(expression, runtime); }
ASTNode::Arrow { sender, receiver, .. } => { walk(sender, runtime); walk(receiver, runtime); }
ASTNode::Nowait { expression, .. } => { walk(expression, runtime); }
ASTNode::BoxDeclaration { name, fields, public_fields, private_fields, methods, constructors, init_fields, weak_fields, is_interface, extends, implements, type_parameters, .. } => { ASTNode::BoxDeclaration { name, fields, public_fields, private_fields, methods, constructors, init_fields, weak_fields, is_interface, extends, implements, type_parameters, .. } => {
for (_mname, mnode) in methods { walk(mnode, runtime); } for (_mname, mnode) in methods { walk(mnode, runtime); }
for (_ckey, cnode) in constructors { walk(cnode, runtime); } for (_ckey, cnode) in constructors { walk(cnode, runtime); }

View File

@ -1,7 +1,7 @@
//! Boxファクトリレジストリ - Box生成の中央管理 //! Boxファクトリレジストリ - Box生成の中央管理
//! //!
//! ビルトインBoxとプラグインBoxを統一的に管理し、 //! プラグインBoxを中心にBox生成を管理するPlugin-First
//! 透過的な置き換えを実現する //! 旧ビルトイン経路は互換目的のAPIとして最小限に保持テスト用途
use crate::box_trait::NyashBox; use crate::box_trait::NyashBox;
use crate::runtime::plugin_config::PluginConfig; use crate::runtime::plugin_config::PluginConfig;
@ -10,14 +10,14 @@ use std::sync::{Arc, RwLock};
/// Box生成方法を表す列挙型 /// Box生成方法を表す列挙型
pub enum BoxProvider { pub enum BoxProvider {
/// ビルトイン実装Rust関数 /// 互換用ビルトイン実装Rust関数、現在は原則未使用
Builtin(BoxConstructor), Builtin(BoxConstructor),
/// プラグイン実装(プラグイン名を保持) /// プラグイン実装(プラグイン名を保持)
Plugin(String), Plugin(String),
} }
/// ビルトインBoxのコンストラクタ関数型 /// 互換用ビルトインBoxのコンストラクタ関数型
pub type BoxConstructor = fn(&[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, String>; pub type BoxConstructor = fn(&[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, String>;
/// Boxファクトリレジストリ /// Boxファクトリレジストリ
@ -34,7 +34,7 @@ impl BoxFactoryRegistry {
} }
} }
/// ビルトインBoxを登録 /// 互換用ビルトインBoxを登録(通常は使用しない)
pub fn register_builtin(&self, name: &str, constructor: BoxConstructor) { pub fn register_builtin(&self, name: &str, constructor: BoxConstructor) {
let mut providers = self.providers.write().unwrap(); let mut providers = self.providers.write().unwrap();
providers.insert(name.to_string(), BoxProvider::Builtin(constructor)); providers.insert(name.to_string(), BoxProvider::Builtin(constructor));

View File

@ -34,6 +34,8 @@ mod enabled {
invoke_fn: unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32, invoke_fn: unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32,
} }
// (moved: public constructor wrapper is declared after the enabled module)
/// v2 Plugin Box wrapper - temporary implementation /// v2 Plugin Box wrapper - temporary implementation
#[derive(Debug)] #[derive(Debug)]
pub struct PluginHandleInner { pub struct PluginHandleInner {
@ -103,6 +105,11 @@ mod enabled {
} }
} }
/// Helper to construct a PluginBoxV2 from raw ids and invoke pointer safely
pub fn make_plugin_box_v2_inner(box_type: String, type_id: u32, instance_id: u32, invoke_fn: unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize) -> i32) -> PluginBoxV2 {
PluginBoxV2 { box_type, inner: std::sync::Arc::new(PluginHandleInner { type_id, invoke_fn, instance_id, fini_method_id: None, finalized: std::sync::atomic::AtomicBool::new(false) }) }
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PluginBoxV2 { pub struct PluginBoxV2 {
pub box_type: String, pub box_type: String,
@ -1207,6 +1214,10 @@ impl PluginLoaderV2 {
} }
} }
// Public constructor wrapper for PluginBoxV2 (enabled build)
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
pub use self::enabled::make_plugin_box_v2_inner as make_plugin_box_v2;
#[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))] #[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))]
mod stub { mod stub {
use crate::bid::{BidResult, BidError}; use crate::bid::{BidResult, BidError};

View File

@ -0,0 +1,51 @@
// Nyash-side Python compiler (Phase 10.7 C2 - minimal)
// Orchestrates: Parser(JSON) -> IR(JSON) -> Nyash source (string)
static box PyCompiler {
parseToJson() {
// Use Nyash-only parser via PyRuntimeBox (no Rust build)
local p
p = new PythonParserNy()
return p.parse("")
}
buildIRFromParse(json) {
// Minimal: analyze Python source from env and build IR with a constant return when possible
// Python snippet: parse code from os.getenv("NYASH_PY_CODE"), find first Return(Constant)
local os, getenv, src, ast, parsef, walkf, first_ret
local py = new PyRuntimeBox()
os = py.import("os")
getenv = os.getattr("getenv")
src = getenv.call("NYASH_PY_CODE").toString()
if (src == null || src == "") {
return new StringBox("{\"module\":{\"functions\":[{\"name\":\"main\",\"return_value\":0}]}}")
}
ast = py.import("ast")
parsef = ast.getattr("parse")
local tree
tree = parsef.call(src)
// Walk via Python: extract first constant return value if present
local res
res = py.eval("next((n.value.value for n in __import__('ast').walk(__import__('ast').parse(__import__('os').getenv('NYASH_PY_CODE') or '')) if isinstance(n, __import__('ast').Return) and isinstance(n.value, __import__('ast').Constant)), None)")
// If number -> return that number; if string -> quoted
local val
val = res.str()
if (val.matches("^[0-9]+$")) {
return new StringBox("{\"module\":{\"functions\":[{\"name\":\"main\",\"return_value\":" + val + "]}}")
} else if (val.length() > 0) {
return new StringBox("{\"module\":{\"functions\":[{\"name\":\"main\",\"return_value\":\"" + val + "\"}]}}")
} else {
return new StringBox("{\"module\":{\"functions\":[{\"name\":\"main\",\"return_value\":0}]}}")
}
}
irToNyashSource(irJson) {
// Build Nyash source from IR (minimal: assume IR has return_value embedded by PyIR.buildReturn)
local src
src = "static box Generated {\n main() {\n return 0\n }\n}"
// Future: parse irJson and extract return_value
return new StringBox(src)
}
}

11
tools/pyc/PyIR.nyash Normal file
View File

@ -0,0 +1,11 @@
// Minimal IR helper (Phase 10.7 C2)
// Builds small JSON snippets for the compiler pipeline
static box PyIR {
buildReturn(value) {
local s
// value can be integer or string literal already
s = "{\"module\":{\"functions\":[{\"name\":\"main\",\"return_value\":" + value + "}]}}"
return new StringBox(s)
}
}

View File

@ -0,0 +1,32 @@
// Nyash-only Python parser using PyRuntimeBox (no Rust build)
static box PythonParserNy {
parse(code) {
local src
if (code == null || code.toString() == "") {
// Fallback: read from env via Python os.getenv
local os, getenv
local py = new PyRuntimeBox()
os = py.import("os")
getenv = os.getattr("getenv")
src = getenv.call("NYASH_PY_CODE").toString()
} else {
src = code.toString()
}
// ast.dump(ast.parse(src))
local ast, parsef, dumpf, tree, dump, dump_str
local py2 = new PyRuntimeBox()
ast = py2.import("ast")
parsef = ast.getattr("parse")
tree = parsef.call(src)
dumpf = ast.getattr("dump")
dump = dumpf.call(tree)
dump_str = dump.str()
// Minimal JSON (escape quotes minimally by replacing \" -> \\\" )
// Note: for robust escaping, prefer Python json.dumps in a later pass
local esc
esc = dump_str.replace("\"", "\\\"")
return new StringBox("{\"ast\":\"Module\",\"dump\":\"" + esc + "\"}")
}
}

24
tools/pyc/README.md Normal file
View File

@ -0,0 +1,24 @@
# Nyash Python Compiler (Phase 10.7 Workbench)
目的: Parser(プラグイン) → Nyash側コンパイラ → Nyashソース → 既存AOT までの最短ルートを、Nyashだけで段階実装する作業場。
## 構成
- `pyc.nyash` — エントリ(最小パイプライン実行)
- `PyCompiler.nyash` — Nyash側コンパイラ本体C2で拡張
- `PyIR.nyash` — IR生成/整形のヘルパ(最小)
## 使い方(最小)
```bash
# 1) NYASH_PY_CODE に Python コードを入れるParserプラグインが拾う
NYASH_PY_CODE=$'def main():\n return 0' \
./target/release/nyash --backend vm tools/pyc/pyc.nyash
```
出力
- Parser JSONdump/counts/unsupported
- 生成された Nyash ソース(現状は最小: return 0
## 次の拡張
- Parser JSON → IR(JSON) への変換def/return最小
- IR → Nyash 生成If/Return/Assign へ拡張)
- All-or-Nothing 運用unsupported_nodes を見て Strict に弾くスイッチ)

24
tools/pyc/pyc.nyash Normal file
View File

@ -0,0 +1,24 @@
// Nyash Python Compiler entry (Phase 10.7 workbench)
// Nyash-only pipeline: Parser/Compiler are implemented in Nyash using PyRuntimeBox
// Usage:
// NYASH_PY_CODE=$'def main():\n return 0' ./target/release/nyash --backend vm tools/pyc/pyc.nyash
static box Main {
main() {
// Load modules via include (returns module boxes)
local PyIR = include "tools/pyc/PyIR.nyash"
local Parser = include "tools/pyc/PythonParserNy.nyash"
local Compiler = include "tools/pyc/PyCompiler.nyash"
local json, ir, src
// Skip echo of source to avoid plugin toString issues
json = Compiler.parseToJson()
ir = Compiler.buildIRFromParse(json)
src = Compiler.irToNyashSource(ir)
return 0
}
}

View File

@ -17,6 +17,7 @@ TEST_FILES=(
"examples/aot_min_string_len.nyash" "examples/aot_min_string_len.nyash"
"examples/aot_string_len_simple.nyash" "examples/aot_string_len_simple.nyash"
"examples/jit_stats_bool_ret.nyash" "examples/jit_stats_bool_ret.nyash"
"examples/aot_py_min_chain.nyash"
) )
# Counter for results # Counter for results
@ -35,7 +36,7 @@ run_test() {
# Run with VM backend # Run with VM backend
echo -n " VM execution... " echo -n " VM execution... "
if NYASH_USE_PLUGIN_BUILTINS=1 ./target/release/nyash --backend vm "$test_file" > /tmp/${test_name}_vm.out 2>&1; then if NYASH_USE_PLUGIN_BUILTINS=1 NYASH_PY_AUTODECODE=1 ./target/release/nyash --backend vm "$test_file" > /tmp/${test_name}_vm.out 2>&1; then
VM_RESULT=$(tail -1 /tmp/${test_name}_vm.out | grep -oP 'Result: \K.*' || echo "NO_RESULT") VM_RESULT=$(tail -1 /tmp/${test_name}_vm.out | grep -oP 'Result: \K.*' || echo "NO_RESULT")
echo "OK (Result: $VM_RESULT)" echo "OK (Result: $VM_RESULT)"
else else