From 4201c6300158648c5c6c24893505b8636de8b92f Mon Sep 17 00:00:00 2001 From: moe-charm Date: Thu, 11 Sep 2025 03:33:33 +0900 Subject: [PATCH] mir: read plugin method signatures from nyash_box (#134) * mir: load plugin method signatures * llvm: read box type ids from nyash_box * mir: show value types in MIR printer --- nyash_box.toml | 34 ++++++ src/backend/llvm/compiler.rs | 20 +++- src/mir/builder.rs | 65 +++++++++--- src/mir/printer.rs | 127 ++++++++++++----------- tests/mir_snapshots/basic_arithmetic.mir | 10 +- tests/mir_snapshots/comparison.mir | 10 +- tests/mir_snapshots/string_concat.mir | 10 +- 7 files changed, 183 insertions(+), 93 deletions(-) create mode 100644 nyash_box.toml diff --git a/nyash_box.toml b/nyash_box.toml new file mode 100644 index 00000000..aeef4b97 --- /dev/null +++ b/nyash_box.toml @@ -0,0 +1,34 @@ +# Central plugin type definitions + +[CounterBox] +type_id = 7 + +[CounterBox.methods.inc] +returns = { type = "i64" } + +[CounterBox.methods.get] +returns = { type = "i64" } + +[MathBox] +type_id = 50 + +[MathBox.methods.sqrt] +returns = { type = "f64" } + +[MathBox.methods.sin] +returns = { type = "f64" } + +[MathBox.methods.cos] +returns = { type = "f64" } + +[MathBox.methods.round] +returns = { type = "f64" } + +[FileBox] +type_id = 6 + +[FileBox.methods.read] +returns = { type = "string" } + +[FileBox.methods.exists] +returns = { type = "bool" } diff --git a/src/backend/llvm/compiler.rs b/src/backend/llvm/compiler.rs index a5abf78f..26f19323 100644 --- a/src/backend/llvm/compiler.rs +++ b/src/backend/llvm/compiler.rs @@ -269,13 +269,27 @@ impl LLVMCompiler { }) } - // Load box type-id mapping from nyash.toml (for NewBox lowering) + // Load box type-id mapping from nyash_box.toml (central plugin registry) let mut box_type_ids: StdHashMap = StdHashMap::new(); + if let Ok(cfg) = std::fs::read_to_string("nyash_box.toml") { + if let Ok(doc) = toml::from_str::(&cfg) { + if let Some(table) = doc.as_table() { + for (box_name, box_val) in table { + if let Some(id) = box_val.get("type_id").and_then(|v| v.as_integer()) { + box_type_ids.insert(box_name.clone(), id as i64); + } + } + } + } + } + // Fallback: legacy box_types table in nyash.toml if let Ok(cfg) = std::fs::read_to_string("nyash.toml") { if let Ok(doc) = toml::from_str::(&cfg) { if let Some(bt) = doc.get("box_types").and_then(|v| v.as_table()) { for (k, v) in bt { - if let Some(id) = v.as_integer() { box_type_ids.insert(k.clone(), id as i64); } + if let Some(id) = v.as_integer() { + box_type_ids.entry(k.clone()).or_insert(id as i64); + } } } } @@ -545,7 +559,7 @@ impl LLVMCompiler { _ => return Err("box receiver must be pointer or i64 handle".to_string()), }; let recv_h = codegen.builder.build_ptr_to_int(recv_p, i64t, "recv_p2i").map_err(|e| e.to_string())?; - // Resolve type_id from metadata (Box("Type")) via nyash.toml + // Resolve type_id from metadata (Box("Type")) using box_type_ids let type_id: i64 = if let Some(crate::mir::MirType::Box(bname)) = func.metadata.value_types.get(box_val) { *box_type_ids.get(bname).unwrap_or(&0) } else if let Some(crate::mir::MirType::String) = func.metadata.value_types.get(box_val) { diff --git a/src/mir/builder.rs b/src/mir/builder.rs index c06de7c3..bb9579aa 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -69,6 +69,9 @@ pub struct MirBuilder { /// Optional per-value type annotations (MIR-level): ValueId -> MirType pub(super) value_types: HashMap, + + /// Plugin method return type signatures loaded from nyash_box.toml + plugin_method_sigs: HashMap<(String, String), super::MirType>, /// Current static box name when lowering a static box body (e.g., "Main") current_static_box: Option, @@ -105,22 +108,20 @@ impl MirBuilder { } } if let Some(bt) = recv_box { - let inferred: Option = match (bt.as_str(), method.as_str()) { - // Built-in box methods - ("StringBox", "length") | ("StringBox", "len") => Some(super::MirType::Integer), - ("StringBox", "is_empty") => Some(super::MirType::Bool), - ("StringBox", "charCodeAt") => Some(super::MirType::Integer), - ("ArrayBox", "length") => Some(super::MirType::Integer), - - // Plugin box methods - ("CounterBox", "get") => Some(super::MirType::Integer), - ("MathBox", "sqrt") => Some(super::MirType::Float), - ("FileBox", "read") => Some(super::MirType::String), - ("FileBox", "exists") => Some(super::MirType::Bool), - _ => None, - }; - if let Some(mt) = inferred { - self.value_types.insert(d, mt); + if let Some(mt) = self.plugin_method_sigs.get(&(bt.clone(), method.clone())) { + self.value_types.insert(d, mt.clone()); + } else { + let inferred: Option = match (bt.as_str(), method.as_str()) { + // Built-in box methods + ("StringBox", "length") | ("StringBox", "len") => Some(super::MirType::Integer), + ("StringBox", "is_empty") => Some(super::MirType::Bool), + ("StringBox", "charCodeAt") => Some(super::MirType::Integer), + ("ArrayBox", "length") => Some(super::MirType::Integer), + _ => None, + }; + if let Some(mt) = inferred { + self.value_types.insert(d, mt); + } } } } @@ -128,6 +129,37 @@ impl MirBuilder { } /// Create a new MIR builder pub fn new() -> Self { + // Load plugin method signatures from nyash_box.toml if available + let mut plugin_method_sigs: HashMap<(String, String), super::MirType> = HashMap::new(); + if let Ok(content) = fs::read_to_string("nyash_box.toml") { + if let Ok(root) = toml::from_str::(&content) { + if let Some(table) = root.as_table() { + for (box_name, box_val) in table { + if let Some(methods) = box_val.get("methods").and_then(|v| v.as_table()) { + for (mname, mval) in methods { + if let Some(ret) = mval.get("returns") { + let ty_str = ret + .as_str() + .map(|s| s.to_string()) + .or_else(|| ret.get("type").and_then(|t| t.as_str()).map(|s| s.to_string())); + if let Some(ts) = ty_str { + let mir_ty = match ts.to_lowercase().as_str() { + "i64" | "int" | "integer" => super::MirType::Integer, + "f64" | "float" => super::MirType::Float, + "bool" | "boolean" => super::MirType::Bool, + "string" => super::MirType::String, + "void" | "unit" => super::MirType::Void, + other => super::MirType::Box(other.to_string()), + }; + plugin_method_sigs.insert((box_name.clone(), mname.clone()), mir_ty); + } + } + } + } + } + } + } + } Self { current_module: None, current_function: None, @@ -141,6 +173,7 @@ impl MirBuilder { weak_fields_by_box: HashMap::new(), field_origin_class: HashMap::new(), value_types: HashMap::new(), + plugin_method_sigs, current_static_box: None, include_loading: HashSet::new(), include_box_map: HashMap::new(), diff --git a/src/mir/printer.rs b/src/mir/printer.rs index 86741e76..a35105c9 100644 --- a/src/mir/printer.rs +++ b/src/mir/printer.rs @@ -4,7 +4,8 @@ * Implements pretty-printing for MIR modules and functions */ -use super::{MirModule, MirFunction, BasicBlock, MirInstruction}; +use super::{MirModule, MirFunction, BasicBlock, MirInstruction, ValueId, MirType}; +use std::collections::HashMap; use std::fmt::Write; use crate::debug::log as dlog; @@ -219,7 +220,7 @@ impl MirPrinter { if i > 0 { writeln!(output).unwrap(); } - output.push_str(&self.print_basic_block(block)); + output.push_str(&self.print_basic_block(block, &function.metadata.value_types)); } } @@ -229,7 +230,7 @@ impl MirPrinter { } /// Print a basic block - pub fn print_basic_block(&self, block: &BasicBlock) -> String { + pub fn print_basic_block(&self, block: &BasicBlock, types: &HashMap) -> String { let mut output = String::new(); // Block header @@ -253,8 +254,8 @@ impl MirPrinter { } else { write!(output, " ").unwrap(); } - - let mut line = self.format_instruction(instruction); + + let mut line = self.format_instruction(instruction, types); if self.show_effects_inline { let eff = instruction.effects(); let cat = if eff.is_pure() { "pure" } else if eff.is_read_only() { "readonly" } else { "side" }; @@ -272,29 +273,37 @@ impl MirPrinter { output } + fn format_dst(&self, dst: &ValueId, types: &HashMap) -> String { + if let Some(ty) = types.get(dst) { + format!("{}: {:?} =", dst, ty) + } else { + format!("{} =", dst) + } + } + /// Format a single instruction - fn format_instruction(&self, instruction: &MirInstruction) -> String { + fn format_instruction(&self, instruction: &MirInstruction, types: &HashMap) -> String { match instruction { MirInstruction::Const { dst, value } => { - format!("{} = const {}", dst, value) + format!("{} const {}", self.format_dst(dst, types), value) }, - + MirInstruction::BinOp { dst, op, lhs, rhs } => { - format!("{} = {} {:?} {}", dst, lhs, op, rhs) + format!("{} {} {:?} {}", self.format_dst(dst, types), lhs, op, rhs) }, - + MirInstruction::UnaryOp { dst, op, operand } => { - format!("{} = {:?} {}", dst, op, operand) + format!("{} {:?} {}", self.format_dst(dst, types), op, operand) }, - + MirInstruction::Compare { dst, op, lhs, rhs } => { - format!("{} = icmp {:?} {}, {}", dst, op, lhs, rhs) + format!("{} icmp {:?} {}, {}", self.format_dst(dst, types), op, lhs, rhs) }, - + MirInstruction::Load { dst, ptr } => { - format!("{} = load {}", dst, ptr) + format!("{} load {}", self.format_dst(dst, types), ptr) }, - + MirInstruction::Store { value, ptr } => { format!("store {} -> {}", value, ptr) }, @@ -304,9 +313,9 @@ impl MirPrinter { .map(|v| format!("{}", v)) .collect::>() .join(", "); - + if let Some(dst) = dst { - format!("{} = call {}({})", dst, func, args_str) + format!("{} call {}({})", self.format_dst(dst, types), func, args_str) } else { format!("call {}({})", func, args_str) } @@ -316,9 +325,9 @@ impl MirPrinter { let c = captures.iter().map(|(n, v)| format!("{}={}", n, v)).collect::>().join(", "); let me_s = me.map(|m| format!(" me={}", m)).unwrap_or_default(); let cap_s = if c.is_empty() { String::new() } else { format!(" [{}]", c) }; - format!("{} = function_new ({}) {{...{}}}{}{}", dst, p, body.len(), cap_s, me_s) + format!("{} function_new ({}) {{...{}}}{}{}", self.format_dst(dst, types), p, body.len(), cap_s, me_s) }, - + MirInstruction::BoxCall { dst, box_val, method, method_id, args, effects: _ } => { let args_str = args.iter() .map(|v| format!("{}", v)) @@ -326,7 +335,7 @@ impl MirPrinter { .join(", "); let id_suffix = method_id.map(|id| format!("[#{}]", id)).unwrap_or_default(); if let Some(dst) = dst { - format!("{} = call {}.{}{}({})", dst, box_val, method, id_suffix, args_str) + format!("{} call {}.{}{}({})", self.format_dst(dst, types), box_val, method, id_suffix, args_str) } else { format!("call {}.{}{}({})", box_val, method, id_suffix, args_str) } @@ -337,7 +346,7 @@ impl MirPrinter { .collect::>() .join(", "); if let Some(dst) = dst { - format!("{} = plugin_invoke {}.{}({})", dst, box_val, method, args_str) + format!("{} plugin_invoke {}.{}({})", self.format_dst(dst, types), box_val, method, args_str) } else { format!("plugin_invoke {}.{}({})", box_val, method, args_str) } @@ -364,61 +373,61 @@ impl MirPrinter { .map(|(bb, val)| format!("[{}, {}]", val, bb)) .collect::>() .join(", "); - format!("{} = phi {}", dst, inputs_str) + format!("{} phi {}", self.format_dst(dst, types), inputs_str) }, - + MirInstruction::NewBox { dst, box_type, args } => { let args_str = args.iter() .map(|v| format!("{}", v)) .collect::>() .join(", "); - format!("{} = new {}({})", dst, box_type, args_str) + format!("{} new {}({})", self.format_dst(dst, types), box_type, args_str) }, - + // Legacy -> Unified print: TypeCheck as TypeOp(check) MirInstruction::TypeCheck { dst, value, expected_type } => { // Print using unified TypeOp style to avoid naming divergence - format!("{} = typeop check {} {}", dst, value, expected_type) + format!("{} typeop check {} {}", self.format_dst(dst, types), value, expected_type) }, - + MirInstruction::Cast { dst, value, target_type } => { - format!("{} = cast {} to {:?}", dst, value, target_type) + format!("{} cast {} to {:?}", self.format_dst(dst, types), value, target_type) }, - + MirInstruction::TypeOp { dst, op, value, ty } => { let op_str = match op { super::TypeOpKind::Check => "check", super::TypeOpKind::Cast => "cast" }; - format!("{} = typeop {} {} {:?}", dst, op_str, value, ty) + format!("{} typeop {} {} {:?}", self.format_dst(dst, types), op_str, value, ty) }, - + MirInstruction::ArrayGet { dst, array, index } => { - format!("{} = {}[{}]", dst, array, index) + format!("{} {}[{}]", self.format_dst(dst, types), array, index) }, - + MirInstruction::ArraySet { array, index, value } => { format!("{}[{}] = {}", array, index, value) }, - + MirInstruction::Copy { dst, src } => { - format!("{} = copy {}", dst, src) + format!("{} copy {}", self.format_dst(dst, types), src) }, - + MirInstruction::Debug { value, message } => { format!("debug {} \"{}\"", value, message) }, - + MirInstruction::Print { value, effects: _ } => { format!("print {}", value) }, - + MirInstruction::Nop => { "nop".to_string() }, - + // Phase 5: Control flow & exception handling MirInstruction::Throw { exception, effects: _ } => { format!("throw {}", exception) }, - + MirInstruction::Catch { exception_type, exception_value, handler_bb } => { if let Some(ref exc_type) = exception_type { format!("catch {} {} -> {}", exc_type, exception_value, handler_bb) @@ -433,11 +442,11 @@ impl MirPrinter { // Phase 6: Box reference operations MirInstruction::RefNew { dst, box_val } => { - format!("{} = ref_new {}", dst, box_val) + format!("{} ref_new {}", self.format_dst(dst, types), box_val) }, - + MirInstruction::RefGet { dst, reference, field } => { - format!("{} = ref_get {}.{}", dst, reference, field) + format!("{} ref_get {}.{}", self.format_dst(dst, types), reference, field) }, MirInstruction::RefSet { reference, field, value } => { @@ -446,52 +455,52 @@ impl MirPrinter { // Legacy -> Unified print: WeakNew as weakref new MirInstruction::WeakNew { dst, box_val } => { - format!("{} = weakref new {}", dst, box_val) + format!("{} weakref new {}", self.format_dst(dst, types), box_val) }, - + // Legacy -> Unified print: WeakLoad as weakref load MirInstruction::WeakLoad { dst, weak_ref } => { - format!("{} = weakref load {}", dst, weak_ref) + format!("{} weakref load {}", self.format_dst(dst, types), weak_ref) }, - + // Legacy -> Unified print: BarrierRead as barrier read MirInstruction::BarrierRead { ptr } => { format!("barrier read {}", ptr) }, - + // Legacy -> Unified print: BarrierWrite as barrier write MirInstruction::BarrierWrite { ptr } => { format!("barrier write {}", ptr) }, - + MirInstruction::WeakRef { dst, op, value } => { let op_str = match op { super::WeakRefOp::New => "new", super::WeakRefOp::Load => "load" }; - format!("{} = weakref {} {}", dst, op_str, value) + format!("{} weakref {} {}", self.format_dst(dst, types), op_str, value) }, - + MirInstruction::Barrier { op, ptr } => { let op_str = match op { super::BarrierOp::Read => "read", super::BarrierOp::Write => "write" }; format!("barrier {} {}", op_str, ptr) }, - + // Phase 7: Async/Future Operations MirInstruction::FutureNew { dst, value } => { - format!("{} = future_new {}", dst, value) + format!("{} future_new {}", self.format_dst(dst, types), value) }, - + MirInstruction::FutureSet { future, value } => { format!("future_set {} = {}", future, value) }, - + MirInstruction::Await { dst, future } => { - format!("{} = await {}", dst, future) + format!("{} await {}", self.format_dst(dst, types), future) }, - + // Phase 9.7: External Function Calls MirInstruction::ExternCall { dst, iface_name, method_name, args, effects } => { let args_str = args.iter().map(|v| format!("{}", v)).collect::>().join(", "); if let Some(dst) = dst { - format!("{} = extern_call {}.{}({}) [effects: {}]", dst, iface_name, method_name, args_str, effects) + format!("{} extern_call {}.{}({}) [effects: {}]", self.format_dst(dst, types), iface_name, method_name, args_str, effects) } else { format!("extern_call {}.{}({}) [effects: {}]", iface_name, method_name, args_str, effects) } diff --git a/tests/mir_snapshots/basic_arithmetic.mir b/tests/mir_snapshots/basic_arithmetic.mir index 85df4c03..b811f064 100644 --- a/tests/mir_snapshots/basic_arithmetic.mir +++ b/tests/mir_snapshots/basic_arithmetic.mir @@ -1,11 +1,11 @@ ; MIR Module: main -define void @main() { +define i64 @main() { bb0: - 0: %0 = const 42 - 1: %1 = const 10 - 2: %2 = %0 Add %1 - 3: print %2 + 0: %0: Integer = const 42 + 1: %1: Integer = const 10 + 2: %2: Integer = %0 Add %1 + 3: extern_call env.console.log(%2) [effects: pure|io] 4: ret %2 } diff --git a/tests/mir_snapshots/comparison.mir b/tests/mir_snapshots/comparison.mir index 62191f5e..23fdcb3a 100644 --- a/tests/mir_snapshots/comparison.mir +++ b/tests/mir_snapshots/comparison.mir @@ -1,11 +1,11 @@ ; MIR Module: main -define void @main() { +define i1 @main() { bb0: - 0: %0 = const 42 - 1: %1 = const 42 - 2: %2 = icmp Eq %0, %1 - 3: print %2 + 0: %0: Integer = const 42 + 1: %1: Integer = const 42 + 2: %2: Bool = icmp Eq %0, %1 + 3: extern_call env.console.log(%2) [effects: pure|io] 4: ret %2 } diff --git a/tests/mir_snapshots/string_concat.mir b/tests/mir_snapshots/string_concat.mir index 2cced2e7..5596599d 100644 --- a/tests/mir_snapshots/string_concat.mir +++ b/tests/mir_snapshots/string_concat.mir @@ -1,11 +1,11 @@ ; MIR Module: main -define void @main() { +define i64 @main() { bb0: - 0: %0 = const "Hello" - 1: %1 = const "World" - 2: %2 = %0 Add %1 - 3: print %2 + 0: %0: String = const "Hello" + 1: %1: String = const "World" + 2: %2: Integer = %0 Add %1 + 3: extern_call env.console.log(%2) [effects: pure|io] 4: ret %2 }