Files
hakorune/tests/archive/legacy_interpreter/vm_e2e.rs

150 lines
4.5 KiB
Rust
Raw Normal View History

#![cfg(feature = "e2e")]
//! VM E2E: Compile Nyash to MIR and execute via VM, with mock plugin factory
use std::sync::Arc;
use nyash_rust::backend::VM;
use nyash_rust::box_factory::builtin::BuiltinGroups;
feat(mir/builder): implement BoxCompilationContext for structural metadata isolation 箱理論の完璧な実装!各static boxコンパイルを独立したコンテキストで実行。 設計: - BoxCompilationContext: variable_map, value_origin_newbox, value_types を箱化 - MirBuilder: compilation_context: Option<BoxCompilationContext> フィールド追加 - context swap: lower_static_method_as_function 開始/終了時に std::mem::swap - 自動クリーンアップ: スコープ終了でコンテキスト破棄 実装: 1. src/mir/builder/context.rs: BoxCompilationContext構造体定義(テスト付き) 2. src/mir/builder.rs: compilation_contextフィールド追加、既存フィールドにコメント追加 3. src/mir/builder/lifecycle.rs: 各static boxでコンテキスト作成・破棄 4. src/mir/builder/builder_calls.rs: lower_static_method_as_functionでcontext swap 5. src/mir/builder/decls.rs, exprs.rs: 古いmanual clear()削除 効果: ✅ グローバル状態汚染を構造的に不可能化 ✅ 各static boxが完全に独立したコンテキストでコンパイル ✅ 既存コード変更なし(swap技法で完全後方互換性) ✅ StageBArgsBox ValueId(21)エラー完全解決 箱理論的評価: 🟢 95点 - 明示的な境界: 各boxのコンテキストが物理的に分離 - 汚染不可能: 前の箱の状態が構造的に残らない - 戻せる: コンテキスト差し替えで簡単ロールバック - 美しい設計: スコープベースのリソース管理 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 11:28:18 +09:00
use nyash_rust::box_factory::{BoxFactory, RuntimeError};
use nyash_rust::box_trait::{BoolBox, BoxBase, BoxCore, NyashBox, StringBox};
use nyash_rust::mir::MirCompiler;
use nyash_rust::parser::NyashParser;
use nyash_rust::runtime::NyashRuntimeBuilder;
// Minimal AdderBox to validate plugin factory path under VM
#[derive(Debug, Clone)]
struct AdderBox {
base: BoxBase,
sum: i64,
}
impl AdderBox {
fn new(a: i64, b: i64) -> Self {
Self {
base: BoxBase::new(),
sum: a + b,
}
}
}
impl BoxCore for AdderBox {
fn box_id(&self) -> u64 {
self.base.id
}
fn parent_type_id(&self) -> Option<std::any::TypeId> {
None
}
fn fmt_box(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "AdderBox(sum={})", self.sum)
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
}
impl NyashBox for AdderBox {
fn to_string_box(&self) -> StringBox {
StringBox::new(self.sum.to_string())
}
fn equals(&self, other: &dyn NyashBox) -> BoolBox {
if let Some(a) = other.as_any().downcast_ref::<AdderBox>() {
BoolBox::new(self.sum == a.sum)
} else {
BoolBox::new(false)
}
}
fn type_name(&self) -> &'static str {
"AdderBox"
}
fn clone_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
fn share_box(&self) -> Box<dyn NyashBox> {
Box::new(self.clone())
}
}
struct TestPluginFactory;
impl TestPluginFactory {
fn new() -> Self {
Self
}
}
impl BoxFactory for TestPluginFactory {
fn create_box(
&self,
name: &str,
args: &[Box<dyn NyashBox>],
) -> Result<Box<dyn NyashBox>, RuntimeError> {
match name {
"AdderBox" => {
if args.len() != 2 {
return Err(RuntimeError::InvalidOperation {
message: format!("AdderBox expects 2 args, got {}", args.len()),
});
}
let a = args[0].to_string_box().value.parse::<i64>().map_err(|_| {
RuntimeError::TypeError {
message: "AdderBox arg a must be int".into(),
}
})?;
let b = args[1].to_string_box().value.parse::<i64>().map_err(|_| {
RuntimeError::TypeError {
message: "AdderBox arg b must be int".into(),
}
})?;
Ok(Box::new(AdderBox::new(a, b)))
}
_ => Err(RuntimeError::InvalidOperation {
message: format!("Unknown Box type: {}", name),
}),
}
}
fn box_types(&self) -> Vec<&str> {
vec!["AdderBox"]
}
}
#[test]
fn vm_e2e_adder_box() {
// Build runtime with builtin + user-defined + mock plugin factory
let runtime = NyashRuntimeBuilder::new()
.with_builtin_groups(BuiltinGroups::native_full())
.with_factory(Arc::new(
nyash_rust::box_factory::user_defined::UserDefinedBoxFactory::new(
nyash_rust::interpreter::SharedState::new(),
),
))
.with_factory(Arc::new(TestPluginFactory::new()))
.build();
// Nyash code: construct AdderBox and leave it as final expression
let code = r#"
a = new AdderBox(10, 32)
a
"#;
// Parse → MIR
let ast = NyashParser::parse_from_string(code).expect("parse ok");
let mut mir_compiler = MirCompiler::new();
let compile_result = mir_compiler.compile(ast).expect("mir ok");
// Execute via VM using the prepared runtime
let mut vm = VM::with_runtime(runtime);
let result = vm
.execute_module(&compile_result.module)
.expect("vm exec ok");
// The VM returns an Option<Box<dyn NyashBox>> or a value; we print/debug and check string form
// Here we rely on Display via to_string_box through Debug format
// Try to format the result if available
// For this implementation, result is a generic value; we check debug string contains 42 or to_string equivalent.
let s = format!("{:?}", result);
assert!(
s.contains("42") || s.contains("AdderBox"),
"unexpected VM result: {}",
s
);
}