From 73247446c7ff1530f343910b2aa455e33ef6ca9e Mon Sep 17 00:00:00 2001 From: Jeff Date: Sun, 10 Nov 2024 19:28:21 -0500 Subject: [PATCH] Major overhaul to VM --- dust-lang/src/chunk.rs | 149 +- dust-lang/src/compiler.rs | 139 +- dust-lang/src/disassembler.rs | 34 +- dust-lang/src/function.rs | 63 + dust-lang/src/instruction.rs | 40 +- dust-lang/src/lib.rs | 10 +- dust-lang/src/native_function/logic.rs | 157 +++ .../mod.rs} | 202 +-- dust-lang/src/scope.rs | 59 + dust-lang/src/type.rs | 110 +- dust-lang/src/value.rs | 1208 ++++++++++------- dust-lang/src/vm.rs | 539 ++++---- dust-lang/tests/basic.rs | 12 +- dust-lang/tests/comparison.rs | 24 +- dust-lang/tests/control_flow.rs | 80 +- dust-lang/tests/functions.rs | 101 +- dust-lang/tests/lists.rs | 42 +- dust-lang/tests/logic.rs | 8 +- dust-lang/tests/loops.rs | 10 +- dust-lang/tests/math.rs | 56 +- dust-lang/tests/native_functions.rs | 9 +- dust-lang/tests/scopes.rs | 40 +- dust-lang/tests/unary_operations.rs | 6 +- dust-lang/tests/variables.rs | 10 +- 24 files changed, 1729 insertions(+), 1379 deletions(-) create mode 100644 dust-lang/src/function.rs create mode 100644 dust-lang/src/native_function/logic.rs rename dust-lang/src/{native_function.rs => native_function/mod.rs} (63%) create mode 100644 dust-lang/src/scope.rs diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 80982a8..e0bfb70 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -4,29 +4,23 @@ //! list of locals that can be executed by the Dust virtual machine. Chunks have a name when they //! belong to a named function. -use std::{ - cmp::Ordering, - fmt::{self, Debug, Display, Formatter}, -}; +use std::fmt::{self, Debug, Display, Formatter}; use serde::{Deserialize, Serialize}; -use crate::{Disassembler, Instruction, Span, Type, Value}; +use crate::{value::ConcreteValue, Disassembler, FunctionType, Instruction, Scope, Span, Type}; /// In-memory representation of a Dust program or function. /// /// See the [module-level documentation](index.html) for more information. #[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)] pub struct Chunk { - name: Option, + name: Option, // TODO: Use a bool indicating whether the chunk is named. If it is, get + r#type: FunctionType, // the name from C0. instructions: Vec<(Instruction, Span)>, - constants: Vec, + constants: Vec, locals: Vec, - - return_type: Type, - current_scope: Scope, - block_index: u8, } impl Chunk { @@ -36,16 +30,18 @@ impl Chunk { instructions: Vec::new(), constants: Vec::new(), locals: Vec::new(), - return_type: Type::None, - current_scope: Scope::default(), - block_index: 0, + r#type: FunctionType { + type_parameters: None, + value_parameters: None, + return_type: Box::new(Type::None), + }, } } pub fn with_data( name: Option, instructions: Vec<(Instruction, Span)>, - constants: Vec, + constants: Vec, locals: Vec, ) -> Self { Self { @@ -53,9 +49,11 @@ impl Chunk { instructions, constants, locals, - return_type: Type::None, - current_scope: Scope::default(), - block_index: 0, + r#type: FunctionType { + type_parameters: None, + value_parameters: None, + return_type: Box::new(Type::None), + }, } } @@ -63,6 +61,10 @@ impl Chunk { self.name.as_ref() } + pub fn r#type(&self) -> &FunctionType { + &self.r#type + } + pub fn set_name(&mut self, name: String) { self.name = Some(name); } @@ -75,16 +77,36 @@ impl Chunk { self.instructions.is_empty() } - pub fn constants(&self) -> &Vec { + pub fn constants(&self) -> &Vec { &self.constants } - pub fn constants_mut(&mut self) -> &mut Vec { + pub fn constants_mut(&mut self) -> &mut Vec { &mut self.constants } - pub fn take_constants(self) -> Vec { + pub fn get_constant(&self, index: u8) -> Result<&ConcreteValue, ChunkError> { self.constants + .get(index as usize) + .ok_or(ChunkError::ConstantIndexOutOfBounds { + index: index as usize, + }) + } + + pub fn push_or_get_constant(&mut self, value: ConcreteValue) -> u8 { + if let Some(index) = self + .constants + .iter() + .position(|constant| *constant == value) + { + index as u8 + } else { + let index = self.constants.len() as u8; + + self.constants.push(value); + + index + } } pub fn instructions(&self) -> &Vec<(Instruction, Span)> { @@ -125,32 +147,6 @@ impl Chunk { }) } - pub fn current_scope(&self) -> Scope { - self.current_scope - } - - pub fn get_constant(&self, index: u8) -> Result<&Value, ChunkError> { - self.constants - .get(index as usize) - .ok_or(ChunkError::ConstantIndexOutOfBounds { - index: index as usize, - }) - } - - pub fn push_or_get_constant(&mut self, value: Value) -> u8 { - if let Some(index) = self - .constants - .iter() - .position(|constant| constant == &value) - { - return index as u8; - } - - self.constants.push(value); - - (self.constants.len() - 1) as u8 - } - pub fn get_identifier(&self, local_index: u8) -> Option { self.locals.get(local_index as usize).and_then(|local| { self.constants @@ -159,22 +155,6 @@ impl Chunk { }) } - pub fn begin_scope(&mut self) { - self.block_index += 1; - self.current_scope.block_index = self.block_index; - self.current_scope.depth += 1; - } - - pub fn end_scope(&mut self) { - self.current_scope.depth -= 1; - - if self.current_scope.depth == 0 { - self.current_scope.block_index = 0; - } else { - self.current_scope.block_index -= 1; - } - } - pub fn get_constant_type(&self, constant_index: u8) -> Result { self.constants .get(constant_index as usize) @@ -193,10 +173,6 @@ impl Chunk { }) } - pub fn return_type(&self) -> &Type { - &self.return_type - } - pub fn disassembler(&self) -> Disassembler { Disassembler::new(self) } @@ -260,45 +236,11 @@ impl Local { } } -/// Variable locality, as defined by its depth and block index. -/// -/// The `block index` is a unique identifier for a block within a chunk. It is used to differentiate -/// between blocks that are not nested together but have the same depth, i.e. sibling scopes. If the -/// `block_index` is 0, then the scope is the root scope of the chunk. The `block_index` is always 0 -/// when the `depth` is 0. See [Chunk::begin_scope][] and [Chunk::end_scope][] to see how scopes are -/// incremented and decremented. -#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Scope { - /// Level of block nesting. - pub depth: u8, - /// Index of the block in the chunk. - pub block_index: u8, -} - -impl Scope { - pub fn new(depth: u8, block_index: u8) -> Self { - Self { depth, block_index } - } - - pub fn contains(&self, other: &Self) -> bool { - match self.depth.cmp(&other.depth) { - Ordering::Less => false, - Ordering::Greater => self.block_index >= other.block_index, - Ordering::Equal => self.block_index == other.block_index, - } - } -} - -impl Display for Scope { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "({}, {})", self.depth, self.block_index) - } -} - /// Errors that can occur when using a [`Chunk`]. #[derive(Clone, Debug, PartialEq)] pub enum ChunkError { ConstantIndexOutOfBounds { index: usize }, + FunctionIndexOutOfBounds { index: usize }, InstructionIndexOutOfBounds { index: usize }, LocalIndexOutOfBounds { index: usize }, } @@ -309,6 +251,9 @@ impl Display for ChunkError { ChunkError::ConstantIndexOutOfBounds { index } => { write!(f, "Constant index {} out of bounds", index) } + ChunkError::FunctionIndexOutOfBounds { index } => { + write!(f, "Function index {} out of bounds", index) + } ChunkError::InstructionIndexOutOfBounds { index } => { write!(f, "Instruction index {} out of bounds", index) } diff --git a/dust-lang/src/compiler.rs b/dust-lang/src/compiler.rs index 0fc2a0f..ef0a4a0 100644 --- a/dust-lang/src/compiler.rs +++ b/dust-lang/src/compiler.rs @@ -13,9 +13,9 @@ use std::{ use colored::Colorize; use crate::{ - AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Instruction, LexError, Lexer, - Local, NativeFunction, Operation, Optimizer, Scope, Span, Token, TokenKind, TokenOwned, Type, - TypeConflict, Value, + value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Instruction, + LexError, Lexer, Local, NativeFunction, Operation, Optimizer, Scope, Span, Token, TokenKind, + TokenOwned, Type, TypeConflict, }; /// Compiles the input and returns a chunk. @@ -59,6 +59,9 @@ pub struct Compiler<'src> { previous_token: Token<'src>, previous_position: Span, + + block_index: u8, + current_scope: Scope, } impl<'src> Compiler<'src> { @@ -83,6 +86,8 @@ impl<'src> Compiler<'src> { current_position, previous_token: Token::Eof, previous_position: Span(0, 0), + block_index: 0, + current_scope: Scope::default(), }) } @@ -149,11 +154,15 @@ impl<'src> Compiler<'src> { .enumerate() .rev() .find_map(|(index, local)| { - let identifier = self + let constant = self .chunk .constants() - .get(local.identifier_index as usize)? - .as_string()?; + .get(local.identifier_index as usize)?; + let identifier = if let ConcreteValue::String(identifier) = constant { + identifier + } else { + return None; + }; if identifier == identifier_text { Some(index as u8) @@ -177,7 +186,7 @@ impl<'src> Compiler<'src> { ) -> (u8, u8) { log::debug!("Declare local {identifier}"); - let identifier = Value::string(identifier); + let identifier = ConcreteValue::string(identifier); let identifier_index = self.chunk.push_or_get_constant(identifier); self.chunk @@ -210,16 +219,6 @@ impl<'src> Compiler<'src> { } } - fn emit_instruction(&mut self, instruction: Instruction, position: Span) { - log::debug!( - "Emitting {} at {}", - instruction.operation().to_string().bold(), - position.to_string() - ); - - self.chunk.instructions_mut().push((instruction, position)); - } - fn pop_last_instruction(&mut self) -> Result<(Instruction, Span), CompileError> { self.chunk .instructions_mut() @@ -316,8 +315,8 @@ impl<'src> Compiler<'src> { pub fn get_register_type(&self, register_index: u8) -> Result { for (index, (instruction, _)) in self.chunk.instructions().iter().enumerate() { - if let Operation::LoadList = instruction.operation() { - if instruction.a() == register_index { + if instruction.a() == register_index { + if let Operation::LoadList = instruction.operation() { let mut length = (instruction.c() - instruction.b() + 1) as usize; let mut item_type = Type::Any; let distance_to_end = self.chunk.len() - index; @@ -342,10 +341,14 @@ impl<'src> Compiler<'src> { length, }); } - } - if instruction.yields_value() && instruction.a() == register_index { - return self.get_instruction_type(instruction); + if let Operation::LoadSelf = instruction.operation() { + return Ok(Type::SelfChunk); + } + + if instruction.yields_value() { + return self.get_instruction_type(instruction); + } } } @@ -355,8 +358,22 @@ impl<'src> Compiler<'src> { }) } - fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), CompileError> { - let constant_index = self.chunk.push_or_get_constant(value); + fn emit_instruction(&mut self, instruction: Instruction, position: Span) { + log::debug!( + "Emitting {} at {}", + instruction.operation().to_string().bold(), + position.to_string() + ); + + self.chunk.instructions_mut().push((instruction, position)); + } + + fn emit_constant( + &mut self, + constant: ConcreteValue, + position: Span, + ) -> Result<(), CompileError> { + let constant_index = self.chunk.push_or_get_constant(constant); let register = self.next_register(); self.emit_instruction( @@ -401,7 +418,7 @@ impl<'src> Compiler<'src> { let byte = u8::from_str_radix(&text[2..], 16) .map_err(|error| CompileError::ParseIntError { error, position })?; - let value = Value::byte(byte); + let value = ConcreteValue::byte(byte); self.emit_constant(value, position)?; @@ -423,7 +440,7 @@ impl<'src> Compiler<'src> { if let Token::Character(character) = self.current_token { self.advance()?; - let value = Value::character(character); + let value = ConcreteValue::character(character); self.emit_constant(value, position)?; @@ -451,7 +468,7 @@ impl<'src> Compiler<'src> { error, position: self.previous_position, })?; - let value = Value::float(float); + let value = ConcreteValue::float(float); self.emit_constant(value, position)?; @@ -479,7 +496,7 @@ impl<'src> Compiler<'src> { error, position: self.previous_position, })?; - let value = Value::integer(integer); + let value = ConcreteValue::integer(integer); self.emit_constant(value, position)?; @@ -501,7 +518,7 @@ impl<'src> Compiler<'src> { if let Token::String(text) = self.current_token { self.advance()?; - let value = Value::string(text); + let value = ConcreteValue::string(text); self.emit_constant(value, position)?; @@ -882,10 +899,15 @@ impl<'src> Compiler<'src> { return self.parse_native_call(native_function); } else if Some(identifier) == self.chunk.name().map(|string| string.as_str()) { let register = self.next_register(); - let scope = self.chunk.current_scope(); self.emit_instruction(Instruction::load_self(register), start_position); - self.declare_local(identifier, Type::SelfChunk, false, scope, register); + self.declare_local( + identifier, + Type::SelfChunk, + false, + self.current_scope, + register, + ); self.previous_expression_type = Type::SelfChunk; @@ -902,14 +924,13 @@ impl<'src> Compiler<'src> { (local.is_mutable, local.scope) }; - let current_scope = self.chunk.current_scope(); - if !current_scope.contains(&local_scope) { + if !self.current_scope.contains(&local_scope) { return Err(CompileError::VariableOutOfScope { identifier: self.chunk.get_identifier(local_index).unwrap(), position: start_position, variable_scope: local_scope, - access_scope: current_scope, + access_scope: self.current_scope, }); } @@ -977,13 +998,16 @@ impl<'src> Compiler<'src> { fn parse_block(&mut self) -> Result<(), CompileError> { self.advance()?; - self.chunk.begin_scope(); + + self.block_index += 1; + + self.current_scope.begin(self.block_index); while !self.allow(Token::RightBrace)? && !self.is_eof() { self.parse(Precedence::None)?; } - self.chunk.end_scope(); + self.current_scope.end(); Ok(()) } @@ -1317,7 +1341,6 @@ impl<'src> Compiler<'src> { fn parse_let_statement(&mut self) -> Result<(), CompileError> { self.advance()?; - let scope = self.chunk.current_scope(); let is_mutable = self.allow(Token::Mut)?; let position = self.current_position; let identifier = if let Token::Identifier(text) = self.current_token { @@ -1350,7 +1373,8 @@ impl<'src> Compiler<'src> { } else { self.get_register_type(register)? }; - let (local_index, _) = self.declare_local(identifier, r#type, is_mutable, scope, register); + let (local_index, _) = + self.declare_local(identifier, r#type, is_mutable, self.current_scope, register); self.emit_instruction( Instruction::define_local(register, local_index, is_mutable), @@ -1405,12 +1429,11 @@ impl<'src> Compiler<'src> { let register = function_compiler.next_register(); - let scope = function_compiler.chunk.current_scope(); let (_, identifier_index) = function_compiler.declare_local( parameter, r#type.clone(), is_mutable, - scope, + function_compiler.current_scope, register, ); @@ -1453,23 +1476,26 @@ impl<'src> Compiler<'src> { value_parameters, return_type, }; - let function = Value::function(function_compiler.finish(), function_type.clone()); + let function = ConcreteValue::function(function_compiler.finish()); + let constant_index = self.chunk.push_or_get_constant(function); let function_end = self.current_position.1; + let register = self.next_register(); self.lexer.skip_to(function_end); if let Some((identifier, identifier_position)) = identifier { - let register = self.next_register(); - let scope = self.chunk.current_scope(); let (local_index, _) = self.declare_local( identifier, Type::Function(function_type), false, - scope, + self.current_scope, register, ); - self.emit_constant(function, Span(function_start, function_end))?; + self.emit_instruction( + Instruction::load_constant(register, constant_index, false), + Span(function_start, function_end), + ); self.emit_instruction( Instruction::define_local(register, local_index, false), identifier_position, @@ -1477,7 +1503,10 @@ impl<'src> Compiler<'src> { self.previous_expression_type = Type::None; } else { - self.emit_constant(function, Span(function_start, function_end))?; + self.emit_instruction( + Instruction::load_constant(register, constant_index, false), + Span(function_start, function_end), + ); self.previous_expression_type = Type::Function(function_type); } @@ -1503,15 +1532,18 @@ impl<'src> Compiler<'src> { } let function_register = last_instruction.a(); - let function_return_type = - if let Type::Function(function_type) = self.get_register_type(function_register)? { - *function_type.return_type - } else { + let register_type = self.get_register_type(function_register)?; + let function_return_type = match register_type { + Type::Function(function_type) => *function_type.return_type, + Type::SelfChunk => (*self.chunk.r#type().return_type).clone(), + _ => { return Err(CompileError::ExpectedFunction { found: self.previous_token.to_owned(), + actual_type: register_type, position: self.previous_position, }); - }; + } + }; let start = self.current_position.0; self.advance()?; @@ -1944,6 +1976,7 @@ pub enum CompileError { }, ExpectedFunction { found: TokenOwned, + actual_type: Type, position: Span, }, InvalidAssignmentTarget { @@ -2048,8 +2081,8 @@ impl AnnotatedError for CompileError { } Self::Chunk { error, .. } => Some(error.to_string()), Self::ExpectedExpression { found, .. } => Some(format!("Found {found}")), - Self::ExpectedFunction { found, .. } => { - Some(format!("Expected \"{found}\" to be a function")) + Self::ExpectedFunction { found, actual_type, .. } => { + Some(format!("Expected \"{found}\" to be a function but it has type {actual_type}")) } Self::ExpectedToken { expected, found, .. diff --git a/dust-lang/src/disassembler.rs b/dust-lang/src/disassembler.rs index 9a0bf8c..0ecd8d5 100644 --- a/dust-lang/src/disassembler.rs +++ b/dust-lang/src/disassembler.rs @@ -45,13 +45,13 @@ use std::env::current_exe; use colored::Colorize; -use crate::{Chunk, ConcreteValue, Local, Value}; +use crate::{value::ConcreteValue, Chunk, Local}; const INSTRUCTION_HEADER: [&str; 4] = [ "Instructions", "------------", - " i BYTECODE OPERATION INFO TYPE POSITION ", - "--- -------- ------------- -------------------- ---------------- ----------", + " i BYTECODE OPERATION INFO POSITION ", + "--- -------- ------------- ------------------------- ----------", ]; const CONSTANT_HEADER: [&str; 4] = [ @@ -254,7 +254,7 @@ impl<'a> Disassembler<'a> { self.chunk.len(), self.chunk.constants().len(), self.chunk.locals().len(), - self.chunk.return_type() + self.chunk.r#type().return_type ); self.push(&info_line, true, false, true, true); @@ -271,7 +271,7 @@ impl<'a> Disassembler<'a> { let position = position.to_string(); let instruction_display = - format!("{index:^3} {bytecode:>8} {operation:13} {info:^20} {position:10}"); + format!("{index:^3} {bytecode:>8} {operation:13} {info:<25} {position:10}"); self.push_details(&instruction_display); } @@ -313,6 +313,19 @@ impl<'a> Disassembler<'a> { } for (index, value) in self.chunk.constants().iter().enumerate() { + if let ConcreteValue::Function(function) = value { + let function_disassembly = function + .chunk() + .disassembler() + .styled(self.styled) + .indent(self.indent + 1) + .disassemble(); + + self.output.push_str(&function_disassembly); + + continue; + } + let value_display = { let value_string = value.to_string(); @@ -325,17 +338,6 @@ impl<'a> Disassembler<'a> { let constant_display = format!("{index:^3} {value_display:^15}"); self.push_details(&constant_display); - - if let Value::Concrete(ConcreteValue::Function(function)) = value { - let function_disassembly = function - .chunk() - .disassembler() - .styled(self.styled) - .indent(self.indent + 1) - .disassemble(); - - self.output.push_str(&function_disassembly); - } } self.push_border(&bottom_border); diff --git a/dust-lang/src/function.rs b/dust-lang/src/function.rs new file mode 100644 index 0000000..44ad368 --- /dev/null +++ b/dust-lang/src/function.rs @@ -0,0 +1,63 @@ +use std::fmt::{self, Display, Formatter}; + +use serde::{Deserialize, Serialize}; + +use crate::{Chunk, Type}; + +#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Function { + chunk: Chunk, +} + +impl Function { + pub fn new(chunk: Chunk) -> Self { + Self { chunk } + } + + pub fn chunk(&self) -> &Chunk { + &self.chunk + } + + pub fn r#type(&self) -> Type { + Type::Function(self.chunk.r#type().clone()) + } + + pub fn as_borrowed(&self) -> FunctionBorrowed { + FunctionBorrowed::new(&self.chunk) + } +} + +impl Display for Function { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.chunk.r#type()) + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] +pub struct FunctionBorrowed<'a> { + chunk: &'a Chunk, +} + +impl<'a> FunctionBorrowed<'a> { + pub fn new(chunk: &'a Chunk) -> Self { + Self { chunk } + } + + pub fn chunk(&self) -> &Chunk { + self.chunk + } + + pub fn r#type(&self) -> Type { + Type::Function(self.chunk.r#type().clone()) + } + + pub fn to_owned(&self) -> Function { + Function::new(self.chunk.clone()) + } +} + +impl<'a> Display for FunctionBorrowed<'a> { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.chunk.r#type()) + } +} diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index ff3fa8e..cc35031 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -657,13 +657,13 @@ mod tests { #[test] fn r#move() { - let mut instruction = Instruction::r#move(0, 1); + let mut instruction = Instruction::r#move(4, 1); instruction.set_b_is_constant(); instruction.set_c_is_constant(); assert_eq!(instruction.operation(), Operation::Move); - assert_eq!(instruction.a(), 0); + assert_eq!(instruction.a(), 4); assert_eq!(instruction.b(), 1); assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant()); @@ -690,13 +690,13 @@ mod tests { #[test] fn load_constant() { - let mut instruction = Instruction::load_constant(0, 1, true); + let mut instruction = Instruction::load_constant(4, 1, true); instruction.set_b_is_constant(); instruction.set_c_is_constant(); assert_eq!(instruction.operation(), Operation::LoadConstant); - assert_eq!(instruction.a(), 0); + assert_eq!(instruction.a(), 4); assert_eq!(instruction.b(), 1); assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant()); @@ -705,10 +705,10 @@ mod tests { #[test] fn load_list() { - let instruction = Instruction::load_list(0, 1); + let instruction = Instruction::load_list(4, 1); assert_eq!(instruction.operation(), Operation::LoadList); - assert_eq!(instruction.a(), 0); + assert_eq!(instruction.a(), 4); assert_eq!(instruction.b(), 1); } @@ -722,12 +722,12 @@ mod tests { #[test] fn declare_local() { - let mut instruction = Instruction::define_local(0, 1, true); + let mut instruction = Instruction::define_local(4, 1, true); instruction.set_b_is_constant(); assert_eq!(instruction.operation(), Operation::DefineLocal); - assert_eq!(instruction.a(), 0); + assert_eq!(instruction.a(), 4); assert_eq!(instruction.b(), 1); assert_eq!(instruction.c(), true as u8); assert!(instruction.b_is_constant()); @@ -735,26 +735,26 @@ mod tests { #[test] fn add() { - let mut instruction = Instruction::add(1, 1, 0); + let mut instruction = Instruction::add(1, 1, 4); instruction.set_b_is_constant(); assert_eq!(instruction.operation(), Operation::Add); assert_eq!(instruction.a(), 1); assert_eq!(instruction.b(), 1); - assert_eq!(instruction.c(), 0); + assert_eq!(instruction.c(), 4); assert!(instruction.b_is_constant()); } #[test] fn subtract() { - let mut instruction = Instruction::subtract(0, 1, 2); + let mut instruction = Instruction::subtract(4, 1, 2); instruction.set_b_is_constant(); instruction.set_c_is_constant(); assert_eq!(instruction.operation(), Operation::Subtract); - assert_eq!(instruction.a(), 0); + assert_eq!(instruction.a(), 4); assert_eq!(instruction.b(), 1); assert_eq!(instruction.c(), 2); assert!(instruction.b_is_constant()); @@ -763,13 +763,13 @@ mod tests { #[test] fn multiply() { - let mut instruction = Instruction::multiply(0, 1, 2); + let mut instruction = Instruction::multiply(4, 1, 2); instruction.set_b_is_constant(); instruction.set_c_is_constant(); assert_eq!(instruction.operation(), Operation::Multiply); - assert_eq!(instruction.a(), 0); + assert_eq!(instruction.a(), 4); assert_eq!(instruction.b(), 1); assert_eq!(instruction.c(), 2); assert!(instruction.b_is_constant()); @@ -778,13 +778,13 @@ mod tests { #[test] fn divide() { - let mut instruction = Instruction::divide(0, 1, 2); + let mut instruction = Instruction::divide(4, 1, 2); instruction.set_b_is_constant(); instruction.set_c_is_constant(); assert_eq!(instruction.operation(), Operation::Divide); - assert_eq!(instruction.a(), 0); + assert_eq!(instruction.a(), 4); assert_eq!(instruction.b(), 1); assert_eq!(instruction.c(), 2); assert!(instruction.b_is_constant()); @@ -827,13 +827,13 @@ mod tests { #[test] fn negate() { - let mut instruction = Instruction::negate(0, 1); + let mut instruction = Instruction::negate(4, 1); instruction.set_b_is_constant(); instruction.set_c_is_constant(); assert_eq!(instruction.operation(), Operation::Negate); - assert_eq!(instruction.a(), 0); + assert_eq!(instruction.a(), 4); assert_eq!(instruction.b(), 1); assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant()); @@ -841,13 +841,13 @@ mod tests { #[test] fn not() { - let mut instruction = Instruction::not(0, 1); + let mut instruction = Instruction::not(4, 1); instruction.set_b_is_constant(); instruction.set_c_is_constant(); assert_eq!(instruction.operation(), Operation::Not); - assert_eq!(instruction.a(), 0); + assert_eq!(instruction.a(), 4); assert_eq!(instruction.b(), 1); assert!(instruction.b_is_constant()); assert!(instruction.b_is_constant()); diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index bd13096..8d04f93 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -5,29 +5,33 @@ pub mod compiler; pub mod disassembler; pub mod dust_error; pub mod formatter; +pub mod function; pub mod instruction; pub mod lexer; pub mod native_function; pub mod operation; pub mod optimizer; +pub mod scope; pub mod token; pub mod r#type; pub mod value; pub mod vm; -pub use crate::chunk::{Chunk, ChunkError, Local, Scope}; +pub use crate::chunk::{Chunk, ChunkError, Local}; pub use crate::compiler::{compile, CompileError, Compiler}; pub use crate::disassembler::Disassembler; pub use crate::dust_error::{AnnotatedError, DustError}; pub use crate::formatter::{format, Formatter}; +pub use crate::function::{Function, FunctionBorrowed}; pub use crate::instruction::Instruction; pub use crate::lexer::{lex, LexError, Lexer}; pub use crate::native_function::{NativeFunction, NativeFunctionError}; pub use crate::operation::Operation; pub use crate::optimizer::Optimizer; -pub use crate::r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict}; +pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict}; +pub use crate::scope::Scope; pub use crate::token::{output_token_list, Token, TokenKind, TokenOwned}; -pub use crate::value::{ConcreteValue, Function, Value, ValueError}; +pub use crate::value::{ConcreteValue, Value, ValueError}; pub use crate::vm::{run, Vm, VmError}; use std::fmt::Display; diff --git a/dust-lang/src/native_function/logic.rs b/dust-lang/src/native_function/logic.rs new file mode 100644 index 0000000..20b1c9e --- /dev/null +++ b/dust-lang/src/native_function/logic.rs @@ -0,0 +1,157 @@ +use std::io::{self, stdout, Write}; + +use crate::{ConcreteValue, Instruction, NativeFunctionError, Span, Value, Vm, VmError}; + +pub fn panic( + vm: &Vm, + instruction: Instruction, + position: Span, +) -> Result, VmError> { + let argument_count = instruction.c(); + let message = if argument_count == 0 { + None + } else { + let mut message = String::new(); + + for argument_index in 0..argument_count { + if argument_index != 0 { + message.push(' '); + } + + let argument = vm.open_register(argument_index, position)?; + + message.push_str(&argument.to_string()); + } + + Some(message) + }; + + Err(VmError::NativeFunction(NativeFunctionError::Panic { + message, + position, + })) +} + +pub fn to_string( + vm: &Vm, + instruction: Instruction, + position: Span, +) -> Result, VmError> { + let argument_count = instruction.c(); + + if argument_count != 1 { + return Err(VmError::NativeFunction( + NativeFunctionError::ExpectedArgumentCount { + expected: 1, + found: argument_count as usize, + position, + }, + )); + } + + let mut string = String::new(); + + for argument_index in 0..argument_count { + let argument = vm.open_register(argument_index, position)?; + + string.push_str(&argument.to_string()); + } + + Ok(Some(ConcreteValue::String(string))) +} + +pub fn read_line( + _: &Vm, + instruction: Instruction, + position: Span, +) -> Result, VmError> { + let argument_count = instruction.c(); + + if argument_count != 0 { + return Err(VmError::NativeFunction( + NativeFunctionError::ExpectedArgumentCount { + expected: 0, + found: argument_count as usize, + position, + }, + )); + } + + let mut buffer = String::new(); + + match io::stdin().read_line(&mut buffer) { + Ok(_) => Ok(Some(ConcreteValue::String( + buffer.trim_end_matches('\n').to_string(), + ))), + Err(error) => Err(VmError::NativeFunction(NativeFunctionError::Io { + error: error.kind(), + position, + })), + } +} + +pub fn write( + vm: &Vm, + instruction: Instruction, + position: Span, +) -> Result, VmError> { + let to_register = instruction.a(); + let argument_count = instruction.c(); + let mut stdout = stdout(); + let map_err = |io_error: io::Error| { + VmError::NativeFunction(NativeFunctionError::Io { + error: io_error.kind(), + position, + }) + }; + + let first_argument = to_register.saturating_sub(argument_count); + + for argument_index in first_argument..to_register { + if argument_index != first_argument { + stdout.write(b" ").map_err(map_err)?; + } + + let argument_string = vm.open_register(argument_index, position)?.to_string(); + + stdout + .write_all(argument_string.as_bytes()) + .map_err(map_err)?; + } + + Ok(None) +} + +pub fn write_line( + vm: &Vm, + instruction: Instruction, + position: Span, +) -> Result, VmError> { + let to_register = instruction.a(); + let argument_count = instruction.c(); + let mut stdout = stdout(); + let map_err = |io_error: io::Error| { + VmError::NativeFunction(NativeFunctionError::Io { + error: io_error.kind(), + position, + }) + }; + + let first_argument = to_register.saturating_sub(argument_count); + + for argument_index in first_argument..to_register { + if argument_index != first_argument { + stdout.write(b" ").map_err(map_err)?; + } + + let argument_string = vm.open_register(argument_index, position)?.to_string(); + + stdout + .write_all(argument_string.as_bytes()) + .map_err(map_err)?; + } + + stdout.write(b"\n").map_err(map_err)?; + + Ok(None) +} diff --git a/dust-lang/src/native_function.rs b/dust-lang/src/native_function/mod.rs similarity index 63% rename from dust-lang/src/native_function.rs rename to dust-lang/src/native_function/mod.rs index 7eec7a3..6dd02ea 100644 --- a/dust-lang/src/native_function.rs +++ b/dust-lang/src/native_function/mod.rs @@ -2,20 +2,20 @@ //! //! Native functions are used either to implement features that are not possible to implement in //! Dust itself or that are more efficient to implement in Rust. +mod logic; + use std::{ fmt::{self, Display, Formatter}, - io::{self, stdin, stdout, Write}, + io::{self}, string::{self}, }; use serde::{Deserialize, Serialize}; -use crate::{ - AnnotatedError, ConcreteValue, FunctionType, Instruction, Span, Type, Value, Vm, VmError, -}; +use crate::{AnnotatedError, ConcreteValue, FunctionType, Instruction, Span, Type, Vm, VmError}; macro_rules! define_native_function { - ($(($name:ident, $byte:literal, $str:expr, $type:expr)),*) => { + ($(($name:ident, $byte:literal, $str:expr, $type:expr, $function:expr)),*) => { /// A dust-native function. /// /// See the [module-level documentation](index.html) for more information. @@ -27,6 +27,19 @@ macro_rules! define_native_function { } impl NativeFunction { + pub fn call( + &self, + vm: &mut Vm, + instruction: Instruction, + span: Span + ) -> Result, VmError> { + match self { + $( + NativeFunction::$name => $function(vm, instruction, span), + )* + } + } + pub fn as_str(&self) -> &'static str { match self { $( @@ -91,18 +104,25 @@ macro_rules! define_native_function { }; } +impl Display for NativeFunction { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + define_native_function! { // Assertion - ( - Assert, - 0_u8, - "assert", - FunctionType { - type_parameters: None, - value_parameters: None, - return_type: Box::new(Type::None) - } - ), + // ( + // Assert, + // 0_u8, + // "assert", + // FunctionType { + // type_parameters: None, + // value_parameters: None, + // return_type: Box::new(Type::None) + // }, + // assert + // ), // (AssertEqual, 1_u8, "assert_equal", false), // (AssertNotEqual, 2_u8, "assert_not_equal", false), ( @@ -113,7 +133,8 @@ define_native_function! { type_parameters: None, value_parameters: None, return_type: Box::new(Type::None) - } + }, + logic::panic ), // // Type conversion @@ -129,7 +150,8 @@ define_native_function! { type_parameters: None, value_parameters: Some(vec![(0, Type::Any)]), return_type: Box::new(Type::String { length: None }) - } + }, + logic::to_string ), // // List and string @@ -189,7 +211,8 @@ define_native_function! { type_parameters: None, value_parameters: None, return_type: Box::new(Type::String { length: None }) - } + }, + logic::read_line ), // (ReadTo, 51_u8, "read_to", false), // (ReadUntil, 52_u8, "read_until", true), @@ -204,7 +227,8 @@ define_native_function! { type_parameters: None, value_parameters: Some(vec![(0, Type::String { length: None })]), return_type: Box::new(Type::None) - } + }, + logic::write ), // (WriteFile, 56_u8, "write_file", false), ( @@ -215,7 +239,8 @@ define_native_function! { type_parameters: None, value_parameters: Some(vec![(0, Type::String { length: None })]), return_type: Box::new(Type::None) - } + }, + logic::write_line ) // // Random @@ -223,143 +248,6 @@ define_native_function! { // (RandomInRange, 59_u8, "random_in_range", true) } -impl NativeFunction { - pub fn call( - &self, - instruction: Instruction, - vm: &Vm, - position: Span, - ) -> Result, VmError> { - let to_register = instruction.a(); - let argument_count = instruction.c(); - - let return_value = match self { - NativeFunction::Panic => { - let message = if argument_count == 0 { - None - } else { - let mut message = String::new(); - - for argument_index in 0..argument_count { - if argument_index != 0 { - message.push(' '); - } - - let argument = vm.open_register(argument_index, position)?; - - message.push_str(&argument.to_string()); - } - - Some(message) - }; - - return Err(VmError::NativeFunction(NativeFunctionError::Panic { - message, - position, - })); - } - - // Type conversion - NativeFunction::ToString => { - let mut string = String::new(); - - for argument_index in 0..argument_count { - let argument = vm.open_register(argument_index, position)?; - - string.push_str(&argument.to_string()); - } - - Some(Value::Concrete(ConcreteValue::String(string))) - } - - // I/O - NativeFunction::ReadLine => { - let mut buffer = String::new(); - - stdin().read_line(&mut buffer).map_err(|io_error| { - VmError::NativeFunction(NativeFunctionError::Io { - error: io_error.kind(), - position, - }) - })?; - - buffer = buffer.trim_end_matches('\n').to_string(); - - Some(Value::Concrete(ConcreteValue::String(buffer))) - } - NativeFunction::Write => { - let to_register = instruction.a(); - let mut stdout = stdout(); - let map_err = |io_error: io::Error| { - VmError::NativeFunction(NativeFunctionError::Io { - error: io_error.kind(), - position, - }) - }; - - let first_argument = to_register.saturating_sub(argument_count); - let last_argument = to_register.saturating_sub(1); - - for argument_index in first_argument..=last_argument { - if argument_index != first_argument { - stdout.write(b" ").map_err(map_err)?; - } - - let argument_string = vm.open_register(argument_index, position)?.to_string(); - - stdout - .write_all(argument_string.as_bytes()) - .map_err(map_err)?; - } - - None - } - NativeFunction::WriteLine => { - let mut stdout = stdout(); - let map_err = |io_error: io::Error| { - VmError::NativeFunction(NativeFunctionError::Io { - error: io_error.kind(), - position, - }) - }; - - let first_index = to_register.saturating_sub(argument_count); - - for (index, register) in (first_index..to_register).enumerate() { - if index != 0 { - stdout.write(b" ").map_err(map_err)?; - } - - let argument = vm.open_register(register, position)?; - - if let Value::Concrete(ConcreteValue::String(string)) = argument { - let bytes = string.as_bytes(); - - stdout.write_all(bytes).map_err(map_err)?; - } else { - let bytes = argument.to_string().into_bytes(); - - stdout.write_all(&bytes).map_err(map_err)?; - } - } - - stdout.write(b"\n").map_err(map_err)?; - - None - } - _ => todo!(), - }; - - Ok(return_value) - } -} - -impl Display for NativeFunction { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.as_str()) - } -} - #[derive(Debug, Clone, PartialEq)] pub enum NativeFunctionError { ExpectedArgumentCount { diff --git a/dust-lang/src/scope.rs b/dust-lang/src/scope.rs new file mode 100644 index 0000000..b0e893a --- /dev/null +++ b/dust-lang/src/scope.rs @@ -0,0 +1,59 @@ +//! Scope, a type that represents the locality of a variable. +use std::{ + cmp::Ordering, + fmt::{self, Display, Formatter}, +}; + +use serde::{Deserialize, Serialize}; + +/// Variable locality, as defined by its depth and block index. +/// +/// The `block index` is a unique identifier for a block within a chunk. It is used to differentiate +/// between blocks that are not nested together but have the same depth, i.e. sibling scopes. If the +/// `block_index` is 0, then the scope is the root scope of the chunk. The `block_index` is always 0 +/// when the `depth` is 0. See [Chunk::begin_scope][] and [Chunk::end_scope][] to see how scopes are +/// incremented and decremented. +#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] +pub struct Scope { + /// Level of block nesting. + pub depth: u8, + /// Index of the block in the chunk. + pub block_index: u8, +} + +impl Scope { + pub fn new(depth: u8, block_index: u8) -> Self { + Self { depth, block_index } + } + + pub fn contains(&self, other: &Self) -> bool { + match self.depth.cmp(&other.depth) { + Ordering::Less => false, + Ordering::Greater => self.block_index >= other.block_index, + Ordering::Equal => self.block_index == other.block_index, + } + } + + #[inline] + pub fn begin(&mut self, block_index: u8) { + self.block_index = block_index; + self.depth += 1; + } + + #[inline] + pub fn end(&mut self) { + self.depth -= 1; + + if self.depth == 0 { + self.block_index = 0; + } else { + self.block_index -= 1; + } + } +} + +impl Display for Scope { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + write!(f, "({}, {})", self.depth, self.block_index) + } +} diff --git a/dust-lang/src/type.rs b/dust-lang/src/type.rs index 5af1af9..fbfbc8a 100644 --- a/dust-lang/src/type.rs +++ b/dust-lang/src/type.rs @@ -36,7 +36,7 @@ pub enum Type { None, Number, Range { - r#type: RangeableType, + r#type: Box, }, SelfChunk, String { @@ -72,6 +72,7 @@ impl Type { | (Type::Character, Type::Character) | (Type::Float, Type::Float) | (Type::Integer, Type::Integer) + | (Type::None, Type::None) | (Type::String { .. }, Type::String { .. }) => return Ok(()), ( Type::Generic { @@ -553,115 +554,8 @@ impl Display for EnumType { } } -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] -pub enum RangeableType { - Byte, - Character, - Float, - Integer, -} - -impl Display for RangeableType { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - RangeableType::Byte => Type::Byte.fmt(f), - RangeableType::Character => Type::Character.fmt(f), - RangeableType::Float => Type::Float.fmt(f), - RangeableType::Integer => Type::Integer.fmt(f), - } - } -} - #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct TypeConflict { pub expected: Type, pub actual: Type, } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn check_type_any() { - let foo = Type::Any; - let bar = Type::Any; - - foo.check(&bar).unwrap(); - } - - #[test] - fn check_type_boolean() { - let foo = Type::Boolean; - let bar = Type::Boolean; - - foo.check(&bar).unwrap(); - } - - #[test] - fn check_type_byte() { - let foo = Type::Byte; - let bar = Type::Byte; - - foo.check(&bar).unwrap(); - } - - #[test] - fn check_type_character() { - let foo = Type::Character; - let bar = Type::Character; - - foo.check(&bar).unwrap(); - } - - #[test] - fn errors() { - let foo = Type::Integer; - let bar = Type::String { length: None }; - - assert_eq!( - foo.check(&bar), - Err(TypeConflict { - actual: bar.clone(), - expected: foo.clone() - }) - ); - assert_eq!( - bar.check(&foo), - Err(TypeConflict { - actual: foo.clone(), - expected: bar.clone() - }) - ); - - let types = [ - Type::Boolean, - Type::Float, - Type::Integer, - Type::List { - item_type: Box::new(Type::Integer), - length: 42, - }, - Type::Range { - r#type: RangeableType::Integer, - }, - Type::String { length: None }, - ]; - - for left in types.clone() { - for right in types.clone() { - if left == right { - continue; - } - - assert_eq!( - left.check(&right), - Err(TypeConflict { - actual: right.clone(), - expected: left.clone() - }) - ); - } - } - } -} diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index e961f5d..5bf03b7 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -1,25 +1,4 @@ -//! Dust value representation -//! -//! # Examples -//! -//! Each type of value has a corresponding method for instantiation: -//! -//! ``` -//! # use dust_lang::Value; -//! let boolean = Value::boolean(true); -//! let float = Value::float(3.14); -//! let integer = Value::integer(42); -//! let string = Value::string("Hello, world!"); -//! ``` -//! -//! Values have a type, which can be retrieved using the `r#type` method: -//! -//! ``` -//! # use dust_lang::*; -//! let value = Value::integer(42); -//! -//! assert_eq!(value.r#type(), Type::Integer); -//! ``` +//! Runtime values used by the VM. use std::{ cmp::Ordering, fmt::{self, Debug, Display, Formatter}, @@ -28,320 +7,287 @@ use std::{ use serde::{Deserialize, Serialize}; -use crate::{Chunk, FunctionType, RangeableType, Type}; +use crate::{function::Function, Chunk, FunctionBorrowed, Type}; -/// Dust value representation -/// -/// See the [module-level documentation][self] for more. -#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] -pub enum Value { - Concrete(ConcreteValue), - Abstract(AbstractValue), +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum ReferenceValue<'a> { + Concrete(&'a ConcreteValue), + Function(FunctionBorrowed<'a>), + List(&'a Vec<&'a ConcreteValue>), } -impl Value { - pub fn boolean(value: bool) -> Self { - Value::Concrete(ConcreteValue::Boolean(value)) - } - - pub fn byte(value: u8) -> Self { - Value::Concrete(ConcreteValue::Byte(value)) - } - - pub fn character(value: char) -> Self { - Value::Concrete(ConcreteValue::Character(value)) - } - - pub fn float(value: f64) -> Self { - Value::Concrete(ConcreteValue::Float(value)) - } - - pub fn function(body: Chunk, r#type: FunctionType) -> Self { - Value::Concrete(ConcreteValue::Function(Function { - chunk: body, - r#type: Type::Function(r#type), - })) - } - - pub fn integer>(into_i64: T) -> Self { - Value::Concrete(ConcreteValue::Integer(into_i64.into())) - } - - pub fn list>>(items: T) -> Self { - Value::Concrete(ConcreteValue::List(items.into())) - } - - pub fn abstract_list(start: u8, end: u8, item_type: Type) -> Self { - Value::Abstract(AbstractValue::List { - start, - end, - item_type, - }) - } - - pub fn string(to_string: T) -> Self { - Value::Concrete(ConcreteValue::String(to_string.to_string())) - } - - pub fn as_string(&self) -> Option<&String> { - if let Value::Concrete(ConcreteValue::String(string)) = self { - Some(string) - } else { - None - } - } - - pub fn is_function(&self) -> bool { - matches!(self, Value::Concrete(ConcreteValue::Function(_))) +impl<'a> ReferenceValue<'a> { + pub fn to_value(self) -> Value<'a> { + Value::Reference(self) } pub fn r#type(&self) -> Type { match self { - Value::Concrete(data) => data.r#type(), - Value::Abstract(AbstractValue::List { - start, - end, - item_type, - }) => { - let length = (end - start + 1) as usize; + ReferenceValue::Concrete(concrete) => concrete.r#type(), + ReferenceValue::Function(function) => function.r#type(), + ReferenceValue::List(list) => { + let item_type = list + .first() + .map(|value| value.r#type()) + .unwrap_or(Type::Any); Type::List { - length, - item_type: Box::new(item_type.clone()), + item_type: Box::new(item_type), + length: list.len(), } } } } - pub fn add(&self, other: &Value) -> Result { - use ConcreteValue::*; - use Value::*; - - let sum = match (self, other) { - (Concrete(Byte(left)), Concrete(Byte(right))) => { - Value::byte(left.saturating_add(*right)) - } - (Concrete(Float(left)), Concrete(Float(right))) => Value::float(left + right), - (Concrete(Integer(left)), Concrete(Integer(right))) => { - Value::integer(left.saturating_add(*right)) - } - (Concrete(String(left)), Concrete(String(right))) => { - Value::string(format!("{}{}", left, right)) - } - _ => return Err(ValueError::CannotAdd(self.clone(), other.clone())), - }; - - Ok(sum) - } - - pub fn subtract(&self, other: &Value) -> Result { - use ConcreteValue::*; - use Value::*; - - let different = match (self, other) { - (Concrete(Byte(left)), Concrete(Byte(right))) => { - Value::byte(left.saturating_sub(*right)) - } - (Concrete(Float(left)), Concrete(Float(right))) => Value::float(left - right), - (Concrete(Integer(left)), Concrete(Integer(right))) => { - Value::integer(left.saturating_sub(*right)) - } - _ => return Err(ValueError::CannotSubtract(self.clone(), other.clone())), - }; - - Ok(different) - } - - pub fn multiply(&self, other: &Value) -> Result { - use ConcreteValue::*; - use Value::*; - - let product = match (self, other) { - (Concrete(Byte(left)), Concrete(Byte(right))) => { - Value::byte(left.saturating_mul(*right)) - } - (Concrete(Float(left)), Concrete(Float(right))) => Value::float(left * right), - (Concrete(Integer(left)), Concrete(Integer(right))) => { - Value::integer(left.saturating_mul(*right)) - } - _ => return Err(ValueError::CannotAdd(self.clone(), other.clone())), - }; - - Ok(product) - } - - pub fn divide(&self, other: &Value) -> Result { - use ConcreteValue::*; - use Value::*; - - let product = match (self, other) { - (Concrete(Byte(left)), Concrete(Byte(right))) => { - Value::byte(left.saturating_div(*right)) - } - (Concrete(Float(left)), Concrete(Float(right))) => Value::float(left / right), - (Concrete(Integer(left)), Concrete(Integer(right))) => { - Value::integer(left.saturating_div(*right)) - } - _ => return Err(ValueError::CannotDivide(self.clone(), other.clone())), - }; - - Ok(product) - } - - pub fn modulo(&self, other: &Value) -> Result { - use ConcreteValue::*; - use Value::*; - - let product = match (self, other) { - (Concrete(Byte(left)), Concrete(Byte(right))) => Value::byte(left % right), - (Concrete(Float(left)), Concrete(Float(right))) => Value::float(left % right), - (Concrete(Integer(left)), Concrete(Integer(right))) => Value::integer(left % right), - _ => return Err(ValueError::CannotModulo(self.clone(), other.clone())), - }; - - Ok(product) - } - - pub fn less_than(&self, other: &Value) -> Result { - let (left, right) = match (self, other) { - (Value::Concrete(left), Value::Concrete(right)) => (left, right), - _ => return Err(ValueError::CannotCompare(self.clone(), other.clone())), - }; - - Ok(Value::boolean(left < right)) - } - - pub fn less_than_or_equal(&self, other: &Value) -> Result { - let (left, right) = match (self, other) { - (Value::Concrete(left), Value::Concrete(right)) => (left, right), - _ => return Err(ValueError::CannotCompare(self.clone(), other.clone())), - }; - - Ok(Value::boolean(left <= right)) - } - - pub fn equal(&self, other: &Value) -> Result { - let (left, right) = match (self, other) { - (Value::Concrete(left), Value::Concrete(right)) => (left, right), - _ => return Err(ValueError::CannotCompare(self.clone(), other.clone())), - }; - - Ok(Value::boolean(left == right)) - } - - pub fn negate(&self) -> Result { - use ConcreteValue::*; - use Value::*; - - let negated = match self { - Concrete(Integer(integer)) => Value::integer(-integer), - Concrete(Float(float)) => Value::float(-float), - _ => return Err(ValueError::CannotNot(self.clone())), - }; - - Ok(negated) - } - - pub fn not(&self) -> Result { - use ConcreteValue::*; - use Value::*; - - let not = match self { - Concrete(Boolean(boolean)) => Value::boolean(!boolean), - Concrete(Byte(byte)) => Value::byte(!byte), - Concrete(Integer(integer)) => Value::integer(!integer), - _ => return Err(ValueError::CannotNot(self.clone())), - }; - - Ok(not) - } -} - -impl From for Value { - fn from(value: bool) -> Self { - Value::boolean(value) - } -} - -impl From for Value { - fn from(value: u8) -> Self { - Value::byte(value) - } -} - -impl From for Value { - fn from(value: char) -> Self { - Value::character(value) - } -} - -impl From for Value { - fn from(value: f64) -> Self { - Value::float(value) - } -} - -impl From for Value { - fn from(value: i32) -> Self { - Value::integer(value as i64) - } -} - -impl From for Value { - fn from(value: i64) -> Self { - Value::integer(value) - } -} - -impl From for Value { - fn from(value: String) -> Self { - Value::string(value) - } -} - -impl From<&str> for Value { - fn from(str: &str) -> Self { - Value::string(str) - } -} - -impl Clone for Value { - fn clone(&self) -> Self { - log::trace!("Cloning value {self}"); - + pub fn into_concrete(self) -> ConcreteValue { match self { - Value::Abstract(object) => Value::Abstract(object.clone()), - Value::Concrete(concrete) => Value::Concrete(concrete.clone()), + ReferenceValue::Concrete(concrete) => concrete.clone(), + ReferenceValue::Function(function) => { + ConcreteValue::Function(Function::new(function.chunk().clone())) + } + ReferenceValue::List(list) => { + let mut items = Vec::new(); + + for value in list { + items.push((*value).clone()); + } + + ConcreteValue::List(items) + } } } } -impl Display for Value { +#[derive(Clone, Debug, PartialEq)] +pub enum Value<'a> { + Concrete(ConcreteValue), + Reference(ReferenceValue<'a>), +} + +impl<'a> Value<'a> { + pub fn r#type(&self) -> Type { + match self { + Value::Concrete(concrete) => concrete.r#type(), + Value::Reference(reference) => reference.r#type(), + } + } + + pub fn into_concrete(self) -> ConcreteValue { + match self { + Value::Concrete(concrete) => concrete, + Value::Reference(reference) => reference.into_concrete(), + } + } + + pub fn as_boolean(&self) -> Option { + match self { + Value::Concrete(ConcreteValue::Boolean(boolean)) => Some(*boolean), + Value::Reference(ReferenceValue::Concrete(ConcreteValue::Boolean(boolean))) => { + Some(*boolean) + } + _ => None, + } + } + pub fn as_function(&self) -> Option { + match self { + Value::Concrete(ConcreteValue::Function(function)) => Some(function.as_borrowed()), + Value::Reference(ReferenceValue::Concrete(ConcreteValue::Function(function))) => { + Some(function.as_borrowed()) + } + _ => None, + } + } + + pub fn add(&self, other: &Self) -> Result { + match (self, other) { + (Value::Concrete(left), Value::Concrete(right)) => left.add(right), + (Value::Reference(ReferenceValue::Concrete(left)), Value::Concrete(right)) => { + left.add(right) + } + (Value::Concrete(left), Value::Reference(ReferenceValue::Concrete(right))) => { + left.add(right) + } + ( + Value::Reference(ReferenceValue::Concrete(left)), + Value::Reference(ReferenceValue::Concrete(right)), + ) => left.add(right), + _ => Err(ValueError::CannotAdd( + self.clone().into_concrete(), + other.clone().into_concrete(), + )), + } + } + + pub fn subtract(&self, other: &Self) -> Result { + let (left, right) = match (self, other) { + (Value::Concrete(left), Value::Concrete(right)) => (left, right), + (Value::Reference(left), Value::Reference(right)) => (*left, *right), + _ => { + return Err(ValueError::CannotSubtract( + self.clone().into_concrete(), + other.clone().into_concrete(), + )); + } + }; + + left.subtract(right) + } + + pub fn multiply(&self, other: &Self) -> Result { + let (left, right) = match (self, other) { + (Value::Concrete(left), Value::Concrete(right)) => (left, right), + (Value::Reference(left), Value::Reference(right)) => (*left, *right), + _ => { + return Err(ValueError::CannotMultiply( + self.clone().into_concrete(), + other.clone().into_concrete(), + )); + } + }; + + left.multiply(right) + } + + pub fn divide(&self, other: &Self) -> Result { + let (left, right) = match (self, other) { + (Value::Concrete(left), Value::Concrete(right)) => (left, right), + (Value::Reference(left), Value::Reference(right)) => (*left, *right), + _ => { + return Err(ValueError::CannotDivide( + self.clone().into_concrete(), + other.clone().into_concrete(), + )); + } + }; + + left.divide(right) + } + + pub fn modulo(&self, other: &Self) -> Result { + let (left, right) = match (self, other) { + (Value::Concrete(left), Value::Concrete(right)) => (left, right), + (Value::Reference(left), Value::Reference(right)) => (*left, *right), + _ => { + return Err(ValueError::CannotModulo( + self.clone().into_concrete(), + other.clone().into_concrete(), + )); + } + }; + + left.modulo(right) + } + + pub fn negate(&self) -> Result { + match self { + Value::Concrete(concrete) => concrete.negate(), + Value::Reference(reference) => reference.negate(), + _ => Err(ValueError::CannotNegate(self.clone().into_concrete())), + } + } + + pub fn not(&self) -> Result { + match self { + Value::Concrete(concrete) => concrete.not(), + Value::Reference(reference) => reference.not(), + _ => Err(ValueError::CannotNot(self.clone().into_concrete())), + } + } + + pub fn equal(&self, other: &Self) -> Result { + let (left, right) = match (self, other) { + (Value::Concrete(left), Value::Concrete(right)) => (left, right), + (Value::Reference(left), Value::Reference(right)) => (*left, *right), + _ => { + return Err(ValueError::CannotCompare( + self.clone().into_concrete(), + other.clone().into_concrete(), + )); + } + }; + + left.equal(right) + } + + pub fn less_than(&self, other: &Self) -> Result { + let (left, right) = match (self, other) { + (Value::Concrete(left), Value::Concrete(right)) => (left, right), + (Value::Reference(left), Value::Reference(right)) => (*left, *right), + _ => { + return Err(ValueError::CannotCompare( + self.clone().into_concrete(), + other.clone().into_concrete(), + )); + } + }; + + left.less_than(right) + } + + pub fn less_than_or_equal(&self, other: &Self) -> Result { + let (left, right) = match (self, other) { + (Value::Concrete(left), Value::Concrete(right)) => (left, right), + (Value::Reference(left), Value::Reference(right)) => (*left, *right), + _ => { + return Err(ValueError::CannotCompare( + self.clone().into_concrete(), + other.clone().into_concrete(), + )); + } + }; + + left.less_than_or_equal(right) + } +} + +impl<'a> Display for Value<'a> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - Value::Abstract(object) => write!(f, "{object}"), - Value::Concrete(concrete) => write!(f, "{concrete}"), - } - } -} + Value::Concrete(concrete) => write!(f, "{}", concrete), + Value::List(list) => { + write!(f, "[")?; -/// Value representation that can be resolved to a concrete value by the VM. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub enum AbstractValue { - List { start: u8, end: u8, item_type: Type }, -} + for (index, value) in list.iter().enumerate() { + if index > 0 { + write!(f, ", ")?; + } + write!(f, "{}", value)?; + } -impl Display for AbstractValue { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - AbstractValue::List { start, end, .. } => { - write!(f, "List [R{}..=R{}]", start, end) + write!(f, "]") + } + Value::Reference(reference) => write!(f, "{}", reference), + Value::FunctionBorrowed(function_reference) => { + write!(f, "{}", function_reference.chunk().r#type()) } } } } -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +impl<'a> Eq for Value<'a> {} + +impl<'a> PartialOrd for Value<'a> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl<'a> Ord for Value<'a> { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (Value::Concrete(left), Value::Concrete(right)) => left.cmp(right), + (Value::Concrete(_), _) => Ordering::Greater, + (Value::List(left), Value::List(right)) => left.cmp(right), + (Value::List(_), _) => Ordering::Greater, + (Value::Reference(left), Value::Reference(right)) => left.cmp(right), + (Value::Reference(_), _) => Ordering::Greater, + (Value::FunctionBorrowed(left), Value::FunctionBorrowed(right)) => left.cmp(right), + (Value::FunctionBorrowed(_), _) => Ordering::Greater, + } + } +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub enum ConcreteValue { Boolean(bool), Byte(u8), @@ -349,27 +295,80 @@ pub enum ConcreteValue { Float(f64), Function(Function), Integer(i64), - List(Vec), + List(Vec), Range(RangeValue), String(String), } impl ConcreteValue { + pub fn boolean(value: bool) -> Self { + ConcreteValue::Boolean(value) + } + + pub fn byte(value: u8) -> Self { + ConcreteValue::Byte(value) + } + + pub fn character(value: char) -> Self { + ConcreteValue::Character(value) + } + + pub fn float(value: f64) -> Self { + ConcreteValue::Float(value) + } + + pub fn function(chunk: Chunk) -> Self { + ConcreteValue::Function(Function::new(chunk)) + } + + pub fn integer>(into_i64: T) -> Self { + ConcreteValue::Integer(into_i64.into()) + } + + pub fn list>>(into_list: T) -> Self { + ConcreteValue::List(into_list.into()) + } + + pub fn range(range: RangeValue) -> Self { + ConcreteValue::Range(range) + } + + pub fn string(to_string: T) -> Self { + ConcreteValue::String(to_string.to_string()) + } + + pub fn as_string(&self) -> Option<&String> { + if let ConcreteValue::String(string) = self { + Some(string) + } else { + None + } + } + + pub fn to_owned_value<'a>(self) -> Value<'a> { + Value::Concrete(self) + } + + pub fn as_reference_value(&self) -> Value { + Value::Reference(self) + } + pub fn r#type(&self) -> Type { match self { ConcreteValue::Boolean(_) => Type::Boolean, ConcreteValue::Byte(_) => Type::Byte, ConcreteValue::Character(_) => Type::Character, ConcreteValue::Float(_) => Type::Float, - ConcreteValue::Function(Function { r#type, .. }) => r#type.clone(), + ConcreteValue::Function(function) => function.r#type(), ConcreteValue::Integer(_) => Type::Integer, - ConcreteValue::List(list) => Type::List { - item_type: list - .first() - .map(|value| Box::new(value.r#type())) - .unwrap_or_else(|| Box::new(Type::Any)), - length: list.len(), - }, + ConcreteValue::List(list) => { + let item_type = list.first().map_or(Type::Any, |item| item.r#type()); + + Type::List { + item_type: Box::new(item_type), + length: list.len(), + } + } ConcreteValue::Range(range) => range.r#type(), ConcreteValue::String(string) => Type::String { length: Some(string.len()), @@ -377,53 +376,171 @@ impl ConcreteValue { } } - pub fn is_rangeable(&self) -> bool { - matches!( - self, - ConcreteValue::Integer(_) - | ConcreteValue::Float(_) - | ConcreteValue::Character(_) - | ConcreteValue::Byte(_) - ) + pub fn add(&self, other: &Self) -> Result { + use ConcreteValue::*; + + let sum = match (self, other) { + (Byte(left), Byte(right)) => ConcreteValue::byte(left.saturating_add(*right)), + (Float(left), Float(right)) => ConcreteValue::float(*left + *right), + (Integer(left), Integer(right)) => ConcreteValue::integer(left.saturating_add(*right)), + (String(left), String(right)) => ConcreteValue::string(format!("{}{}", left, right)), + _ => return Err(ValueError::CannotAdd(self.clone(), other.clone())), + }; + + Ok(sum) + } + + pub fn subtract(&self, other: &Self) -> Result { + use ConcreteValue::*; + + let difference = match (self, other) { + (Byte(left), Byte(right)) => ConcreteValue::byte(left.saturating_sub(*right)), + (Float(left), Float(right)) => ConcreteValue::float(left - right), + (Integer(left), Integer(right)) => ConcreteValue::integer(left.saturating_sub(*right)), + _ => return Err(ValueError::CannotSubtract(self.clone(), other.clone())), + }; + + Ok(difference) + } + + pub fn multiply(&self, other: &Self) -> Result { + use ConcreteValue::*; + + let product = match (self, other) { + (Byte(left), Byte(right)) => ConcreteValue::byte(left.saturating_mul(*right)), + (Float(left), Float(right)) => ConcreteValue::float(left * right), + (Integer(left), Integer(right)) => ConcreteValue::integer(left.saturating_mul(*right)), + _ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())), + }; + + Ok(product) + } + + pub fn divide(&self, other: &Self) -> Result { + use ConcreteValue::*; + + let quotient = match (self, other) { + (Byte(left), Byte(right)) => ConcreteValue::byte(left.saturating_div(*right)), + (Float(left), Float(right)) => ConcreteValue::float(left / right), + (Integer(left), Integer(right)) => ConcreteValue::integer(left.saturating_div(*right)), + _ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())), + }; + + Ok(quotient) + } + + pub fn modulo(&self, other: &Self) -> Result { + use ConcreteValue::*; + + let product = match (self, other) { + (Byte(left), Byte(right)) => ConcreteValue::byte(left.wrapping_rem(*right)), + (Float(left), Float(right)) => ConcreteValue::float(left % right), + (Integer(left), Integer(right)) => { + ConcreteValue::integer(left.wrapping_rem_euclid(*right)) + } + _ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())), + }; + + Ok(product) + } + + pub fn negate(&self) -> Result { + use ConcreteValue::*; + + let negated = match self { + Boolean(value) => ConcreteValue::boolean(!value), + Byte(value) => ConcreteValue::byte(value.wrapping_neg()), + Float(value) => ConcreteValue::float(-value), + Integer(value) => ConcreteValue::integer(value.wrapping_neg()), + _ => return Err(ValueError::CannotNegate(self.clone())), + }; + + Ok(negated) + } + + pub fn not(&self) -> Result { + use ConcreteValue::*; + + let not = match self { + Boolean(value) => ConcreteValue::boolean(!value), + _ => return Err(ValueError::CannotNot(self.clone())), + }; + + Ok(not) + } + + pub fn equal(&self, other: &ConcreteValue) -> Result { + use ConcreteValue::*; + + let equal = match (self, other) { + (Boolean(left), Boolean(right)) => ConcreteValue::boolean(left == right), + (Byte(left), Byte(right)) => ConcreteValue::boolean(left == right), + (Character(left), Character(right)) => ConcreteValue::boolean(left == right), + (Float(left), Float(right)) => ConcreteValue::boolean(left == right), + (Function(left), Function(right)) => ConcreteValue::boolean(left == right), + (Integer(left), Integer(right)) => ConcreteValue::boolean(left == right), + (List(left), List(right)) => ConcreteValue::boolean(left == right), + (Range(left), Range(right)) => ConcreteValue::boolean(left == right), + (String(left), String(right)) => ConcreteValue::boolean(left == right), + _ => return Err(ValueError::CannotCompare(self.clone(), other.clone())), + }; + + Ok(equal) + } + + pub fn less_than(&self, other: &ConcreteValue) -> Result { + use ConcreteValue::*; + + let less_than = match (self, other) { + (Boolean(left), Boolean(right)) => ConcreteValue::boolean(left < right), + (Byte(left), Byte(right)) => ConcreteValue::boolean(left < right), + (Character(left), Character(right)) => ConcreteValue::boolean(left < right), + (Float(left), Float(right)) => ConcreteValue::boolean(left < right), + (Function(left), Function(right)) => ConcreteValue::boolean(left < right), + (Integer(left), Integer(right)) => ConcreteValue::boolean(left < right), + (List(left), List(right)) => ConcreteValue::boolean(left < right), + (Range(left), Range(right)) => ConcreteValue::boolean(left < right), + (String(left), String(right)) => ConcreteValue::boolean(left < right), + _ => return Err(ValueError::CannotCompare(self.clone(), other.clone())), + }; + + Ok(less_than) + } + + pub fn less_than_or_equal(&self, other: &ConcreteValue) -> Result { + use ConcreteValue::*; + + let less_than_or_equal = match (self, other) { + (Boolean(left), Boolean(right)) => ConcreteValue::boolean(left <= right), + (Byte(left), Byte(right)) => ConcreteValue::boolean(left <= right), + (Character(left), Character(right)) => ConcreteValue::boolean(left <= right), + (Float(left), Float(right)) => ConcreteValue::boolean(left <= right), + (Function(left), Function(right)) => ConcreteValue::boolean(left <= right), + (Integer(left), Integer(right)) => ConcreteValue::boolean(left <= right), + (List(left), List(right)) => ConcreteValue::boolean(left <= right), + (Range(left), Range(right)) => ConcreteValue::boolean(left <= right), + (String(left), String(right)) => ConcreteValue::boolean(left <= right), + _ => return Err(ValueError::CannotCompare(self.clone(), other.clone())), + }; + + Ok(less_than_or_equal) } } -impl Display for ConcreteValue { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { +impl Clone for ConcreteValue { + fn clone(&self) -> Self { + log::trace!("Cloning concrete value {}", self); + match self { - ConcreteValue::Boolean(boolean) => write!(f, "{boolean}"), - ConcreteValue::Byte(byte) => write!(f, "0x{byte:02x}"), - ConcreteValue::Character(character) => write!(f, "{character}"), - ConcreteValue::Float(float) => { - write!(f, "{float}")?; - - if float.fract() == 0.0 { - write!(f, ".0")?; - } - - Ok(()) - } - ConcreteValue::Function(Function { r#type, .. }) => { - write!(f, "{}", r#type) - } - ConcreteValue::Integer(integer) => write!(f, "{integer}"), - ConcreteValue::List(items) => { - write!(f, "[")?; - - for (index, item) in items.iter().enumerate() { - if index > 0 { - write!(f, ", ")?; - } - - write!(f, "{item}")?; - } - - write!(f, "]") - } - ConcreteValue::Range(range_value) => { - write!(f, "{range_value}") - } - ConcreteValue::String(string) => write!(f, "{string}"), + ConcreteValue::Boolean(boolean) => ConcreteValue::Boolean(*boolean), + ConcreteValue::Byte(byte) => ConcreteValue::Byte(*byte), + ConcreteValue::Character(character) => ConcreteValue::Character(*character), + ConcreteValue::Float(float) => ConcreteValue::Float(*float), + ConcreteValue::Function(function) => ConcreteValue::Function(function.clone()), + ConcreteValue::Integer(integer) => ConcreteValue::Integer(*integer), + ConcreteValue::List(list) => ConcreteValue::List(list.clone()), + ConcreteValue::Range(range) => ConcreteValue::Range(range.clone()), + ConcreteValue::String(string) => ConcreteValue::String(string.clone()), } } } @@ -446,15 +563,7 @@ impl Ord for ConcreteValue { (ConcreteValue::Character(left), ConcreteValue::Character(right)) => left.cmp(right), (ConcreteValue::Character(_), _) => Ordering::Greater, (ConcreteValue::Float(left), ConcreteValue::Float(right)) => { - if left.is_nan() && right.is_nan() { - Ordering::Equal - } else if left.is_nan() { - Ordering::Less - } else if right.is_nan() { - Ordering::Greater - } else { - left.partial_cmp(right).unwrap() - } + left.to_bits().cmp(&right.to_bits()) } (ConcreteValue::Float(_), _) => Ordering::Greater, (ConcreteValue::Function(left), ConcreteValue::Function(right)) => left.cmp(right), @@ -471,147 +580,185 @@ impl Ord for ConcreteValue { } } -#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] -pub struct Function { - chunk: Chunk, - r#type: Type, -} +impl Display for ConcreteValue { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + ConcreteValue::Boolean(boolean) => write!(f, "{boolean}"), + ConcreteValue::Byte(byte) => write!(f, "0x{byte:02x}"), + ConcreteValue::Character(character) => write!(f, "{character}"), + ConcreteValue::Float(float) => { + write!(f, "{float}")?; -impl Function { - pub fn new(chunk: Chunk, r#type: Type) -> Self { - Self { chunk, r#type } - } + if float.fract() == 0.0 { + write!(f, ".0")?; + } - pub fn chunk(&self) -> &Chunk { - &self.chunk - } + Ok(()) + } + ConcreteValue::Function(function) => write!(f, "{function}"), + ConcreteValue::Integer(integer) => write!(f, "{integer}"), + ConcreteValue::List(list) => { + write!(f, "[")?; - pub fn chunk_mut(&mut self) -> &mut Chunk { - &mut self.chunk - } + for (index, element) in list.iter().enumerate() { + if index > 0 { + write!(f, ", ")?; + } - pub fn take_chunk(self) -> Chunk { - self.chunk - } + write!(f, "{element}")?; + } - pub fn r#type(&self) -> &Type { - &self.r#type + write!(f, "]") + } + ConcreteValue::Range(range_value) => { + write!(f, "{range_value}") + } + ConcreteValue::String(string) => write!(f, "{string}"), + } } } -impl Display for Function { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{}", self.r#type) - } -} - -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] pub enum RangeValue { - ByteRange(Range), - ByteRangeInclusive(RangeInclusive), - CharacterRange(Range), - CharacterRangeInclusive(RangeInclusive), - FloatRange(Range), - FloatRangeInclusive(RangeInclusive), - IntegerRange(Range), - IntegerRangeInclusive(RangeInclusive), + ByteRange { start: u8, end: u8 }, + ByteRangeInclusive { start: u8, end: u8 }, + CharacterRange { start: char, end: char }, + CharacterRangeInclusive { start: char, end: char }, + FloatRange { start: f64, end: f64 }, + FloatRangeInclusive { start: f64, end: f64 }, + IntegerRange { start: i64, end: i64 }, + IntegerRangeInclusive { start: i64, end: i64 }, } impl RangeValue { pub fn r#type(&self) -> Type { let inner_type = match self { - RangeValue::ByteRange(_) => RangeableType::Byte, - RangeValue::ByteRangeInclusive(_) => RangeableType::Byte, - RangeValue::CharacterRange(_) => RangeableType::Character, - RangeValue::CharacterRangeInclusive(_) => RangeableType::Character, - RangeValue::FloatRange(_) => RangeableType::Float, - RangeValue::FloatRangeInclusive(_) => RangeableType::Float, - RangeValue::IntegerRange(_) => RangeableType::Integer, - RangeValue::IntegerRangeInclusive(_) => RangeableType::Integer, + RangeValue::ByteRange { .. } | RangeValue::ByteRangeInclusive { .. } => Type::Byte, + RangeValue::CharacterRange { .. } | RangeValue::CharacterRangeInclusive { .. } => { + Type::Character + } + RangeValue::FloatRange { .. } | RangeValue::FloatRangeInclusive { .. } => Type::Float, + RangeValue::IntegerRange { .. } | RangeValue::IntegerRangeInclusive { .. } => { + Type::Integer + } }; - Type::Range { r#type: inner_type } + Type::Range { + r#type: Box::new(inner_type), + } } } impl From> for RangeValue { fn from(range: Range) -> Self { - RangeValue::ByteRange(range) + RangeValue::ByteRange { + start: range.start, + end: range.end, + } } } impl From> for RangeValue { fn from(range: RangeInclusive) -> Self { - RangeValue::ByteRangeInclusive(range) + RangeValue::ByteRangeInclusive { + start: *range.start(), + end: *range.end(), + } } } impl From> for RangeValue { fn from(range: Range) -> Self { - RangeValue::CharacterRange(range) + RangeValue::CharacterRange { + start: range.start, + end: range.end, + } } } impl From> for RangeValue { fn from(range: RangeInclusive) -> Self { - RangeValue::CharacterRangeInclusive(range) + RangeValue::CharacterRangeInclusive { + start: *range.start(), + end: *range.end(), + } } } impl From> for RangeValue { fn from(range: Range) -> Self { - RangeValue::FloatRange(range) + RangeValue::FloatRange { + start: range.start, + end: range.end, + } } } impl From> for RangeValue { fn from(range: RangeInclusive) -> Self { - RangeValue::FloatRangeInclusive(range) + RangeValue::FloatRangeInclusive { + start: *range.start(), + end: *range.end(), + } } } impl From> for RangeValue { fn from(range: Range) -> Self { - RangeValue::IntegerRange(range.start as i64..range.end as i64) + RangeValue::IntegerRange { + start: range.start as i64, + end: range.end as i64, + } } } impl From> for RangeValue { fn from(range: RangeInclusive) -> Self { - RangeValue::IntegerRangeInclusive(*range.start() as i64..=*range.end() as i64) + RangeValue::IntegerRangeInclusive { + start: *range.start() as i64, + end: *range.end() as i64, + } } } impl From> for RangeValue { fn from(range: Range) -> Self { - RangeValue::IntegerRange(range) + RangeValue::IntegerRange { + start: range.start, + end: range.end, + } } } impl From> for RangeValue { fn from(range: RangeInclusive) -> Self { - RangeValue::IntegerRangeInclusive(range) + RangeValue::IntegerRangeInclusive { + start: *range.start(), + end: *range.end(), + } } } impl Display for RangeValue { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - RangeValue::ByteRange(range) => write!(f, "{}..{}", range.start, range.end), - RangeValue::ByteRangeInclusive(range) => { - write!(f, "{}..={}", range.start(), range.end()) + RangeValue::ByteRange { start, end } => write!(f, "{}..{}", start, end), + RangeValue::ByteRangeInclusive { start, end } => { + write!(f, "{}..={}", start, end) } - RangeValue::CharacterRange(range) => write!(f, "{}..{}", range.start, range.end), - RangeValue::CharacterRangeInclusive(range) => { - write!(f, "{}..={}", range.start(), range.end()) + RangeValue::CharacterRange { start, end } => { + write!(f, "{}..{}", start, end) } - RangeValue::FloatRange(range) => write!(f, "{}..{}", range.start, range.end), - RangeValue::FloatRangeInclusive(range) => { - write!(f, "{}..={}", range.start(), range.end()) + RangeValue::CharacterRangeInclusive { start, end } => { + write!(f, "{}..={}", start, end) } - RangeValue::IntegerRange(range) => write!(f, "{}..{}", range.start, range.end), - RangeValue::IntegerRangeInclusive(range) => { - write!(f, "{}..={}", range.start(), range.end()) + RangeValue::FloatRange { start, end } => write!(f, "{}..{}", start, end), + RangeValue::FloatRangeInclusive { start, end } => { + write!(f, "{}..={}", start, end) + } + RangeValue::IntegerRange { start, end } => write!(f, "{}..{}", start, end), + RangeValue::IntegerRangeInclusive { start, end } => { + write!(f, "{}..={}", start, end) } } } @@ -628,105 +775,174 @@ impl PartialOrd for RangeValue { impl Ord for RangeValue { fn cmp(&self, other: &Self) -> Ordering { match (self, other) { - (RangeValue::ByteRange(left), RangeValue::ByteRange(right)) => { - let start_cmp = left.start.cmp(&right.start); - - if start_cmp != Ordering::Equal { - start_cmp - } else { - left.end.cmp(&right.end) - } - } - (RangeValue::ByteRange(_), _) => Ordering::Greater, - (RangeValue::ByteRangeInclusive(left), RangeValue::ByteRangeInclusive(right)) => { - let start_cmp = left.start().cmp(right.start()); - - if start_cmp != Ordering::Equal { - start_cmp - } else { - left.end().cmp(right.end()) - } - } - (RangeValue::ByteRangeInclusive(_), _) => Ordering::Greater, - (RangeValue::CharacterRange(left), RangeValue::CharacterRange(right)) => { - let start_cmp = left.start.cmp(&right.start); - - if start_cmp != Ordering::Equal { - start_cmp - } else { - left.end.cmp(&right.end) - } - } - (RangeValue::CharacterRange(_), _) => Ordering::Greater, ( - RangeValue::CharacterRangeInclusive(left), - RangeValue::CharacterRangeInclusive(right), + RangeValue::ByteRange { + start: left_start, + end: left_end, + }, + RangeValue::ByteRange { + start: right_start, + end: right_end, + }, ) => { - let start_cmp = left.start().cmp(right.start()); + let start_cmp = left_start.cmp(right_start); if start_cmp != Ordering::Equal { start_cmp } else { - left.end().cmp(right.end()) + left_end.cmp(right_end) } } - (RangeValue::CharacterRangeInclusive(_), _) => Ordering::Greater, - (RangeValue::FloatRange(left), RangeValue::FloatRange(right)) => { - let start_cmp = left.start.to_bits().cmp(&right.start.to_bits()); + (RangeValue::ByteRange { .. }, _) => Ordering::Greater, + ( + RangeValue::ByteRangeInclusive { + start: left_start, + end: left_end, + }, + RangeValue::ByteRangeInclusive { + start: right_start, + end: right_end, + }, + ) => { + let start_cmp = left_start.cmp(&right_start); if start_cmp != Ordering::Equal { start_cmp } else { - left.end.to_bits().cmp(&right.end.to_bits()) + left_end.cmp(&right_end) } } - (RangeValue::FloatRange(_), _) => Ordering::Greater, - (RangeValue::FloatRangeInclusive(left), RangeValue::FloatRangeInclusive(right)) => { - let start_cmp = left.start().to_bits().cmp(&right.start().to_bits()); + (RangeValue::ByteRangeInclusive { .. }, _) => Ordering::Greater, + ( + RangeValue::CharacterRange { + start: left_start, + end: left_end, + }, + RangeValue::CharacterRange { + start: right_start, + end: right_end, + }, + ) => { + let start_cmp = left_start.cmp(right_start); if start_cmp != Ordering::Equal { start_cmp } else { - left.end().to_bits().cmp(&right.end().to_bits()) + left_end.cmp(right_end) } } - (RangeValue::FloatRangeInclusive(_), _) => Ordering::Greater, - (RangeValue::IntegerRange(left), RangeValue::IntegerRange(right)) => { - let start_cmp = left.start.cmp(&right.start); + (RangeValue::CharacterRange { .. }, _) => Ordering::Greater, + ( + RangeValue::CharacterRangeInclusive { + start: left_start, + end: left_end, + }, + RangeValue::CharacterRangeInclusive { + start: right_start, + end: right_end, + }, + ) => { + let start_cmp = left_start.cmp(right_start); if start_cmp != Ordering::Equal { start_cmp } else { - left.end.cmp(&right.end) + left_end.cmp(right_end) } } - (RangeValue::IntegerRange(_), _) => Ordering::Greater, - (RangeValue::IntegerRangeInclusive(left), RangeValue::IntegerRangeInclusive(right)) => { - let start_cmp = left.start().cmp(right.start()); + (RangeValue::CharacterRangeInclusive { .. }, _) => Ordering::Greater, + ( + RangeValue::FloatRange { + start: left_start, + end: left_end, + }, + RangeValue::FloatRange { + start: right_start, + end: right_end, + }, + ) => { + let start_cmp = left_start.partial_cmp(right_start).unwrap(); if start_cmp != Ordering::Equal { start_cmp } else { - left.end().cmp(right.end()) + left_end.partial_cmp(right_end).unwrap() } } - (RangeValue::IntegerRangeInclusive(_), _) => Ordering::Greater, + (RangeValue::FloatRange { .. }, _) => Ordering::Greater, + ( + RangeValue::FloatRangeInclusive { + start: left_start, + end: left_end, + }, + RangeValue::FloatRangeInclusive { + start: right_start, + end: right_end, + }, + ) => { + let start_cmp = left_start.partial_cmp(right_start).unwrap(); + + if start_cmp != Ordering::Equal { + start_cmp + } else { + left_end.partial_cmp(right_end).unwrap() + } + } + (RangeValue::FloatRangeInclusive { .. }, _) => Ordering::Greater, + ( + RangeValue::IntegerRange { + start: left_start, + end: left_end, + }, + RangeValue::IntegerRange { + start: right_start, + end: right_end, + }, + ) => { + let start_cmp = left_start.cmp(right_start); + + if start_cmp != Ordering::Equal { + start_cmp + } else { + left_end.cmp(right_end) + } + } + (RangeValue::IntegerRange { .. }, _) => Ordering::Greater, + ( + RangeValue::IntegerRangeInclusive { + start: left_start, + end: left_end, + }, + RangeValue::IntegerRangeInclusive { + start: right_start, + end: right_end, + }, + ) => { + let start_cmp = left_start.cmp(right_start); + + if start_cmp != Ordering::Equal { + start_cmp + } else { + left_end.cmp(right_end) + } + } + (RangeValue::IntegerRangeInclusive { .. }, _) => Ordering::Greater, } } } #[derive(Clone, Debug, PartialEq)] pub enum ValueError { - CannotAdd(Value, Value), - CannotAnd(Value, Value), - CannotCompare(Value, Value), - CannotDivide(Value, Value), - CannotModulo(Value, Value), - CannotMultiply(Value, Value), - CannotNegate(Value), - CannotNot(Value), - CannotSubtract(Value, Value), - CannotOr(Value, Value), + CannotAdd(ConcreteValue, ConcreteValue), + CannotAnd(ConcreteValue, ConcreteValue), + CannotCompare(ConcreteValue, ConcreteValue), + CannotDivide(ConcreteValue, ConcreteValue), + CannotModulo(ConcreteValue, ConcreteValue), + CannotMultiply(ConcreteValue, ConcreteValue), + CannotNegate(ConcreteValue), + CannotNot(ConcreteValue), + CannotSubtract(ConcreteValue, ConcreteValue), + CannotOr(ConcreteValue, ConcreteValue), } impl Display for ValueError { diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 888aa5f..a9f51c8 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -3,19 +3,20 @@ use std::{ cmp::Ordering, collections::HashMap, fmt::{self, Display, Formatter}, + mem::replace, + rc::Weak, }; use crate::{ - compile, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, + compile, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionBorrowed, Instruction, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError, }; -pub fn run(source: &str) -> Result, DustError> { +pub fn run(source: &str) -> Result, DustError> { let chunk = compile(source)?; let mut vm = Vm::new(&chunk, None); vm.run() - .map(|option| option.cloned()) .map_err(|error| DustError::Runtime { error, source }) } @@ -31,19 +32,19 @@ pub fn run_and_display_output(source: &str) { /// /// See the [module-level documentation](index.html) for more information. #[derive(Debug, Eq, PartialEq)] -pub struct Vm<'chunk, 'parent> { +pub struct Vm<'chunk, 'parent, 'stack> { ip: usize, chunk: &'chunk Chunk, - stack: Vec, + stack: Vec>, local_definitions: HashMap, last_assigned_register: Option, - parent: Option<&'parent Vm<'chunk, 'parent>>, + parent: Option<&'parent Vm<'chunk, 'stack, 'parent>>, } -impl<'chunk, 'parent> Vm<'chunk, 'parent> { +impl<'chunk, 'parent, 'stack: 'chunk + 'parent> Vm<'chunk, 'parent, 'stack> { const STACK_LIMIT: usize = u16::MAX as usize; - pub fn new(chunk: &'chunk Chunk, parent: Option<&'parent Vm<'chunk, 'parent>>) -> Self { + pub fn new(chunk: &'chunk Chunk, parent: Option<&'parent Vm<'chunk, 'parent, 'stack>>) -> Self { Self { ip: 0, chunk, @@ -54,27 +55,7 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { } } - pub fn run(&mut self) -> Result, VmError> { - // DRY helper to get constant or register values for binary operations - fn get_arguments<'a>( - vm: &'a mut Vm, - instruction: Instruction, - position: Span, - ) -> Result<(&'a Value, &'a Value), VmError> { - let left = if instruction.b_is_constant() { - vm.get_constant(instruction.b(), position)? - } else { - vm.open_register(instruction.b(), position)? - }; - let right = if instruction.c_is_constant() { - vm.get_constant(instruction.c(), position)? - } else { - vm.open_register(instruction.c(), position)? - }; - - Ok((left, right)) - } - + pub fn run(&'stack mut self) -> Result, VmError> { while let Ok((instruction, position)) = self.read(Span(0, 0)).copied() { log::info!( "{} | {} | {} | {}", @@ -117,9 +98,9 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { let to_register = instruction.a(); let boolean = instruction.b_as_boolean(); let jump = instruction.c_as_boolean(); - let value = Value::boolean(boolean); + let boolean = ConcreteValue::boolean(boolean); - self.set_register(to_register, Register::Value(value), position)?; + self.set_register(to_register, Register::Value(boolean), position)?; if jump { self.ip += 1; @@ -143,31 +124,23 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { Operation::LoadList => { let to_register = instruction.a(); let start_register = instruction.b(); - let item_type = (start_register..to_register) - .find_map(|register_index| { - if let Ok(value) = self.open_register(register_index, position) { - Some(value.r#type()) - } else { - None - } - }) - .unwrap_or(Type::Any); - let value = Value::abstract_list(start_register, to_register, item_type); + let mut list = Vec::new(); - self.set_register(to_register, Register::Value(value), position)?; + for register_index in start_register..to_register { + let value = self.open_register(register_index, position)?; + + list.push(value); + } + + self.set_register(to_register, Register::List(list), position)?; } Operation::LoadSelf => { let to_register = instruction.a(); - let value = Value::function( - self.chunk.clone(), - FunctionType { - type_parameters: None, - value_parameters: None, - return_type: Box::new(Type::None), - }, - ); + let function = Value::FunctionBorrowed(FunctionBorrowed::new(self.chunk)); - self.set_register(to_register, Register::Value(value), position)?; + // self.set_register(to_register, Register::Value(function), position)?; + + todo!() } Operation::DefineLocal => { let from_register = instruction.a(); @@ -209,45 +182,45 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { } Operation::Add => { let to_register = instruction.a(); - let (left, right) = get_arguments(self, instruction, position)?; + let (left, right) = self.get_arguments(instruction, position)?; let sum = left - .add(right) + .add(&right) .map_err(|error| VmError::Value { error, position })?; self.set_register(to_register, Register::Value(sum), position)?; } Operation::Subtract => { let to_register = instruction.a(); - let (left, right) = get_arguments(self, instruction, position)?; + let (left, right) = self.get_arguments(instruction, position)?; let difference = left - .subtract(right) + .subtract(&right) .map_err(|error| VmError::Value { error, position })?; self.set_register(to_register, Register::Value(difference), position)?; } Operation::Multiply => { let to_register = instruction.a(); - let (left, right) = get_arguments(self, instruction, position)?; + let (left, right) = self.get_arguments(instruction, position)?; let product = left - .multiply(right) + .multiply(&right) .map_err(|error| VmError::Value { error, position })?; self.set_register(to_register, Register::Value(product), position)?; } Operation::Divide => { let to_register = instruction.a(); - let (left, right) = get_arguments(self, instruction, position)?; + let (left, right) = self.get_arguments(instruction, position)?; let quotient = left - .divide(right) + .divide(&right) .map_err(|error| VmError::Value { error, position })?; self.set_register(to_register, Register::Value(quotient), position)?; } Operation::Modulo => { let to_register = instruction.a(); - let (left, right) = get_arguments(self, instruction, position)?; + let (left, right) = self.get_arguments(instruction, position)?; let remainder = left - .modulo(right) + .modulo(&right) .map_err(|error| VmError::Value { error, position })?; self.set_register(to_register, Register::Value(remainder), position)?; @@ -256,11 +229,11 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { let register = instruction.a(); let test_value = instruction.c_as_boolean(); let value = self.open_register(register, position)?; - let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value { - *boolean + let boolean = if let Some(boolean) = value.as_boolean() { + boolean } else { return Err(VmError::ExpectedBoolean { - found: value.clone(), + found: value.into_concrete(), position, }); }; @@ -276,34 +249,26 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { Operation::Jump ); - let (left, right) = get_arguments(self, instruction, position)?; - let equal_result = left - .equal(right) - .map_err(|error| VmError::Value { error, position })?; - let boolean = - if let Value::Concrete(ConcreteValue::Boolean(boolean)) = equal_result { - boolean - } else { - return Err(VmError::ExpectedBoolean { - found: equal_result.clone(), - position, - }); - }; let compare_to = instruction.a_as_boolean(); + let (left, right) = self.get_arguments(instruction, position)?; + let equal_result = left + .equal(&right) + .map_err(|error| VmError::Value { error, position })?; + let is_equal = if let ConcreteValue::Boolean(boolean) = equal_result { + boolean + } else { + return Err(VmError::ExpectedBoolean { + found: equal_result.clone(), + position, + }); + }; - if boolean == compare_to { + if is_equal == compare_to { self.ip += 1; } else { - let (jump, _) = self.get_instruction(self.ip, position)?; - let jump_distance = jump.a(); - let is_positive = jump.b_as_boolean(); - let new_ip = if is_positive { - self.ip + jump_distance as usize - } else { - self.ip - jump_distance as usize - }; + let jump = self.get_instruction(self.ip, position)?.0; - self.ip = new_ip; + self.jump(jump); } } Operation::Less => { @@ -312,34 +277,26 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { Operation::Jump ); - let (left, right) = get_arguments(self, instruction, position)?; - let less_result = left - .less_than(right) - .map_err(|error| VmError::Value { error, position })?; - let boolean = - if let Value::Concrete(ConcreteValue::Boolean(boolean)) = less_result { - boolean - } else { - return Err(VmError::ExpectedBoolean { - found: less_result.clone(), - position, - }); - }; let compare_to = instruction.a_as_boolean(); + let (left, right) = self.get_arguments(instruction, position)?; + let less_result = left + .less_than(&right) + .map_err(|error| VmError::Value { error, position })?; + let is_less_than = if let ConcreteValue::Boolean(boolean) = less_result { + boolean + } else { + return Err(VmError::ExpectedBoolean { + found: less_result.clone(), + position, + }); + }; - if boolean == compare_to { + if is_less_than == compare_to { self.ip += 1; } else { let jump = self.get_instruction(self.ip, position)?.0; - let jump_distance = jump.a(); - let is_positive = jump.b_as_boolean(); - let new_ip = if is_positive { - self.ip + jump_distance as usize - } else { - self.ip - jump_distance as usize - }; - self.ip = new_ip; + self.jump(jump); } } Operation::LessEqual => { @@ -348,43 +305,32 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { Operation::Jump ); - 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 })?; - let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = - less_or_equal_result - { - boolean - } else { - return Err(VmError::ExpectedBoolean { - found: less_or_equal_result.clone(), - position, - }); - }; let compare_to = instruction.a_as_boolean(); + let (left, right) = self.get_arguments(instruction, position)?; + let less_or_equal_result = left + .less_than_or_equal(&right) + .map_err(|error| VmError::Value { error, position })?; + let is_less_than_or_equal = + if let ConcreteValue::Boolean(boolean) = less_or_equal_result { + boolean + } else { + return Err(VmError::ExpectedBoolean { + found: less_or_equal_result.clone(), + position, + }); + }; - if boolean == compare_to { + if is_less_than_or_equal == compare_to { self.ip += 1; } else { let jump = self.get_instruction(self.ip, position)?.0; - let jump_distance = jump.a(); - let is_positive = jump.b_as_boolean(); - let new_ip = if is_positive { - self.ip + jump_distance as usize - } else { - self.ip - jump_distance as usize - }; - self.ip = new_ip; + self.jump(jump); } } Operation::Negate => { - let value = if instruction.b_is_constant() { - self.get_constant(instruction.b(), position)? - } else { - self.open_register(instruction.b(), position)? - }; + let value = + self.get_argument(instruction.b(), instruction.b_is_constant(), position)?; let negated = value .negate() .map_err(|error| VmError::Value { error, position })?; @@ -392,42 +338,30 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { self.set_register(instruction.a(), Register::Value(negated), position)?; } Operation::Not => { - let value = if instruction.b_is_constant() { - self.get_constant(instruction.b(), position)? - } else { - self.open_register(instruction.b(), position)? - }; + let value = + self.get_argument(instruction.b(), instruction.b_is_constant(), position)?; let not = value .not() .map_err(|error| VmError::Value { error, position })?; self.set_register(instruction.a(), Register::Value(not), position)?; } - Operation::Jump => { - let jump_distance = instruction.b(); - let is_positive = instruction.c_as_boolean(); - let new_ip = if is_positive { - self.ip + jump_distance as usize - } else { - self.ip - jump_distance as usize - 1 - }; - self.ip = new_ip; - } + Operation::Jump => self.jump(instruction), Operation::Call => { let to_register = instruction.a(); let function_register = instruction.b(); let argument_count = instruction.c(); let value = self.open_register(function_register, position)?; - let function = if let Value::Concrete(ConcreteValue::Function(function)) = value - { + let function = if let Some(function) = value.as_function() { function } else { return Err(VmError::ExpectedFunction { - found: value.clone(), + found: value.into_concrete(), position, }); }; let mut function_vm = Vm::new(function.chunk(), Some(self)); + let first_argument_index = function_register + 1; for argument_index in @@ -442,7 +376,7 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { )? } - let return_value = function_vm.run()?.cloned(); + let return_value = function_vm.run()?; if let Some(value) = return_value { self.set_register(to_register, Register::Value(value), position)?; @@ -450,12 +384,12 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { } Operation::CallNative => { let native_function = NativeFunction::from(instruction.b()); - let return_value = native_function.call(instruction, self, position)?; + let return_value = native_function.call(self, instruction, position)?; - if let Some(value) = return_value { + if let Some(concrete_value) = return_value { let to_register = instruction.a(); - self.set_register(to_register, Register::Value(value), position)?; + self.set_register(to_register, Register::Value(concrete_value), position)?; } } Operation::Return => { @@ -465,13 +399,12 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { return Ok(None); } - let return_value = if let Some(register_index) = self.last_assigned_register { - self.open_register(register_index, position)? + return if let Some(register_index) = self.last_assigned_register { + self.open_register(register_index, position) + .map(|value| Some(value.into_concrete())) } else { - return Err(VmError::StackUnderflow { position }); + Err(VmError::StackUnderflow { position }) }; - - return Ok(Some(return_value)); } } } @@ -479,10 +412,149 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { Ok(None) } + pub(crate) fn open_register( + &'stack self, + register_index: u8, + position: Span, + ) -> Result<&'stack ConcreteValue, VmError> { + let register_index = register_index as usize; + let register = + self.stack + .get(register_index) + .ok_or_else(|| VmError::RegisterIndexOutOfBounds { + index: register_index, + position, + })?; + + log::trace!("Open R{register_index} to {register}"); + + match register { + Register::Value(value) => Ok(value), + Register::List(list) => Ok(ConcreteValue::List( + list.into_iter() + .map(|concrete_value| (*concrete_value).clone()) + .collect(), + )), + Register::StackPointer(register_index) => self.open_register(*register_index, position), + Register::ConstantPointer(constant_index) => { + self.get_constant(*constant_index, position) + } + Register::ParentStackPointer(register_index) => { + let parent = self.parent.ok_or(VmError::ExpectedParent { position })?; + + parent.open_register(*register_index, position) + } + Register::ParentConstantPointer(constant_index) => { + let parent = self + .parent + .as_ref() + .ok_or(VmError::ExpectedParent { position })?; + let constant = parent.get_constant(*constant_index, position)?; + + Ok(constant) + } + Register::Empty => Err(VmError::EmptyRegister { + index: register_index, + position, + }), + } + } + + fn get_concrete_from_register( + &'stack self, + register_index: u8, + position: Span, + ) -> Result { + let register_index = register_index as usize; + let register = + self.stack + .get(register_index) + .ok_or_else(|| VmError::RegisterIndexOutOfBounds { + index: register_index, + position, + })?; + + let value = match register { + Register::Value(concrete_value) => concrete_value.clone(), + Register::List(list) => { + let items = list.into_iter().map(|value| (*value).clone()).collect(); + + ConcreteValue::List(items) + } + Register::StackPointer(register_index) => { + self.get_concrete_from_register(*register_index, position)? + } + Register::ConstantPointer(constant_pointer) => { + self.get_constant(*constant_pointer, position)?.clone() + } + Register::ParentStackPointer(register_index) => { + let parent = self.parent.ok_or(VmError::ExpectedParent { position })?; + + parent.get_concrete_from_register(*register_index, position)? + } + Register::ParentConstantPointer(constant_index) => { + let parent = self + .parent + .as_ref() + .ok_or(VmError::ExpectedParent { position })?; + + parent.get_constant(*constant_index, position)?.clone() + } + Register::Empty => { + return Err(VmError::EmptyRegister { + index: register_index, + position, + }) + } + }; + + Ok(value) + } + + /// DRY helper for handling JUMP instructions + fn jump(&mut self, jump: Instruction) { + let jump_distance = jump.b(); + let is_positive = jump.c_as_boolean(); + let new_ip = if is_positive { + self.ip + jump_distance as usize + } else { + self.ip - jump_distance as usize - 1 + }; + self.ip = new_ip; + } + + /// DRY helper to get a constant or register values + fn get_argument( + &'stack self, + index: u8, + is_constant: bool, + position: Span, + ) -> Result, VmError> { + let argument = if is_constant { + self.get_constant(index, position)?.as_reference_value() + } else { + self.open_register(index, position)? + }; + + Ok(argument) + } + + /// DRY helper to get two arguments for binary operations + fn get_arguments( + &'stack self, + instruction: Instruction, + position: Span, + ) -> Result<(Value<'stack>, Value<'stack>), VmError> { + let left = self.get_argument(instruction.b(), instruction.b_is_constant(), position)?; + let right = self.get_argument(instruction.c(), instruction.c_is_constant(), position)?; + + Ok((left, right)) + } + fn set_register( &mut self, to_register: u8, - register: Register, + register: Register<'stack>, position: Span, ) -> Result<(), VmError> { self.last_assigned_register = Some(to_register); @@ -527,71 +599,18 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { } } - fn get_constant(&self, index: u8, position: Span) -> Result<&Value, VmError> { + fn get_constant(&self, index: u8, position: Span) -> Result<&'chunk ConcreteValue, VmError> { self.chunk .get_constant(index) .map_err(|error| VmError::Chunk { error, position }) } - pub fn open_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, - })?; - - log::trace!("Open R{register_index} to {register}"); - - match register { - Register::Value(value) => Ok(value), - Register::StackPointer(register_index) => self.open_register(*register_index, position), - Register::ConstantPointer(constant_index) => { - self.get_constant(*constant_index, position) - } - Register::ParentStackPointer(register_index) => { - let parent = self - .parent - .as_ref() - .ok_or(VmError::ExpectedParent { position })?; - - parent.open_register(*register_index, position) - } - Register::ParentConstantPointer(constant_index) => { - let parent = self - .parent - .as_ref() - .ok_or(VmError::ExpectedParent { position })?; - - 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> { - let max_ip = self.chunk.len() - 1; + let ip = self.ip; - if self.ip > max_ip { - return self.get_instruction(max_ip, position); - } else { - self.ip += 1; - } + self.ip += 1; - self.get_instruction(self.ip - 1, position) - } - - fn define_local(&mut self, local_index: u8, register_index: u8) -> Result<(), VmError> { - log::debug!("Define local L{}", local_index); - - self.local_definitions.insert(local_index, register_index); - - Ok(()) + self.get_instruction(ip, position) } fn get_instruction( @@ -603,23 +622,45 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { .get_instruction(index) .map_err(|error| VmError::Chunk { error, position }) } + + fn define_local(&mut self, local_index: u8, register_index: u8) -> Result<(), VmError> { + log::debug!("Define local L{}", local_index); + + self.local_definitions.insert(local_index, register_index); + + Ok(()) + } } #[derive(Debug, Eq, PartialEq)] -enum Register { +enum Register<'stack> { Empty, - Value(Value), + Value(ConcreteValue), + List(Vec<&'stack ConcreteValue>), StackPointer(u8), ConstantPointer(u8), ParentStackPointer(u8), ParentConstantPointer(u8), } -impl Display for Register { +impl<'stack> Display for Register<'stack> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { Self::Empty => write!(f, "empty"), Self::Value(value) => write!(f, "{}", value), + Self::List(values) => { + write!(f, "[")?; + + for (index, value) in values.iter().enumerate() { + if index > 0 { + write!(f, ", ")?; + } + + write!(f, "{}", value)?; + } + + write!(f, "]") + } Self::StackPointer(index) => write!(f, "R{}", index), Self::ConstantPointer(index) => write!(f, "C{}", index), Self::ParentStackPointer(index) => write!(f, "PR{}", index), @@ -631,25 +672,52 @@ impl Display for Register { #[derive(Clone, Debug, PartialEq)] pub enum VmError { // Stack errors - StackOverflow { position: Span }, - StackUnderflow { position: Span }, + StackOverflow { + position: Span, + }, + StackUnderflow { + position: Span, + }, // Register errors - EmptyRegister { index: usize, position: Span }, - RegisterIndexOutOfBounds { index: usize, position: Span }, + EmptyRegister { + index: usize, + position: Span, + }, + RegisterIndexOutOfBounds { + index: usize, + position: Span, + }, // Local errors - UndefinedLocal { local_index: u8, position: Span }, + UndefinedLocal { + local_index: u8, + position: Span, + }, // Execution errors - ExpectedBoolean { found: Value, position: Span }, - ExpectedFunction { found: Value, position: Span }, - ExpectedParent { position: Span }, + ExpectedBoolean { + found: ConcreteValue, + position: Span, + }, + ExpectedFunction { + found: ConcreteValue, + position: Span, + }, + ExpectedParent { + position: Span, + }, // Wrappers for foreign errors - Chunk { error: ChunkError, position: Span }, + Chunk { + error: ChunkError, + position: Span, + }, NativeFunction(NativeFunctionError), - Value { error: ValueError, position: Span }, + Value { + error: ValueError, + position: Span, + }, } impl AnnotatedError for VmError { @@ -678,6 +746,7 @@ impl AnnotatedError for VmError { Self::Chunk { error, .. } => Some(error.to_string()), Self::EmptyRegister { index, .. } => Some(format!("Register R{index} is empty")), Self::ExpectedFunction { found, .. } => Some(format!("{found} is not a function")), + Self::RegisterIndexOutOfBounds { index, .. } => { Some(format!("Register {index} does not exist")) } diff --git a/dust-lang/tests/basic.rs b/dust-lang/tests/basic.rs index a993468..a825b7a 100644 --- a/dust-lang/tests/basic.rs +++ b/dust-lang/tests/basic.rs @@ -12,12 +12,12 @@ fn constant() { (Instruction::load_constant(0, 0, false), Span(0, 2)), (Instruction::r#return(true), Span(2, 2)) ], - vec![Value::integer(42)], + vec![ValueOwned::Primitive(Primitive::Integer(42))], vec![] )) ); - assert_eq!(run(source), Ok(Some(Value::integer(42)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(42)))); } #[test] @@ -57,10 +57,14 @@ fn parentheses_precedence() { ), (Instruction::r#return(true), Span(11, 11)), ], - vec![Value::integer(1), Value::integer(2), Value::integer(3)], + vec![ + ValueOwned::integer(1), + ValueOwned::integer(2), + ValueOwned::integer(3) + ], vec![] )) ); - assert_eq!(run(source), Ok(Some(Value::integer(9)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(9)))); } diff --git a/dust-lang/tests/comparison.rs b/dust-lang/tests/comparison.rs index 487889a..eaf03e4 100644 --- a/dust-lang/tests/comparison.rs +++ b/dust-lang/tests/comparison.rs @@ -20,12 +20,12 @@ fn equal() { (Instruction::load_boolean(0, false, false), Span(2, 4)), (Instruction::r#return(true), Span(6, 6)), ], - vec![Value::integer(1), Value::integer(2)], + vec![ValueOwned::integer(1), ValueOwned::integer(2)], vec![] )), ); - assert_eq!(run(source), Ok(Some(Value::boolean(false)))); + assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false)))); } #[test] @@ -48,12 +48,12 @@ fn greater() { (Instruction::load_boolean(0, false, false), Span(2, 3)), (Instruction::r#return(true), Span(5, 5)), ], - vec![Value::integer(1), Value::integer(2)], + vec![ValueOwned::integer(1), ValueOwned::integer(2)], vec![] )), ); - assert_eq!(run(source), Ok(Some(Value::boolean(false)))); + assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false)))); } #[test] @@ -76,12 +76,12 @@ fn greater_than_or_equal() { (Instruction::load_boolean(0, false, false), Span(2, 4)), (Instruction::r#return(true), Span(6, 6)), ], - vec![Value::integer(1), Value::integer(2)], + vec![ValueOwned::integer(1), ValueOwned::integer(2)], vec![] )), ); - assert_eq!(run(source), Ok(Some(Value::boolean(false)))); + assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false)))); } #[test] @@ -104,12 +104,12 @@ fn less_than() { (Instruction::load_boolean(0, false, false), Span(2, 3)), (Instruction::r#return(true), Span(5, 5)), ], - vec![Value::integer(1), Value::integer(2)], + vec![ValueOwned::integer(1), ValueOwned::integer(2)], vec![] )), ); - assert_eq!(run(source), Ok(Some(Value::boolean(true)))); + assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true)))); } #[test] @@ -132,12 +132,12 @@ fn less_than_or_equal() { (Instruction::load_boolean(0, false, false), Span(2, 4)), (Instruction::r#return(true), Span(6, 6)), ], - vec![Value::integer(1), Value::integer(2)], + vec![ValueOwned::integer(1), ValueOwned::integer(2)], vec![] )), ); - assert_eq!(run(source), Ok(Some(Value::boolean(true)))); + assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true)))); } #[test] @@ -160,10 +160,10 @@ fn not_equal() { (Instruction::load_boolean(0, false, false), Span(2, 4)), (Instruction::r#return(true), Span(6, 6)), ], - vec![Value::integer(1), Value::integer(2)], + vec![ValueOwned::integer(1), ValueOwned::integer(2)], vec![] )), ); - assert_eq!(run(source), Ok(Some(Value::boolean(true)))); + assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true)))); } diff --git a/dust-lang/tests/control_flow.rs b/dust-lang/tests/control_flow.rs index d92e643..e8cf3c4 100644 --- a/dust-lang/tests/control_flow.rs +++ b/dust-lang/tests/control_flow.rs @@ -22,12 +22,12 @@ fn equality_assignment_long() { (Instruction::get_local(1, 0), Span(43, 44)), (Instruction::r#return(true), Span(44, 44)), ], - vec![Value::integer(4), Value::string("a")], + vec![ValueOwned::integer(4), ValueOwned::string("a")], vec![Local::new(1, Type::Boolean, false, Scope::default(),)] )), ); - assert_eq!(run(source), Ok(Some(Value::boolean(true)))); + assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true)))); } #[test] @@ -52,12 +52,12 @@ fn equality_assignment_short() { (Instruction::get_local(1, 0), Span(15, 16)), (Instruction::r#return(true), Span(16, 16)), ], - vec![Value::integer(4), Value::string("a")], + vec![ValueOwned::integer(4), ValueOwned::string("a")], vec![Local::new(1, Type::Boolean, false, Scope::default())] )), ); - assert_eq!(run(source), Ok(Some(Value::boolean(true)))); + assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true)))); } #[test] @@ -104,18 +104,18 @@ fn if_else_assigment_false() { (Instruction::r#return(true), Span(149, 149)), ], vec![ - Value::integer(4), - Value::integer(3), - Value::integer(1), - Value::integer(2), - Value::integer(42), - Value::string("a") + ValueOwned::integer(4), + ValueOwned::integer(3), + ValueOwned::integer(1), + ValueOwned::integer(2), + ValueOwned::integer(42), + ValueOwned::string("a") ], vec![Local::new(5, Type::Integer, false, Scope::default())] )), ); - assert_eq!(run(source), Ok(Some(Value::integer(42)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(42)))); } #[test] @@ -162,18 +162,18 @@ fn if_else_assigment_true() { (Instruction::r#return(true), Span(149, 149)), ], vec![ - Value::integer(4), - Value::integer(1), - Value::integer(2), - Value::integer(3), - Value::integer(42), - Value::string("a") + ValueOwned::integer(4), + ValueOwned::integer(1), + ValueOwned::integer(2), + ValueOwned::integer(3), + ValueOwned::integer(42), + ValueOwned::string("a") ], vec![Local::new(5, Type::Integer, false, Scope::default())] )), ); - assert_eq!(run(source), Ok(Some(Value::integer(42)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(42)))); } #[test] @@ -210,10 +210,10 @@ fn if_else_complex() { (Instruction::r#return(false), Span(95, 95)), ], vec![ - Value::integer(1), - Value::integer(2), - Value::integer(3), - Value::integer(4), + ValueOwned::integer(1), + ValueOwned::integer(2), + ValueOwned::integer(3), + ValueOwned::integer(4), ], vec![] )) @@ -275,21 +275,21 @@ fn if_else_complex() { // (Instruction::r#return(true), Span(146, 146)), // ], // vec![ -// Value::integer(0), -// Value::integer(1), -// Value::integer(0), -// Value::integer(2), -// Value::integer(1), -// Value::integer(0), -// Value::integer(3), -// Value::integer(3), -// Value::integer(4) +// ValueOwned::integer(0), +// ValueOwned::integer(1), +// ValueOwned::integer(0), +// ValueOwned::integer(2), +// ValueOwned::integer(1), +// ValueOwned::integer(0), +// ValueOwned::integer(3), +// ValueOwned::integer(3), +// ValueOwned::integer(4) // ], // vec![] // )) // ); -// assert_eq!(run(source), Ok(Some(Value::integer(4)))); +// assert_eq!(run(source), Ok(Some(ValueOwned::integer(4)))); // } #[test] @@ -316,12 +316,16 @@ fn if_else_false() { (Instruction::r#move(1, 0), Span(33, 33)), (Instruction::r#return(true), Span(33, 33)), ], - vec![Value::integer(1), Value::integer(2), Value::integer(42)], + vec![ + ValueOwned::integer(1), + ValueOwned::integer(2), + ValueOwned::integer(42) + ], vec![] )), ); - assert_eq!(run(source), Ok(Some(Value::integer(42)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(42)))); } #[test] @@ -348,12 +352,12 @@ fn if_else_true() { (Instruction::r#move(1, 0), Span(33, 33)), (Instruction::r#return(true), Span(33, 33)) ], - vec![Value::integer(1), Value::integer(42)], + vec![ValueOwned::integer(1), ValueOwned::integer(42)], vec![] )), ); - assert_eq!(run(source), Ok(Some(Value::integer(42)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(42)))); } #[test] @@ -378,7 +382,7 @@ fn if_false() { ), (Instruction::r#return(false), Span(21, 21)) ], - vec![Value::integer(1), Value::integer(2)], + vec![ValueOwned::integer(1), ValueOwned::integer(2)], vec![] )), ); @@ -408,7 +412,7 @@ fn if_true() { ), (Instruction::r#return(false), Span(21, 21)) ], - vec![Value::integer(1)], + vec![ValueOwned::integer(1)], vec![] )), ); diff --git a/dust-lang/tests/functions.rs b/dust-lang/tests/functions.rs index d32db21..cb07dfd 100644 --- a/dust-lang/tests/functions.rs +++ b/dust-lang/tests/functions.rs @@ -6,25 +6,18 @@ fn function() { assert_eq!( run(source), - Ok(Some(Value::function( - Chunk::with_data( - None, - vec![ - (Instruction::add(2, 0, 1), Span(30, 31)), - (Instruction::r#return(true), Span(35, 35)), - ], - vec![Value::string("a"), Value::string("b"),], - vec![ - Local::new(0, Type::Integer, false, Scope::default()), - Local::new(1, Type::Integer, false, Scope::default()) - ] - ), - FunctionType { - type_parameters: None, - value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]), - return_type: Box::new(Type::Integer), - } - ))) + Ok(Some(ValueOwned::function(Chunk::with_data( + None, + vec![ + (Instruction::add(2, 0, 1), Span(30, 31)), + (Instruction::r#return(true), Span(35, 35)), + ], + vec![ValueOwned::string("a"), ValueOwned::string("b"),], + vec![ + Local::new(0, Type::Integer, false, Scope::default()), + Local::new(1, Type::Integer, false, Scope::default()) + ] + )))) ); } @@ -44,33 +37,26 @@ fn function_call() { (Instruction::r#return(true), Span(41, 41)), ], vec![ - Value::function( - Chunk::with_data( - None, - vec![ - (Instruction::add(2, 0, 1), Span(30, 31)), - (Instruction::r#return(true), Span(35, 36)), - ], - vec![Value::string("a"), Value::string("b"),], - vec![ - Local::new(0, Type::Integer, false, Scope::default()), - Local::new(1, Type::Integer, false, Scope::default()) - ] - ), - FunctionType { - type_parameters: None, - value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]), - return_type: Box::new(Type::Integer), - } - ), - Value::integer(1), - Value::integer(2) + ValueOwned::function(Chunk::with_data( + None, + vec![ + (Instruction::add(2, 0, 1), Span(30, 31)), + (Instruction::r#return(true), Span(35, 36)), + ], + vec![ValueOwned::string("a"), ValueOwned::string("b"),], + vec![ + Local::new(0, Type::Integer, false, Scope::default()), + Local::new(1, Type::Integer, false, Scope::default()) + ] + )), + ValueOwned::integer(1), + ValueOwned::integer(2) ], vec![] )), ); - assert_eq!(run(source), Ok(Some(Value::integer(3)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(3)))); } #[test] @@ -87,26 +73,19 @@ fn function_declaration() { (Instruction::r#return(false), Span(40, 40)) ], vec![ - Value::string("add"), - Value::function( - Chunk::with_data( - None, - vec![ - (Instruction::add(2, 0, 1), Span(35, 36)), - (Instruction::r#return(true), Span(40, 40)), - ], - vec![Value::string("a"), Value::string("b")], - vec![ - Local::new(0, Type::Integer, false, Scope::default()), - Local::new(1, Type::Integer, false, Scope::default()) - ] - ), - FunctionType { - type_parameters: None, - value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]), - return_type: Box::new(Type::Integer), - }, - ) + ValueOwned::string("add"), + ValueOwned::function(Chunk::with_data( + None, + vec![ + (Instruction::add(2, 0, 1), Span(35, 36)), + (Instruction::r#return(true), Span(40, 40)), + ], + vec![ValueOwned::string("a"), ValueOwned::string("b")], + vec![ + Local::new(0, Type::Integer, false, Scope::default()), + Local::new(1, Type::Integer, false, Scope::default()) + ] + ),) ], vec![Local::new( 0, diff --git a/dust-lang/tests/lists.rs b/dust-lang/tests/lists.rs index 5141caf..dcb6a13 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::abstract_list(0, 0, Type::Any)))); + assert_eq!(run(source), Ok(Some(ValueOwned::list([])))); } #[test] @@ -35,14 +35,22 @@ 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![ + ValueOwned::integer(1), + ValueOwned::integer(2), + ValueOwned::integer(3) + ], vec![] )), ); assert_eq!( run(source), - Ok(Some(Value::abstract_list(0, 3, Type::Integer))) + Ok(Some(ValueOwned::list([ + ValueOwned::integer(1), + ValueOwned::integer(2), + ValueOwned::integer(3) + ]))) ); } @@ -74,11 +82,11 @@ fn list_with_complex_expression() { (Instruction::r#return(true), Span(18, 18)), ], vec![ - Value::integer(1), - Value::integer(2), - Value::integer(3), - Value::integer(4), - Value::integer(5) + ValueOwned::integer(1), + ValueOwned::integer(2), + ValueOwned::integer(3), + ValueOwned::integer(4), + ValueOwned::integer(5) ], vec![] )), @@ -86,7 +94,10 @@ fn list_with_complex_expression() { assert_eq!( run(source), - Ok(Some(Value::abstract_list(0, 4, Type::Integer))) + Ok(Some(ValueOwned::list([ + ValueOwned::integer(0), + ValueOwned::integer(4) + ]))) ); } @@ -111,10 +122,10 @@ fn list_with_simple_expression() { (Instruction::r#return(true), Span(13, 13)), ], vec![ - Value::integer(1), - Value::integer(2), - Value::integer(3), - Value::integer(4), + ValueOwned::integer(1), + ValueOwned::integer(2), + ValueOwned::integer(3), + ValueOwned::integer(4), ], vec![] )), @@ -122,6 +133,9 @@ fn list_with_simple_expression() { assert_eq!( run(source), - Ok(Some(Value::abstract_list(0, 3, Type::Integer))) + Ok(Some(ValueOwned::list([ + ValueOwned::integer(0), + ValueOwned::integer(3) + ]))) ); } diff --git a/dust-lang/tests/logic.rs b/dust-lang/tests/logic.rs index 0a418c6..3be25be 100644 --- a/dust-lang/tests/logic.rs +++ b/dust-lang/tests/logic.rs @@ -20,7 +20,7 @@ fn and() { )) ); - assert_eq!(run(source), Ok(Some(Value::boolean(false)))); + assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false)))); } #[test] @@ -43,7 +43,7 @@ fn or() { )) ); - assert_eq!(run(source), Ok(Some(Value::boolean(true)))); + assert_eq!(run(source), Ok(Some(ValueOwned::boolean(true)))); } #[test] @@ -65,7 +65,7 @@ fn variable_and() { (Instruction::get_local(3, 1), Span(34, 35)), (Instruction::r#return(true), Span(35, 35)), ], - vec![Value::string("a"), Value::string("b"),], + vec![ValueOwned::string("a"), ValueOwned::string("b"),], vec![ Local::new(0, Type::Boolean, false, Scope::default()), Local::new(1, Type::Boolean, false, Scope::default()), @@ -73,5 +73,5 @@ fn variable_and() { )) ); - assert_eq!(run(source), Ok(Some(Value::boolean(false)))); + assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false)))); } diff --git a/dust-lang/tests/loops.rs b/dust-lang/tests/loops.rs index df01134..c16f1d2 100644 --- a/dust-lang/tests/loops.rs +++ b/dust-lang/tests/loops.rs @@ -22,14 +22,14 @@ fn r#while() { (Instruction::r#return(true), Span(42, 42)), ], vec![ - Value::integer(0), - Value::string("x"), - Value::integer(5), - Value::integer(1), + ValueOwned::integer(0), + ValueOwned::string("x"), + ValueOwned::integer(5), + ValueOwned::integer(1), ], vec![Local::new(1, Type::Integer, true, Scope::default())] )), ); - assert_eq!(run(source), Ok(Some(Value::integer(5)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(5)))); } diff --git a/dust-lang/tests/math.rs b/dust-lang/tests/math.rs index a3297b8..02f2e3c 100644 --- a/dust-lang/tests/math.rs +++ b/dust-lang/tests/math.rs @@ -17,12 +17,12 @@ fn add() { ), (Instruction::r#return(true), Span(5, 5)) ], - vec![Value::integer(1), Value::integer(2)], + vec![ValueOwned::integer(1), ValueOwned::integer(2)], vec![] )) ); - assert_eq!(run(source), Ok(Some(Value::integer(3)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(3)))); } #[test] @@ -40,12 +40,16 @@ fn add_assign() { (Instruction::get_local(1, 0), Span(23, 24)), (Instruction::r#return(true), Span(24, 24)) ], - vec![Value::integer(1), Value::string("a"), Value::integer(2)], + vec![ + ValueOwned::integer(1), + ValueOwned::string("a"), + ValueOwned::integer(2) + ], vec![Local::new(1, Type::Integer, true, Scope::default())] )) ); - assert_eq!(run(source), Ok(Some(Value::integer(3)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(3)))); } #[test] @@ -97,12 +101,12 @@ fn divide() { ), (Instruction::r#return(true), Span(5, 5)) ], - vec![Value::integer(2)], + vec![ValueOwned::integer(2)], vec![] )) ); - assert_eq!(run(source), Ok(Some(Value::integer(1)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(1)))); } #[test] @@ -123,12 +127,12 @@ fn divide_assign() { (Instruction::get_local(1, 0), Span(23, 24)), (Instruction::r#return(true), Span(24, 24)) ], - vec![Value::integer(2), Value::string("a")], + vec![ValueOwned::integer(2), ValueOwned::string("a")], vec![Local::new(1, Type::Integer, true, Scope::default())] )) ); - assert_eq!(run(source), Ok(Some(Value::integer(1)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(1)))); } #[test] @@ -176,17 +180,17 @@ fn math_operator_precedence() { (Instruction::r#return(true), Span(17, 17)), ], vec![ - Value::integer(1), - Value::integer(2), - Value::integer(3), - Value::integer(4), - Value::integer(5), + ValueOwned::integer(1), + ValueOwned::integer(2), + ValueOwned::integer(3), + ValueOwned::integer(4), + ValueOwned::integer(5), ], vec![] )) ); - assert_eq!(run(source), Ok(Some(Value::integer(1)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(1)))); } #[test] @@ -206,12 +210,12 @@ fn multiply() { ), (Instruction::r#return(true), Span(5, 5)), ], - vec![Value::integer(1), Value::integer(2)], + vec![ValueOwned::integer(1), ValueOwned::integer(2)], vec![] )) ); - assert_eq!(run(source), Ok(Some(Value::integer(2)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(2)))); } #[test] @@ -232,12 +236,16 @@ fn multiply_assign() { (Instruction::get_local(1, 0), Span(22, 23)), (Instruction::r#return(true), Span(23, 23)) ], - vec![Value::integer(2), Value::string("a"), Value::integer(3)], + vec![ + ValueOwned::integer(2), + ValueOwned::string("a"), + ValueOwned::integer(3) + ], vec![Local::new(1, Type::Integer, true, Scope::default())] )) ); - assert_eq!(run(source), Ok(Some(Value::integer(6)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(6)))); } #[test] @@ -273,12 +281,12 @@ fn subtract() { ), (Instruction::r#return(true), Span(5, 5)), ], - vec![Value::integer(1), Value::integer(2)], + vec![ValueOwned::integer(1), ValueOwned::integer(2)], vec![] )) ); - assert_eq!(run(source), Ok(Some(Value::integer(-1)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(-1)))); } #[test] @@ -299,12 +307,16 @@ fn subtract_assign() { (Instruction::get_local(1, 0), Span(24, 25)), (Instruction::r#return(true), Span(25, 25)), ], - vec![Value::integer(42), Value::string("x"), Value::integer(2)], + vec![ + ValueOwned::integer(42), + ValueOwned::string("x"), + ValueOwned::integer(2) + ], vec![Local::new(1, Type::Integer, true, Scope::default())] )), ); - assert_eq!(run(source), Ok(Some(Value::integer(40)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(40)))); } #[test] diff --git a/dust-lang/tests/native_functions.rs b/dust-lang/tests/native_functions.rs index 5a742c7..aec31a4 100644 --- a/dust-lang/tests/native_functions.rs +++ b/dust-lang/tests/native_functions.rs @@ -17,7 +17,10 @@ fn panic() { ), (Instruction::r#return(true), Span(27, 27)) ], - vec![Value::string("Goodbye world!"), Value::integer(42)], + vec![ + ValueOwned::string("Goodbye world!"), + ValueOwned::integer(42) + ], vec![] )), ); @@ -50,10 +53,10 @@ fn to_string() { ), (Instruction::r#return(true), Span(13, 13)) ], - vec![Value::integer(42)], + vec![ValueOwned::integer(42)], vec![] )), ); - assert_eq!(run(source), Ok(Some(Value::string("42")))) + assert_eq!(run(source), Ok(Some(ValueOwned::string("42")))) } diff --git a/dust-lang/tests/scopes.rs b/dust-lang/tests/scopes.rs index 079e9f5..f9bdaa8 100644 --- a/dust-lang/tests/scopes.rs +++ b/dust-lang/tests/scopes.rs @@ -9,7 +9,7 @@ fn allow_access_to_parent_scope() { } "#; - assert_eq!(run(source), Ok(Some(Value::integer(1)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(1)))); } #[test] @@ -44,15 +44,15 @@ fn block_scope() { (Instruction::r#return(false), Span(165, 165)) ], vec![ - Value::integer(0), - Value::string("a"), - Value::integer(42), - Value::string("b"), - Value::integer(1), - Value::string("c"), - Value::integer(2), - Value::string("d"), - Value::string("e"), + ValueOwned::integer(0), + ValueOwned::string("a"), + ValueOwned::integer(42), + ValueOwned::string("b"), + ValueOwned::integer(1), + ValueOwned::string("c"), + ValueOwned::integer(2), + ValueOwned::string("d"), + ValueOwned::string("e"), ], vec![ Local::new(1, Type::Integer, false, Scope::new(0, 0)), @@ -115,16 +115,16 @@ fn multiple_block_scopes() { (Instruction::r#return(false), Span(307, 307)) ], vec![ - Value::integer(0), - Value::string("a"), - Value::integer(42), - Value::string("b"), - Value::integer(1), - Value::string("c"), - Value::integer(2), - Value::string("d"), - Value::string("q"), - Value::string("e"), + ValueOwned::integer(0), + ValueOwned::string("a"), + ValueOwned::integer(42), + ValueOwned::string("b"), + ValueOwned::integer(1), + ValueOwned::string("c"), + ValueOwned::integer(2), + ValueOwned::string("d"), + ValueOwned::string("q"), + ValueOwned::string("e"), ], vec![ Local::new(1, Type::Integer, false, Scope::new(0, 0)), diff --git a/dust-lang/tests/unary_operations.rs b/dust-lang/tests/unary_operations.rs index bf31068..e906356 100644 --- a/dust-lang/tests/unary_operations.rs +++ b/dust-lang/tests/unary_operations.rs @@ -12,12 +12,12 @@ fn negate() { (*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)), (Instruction::r#return(true), Span(5, 5)), ], - vec![Value::integer(42)], + vec![ValueOwned::integer(42)], vec![] )), ); - assert_eq!(run(source), Ok(Some(Value::integer(-42)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(-42)))); } #[test] @@ -38,5 +38,5 @@ fn not() { )), ); - assert_eq!(run(source), Ok(Some(Value::boolean(false)))); + assert_eq!(run(source), Ok(Some(ValueOwned::boolean(false)))); } diff --git a/dust-lang/tests/variables.rs b/dust-lang/tests/variables.rs index 2ee5a51..085209c 100644 --- a/dust-lang/tests/variables.rs +++ b/dust-lang/tests/variables.rs @@ -13,7 +13,7 @@ fn define_local() { (Instruction::define_local(0, 0, false), Span(4, 5)), (Instruction::r#return(false), Span(11, 11)) ], - vec![Value::integer(42), Value::string("x")], + vec![ValueOwned::integer(42), ValueOwned::string("x")], vec![Local::new(1, Type::Integer, false, Scope::default())] )), ); @@ -54,10 +54,14 @@ fn set_local() { (Instruction::get_local(2, 0), Span(24, 25)), (Instruction::r#return(true), Span(25, 25)), ], - vec![Value::integer(41), Value::string("x"), Value::integer(42)], + vec![ + ValueOwned::integer(41), + ValueOwned::string("x"), + ValueOwned::integer(42) + ], vec![Local::new(1, Type::Integer, true, Scope::default())] )), ); - assert_eq!(run(source), Ok(Some(Value::integer(42)))); + assert_eq!(run(source), Ok(Some(ValueOwned::integer(42)))); }