Implement Phase 1: MIR + VM backend with golden tests
Co-authored-by: moe-charm <217100418+moe-charm@users.noreply.github.com>
This commit is contained in:
5
local_tests/basic_mir.nyash
Normal file
5
local_tests/basic_mir.nyash
Normal file
@ -0,0 +1,5 @@
|
||||
// Basic MIR test - minimal scope only
|
||||
x = 42
|
||||
y = 10
|
||||
result = x + y
|
||||
print("Result: " + result)
|
||||
7
src/backend/mod.rs
Normal file
7
src/backend/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
/*!
|
||||
* Backend module - Different execution backends for MIR
|
||||
*/
|
||||
|
||||
pub mod vm;
|
||||
|
||||
pub use vm::{VM, VMError};
|
||||
409
src/backend/vm.rs
Normal file
409
src/backend/vm.rs
Normal file
@ -0,0 +1,409 @@
|
||||
/*!
|
||||
* VM Backend - Execute MIR instructions in a virtual machine
|
||||
*
|
||||
* Simple stack-based VM for executing MIR code
|
||||
*/
|
||||
|
||||
use crate::mir::{MirModule, MirFunction, MirInstruction, ConstValue, BinaryOp, CompareOp, UnaryOp, ValueId, BasicBlockId};
|
||||
use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// VM execution error
|
||||
#[derive(Debug)]
|
||||
pub enum VMError {
|
||||
InvalidValue(String),
|
||||
InvalidInstruction(String),
|
||||
InvalidBasicBlock(String),
|
||||
DivisionByZero,
|
||||
StackUnderflow,
|
||||
TypeError(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for VMError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
VMError::InvalidValue(msg) => write!(f, "Invalid value: {}", msg),
|
||||
VMError::InvalidInstruction(msg) => write!(f, "Invalid instruction: {}", msg),
|
||||
VMError::InvalidBasicBlock(msg) => write!(f, "Invalid basic block: {}", msg),
|
||||
VMError::DivisionByZero => write!(f, "Division by zero"),
|
||||
VMError::StackUnderflow => write!(f, "Stack underflow"),
|
||||
VMError::TypeError(msg) => write!(f, "Type error: {}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for VMError {}
|
||||
|
||||
/// VM value representation
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum VMValue {
|
||||
Integer(i64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
String(String),
|
||||
Void,
|
||||
}
|
||||
|
||||
impl VMValue {
|
||||
/// Convert to NyashBox for output
|
||||
pub fn to_nyash_box(&self) -> Box<dyn NyashBox> {
|
||||
match self {
|
||||
VMValue::Integer(i) => Box::new(IntegerBox::new(*i)),
|
||||
VMValue::Float(f) => Box::new(StringBox::new(&f.to_string())), // Simplified for now
|
||||
VMValue::Bool(b) => Box::new(BoolBox::new(*b)),
|
||||
VMValue::String(s) => Box::new(StringBox::new(s)),
|
||||
VMValue::Void => Box::new(VoidBox::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get string representation for printing
|
||||
pub fn to_string(&self) -> String {
|
||||
match self {
|
||||
VMValue::Integer(i) => i.to_string(),
|
||||
VMValue::Float(f) => f.to_string(),
|
||||
VMValue::Bool(b) => b.to_string(),
|
||||
VMValue::String(s) => s.clone(),
|
||||
VMValue::Void => "void".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to convert to integer
|
||||
pub fn as_integer(&self) -> Result<i64, VMError> {
|
||||
match self {
|
||||
VMValue::Integer(i) => Ok(*i),
|
||||
_ => Err(VMError::TypeError(format!("Expected integer, got {:?}", self))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to convert to bool
|
||||
pub fn as_bool(&self) -> Result<bool, VMError> {
|
||||
match self {
|
||||
VMValue::Bool(b) => Ok(*b),
|
||||
VMValue::Integer(i) => Ok(*i != 0),
|
||||
_ => Err(VMError::TypeError(format!("Expected bool, got {:?}", self))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ConstValue> for VMValue {
|
||||
fn from(const_val: &ConstValue) -> Self {
|
||||
match const_val {
|
||||
ConstValue::Integer(i) => VMValue::Integer(*i),
|
||||
ConstValue::Float(f) => VMValue::Float(*f),
|
||||
ConstValue::Bool(b) => VMValue::Bool(*b),
|
||||
ConstValue::String(s) => VMValue::String(s.clone()),
|
||||
ConstValue::Null => VMValue::Void, // Simplified
|
||||
ConstValue::Void => VMValue::Void,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Virtual Machine state
|
||||
pub struct VM {
|
||||
/// Value storage (maps ValueId to actual values)
|
||||
values: HashMap<ValueId, VMValue>,
|
||||
/// Current function being executed
|
||||
current_function: Option<String>,
|
||||
/// Current basic block
|
||||
current_block: Option<BasicBlockId>,
|
||||
/// Program counter within current block
|
||||
pc: usize,
|
||||
/// Return value from last execution
|
||||
last_result: Option<VMValue>,
|
||||
}
|
||||
|
||||
impl VM {
|
||||
/// Create a new VM instance
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
values: HashMap::new(),
|
||||
current_function: None,
|
||||
current_block: None,
|
||||
pc: 0,
|
||||
last_result: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a MIR module
|
||||
pub fn execute_module(&mut self, module: &MirModule) -> Result<Box<dyn NyashBox>, VMError> {
|
||||
// Find main function
|
||||
let main_function = module.get_function("main")
|
||||
.ok_or_else(|| VMError::InvalidInstruction("No main function found".to_string()))?;
|
||||
|
||||
// Execute main function
|
||||
let result = self.execute_function(main_function)?;
|
||||
|
||||
// Convert result to NyashBox
|
||||
Ok(result.to_nyash_box())
|
||||
}
|
||||
|
||||
/// Execute a single function
|
||||
fn execute_function(&mut self, function: &MirFunction) -> Result<VMValue, VMError> {
|
||||
self.current_function = Some(function.signature.name.clone());
|
||||
|
||||
// Start at entry block
|
||||
let mut current_block = function.entry_block;
|
||||
|
||||
loop {
|
||||
let block = function.get_block(current_block)
|
||||
.ok_or_else(|| VMError::InvalidBasicBlock(format!("Block {} not found", current_block)))?;
|
||||
|
||||
self.current_block = Some(current_block);
|
||||
self.pc = 0;
|
||||
|
||||
let mut next_block = None;
|
||||
let mut should_return = None;
|
||||
|
||||
// Execute instructions in this block
|
||||
for (index, instruction) in block.instructions.iter().enumerate() {
|
||||
self.pc = index;
|
||||
|
||||
match self.execute_instruction(instruction)? {
|
||||
ControlFlow::Continue => continue,
|
||||
ControlFlow::Jump(target) => {
|
||||
next_block = Some(target);
|
||||
break;
|
||||
},
|
||||
ControlFlow::Return(value) => {
|
||||
should_return = Some(value);
|
||||
break;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Handle control flow
|
||||
if let Some(return_value) = should_return {
|
||||
return Ok(return_value);
|
||||
} else if let Some(target) = next_block {
|
||||
current_block = target;
|
||||
} else {
|
||||
// Block ended without terminator - this shouldn't happen in well-formed MIR
|
||||
// but let's handle it gracefully by returning void
|
||||
return Ok(VMValue::Void);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a single instruction
|
||||
fn execute_instruction(&mut self, instruction: &MirInstruction) -> Result<ControlFlow, VMError> {
|
||||
match instruction {
|
||||
MirInstruction::Const { dst, value } => {
|
||||
let vm_value = VMValue::from(value);
|
||||
self.values.insert(*dst, vm_value);
|
||||
Ok(ControlFlow::Continue)
|
||||
},
|
||||
|
||||
MirInstruction::BinOp { dst, op, lhs, rhs } => {
|
||||
let left = self.get_value(*lhs)?;
|
||||
let right = self.get_value(*rhs)?;
|
||||
let result = self.execute_binary_op(op, &left, &right)?;
|
||||
self.values.insert(*dst, result);
|
||||
Ok(ControlFlow::Continue)
|
||||
},
|
||||
|
||||
MirInstruction::UnaryOp { dst, op, operand } => {
|
||||
let operand_val = self.get_value(*operand)?;
|
||||
let result = self.execute_unary_op(op, &operand_val)?;
|
||||
self.values.insert(*dst, result);
|
||||
Ok(ControlFlow::Continue)
|
||||
},
|
||||
|
||||
MirInstruction::Compare { dst, op, lhs, rhs } => {
|
||||
let left = self.get_value(*lhs)?;
|
||||
let right = self.get_value(*rhs)?;
|
||||
let result = self.execute_compare_op(op, &left, &right)?;
|
||||
self.values.insert(*dst, VMValue::Bool(result));
|
||||
Ok(ControlFlow::Continue)
|
||||
},
|
||||
|
||||
MirInstruction::Print { value, .. } => {
|
||||
let val = self.get_value(*value)?;
|
||||
println!("{}", val.to_string());
|
||||
Ok(ControlFlow::Continue)
|
||||
},
|
||||
|
||||
MirInstruction::Return { value } => {
|
||||
let return_value = if let Some(val_id) = value {
|
||||
self.get_value(*val_id)?
|
||||
} else {
|
||||
VMValue::Void
|
||||
};
|
||||
Ok(ControlFlow::Return(return_value))
|
||||
},
|
||||
|
||||
MirInstruction::Jump { target } => {
|
||||
Ok(ControlFlow::Jump(*target))
|
||||
},
|
||||
|
||||
MirInstruction::Branch { condition, then_bb, else_bb } => {
|
||||
let cond_val = self.get_value(*condition)?;
|
||||
let cond_bool = cond_val.as_bool()?;
|
||||
|
||||
if cond_bool {
|
||||
Ok(ControlFlow::Jump(*then_bb))
|
||||
} else {
|
||||
Ok(ControlFlow::Jump(*else_bb))
|
||||
}
|
||||
},
|
||||
|
||||
MirInstruction::Phi { dst, inputs } => {
|
||||
// For now, simplified phi - use first available input
|
||||
// In a real implementation, we'd need to track which block we came from
|
||||
if let Some((_, value_id)) = inputs.first() {
|
||||
let value = self.get_value(*value_id)?;
|
||||
self.values.insert(*dst, value);
|
||||
}
|
||||
Ok(ControlFlow::Continue)
|
||||
},
|
||||
|
||||
_ => {
|
||||
Err(VMError::InvalidInstruction(format!("Unsupported instruction: {:?}", instruction)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a value from storage
|
||||
fn get_value(&self, value_id: ValueId) -> Result<VMValue, VMError> {
|
||||
self.values.get(&value_id)
|
||||
.cloned()
|
||||
.ok_or_else(|| VMError::InvalidValue(format!("Value {} not found", value_id)))
|
||||
}
|
||||
|
||||
/// Execute binary operation
|
||||
fn execute_binary_op(&self, op: &BinaryOp, left: &VMValue, right: &VMValue) -> Result<VMValue, VMError> {
|
||||
match (left, right) {
|
||||
(VMValue::Integer(l), VMValue::Integer(r)) => {
|
||||
let result = match op {
|
||||
BinaryOp::Add => *l + *r,
|
||||
BinaryOp::Sub => *l - *r,
|
||||
BinaryOp::Mul => *l * *r,
|
||||
BinaryOp::Div => {
|
||||
if *r == 0 {
|
||||
return Err(VMError::DivisionByZero);
|
||||
}
|
||||
*l / *r
|
||||
},
|
||||
_ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))),
|
||||
};
|
||||
Ok(VMValue::Integer(result))
|
||||
},
|
||||
|
||||
(VMValue::String(l), VMValue::Integer(r)) => {
|
||||
// String + Integer concatenation
|
||||
match op {
|
||||
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))),
|
||||
_ => Err(VMError::TypeError("String-integer operations only support addition".to_string())),
|
||||
}
|
||||
},
|
||||
|
||||
(VMValue::String(l), VMValue::String(r)) => {
|
||||
// String concatenation
|
||||
match op {
|
||||
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))),
|
||||
_ => Err(VMError::TypeError("String operations only support addition".to_string())),
|
||||
}
|
||||
},
|
||||
|
||||
_ => Err(VMError::TypeError(format!("Unsupported binary operation: {:?} on {:?} and {:?}", op, left, right))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute unary operation
|
||||
fn execute_unary_op(&self, op: &UnaryOp, operand: &VMValue) -> Result<VMValue, VMError> {
|
||||
match (op, operand) {
|
||||
(UnaryOp::Neg, VMValue::Integer(i)) => Ok(VMValue::Integer(-i)),
|
||||
(UnaryOp::Not, VMValue::Bool(b)) => Ok(VMValue::Bool(!b)),
|
||||
_ => Err(VMError::TypeError(format!("Unsupported unary operation: {:?} on {:?}", op, operand))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute comparison operation
|
||||
fn execute_compare_op(&self, op: &CompareOp, left: &VMValue, right: &VMValue) -> Result<bool, VMError> {
|
||||
match (left, right) {
|
||||
(VMValue::Integer(l), VMValue::Integer(r)) => {
|
||||
let result = match op {
|
||||
CompareOp::Eq => l == r,
|
||||
CompareOp::Ne => l != r,
|
||||
CompareOp::Lt => l < r,
|
||||
CompareOp::Le => l <= r,
|
||||
CompareOp::Gt => l > r,
|
||||
CompareOp::Ge => l >= r,
|
||||
};
|
||||
Ok(result)
|
||||
},
|
||||
|
||||
(VMValue::String(l), VMValue::String(r)) => {
|
||||
let result = match op {
|
||||
CompareOp::Eq => l == r,
|
||||
CompareOp::Ne => l != r,
|
||||
CompareOp::Lt => l < r,
|
||||
CompareOp::Le => l <= r,
|
||||
CompareOp::Gt => l > r,
|
||||
CompareOp::Ge => l >= r,
|
||||
};
|
||||
Ok(result)
|
||||
},
|
||||
|
||||
_ => Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Control flow result from instruction execution
|
||||
enum ControlFlow {
|
||||
Continue,
|
||||
Jump(BasicBlockId),
|
||||
Return(VMValue),
|
||||
}
|
||||
|
||||
impl Default for VM {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirType, EffectMask, BasicBlock};
|
||||
|
||||
#[test]
|
||||
fn test_basic_vm_execution() {
|
||||
let mut vm = VM::new();
|
||||
|
||||
// Test constant loading
|
||||
let const_instr = MirInstruction::Const {
|
||||
dst: ValueId(1),
|
||||
value: ConstValue::Integer(42),
|
||||
};
|
||||
|
||||
let result = vm.execute_instruction(&const_instr);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let value = vm.get_value(ValueId(1)).unwrap();
|
||||
assert_eq!(value.as_integer().unwrap(), 42);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_binary_operations() {
|
||||
let mut vm = VM::new();
|
||||
|
||||
// Load constants
|
||||
vm.values.insert(ValueId(1), VMValue::Integer(10));
|
||||
vm.values.insert(ValueId(2), VMValue::Integer(32));
|
||||
|
||||
// Test addition
|
||||
let add_instr = MirInstruction::BinOp {
|
||||
dst: ValueId(3),
|
||||
op: BinaryOp::Add,
|
||||
lhs: ValueId(1),
|
||||
rhs: ValueId(2),
|
||||
};
|
||||
|
||||
let result = vm.execute_instruction(&add_instr);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let value = vm.get_value(ValueId(3)).unwrap();
|
||||
assert_eq!(value.as_integer().unwrap(), 42);
|
||||
}
|
||||
}
|
||||
68
src/main.rs
68
src/main.rs
@ -31,6 +31,10 @@ use interpreter::NyashInterpreter;
|
||||
// 🚀 MIR Infrastructure
|
||||
pub mod mir;
|
||||
use mir::{MirCompiler, MirPrinter};
|
||||
|
||||
// 🚀 Backend Infrastructure
|
||||
pub mod backend;
|
||||
use backend::VM;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::process;
|
||||
@ -73,6 +77,13 @@ fn main() {
|
||||
.help("Show verbose MIR output with statistics")
|
||||
.action(clap::ArgAction::SetTrue)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("backend")
|
||||
.long("backend")
|
||||
.value_name("BACKEND")
|
||||
.help("Choose execution backend: 'interpreter' (default) or 'vm'")
|
||||
.default_value("interpreter")
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
// デバッグ燃料の解析
|
||||
@ -82,12 +93,16 @@ fn main() {
|
||||
let dump_mir = matches.get_flag("dump-mir");
|
||||
let verify_mir = matches.get_flag("verify");
|
||||
let mir_verbose = matches.get_flag("mir-verbose");
|
||||
let backend = matches.get_one::<String>("backend").unwrap();
|
||||
|
||||
if let Some(filename) = matches.get_one::<String>("file") {
|
||||
// File mode: parse and execute the provided .nyash file
|
||||
if dump_mir || verify_mir {
|
||||
println!("🚀 Nyash MIR Compiler - Processing file: {} 🚀", filename);
|
||||
execute_mir_mode(filename, dump_mir, verify_mir, mir_verbose);
|
||||
} else if backend == "vm" {
|
||||
println!("🚀 Nyash VM Backend - Executing file: {} 🚀", filename);
|
||||
execute_vm_mode(filename);
|
||||
} else {
|
||||
println!("🦀 Nyash Rust Implementation - Executing file: {} 🦀", filename);
|
||||
if let Some(fuel) = debug_fuel {
|
||||
@ -1170,6 +1185,59 @@ fn execute_mir_mode(filename: &str, dump_mir: bool, verify_mir: bool, verbose: b
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute VM mode
|
||||
fn execute_vm_mode(filename: &str) {
|
||||
// Read the source file
|
||||
let source = match fs::read_to_string(filename) {
|
||||
Ok(content) => content,
|
||||
Err(e) => {
|
||||
eprintln!("❌ Error reading file '{}': {}", filename, e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Parse to AST
|
||||
let ast = match NyashParser::parse_from_string(&source) {
|
||||
Ok(ast) => ast,
|
||||
Err(e) => {
|
||||
eprintln!("❌ Parse error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Compile to MIR
|
||||
let mut compiler = MirCompiler::new();
|
||||
let compile_result = match compiler.compile(ast) {
|
||||
Ok(result) => result,
|
||||
Err(e) => {
|
||||
eprintln!("❌ MIR compilation error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
// Check for verification errors
|
||||
if let Err(errors) = &compile_result.verification_result {
|
||||
eprintln!("❌ MIR verification failed with {} error(s):", errors.len());
|
||||
for (i, error) in errors.iter().enumerate() {
|
||||
eprintln!(" {}: {}", i + 1, error);
|
||||
}
|
||||
// Continue execution anyway for now
|
||||
}
|
||||
|
||||
// Execute with VM
|
||||
let mut vm = VM::new();
|
||||
match vm.execute_module(&compile_result.module) {
|
||||
Ok(result) => {
|
||||
println!("✅ VM execution completed successfully!");
|
||||
println!("Result: {}", result.to_string_box().value);
|
||||
},
|
||||
Err(e) => {
|
||||
eprintln!("❌ VM runtime error: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@ -132,6 +132,10 @@ impl MirBuilder {
|
||||
self.build_function_call(name.clone(), arguments.clone())
|
||||
},
|
||||
|
||||
ASTNode::Print { expression, .. } => {
|
||||
self.build_print_statement(*expression.clone())
|
||||
},
|
||||
|
||||
ASTNode::Program { statements, .. } => {
|
||||
self.build_block(statements.clone())
|
||||
},
|
||||
@ -271,6 +275,20 @@ impl MirBuilder {
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
/// Build print statement - converts to console output
|
||||
fn build_print_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
|
||||
let value = self.build_expression(expression)?;
|
||||
|
||||
// For now, use a special Print instruction (minimal scope)
|
||||
self.emit_instruction(MirInstruction::Print {
|
||||
value,
|
||||
effects: EffectMask::PURE.add(Effect::IO),
|
||||
})?;
|
||||
|
||||
// Return the value that was printed
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
/// Build a block of statements
|
||||
fn build_block(&mut self, statements: Vec<ASTNode>) -> Result<ValueId, String> {
|
||||
let mut last_value = None;
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
* SSA-form instructions with effect tracking for optimization
|
||||
*/
|
||||
|
||||
use super::{ValueId, LocalId, EffectMask, Effect};
|
||||
use super::{ValueId, EffectMask, Effect};
|
||||
// use crate::value::NyashValue; // Commented out to avoid circular dependency
|
||||
use std::fmt;
|
||||
|
||||
@ -169,6 +169,13 @@ pub enum MirInstruction {
|
||||
message: String,
|
||||
},
|
||||
|
||||
/// Print instruction for console output
|
||||
/// `print %value`
|
||||
Print {
|
||||
value: ValueId,
|
||||
effects: EffectMask,
|
||||
},
|
||||
|
||||
/// No-op instruction (for optimization placeholders)
|
||||
Nop,
|
||||
}
|
||||
@ -264,6 +271,9 @@ impl MirInstruction {
|
||||
|
||||
// Debug has debug effect
|
||||
MirInstruction::Debug { .. } => EffectMask::PURE.add(Effect::Debug),
|
||||
|
||||
// Print has external write effect
|
||||
MirInstruction::Print { effects, .. } => *effects,
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,6 +301,7 @@ impl MirInstruction {
|
||||
MirInstruction::Return { .. } |
|
||||
MirInstruction::ArraySet { .. } |
|
||||
MirInstruction::Debug { .. } |
|
||||
MirInstruction::Print { .. } |
|
||||
MirInstruction::Nop => None,
|
||||
}
|
||||
}
|
||||
@ -307,7 +318,8 @@ impl MirInstruction {
|
||||
MirInstruction::TypeCheck { value: operand, .. } |
|
||||
MirInstruction::Cast { value: operand, .. } |
|
||||
MirInstruction::Copy { src: operand, .. } |
|
||||
MirInstruction::Debug { value: operand, .. } => vec![*operand],
|
||||
MirInstruction::Debug { value: operand, .. } |
|
||||
MirInstruction::Print { value: operand, .. } => vec![*operand],
|
||||
|
||||
MirInstruction::BinOp { lhs, rhs, .. } |
|
||||
MirInstruction::Compare { lhs, rhs, .. } |
|
||||
|
||||
@ -294,6 +294,10 @@ impl MirPrinter {
|
||||
format!("debug {} \"{}\"", value, message)
|
||||
},
|
||||
|
||||
MirInstruction::Print { value, effects: _ } => {
|
||||
format!("print {}", value)
|
||||
},
|
||||
|
||||
MirInstruction::Nop => {
|
||||
"nop".to_string()
|
||||
},
|
||||
|
||||
12
tests/mir_snapshots/basic_arithmetic.mir
Normal file
12
tests/mir_snapshots/basic_arithmetic.mir
Normal file
@ -0,0 +1,12 @@
|
||||
; MIR Module: main
|
||||
|
||||
define void @main() {
|
||||
bb0:
|
||||
0: %0 = const 42
|
||||
1: %1 = const 10
|
||||
2: %2 = %0 Add %1
|
||||
3: print %2
|
||||
4: ret %2
|
||||
}
|
||||
|
||||
|
||||
2
tests/mir_snapshots/basic_arithmetic.nyash
Normal file
2
tests/mir_snapshots/basic_arithmetic.nyash
Normal file
@ -0,0 +1,2 @@
|
||||
// Basic arithmetic test - expression statement
|
||||
print(42 + 10)
|
||||
12
tests/mir_snapshots/comparison.mir
Normal file
12
tests/mir_snapshots/comparison.mir
Normal file
@ -0,0 +1,12 @@
|
||||
; MIR Module: main
|
||||
|
||||
define void @main() {
|
||||
bb0:
|
||||
0: %0 = const 42
|
||||
1: %1 = const 42
|
||||
2: %2 = icmp Eq %0, %1
|
||||
3: print %2
|
||||
4: ret %2
|
||||
}
|
||||
|
||||
|
||||
2
tests/mir_snapshots/comparison.nyash
Normal file
2
tests/mir_snapshots/comparison.nyash
Normal file
@ -0,0 +1,2 @@
|
||||
// Comparison test
|
||||
print(42 == 42)
|
||||
12
tests/mir_snapshots/string_concat.mir
Normal file
12
tests/mir_snapshots/string_concat.mir
Normal file
@ -0,0 +1,12 @@
|
||||
; MIR Module: main
|
||||
|
||||
define void @main() {
|
||||
bb0:
|
||||
0: %0 = const "Hello"
|
||||
1: %1 = const "World"
|
||||
2: %2 = %0 Add %1
|
||||
3: print %2
|
||||
4: ret %2
|
||||
}
|
||||
|
||||
|
||||
2
tests/mir_snapshots/string_concat.nyash
Normal file
2
tests/mir_snapshots/string_concat.nyash
Normal file
@ -0,0 +1,2 @@
|
||||
// String concatenation test
|
||||
print("Hello" + "World")
|
||||
Reference in New Issue
Block a user