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
This commit is contained in:
moe-charm
2025-09-11 03:33:33 +09:00
committed by GitHub
parent e114f9bfe3
commit 4201c63001
7 changed files with 183 additions and 93 deletions

34
nyash_box.toml Normal file
View File

@ -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" }

View File

@ -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<String, i64> = StdHashMap::new();
if let Ok(cfg) = std::fs::read_to_string("nyash_box.toml") {
if let Ok(doc) = toml::from_str::<toml::Value>(&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::<toml::Value>(&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) {

View File

@ -69,6 +69,9 @@ pub struct MirBuilder {
/// Optional per-value type annotations (MIR-level): ValueId -> MirType
pub(super) value_types: HashMap<ValueId, super::MirType>,
/// 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<String>,
@ -105,22 +108,20 @@ impl MirBuilder {
}
}
if let Some(bt) = recv_box {
let inferred: Option<super::MirType> = 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<super::MirType> = 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::<toml::Value>(&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(),

View File

@ -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<ValueId, MirType>) -> 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<ValueId, MirType>) -> 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<ValueId, MirType>) -> 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::<Vec<_>>()
.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::<Vec<_>>().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::<Vec<_>>()
.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::<Vec<_>>()
.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::<Vec<_>>()
.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::<Vec<_>>().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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}