diff --git a/dust-lang/src/compiler.rs b/dust-lang/src/compiler.rs index 7b5d6f6..35b22ae 100644 --- a/dust-lang/src/compiler.rs +++ b/dust-lang/src/compiler.rs @@ -95,18 +95,21 @@ impl<'src> Compiler<'src> { } fn next_register(&mut self) -> u8 { - self.chunk + let current = self + .chunk .instructions() .iter() .rev() .find_map(|(instruction, _)| { if instruction.yields_value() { - Some(instruction.a() + 1) + Some(instruction.a()) } else { None } }) - .unwrap_or(self.minimum_register) + .unwrap_or(self.minimum_register); + + current.overflowing_add(1).0 } fn advance(&mut self) -> Result<(), CompileError> { diff --git a/dust-lang/src/disassembler.rs b/dust-lang/src/disassembler.rs index 9c17e1d..8a5f4c8 100644 --- a/dust-lang/src/disassembler.rs +++ b/dust-lang/src/disassembler.rs @@ -50,8 +50,8 @@ use crate::{Chunk, ConcreteValue, Local, Value}; const INSTRUCTION_HEADER: [&str; 4] = [ "Instructions", "------------", - " i BYTECODE OPERATION INFO TYPE POSITION ", - "--- -------- ------------- -------------------- ---------------- ------------", + " i BYTECODE OPERATION INFO TYPE POSITION ", + "--- -------- ------------- -------------------- ---------------- ----------", ]; const CONSTANT_HEADER: [&str; 4] = [ @@ -277,7 +277,7 @@ impl<'a> Disassembler<'a> { let position = position.to_string(); let instruction_display = format!( - "{index:^3} {bytecode:>8} {operation:13} {info:20} {type_display:^16} {position:12}" + "{index:^3} {bytecode:>8} {operation:13} {info:^20} {type_display:^16} {position:10}" ); self.push_details(&instruction_display); diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index b6d2db2..78b5ed1 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -28,7 +28,7 @@ use std::{ use serde::{Deserialize, Serialize}; -use crate::{Chunk, FunctionType, RangeableType, Span, Type, Vm, VmError}; +use crate::{Chunk, FunctionType, RangeableType, Type}; /// Dust value representation /// @@ -255,25 +255,6 @@ impl Value { Ok(not) } - - pub fn to_concrete(self, vm: &mut Vm, position: Span) -> Result { - match self { - Value::Concrete(_) => Ok(self), - Value::Abstract(AbstractValue::List { start, end, .. }) => { - let mut items = Vec::new(); - - for register_index in start..end { - let get_value = vm.empty_register(register_index, position); - - if let Ok(value) = get_value { - items.push(value); - } - } - - Ok(Value::Concrete(ConcreteValue::List(items))) - } - } - } } impl From for Value { diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 9fc7dcc..dd5dde2 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -1,5 +1,8 @@ //! Virtual machine and errors -use std::{cmp::Ordering, mem::replace}; +use std::{ + cmp::Ordering, + fmt::{self, Display, Formatter}, +}; use crate::{ compile, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, @@ -9,42 +12,53 @@ use crate::{ pub fn run(source: &str) -> Result, DustError> { let chunk = compile(source)?; - let vm = Vm::new(chunk); + let mut vm = Vm::new(chunk, None); vm.run() + .map(|option| option.cloned()) .map_err(|error| DustError::Runtime { error, source }) } +pub fn run_and_display_output(source: &str) { + match run(source) { + Ok(Some(value)) => println!("{}", value), + Ok(None) => {} + Err(error) => eprintln!("{}", error.report()), + } +} + /// Dust virtual machine. /// /// See the [module-level documentation](index.html) for more information. #[derive(Debug, Eq, PartialEq)] -pub struct Vm { +pub struct Vm<'parent> { ip: usize, chunk: Chunk, stack: Vec, last_assigned_register: Option, + parent: Option<&'parent Vm<'parent>>, } -impl Vm { +impl<'parent> Vm<'parent> { const STACK_LIMIT: usize = u16::MAX as usize; - pub fn new(chunk: Chunk) -> Self { + pub fn new(chunk: Chunk, parent: Option<&'parent Vm<'parent>>) -> Self { Self { ip: 0, chunk, stack: Vec::new(), last_assigned_register: None, + parent, } } - pub fn run(mut self) -> Result, VmError> { + pub fn run(&mut self) -> Result, VmError> { // DRY helper to get constant or register values for binary operations - fn get_arguments( - vm: &mut Vm, + fn get_arguments<'a>( + vm: &'a mut Vm, instruction: Instruction, position: Span, - ) -> Result<(&Value, &Value), VmError> { + ) -> Result<(&'a Value, &'a Value), VmError> { let left = if instruction.b_is_constant() { vm.get_constant(instruction.b(), position)? } else { @@ -78,7 +92,11 @@ impl Vm { .is_some_and(|register| !matches!(register, Register::Empty)); if from_register_has_value { - self.set_pointer(to_register, from_register, position)?; + self.set_register( + to_register, + Register::StackPointer(from_register), + position, + )?; } } Operation::Close => { @@ -99,7 +117,7 @@ impl Vm { let jump = instruction.c_as_boolean(); let value = Value::boolean(boolean); - self.set_register(to_register, value, position)?; + self.set_register(to_register, Register::Value(value), position)?; if jump { self.ip += 1; @@ -110,7 +128,11 @@ impl Vm { let from_constant = instruction.b(); let jump = instruction.c_as_boolean(); - self.set_constant(to_register, from_constant, position)?; + self.set_register( + to_register, + Register::ConstantPointer(from_constant), + position, + )?; if jump { self.ip += 1 @@ -130,7 +152,7 @@ impl Vm { .unwrap_or(Type::Any); let value = Value::abstract_list(start_register, to_register, item_type); - self.set_register(to_register, value, position)?; + self.set_register(to_register, Register::Value(value), position)?; } Operation::LoadSelf => { let to_register = instruction.a(); @@ -143,7 +165,7 @@ impl Vm { }, ); - self.set_register(to_register, value, position)?; + self.set_register(to_register, Register::Value(value), position)?; } Operation::DefineLocal => { let from_register = instruction.a(); @@ -156,7 +178,11 @@ impl Vm { let local_index = instruction.b(); let local = self.get_local(local_index, position)?; - self.set_pointer(to_register, local.register_index, position)?; + self.set_register( + to_register, + Register::StackPointer(local.register_index), + position, + )?; } Operation::SetLocal => { let register = instruction.a(); @@ -165,44 +191,49 @@ impl Vm { self.define_local(local_index, register, position)?; } Operation::Add => { - let (left, right) = get_arguments(&mut self, instruction, position)?; + let to_register = instruction.a(); + let (left, right) = get_arguments(self, instruction, position)?; let sum = left .add(right) .map_err(|error| VmError::Value { error, position })?; - self.set_register(instruction.a(), sum, position)?; + self.set_register(to_register, Register::Value(sum), position)?; } Operation::Subtract => { - let (left, right) = get_arguments(&mut self, instruction, position)?; + let to_register = instruction.a(); + let (left, right) = get_arguments(self, instruction, position)?; let difference = left .subtract(right) .map_err(|error| VmError::Value { error, position })?; - self.set_register(instruction.a(), difference, position)?; + self.set_register(to_register, Register::Value(difference), position)?; } Operation::Multiply => { - let (left, right) = get_arguments(&mut self, instruction, position)?; + let to_register = instruction.a(); + let (left, right) = get_arguments(self, instruction, position)?; let product = left .multiply(right) .map_err(|error| VmError::Value { error, position })?; - self.set_register(instruction.a(), product, position)?; + self.set_register(to_register, Register::Value(product), position)?; } Operation::Divide => { - let (left, right) = get_arguments(&mut self, instruction, position)?; + let to_register = instruction.a(); + let (left, right) = get_arguments(self, instruction, position)?; let quotient = left .divide(right) .map_err(|error| VmError::Value { error, position })?; - self.set_register(instruction.a(), quotient, position)?; + self.set_register(to_register, Register::Value(quotient), position)?; } Operation::Modulo => { - let (left, right) = get_arguments(&mut self, instruction, position)?; + let to_register = instruction.a(); + let (left, right) = get_arguments(self, instruction, position)?; let remainder = left .modulo(right) .map_err(|error| VmError::Value { error, position })?; - self.set_register(instruction.a(), remainder, position)?; + self.set_register(to_register, Register::Value(remainder), position)?; } Operation::Test => { let register = instruction.a(); @@ -228,7 +259,7 @@ impl Vm { Operation::Jump ); - let (left, right) = get_arguments(&mut self, instruction, position)?; + let (left, right) = get_arguments(self, instruction, position)?; let equal_result = left .equal(right) .map_err(|error| VmError::Value { error, position })?; @@ -264,7 +295,7 @@ impl Vm { Operation::Jump ); - let (left, right) = get_arguments(&mut self, instruction, position)?; + let (left, right) = get_arguments(self, instruction, position)?; let less_result = left .less_than(right) .map_err(|error| VmError::Value { error, position })?; @@ -300,7 +331,7 @@ impl Vm { Operation::Jump ); - let (left, right) = get_arguments(&mut self, instruction, position)?; + let (left, right) = get_arguments(self, instruction, position)?; let less_or_equal_result = left .less_than_or_equal(right) .map_err(|error| VmError::Value { error, position })?; @@ -341,7 +372,7 @@ impl Vm { .negate() .map_err(|error| VmError::Value { error, position })?; - self.set_register(instruction.a(), negated, position)?; + self.set_register(instruction.a(), Register::Value(negated), position)?; } Operation::Not => { let value = if instruction.b_is_constant() { @@ -353,7 +384,7 @@ impl Vm { .not() .map_err(|error| VmError::Value { error, position })?; - self.set_register(instruction.a(), not, position)?; + self.set_register(instruction.a(), Register::Value(not), position)?; } Operation::Jump => { let jump_distance = instruction.b(); @@ -379,36 +410,35 @@ impl Vm { position, }); }; - let mut function_vm = Vm::new(function.take_chunk()); + let mut function_vm = Vm::new(function.take_chunk(), Some(self)); let first_argument_index = function_register + 1; for argument_index in first_argument_index..first_argument_index + argument_count { - let argument = match self.get_register(argument_index, position) { - Ok(value) => value.clone(), - Err(VmError::EmptyRegister { .. }) => continue, - Err(error) => return Err(error), - }; let top_of_stack = function_vm.stack.len() as u8; - function_vm.set_register(top_of_stack, argument, position)?; + function_vm.set_register( + top_of_stack, + Register::ParentStackPointer(argument_index), + position, + )? } - let return_value = function_vm.run()?; + let return_value = function_vm.run()?.cloned(); if let Some(value) = return_value { - self.set_register(to_register, value, position)?; + self.set_register(to_register, Register::Value(value), position)?; } } Operation::CallNative => { let native_function = NativeFunction::from(instruction.b()); - let return_value = native_function.call(instruction, &self, position)?; + let return_value = native_function.call(instruction, self, position)?; if let Some(value) = return_value { let to_register = instruction.a(); - self.set_register(to_register, value, position)?; + self.set_register(to_register, Register::Value(value), position)?; } } Operation::Return => { @@ -418,15 +448,13 @@ impl Vm { return Ok(None); } - if let Some(register) = self.last_assigned_register { - let value = self - .empty_register(register, position)? - .to_concrete(&mut self, position)?; - - return Ok(Some(value)); + let return_value = if let Some(register_index) = self.last_assigned_register { + self.get_register(register_index, position)? } else { return Err(VmError::StackUnderflow { position }); - } + }; + + return Ok(Some(return_value)); } } } @@ -437,7 +465,7 @@ impl Vm { fn set_register( &mut self, to_register: u8, - value: Value, + register: Register, position: Span, ) -> Result<(), VmError> { self.last_assigned_register = Some(to_register); @@ -451,16 +479,16 @@ impl Vm { match to_register.cmp(&length) { Ordering::Less => { - log::trace!("Change R{to_register} to {value}"); + log::trace!("Change R{to_register} to {register}"); - self.stack[to_register] = Register::Value(value); + self.stack[to_register] = register; Ok(()) } Ordering::Equal => { - log::trace!("Set R{to_register} to {value}"); + log::trace!("Set R{to_register} to {register}"); - self.stack.push(Register::Value(value)); + self.stack.push(register); Ok(()) } @@ -473,157 +501,58 @@ impl Vm { self.stack.push(Register::Empty); } - log::trace!("Set R{to_register} to {value}"); + log::trace!("Set R{to_register} to {register}"); - self.stack.push(Register::Value(value)); + self.stack.push(register); Ok(()) } } } - fn set_pointer( - &mut self, - to_register: u8, - from_register: u8, - position: Span, - ) -> Result<(), VmError> { - self.last_assigned_register = Some(to_register); - - let length = self.stack.len(); - let to_register = to_register as usize; - - if length == Self::STACK_LIMIT { - return Err(VmError::StackOverflow { position }); - } - - match to_register.cmp(&length) { - Ordering::Less => { - log::trace!("Change R{to_register} to R{from_register}"); - - self.stack[to_register] = Register::Pointer(from_register); - } - Ordering::Equal => { - log::trace!("Set R{to_register} to R{from_register}"); - - self.stack.push(Register::Pointer(from_register)); - } - Ordering::Greater => { - let difference = to_register - length; - - for index in 0..difference { - log::trace!("Set R{index} to empty"); - - self.stack.push(Register::Empty); - } - - log::trace!("Set R{to_register} to R{from_register}"); - - self.stack.push(Register::Pointer(from_register)); - } - } - - Ok(()) - } - - fn set_constant( - &mut self, - to_register: u8, - constant_index: u8, - position: Span, - ) -> Result<(), VmError> { - self.last_assigned_register = Some(to_register); - - let length = self.stack.len(); - let to_register = to_register as usize; - - if length == Self::STACK_LIMIT { - return Err(VmError::StackOverflow { position }); - } - - match to_register.cmp(&length) { - Ordering::Less => { - log::trace!("Change R{to_register} to C{constant_index}"); - - self.stack[to_register] = Register::Constant(constant_index); - } - Ordering::Equal => { - log::trace!("Set R{to_register} to C{constant_index}"); - - self.stack.push(Register::Constant(constant_index)); - } - Ordering::Greater => { - let difference = to_register - length; - - for index in 0..difference { - log::trace!("Set R{index} to empty"); - - self.stack.push(Register::Empty); - } - - log::trace!("Set R{to_register} to C{constant_index}"); - - self.stack.push(Register::Constant(constant_index)); - } - } - - Ok(()) - } - fn get_constant(&self, index: u8, position: Span) -> Result<&Value, VmError> { self.chunk .get_constant(index) .map_err(|error| VmError::Chunk { error, position }) } - pub fn get_register(&self, index: u8, position: Span) -> Result<&Value, VmError> { - let index = index as usize; - let register = self - .stack - .get(index) - .ok_or_else(|| VmError::RegisterIndexOutOfBounds { index, position })?; + pub fn get_register(&self, register_index: u8, position: Span) -> Result<&Value, VmError> { + let register_index = register_index as usize; + let register = + self.stack + .get(register_index) + .ok_or_else(|| VmError::RegisterIndexOutOfBounds { + index: register_index, + position, + })?; match register { Register::Value(value) => Ok(value), - Register::Pointer(register_index) => self.get_register(*register_index, position), - Register::Constant(constant_index) => self.get_constant(*constant_index, position), - Register::Empty => Err(VmError::EmptyRegister { index, position }), - } - } - - pub fn empty_register(&mut self, index: u8, position: Span) -> Result { - let index = index as usize; - - if index >= self.stack.len() { - return Err(VmError::RegisterIndexOutOfBounds { index, position }); - } - - let register = replace(&mut self.stack[index], Register::Empty); - let value = match register { - Register::Value(value) => value, - Register::Pointer(register_index) => self.empty_register(register_index, position)?, - Register::Constant(constant_index) => { - let constant_index = constant_index as usize; - - if constant_index >= self.chunk.constants().len() { - return Err(VmError::Chunk { - error: ChunkError::ConstantIndexOutOfBounds { - index: constant_index, - }, - position, - }); - } - - let constant = &mut self.chunk.constants_mut()[constant_index]; - - replace(constant, Value::integer(0)) + Register::StackPointer(register_index) => self.get_register(*register_index, position), + Register::ConstantPointer(constant_index) => { + self.get_constant(*constant_index, position) } - Register::Empty => return Err(VmError::EmptyRegister { index, position }), - }; + Register::ParentStackPointer(register_index) => { + let parent = self + .parent + .as_ref() + .ok_or(VmError::ExpectedParent { position })?; - self.chunk.is_poisoned = true; + parent.get_register(*register_index, position) + } + Register::ParentConstantPointer(constant_index) => { + let parent = self + .parent + .as_ref() + .ok_or(VmError::ExpectedParent { position })?; - Ok(value) + parent.get_constant(*constant_index, position) + } + Register::Empty => Err(VmError::EmptyRegister { + index: register_index, + position, + }), + } } fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> { @@ -681,8 +610,23 @@ impl Vm { enum Register { Empty, Value(Value), - Pointer(u8), - Constant(u8), + StackPointer(u8), + ConstantPointer(u8), + ParentStackPointer(u8), + ParentConstantPointer(u8), +} + +impl Display for Register { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + match self { + Self::Empty => write!(f, "empty"), + Self::Value(value) => write!(f, "{}", value), + Self::StackPointer(index) => write!(f, "R{}", index), + Self::ConstantPointer(index) => write!(f, "C{}", index), + Self::ParentStackPointer(index) => write!(f, "PR{}", index), + Self::ParentConstantPointer(index) => write!(f, "PC{}", index), + } + } } #[derive(Clone, Debug, PartialEq)] @@ -698,6 +642,7 @@ pub enum VmError { // Execution errors ExpectedBoolean { found: Value, position: Span }, ExpectedFunction { found: Value, position: Span }, + ExpectedParent { position: Span }, // Wrappers for foreign errors Chunk { error: ChunkError, position: Span }, @@ -716,6 +661,7 @@ impl AnnotatedError for VmError { Self::EmptyRegister { .. } => "Empty register", Self::ExpectedBoolean { .. } => "Expected boolean", Self::ExpectedFunction { .. } => "Expected function", + Self::ExpectedParent { .. } => "Expected parent", Self::NativeFunction(error) => error.description(), Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds", Self::StackOverflow { .. } => "Stack overflow", @@ -744,6 +690,7 @@ impl AnnotatedError for VmError { Self::EmptyRegister { position, .. } => *position, Self::ExpectedBoolean { position, .. } => *position, Self::ExpectedFunction { position, .. } => *position, + Self::ExpectedParent { position } => *position, Self::NativeFunction(error) => error.position(), Self::RegisterIndexOutOfBounds { position, .. } => *position, Self::StackOverflow { position } => *position, diff --git a/dust-lang/tests/lists.rs b/dust-lang/tests/lists.rs index 072ae91..5141caf 100644 --- a/dust-lang/tests/lists.rs +++ b/dust-lang/tests/lists.rs @@ -17,7 +17,7 @@ fn empty_list() { )), ); - assert_eq!(run(source), Ok(Some(Value::list(vec![])))); + assert_eq!(run(source), Ok(Some(Value::abstract_list(0, 0, Type::Any)))); } #[test] @@ -35,18 +35,14 @@ fn list() { (Instruction::load_list(3, 0), Span(0, 9)), (Instruction::r#return(true), Span(9, 9)), ], - vec![Value::integer(1), Value::integer(2), Value::integer(3),], + vec![Value::integer(1), Value::integer(2), Value::integer(3)], vec![] )), ); assert_eq!( run(source), - Ok(Some(Value::list([ - Value::integer(1), - Value::integer(2), - Value::integer(3) - ]))) + Ok(Some(Value::abstract_list(0, 3, Type::Integer))) ); } @@ -90,10 +86,7 @@ fn list_with_complex_expression() { assert_eq!( run(source), - Ok(Some(Value::list([ - Value::integer(1), - Value::integer(2 + 3 - 4 * 5) - ]))) + Ok(Some(Value::abstract_list(0, 4, Type::Integer))) ); } @@ -129,10 +122,6 @@ fn list_with_simple_expression() { assert_eq!( run(source), - Ok(Some(Value::list([ - Value::integer(1), - Value::integer(2 + 3), - Value::integer(4) - ]))) + Ok(Some(Value::abstract_list(0, 3, Type::Integer))) ); } diff --git a/examples/assets/fibonacci.js b/examples/assets/fibonacci.js index 7f1e036..3cc6cb3 100644 --- a/examples/assets/fibonacci.js +++ b/examples/assets/fibonacci.js @@ -8,4 +8,4 @@ function fib(n) { } } -console.log(fib(10)); +console.log(fib(20)); diff --git a/examples/fibonacci.ds b/examples/fibonacci.ds index 449b90d..bbe5678 100644 --- a/examples/fibonacci.ds +++ b/examples/fibonacci.ds @@ -5,4 +5,4 @@ fn fib (n: int) -> int { fib(n - 1) + fib(n - 2) } -fib(10) +fib(20)