diff --git a/valuescript_common/src/instruction_byte.rs b/valuescript_common/src/instruction_byte.rs index 48138a8..501abd7 100644 --- a/valuescript_common/src/instruction_byte.rs +++ b/valuescript_common/src/instruction_byte.rs @@ -54,6 +54,7 @@ pub enum InstructionByte { ThisSubCall = 0x33, Next = 0x34, UnpackIterRes = 0x35, + Cat = 0x36, } impl InstructionByte { @@ -115,6 +116,7 @@ impl InstructionByte { 0x33 => ThisSubCall, 0x34 => Next, 0x35 => UnpackIterRes, + 0x36 => Cat, _ => panic!("Unrecognized instruction: {}", byte), }; diff --git a/valuescript_compiler/src/asm.rs b/valuescript_compiler/src/asm.rs index 5846529..f8b4455 100644 --- a/valuescript_compiler/src/asm.rs +++ b/valuescript_compiler/src/asm.rs @@ -292,6 +292,7 @@ pub enum Instruction { ThisSubCall(Value, Value, Value, Register), Next(Register, Register), UnpackIterRes(Register, Register, Register), + Cat(Value, Register), } impl std::fmt::Display for Instruction { @@ -457,6 +458,9 @@ impl std::fmt::Display for Instruction { obj, value_register, done_register ) } + Instruction::Cat(iterables, register) => { + write!(f, "cat {} {}", iterables, register) + } } } } @@ -521,6 +525,7 @@ impl Instruction { ThisSubCall(..) => InstructionByte::ThisSubCall, Next(..) => InstructionByte::Next, UnpackIterRes(..) => InstructionByte::UnpackIterRes, + Cat(..) => InstructionByte::Cat, } } } diff --git a/valuescript_compiler/src/assembler.rs b/valuescript_compiler/src/assembler.rs index be613fe..0274551 100644 --- a/valuescript_compiler/src/assembler.rs +++ b/valuescript_compiler/src/assembler.rs @@ -240,6 +240,10 @@ impl Assembler { self.register(value_dst); self.register(done_dst); } + Cat(iterables, dst) => { + self.value(iterables); + self.register(dst); + } } } diff --git a/valuescript_compiler/src/assembly_parser.rs b/valuescript_compiler/src/assembly_parser.rs index 30e03a8..5f628ad 100644 --- a/valuescript_compiler/src/assembly_parser.rs +++ b/valuescript_compiler/src/assembly_parser.rs @@ -229,6 +229,7 @@ impl<'a> AssemblyParser<'a> { ("this_subcall", InstructionByte::ThisSubCall), ("next", InstructionByte::Next), ("unpack_iter_res", InstructionByte::UnpackIterRes), + ("cat", InstructionByte::Cat), ]); for (word, instruction) in instruction_word_map { @@ -693,6 +694,7 @@ impl<'a> AssemblyParser<'a> { self.assemble_register(), self.assemble_register(), ), + Cat => Instruction::Cat(self.assemble_value(), self.assemble_register()), } } diff --git a/valuescript_compiler/src/instruction_mutates_this.rs b/valuescript_compiler/src/instruction_mutates_this.rs index ad9edef..96041b2 100644 --- a/valuescript_compiler/src/instruction_mutates_this.rs +++ b/valuescript_compiler/src/instruction_mutates_this.rs @@ -48,7 +48,8 @@ pub fn instruction_mutates_this(instruction: &Instruction) -> bool { | ImportStar(_, reg) | SetCatch(_, reg) | ConstSubCall(_, _, _, reg) - | ThisSubCall(_, _, _, reg) => reg == &Register::This, + | ThisSubCall(_, _, _, reg) + | Cat(_, reg) => reg == &Register::This, Next(iter, res) => iter == &Register::This || res == &Register::This, UnpackIterRes(_, value_reg, done_reg) => { diff --git a/valuescript_compiler/src/link_module.rs b/valuescript_compiler/src/link_module.rs index 5b02c74..1ea5c0e 100644 --- a/valuescript_compiler/src/link_module.rs +++ b/valuescript_compiler/src/link_module.rs @@ -244,7 +244,8 @@ where | UnaryMinus(arg, _) | Import(arg, _) | ImportStar(arg, _) - | Throw(arg) => { + | Throw(arg) + | Cat(arg, _) => { self.value(Some(owner), arg); } OpPlus(arg1, arg2, _) diff --git a/valuescript_vm/src/bytecode_stack_frame.rs b/valuescript_vm/src/bytecode_stack_frame.rs index d790c68..26372d6 100644 --- a/valuescript_vm/src/bytecode_stack_frame.rs +++ b/valuescript_vm/src/bytecode_stack_frame.rs @@ -519,6 +519,34 @@ impl StackFrameTrait for BytecodeStackFrame { operations::op_sub(self.registers[iter_res_i].clone(), "done".to_val())?; } } + + Cat => { + let mut vals = Vec::::new(); + + let bytecode_type = self.decoder.decode_type(); + + if bytecode_type != BytecodeType::Array { + panic!("Not implemented: cat instruction not using inline array"); + } + + while self.decoder.peek_type() != BytecodeType::End { + let iterable = self.decoder.decode_val(&self.registers); + + match iterable { + Val::Array(array_data) => { + for item in &array_data.elements { + vals.push(item.clone()); + } + } + _ => todo!("Cat: Non-array iterable"), + } + } + + // TODO: Just skip the byte in release builds? + assert!(self.decoder.decode_type() == BytecodeType::End); + + self.registers[self.decoder.decode_register_index().unwrap()] = vals.to_val(); + } }; Ok(FrameStepOk::Continue)