Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions mrubyedge/examples/default-arg.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
def incr(times, state=[0])
return if times == 0
p "state: #{state.inspect} #{state.object_id}"
state[0] += 1
incr(times - 1, state)
state
end

p incr(3)
21 changes: 21 additions & 0 deletions mrubyedge/examples/hanoi.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Tower of Hanoi
# Move disks from source to destination using auxiliary peg

def hanoi(n, source, destination, auxiliary, step)
if n == 1
step[0] += 1
puts "Step #{step[0]}: Move disk 1 from #{source} to #{destination}"
return
end

hanoi(n - 1, source, auxiliary, destination, step)
step[0] += 1
puts "Step #{step[0]}: Move disk #{n} from #{source} to #{destination}"
hanoi(n - 1, auxiliary, destination, source, step)
end

puts "Tower of Hanoi with 3 disks:"
puts "----------------------------"
hanoi(3, 'A', 'C', 'B', [0])
puts "----------------------------"
puts "Complete!"
32 changes: 24 additions & 8 deletions mrubyedge/src/yamrb/optable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,7 @@ impl From<u32> for EnterArgInfo {

pub(crate) fn op_enter(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let a = operand.as_w()?;
let argc = vm.current_callinfo.as_ref().map_or(0, |ci| ci.n_args);
let arg_info = EnterArgInfo::from(a);
let m1_argc = arg_info.m1 as usize;
for i in 0..m1_argc {
Expand All @@ -1214,9 +1215,24 @@ pub(crate) fn op_enter(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
}
}
}
let optional_arg = arg_info.o as usize;
if optional_arg > 0 {
let m2_argc = arg_info.m2 as usize;
let total_preset_args = argc.saturating_sub(m1_argc + m2_argc);
for peek_pc in 0..total_preset_args {
match vm.current_irep.code[vm.pc.get() + peek_pc].code {
OpCode::JMP => {}
_ => {
unreachable!("unexpected opcode while processing optional args")
}
}
}
vm.pc.set(vm.pc.get() + total_preset_args);
}

let splat_arg = arg_info.r as usize;
if splat_arg == 1 {
let total_args = vm.current_callinfo.as_ref().map_or(0, |ci| ci.n_args);
let total_args = argc;
let passed_args = total_args.saturating_sub(m1_argc);
let mut array = Vec::new();
for i in 0..passed_args {
Expand Down Expand Up @@ -1438,7 +1454,7 @@ pub(crate) fn op_addi(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
unreachable!("addi supports only integer")
}
};
vm.current_regs()[a as usize].replace(Rc::new(result));
vm.current_regs()[a as usize].replace(result.to_refcount_assigned());
Ok(())
}

Expand All @@ -1453,7 +1469,7 @@ pub(crate) fn op_sub(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
unreachable!("sub supports only integer")
}
};
vm.current_regs()[a].replace(Rc::new(result));
vm.current_regs()[a].replace(result.to_refcount_assigned());
Ok(())
}

Expand All @@ -1467,7 +1483,7 @@ pub(crate) fn op_subi(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
unreachable!("subi supports only integer")
}
};
vm.current_regs()[a as usize].replace(Rc::new(result));
vm.current_regs()[a as usize].replace(result.to_refcount_assigned());
Ok(())
}

Expand Down Expand Up @@ -1603,7 +1619,7 @@ fn do_op_array(vm: &mut VM, this: usize, start: usize, n: usize) -> Result<(), E
}
}
let val = RObject::array(ary);
vm.current_regs()[this].replace(Rc::new(val));
vm.current_regs()[this].replace(val.to_refcount_assigned());
Ok(())
}

Expand All @@ -1627,7 +1643,7 @@ pub(crate) fn op_arycat(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
ary1.push(item.clone());
}
let val = RObject::array(ary1);
vm.current_regs()[a].replace(Rc::new(val));
vm.current_regs()[a].replace(val.to_refcount_assigned());
}
_ => {
unreachable!("arycat supports only array")
Expand Down Expand Up @@ -1687,15 +1703,15 @@ pub(crate) fn op_symbol(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let symstr = vm.current_irep.pool[b as usize].as_str().to_string();
let sym = RSym::new(symstr);
let val = RObject::symbol(sym);
vm.current_regs()[a as usize].replace(Rc::new(val));
vm.current_regs()[a as usize].replace(val.to_refcount_assigned());
Ok(())
}

pub(crate) fn op_string(vm: &mut VM, operand: &Fetched) -> Result<(), Error> {
let (a, b) = operand.as_bb()?;
let str = vm.current_irep.pool[b as usize].as_str().to_string();
let val = RObject::string(str);
vm.current_regs()[a as usize].replace(Rc::new(val));
vm.current_regs()[a as usize].replace(val.to_refcount_assigned());
Ok(())
}

Expand Down
6 changes: 5 additions & 1 deletion mrubyedge/src/yamrb/prelude/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,11 @@ pub fn mrb_array_set_index(this: Rc<RObject>, args: &[Rc<RObject>]) -> Result<Rc
match &this.value {
RValue::Array(a) => {
let mut a = a.borrow_mut();
a.insert(index, value.clone());
if a.len() <= index {
a.insert(index, value.clone());
} else {
a[index] = value.clone();
}
}
_ => {
return Err(Error::RuntimeError(
Expand Down
68 changes: 68 additions & 0 deletions mrubyedge/tests/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,71 @@ fn array_join_test() {
let result: String = result.as_ref().try_into().unwrap();
assert_eq!(result, "1,2,3");
}

#[test]
fn array_reference_mutation_test() {
let code = r#"
def incr(times, state)
return state if times == 0
state[0] += 1
incr(times - 1, state)
end

def test_array_reference
arr = [0]
incr(3, arr)
arr[0]
end
"#;
let binary = mrbc_compile("array_reference_mutation", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
vm.run().unwrap();

let args = vec![];
let result = mrb_funcall(&mut vm, None, "test_array_reference", &args).unwrap();
let result: i64 = result.as_ref().try_into().unwrap();
assert_eq!(result, 3);
}

#[test]
fn array_reference_mutation_recursive_test() {
let code = r#"
def incr_recursive(times, state, results)
return results if times == 0
state[0] += 1
results << state[0]
incr_recursive(times - 1, state, results)
end

def test_recursive_mutation
arr = [0]
results = []
incr_recursive(5, arr, results)
[arr[0], results]
end
"#;
let binary = mrbc_compile("array_reference_recursive", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
vm.run().unwrap();

let args = vec![];
let result = mrb_funcall(&mut vm, None, "test_recursive_mutation", &args).unwrap();
let outer: Vec<std::rc::Rc<mrubyedge::yamrb::value::RObject>> =
result.as_ref().try_into().unwrap();

// arr[0] should be 5
let final_count: i64 = outer[0].as_ref().try_into().unwrap();
assert_eq!(final_count, 5);

// results should be [1, 2, 3, 4, 5]
let results: Vec<std::rc::Rc<mrubyedge::yamrb::value::RObject>> =
outer[1].as_ref().try_into().unwrap();
assert_eq!(results.len(), 5);

for (i, item) in results.iter().enumerate() {
let val: i64 = item.as_ref().try_into().unwrap();
assert_eq!(val, (i + 1) as i64);
}
}
132 changes: 132 additions & 0 deletions mrubyedge/tests/optional_args.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
extern crate mec_mrbc_sys;
extern crate mrubyedge;

mod helpers;
use helpers::*;

#[test]
fn default_arg_array_reused() {
let code = r#"
def incr(times, state=[0])
return state if times == 0
state[0] += 1
incr(times - 1, state)
end

result = incr(3)
result[0]
"#;
let binary = mrbc_compile("default_arg_array_reused", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let result_int: i32 = result.as_ref().try_into().unwrap();
assert_eq!(result_int, 3);
}

#[test]
fn default_arg_simple() {
let code = r##"
def greet(name, greeting="Hello")
"#{greeting}, #{name}!"
end

greet("Alice")
"##;
let binary = mrbc_compile("default_arg_simple", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let result_str: String = result.as_ref().try_into().unwrap();
assert_eq!(result_str, "Hello, Alice!");
}

#[test]
fn default_arg_multiple() {
let code = r#"
def create_point(x=0, y=3, z=6)
[x, y, z]
end

result = create_point()
result[0] + result[1] + result[2]
"#;
let binary = mrbc_compile("default_arg_multiple", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let result_int: i32 = result.as_ref().try_into().unwrap();
assert_eq!(result_int, 9);
}

#[test]
fn default_arg_override() {
let code = r#"
def add(a, b=10)
a + b
end

add(5, 20)
"#;
let binary = mrbc_compile("default_arg_override", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let result_int: i32 = result.as_ref().try_into().unwrap();
assert_eq!(result_int, 25);
}

#[test]
fn default_arg_partial_override() {
let code = r#"
def calc(a, b=2, c=3)
a * b + c
end

calc(5, 4)
"#;
let binary = mrbc_compile("default_arg_partial_override", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let result_int: i32 = result.as_ref().try_into().unwrap();
assert_eq!(result_int, 23);
}

#[test]
fn default_arg_hash_reused() {
let code = r#"
def update_counter(times, state={count: 0, other: 2})
return state if times == 0
state[:count] += 1
update_counter(times - 1, state)
end

result = update_counter(5)
result[:count] + result[:other]
"#;
let binary = mrbc_compile("default_arg_hash_reused", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let result_int: i32 = result.as_ref().try_into().unwrap();
assert_eq!(result_int, 5 + 2);
}

#[test]
fn default_arg_mixed_types() {
let code = r##"
def format_message(msg, prefix="Info", level=1, enabled=true)
return "disabled" unless enabled
"[#{prefix}:#{level}] #{msg}"
end

format_message("Test")
"##;
let binary = mrbc_compile("default_arg_mixed_types", code);
let mut rite = mrubyedge::rite::load(&binary).unwrap();
let mut vm = mrubyedge::yamrb::vm::VM::open(&mut rite);
let result = vm.run().unwrap();
let result_str: String = result.as_ref().try_into().unwrap();
assert_eq!(result_str, "[Info:1] Test");
}