From f08c7c6f1f2272c2e0afeb8fdc738dfed456f209 Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 6 Nov 2024 03:36:46 -0500 Subject: [PATCH] Refine values to be either abstract or concrete --- dust-lang/src/chunk.rs | 84 ++++- dust-lang/src/instruction.rs | 22 +- dust-lang/src/lib.rs | 2 +- dust-lang/src/native_function.rs | 8 +- dust-lang/src/parser.rs | 79 ++--- dust-lang/src/value.rs | 529 +++++++++++++------------------ dust-lang/src/vm.rs | 178 ++++------- dust-lang/tests/lists.rs | 36 ++- 8 files changed, 423 insertions(+), 515 deletions(-) diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index a21b0f5..cc6ae09 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -7,7 +7,32 @@ //! # Disassembly //! //! Chunks can be disassembled into a human-readable format using the `disassemble` method. The -//! output is designed to be displayed in a terminal and is styled for readability. +//! output is designed to be displayed in a terminal and can be styled for better readability. +//! +//! ```text +//! ┌──────────────────────────────────────────────────────────────────────────────┐ +//! │ /var/home/jeff/Repositories/dust/target/debug/dust-shell │ +//! │ 3 instructions, 1 constants, 0 locals, returns none │ +//! │ Instructions │ +//! │ ------------ │ +//! │ INDEX BYTECODE OPERATION INFO TYPE POSITION │ +//! │ ----- -------- ------------- ------------------------- --------- ----------- │ +//! │ 0 00000003 LOAD_CONSTANT R0 = C0 str (11, 26) │ +//! │ 1 01390117 CALL_NATIVE write_line(R0) (0, 27) │ +//! │ 2 00000018 RETURN (27, 27) │ +//! │┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈│ +//! │ Locals │ +//! │ ------ │ +//! │ INDEX IDENTIFIER TYPE MUTABLE SCOPE REGISTER │ +//! │ ----- ---------- -------- ------- ------- -------- │ +//! │┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈│ +//! │ Constants │ +//! │ --------- │ +//! │ INDEX VALUE │ +//! │ ----- --------------- │ +//! │ 0 Hello, world! │ +//! └──────────────────────────────────────────────────────────────────────────────┘ +//! ``` use std::{ cmp::Ordering, @@ -18,7 +43,7 @@ use std::{ use colored::Colorize; use serde::{Deserialize, Serialize}; -use crate::{Instruction, Operation, Span, Type, Value}; +use crate::{ConcreteValue, Instruction, Operation, Span, Type, Value}; /// In-memory representation of a Dust program or function. /// @@ -26,6 +51,7 @@ use crate::{Instruction, Operation, Span, Type, Value}; #[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)] pub struct Chunk { name: Option, + pub is_poisoned: bool, instructions: Vec<(Instruction, Span)>, constants: Vec, @@ -39,6 +65,7 @@ impl Chunk { pub fn new(name: Option) -> Self { Self { name, + is_poisoned: false, instructions: Vec::new(), constants: Vec::new(), locals: Vec::new(), @@ -55,6 +82,7 @@ impl Chunk { ) -> Self { Self { name, + is_poisoned: false, instructions, constants, locals, @@ -214,13 +242,27 @@ impl Chunk { } pub fn return_type(&self) -> Option { - self.instructions.iter().rev().find_map(|(instruction, _)| { - if instruction.yields_value() { - instruction.yielded_type(self) - } else { - None - } - }) + let returns_value = self + .instructions() + .last() + .map(|(instruction, _)| { + debug_assert!(matches!(instruction.operation(), Operation::Return)); + + instruction.b_as_boolean() + }) + .unwrap_or(false); + + if returns_value { + self.instructions.iter().rev().find_map(|(instruction, _)| { + if instruction.yields_value() { + instruction.yielded_type(self) + } else { + None + } + }) + } else { + None + } } pub fn disassembler(&self) -> ChunkDisassembler { @@ -333,8 +375,12 @@ impl<'a> ChunkDisassembler<'a> { "----- -------- ------------- ------------------------- --------- -----------", ]; - const CONSTANT_HEADER: [&'static str; 4] = - ["Constants", "---------", "INDEX VALUE", "----- -----"]; + const CONSTANT_HEADER: [&'static str; 4] = [ + "Constants", + "---------", + "INDEX VALUE ", + "----- ---------------", + ]; const LOCAL_HEADER: [&'static str; 4] = [ "Locals", @@ -571,12 +617,21 @@ impl<'a> ChunkDisassembler<'a> { } for (index, value) in self.chunk.constants.iter().enumerate() { - let constant_display = format!("{index:<5} {value:<5}"); + let value_display = { + let value_string = value.to_string(); + + if value_string.len() > 15 { + format!("{value_string:.12}...") + } else { + value_string + } + }; + let constant_display = format!("{index:<5} {value_display:^15}"); self.push_details(&constant_display); if let Some(function_disassembly) = match value { - Value::Function(function) => Some({ + Value::Concrete(ConcreteValue::Function(function)) => Some({ function .chunk() .disassembler() @@ -584,8 +639,7 @@ impl<'a> ChunkDisassembler<'a> { .indent(self.indent + 1) .disassemble() }), - Value::Primitive(_) => None, - Value::Object(_) => None, + _ => None, } { self.output.push_str(&function_disassembly); } diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index 275d112..2bcef0d 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -4,7 +4,7 @@ //! - Bits 0-6: The operation code. //! - Bit 7: A flag indicating whether the B argument is a constant. //! - Bit 8: A flag indicating whether the C argument is a constant. -//! - Bits 9-16: The A argument. +//! - Bits 9-16: The A argument, //! - Bits 17-24: The B argument. //! - Bits 25-32: The C argument. //! @@ -49,8 +49,8 @@ impl Instruction { let mut instruction = Instruction(Operation::LoadBoolean as u32); instruction.set_a(to_register); - instruction.set_b(if value { 1 } else { 0 }); - instruction.set_c(if skip { 1 } else { 0 }); + instruction.set_b_to_boolean(value); + instruction.set_c_to_boolean(skip); instruction } @@ -65,12 +65,11 @@ impl Instruction { instruction } - pub fn load_list(to_register: u8, start_register: u8, end_register: u8) -> Instruction { + pub fn load_list(to_register: u8, start_register: u8) -> Instruction { let mut instruction = Instruction(Operation::LoadList as u32); instruction.set_a(to_register); instruction.set_b(start_register); - instruction.set_c(end_register); instruction } @@ -183,7 +182,7 @@ impl Instruction { pub fn equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction { let mut instruction = Instruction(Operation::Equal as u32); - instruction.set_a(if comparison_boolean { 1 } else { 0 }); + instruction.set_a_to_boolean(comparison_boolean); instruction.set_b(left_index); instruction.set_c(right_index); @@ -193,7 +192,7 @@ impl Instruction { pub fn less(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction { let mut instruction = Instruction(Operation::Less as u32); - instruction.set_a(if comparison_boolean { 1 } else { 0 }); + instruction.set_a_to_boolean(comparison_boolean); instruction.set_b(left_index); instruction.set_c(right_index); @@ -203,7 +202,7 @@ impl Instruction { pub fn less_equal(comparison_boolean: bool, left_index: u8, right_index: u8) -> Instruction { let mut instruction = Instruction(Operation::LessEqual as u32); - instruction.set_a(if comparison_boolean { 1 } else { 0 }); + instruction.set_a_to_boolean(comparison_boolean); instruction.set_b(left_index); instruction.set_c(right_index); @@ -265,7 +264,7 @@ impl Instruction { pub fn r#return(should_return_value: bool) -> Instruction { let mut instruction = Instruction(Operation::Return as u32); - instruction.set_b(if should_return_value { 1 } else { 0 }); + instruction.set_b_to_boolean(should_return_value); instruction } @@ -398,7 +397,7 @@ impl Instruction { chunk.get_register_type(self.b()) } } - Equal | Less | LessEqual | Test | Not | LoadBoolean => Some(Type::Boolean), + LoadBoolean | Not => Some(Type::Boolean), Negate => { if self.b_is_constant() { chunk.get_constant_type(self.b()) @@ -725,12 +724,11 @@ mod tests { #[test] fn load_list() { - let instruction = Instruction::load_list(0, 1, 2); + let instruction = Instruction::load_list(0, 1); assert_eq!(instruction.operation(), Operation::LoadList); assert_eq!(instruction.a(), 0); assert_eq!(instruction.b(), 1); - assert_eq!(instruction.c(), 2); } #[test] diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 97a7ccc..f439446 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -23,7 +23,7 @@ pub use crate::operation::Operation; pub use crate::parser::{parse, ParseError}; pub use crate::r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict}; pub use crate::token::{Token, TokenKind, TokenOwned}; -pub use crate::value::{Function, Primitive, Value, ValueError}; +pub use crate::value::{ConcreteValue, Function, Value, ValueError}; pub use crate::vm::{run, Vm, VmError}; use std::fmt::Display; diff --git a/dust-lang/src/native_function.rs b/dust-lang/src/native_function.rs index 9e09fe0..80d5052 100644 --- a/dust-lang/src/native_function.rs +++ b/dust-lang/src/native_function.rs @@ -10,7 +10,9 @@ use std::{ use serde::{Deserialize, Serialize}; -use crate::{AnnotatedError, FunctionType, Instruction, Primitive, Span, Type, Value, Vm, VmError}; +use crate::{ + AnnotatedError, ConcreteValue, FunctionType, Instruction, Span, Type, Value, Vm, VmError, +}; macro_rules! define_native_function { ($(($name:ident, $byte:literal, $str:expr, $type:expr)),*) => { @@ -259,7 +261,7 @@ impl NativeFunction { string.push_str(&argument.to_string()); } - Some(Value::Primitive(Primitive::String(string))) + Some(Value::Concrete(ConcreteValue::String(string))) } // I/O @@ -275,7 +277,7 @@ impl NativeFunction { buffer = buffer.trim_end_matches('\n').to_string(); - Some(Value::Primitive(Primitive::String(buffer))) + Some(Value::Concrete(ConcreteValue::String(buffer))) } NativeFunction::Write => { let to_register = instruction.a(); diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index 997d1cc..b13a0d0 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -810,41 +810,10 @@ impl<'src> Parser<'src> { fn parse_variable(&mut self, allowed: Allowed) -> Result<(), ParseError> { let start_position = self.current_position; + let identifier = if let Token::Identifier(text) = self.current_token { + self.advance()?; - let local_index = if let Token::Identifier(text) = self.current_token { - if let Ok(local_index) = self.get_local_index(text) { - local_index - } else if let Some(name) = self.chunk.name() { - if name.as_str() == text { - let register = self.next_register(); - let scope = self.chunk.current_scope(); - - self.emit_instruction(Instruction::load_self(register), start_position); - self.declare_local(text, None, false, scope, register); - - self.current_is_expression = true; - - return Ok(()); - } - - return if NativeFunction::from_str(text).is_some() { - self.parse_native_call(allowed) - } else { - Err(ParseError::UndeclaredVariable { - identifier: text.to_string(), - position: start_position, - }) - }; - } else { - return if NativeFunction::from_str(text).is_some() { - self.parse_native_call(allowed) - } else { - Err(ParseError::UndeclaredVariable { - identifier: text.to_string(), - position: start_position, - }) - }; - } + text } else { return Err(ParseError::ExpectedToken { expected: TokenKind::Identifier, @@ -852,8 +821,26 @@ impl<'src> Parser<'src> { position: start_position, }); }; + let local_index = if let Ok(local_index) = self.get_local_index(identifier) { + local_index + } else if let Some(native_function) = NativeFunction::from_str(identifier) { + 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.advance()?; + self.emit_instruction(Instruction::load_self(register), start_position); + self.declare_local(identifier, None, false, scope, register); + + self.current_is_expression = true; + + return Ok(()); + } else { + return Err(ParseError::UndeclaredVariable { + identifier: identifier.to_string(), + position: start_position, + }); + }; let (is_mutable, local_scope) = { let local = self.get_local(local_index)?; @@ -988,11 +975,10 @@ impl<'src> Parser<'src> { } let to_register = self.next_register(); - let end_register = to_register.saturating_sub(1); let end = self.current_position.1; self.emit_instruction( - Instruction::load_list(to_register, start_register, end_register), + Instruction::load_list(to_register, start_register), Span(start, end), ); @@ -1187,21 +1173,10 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_native_call(&mut self, _: Allowed) -> Result<(), ParseError> { - let native_function = if let Token::Identifier(text) = self.current_token { - NativeFunction::from_str(text).unwrap() - } else { - return Err(ParseError::ExpectedToken { - expected: TokenKind::Identifier, - found: self.current_token.to_owned(), - position: self.current_position, - }); - }; - let start = self.current_position.0; + fn parse_native_call(&mut self, function: NativeFunction) -> Result<(), ParseError> { + let start = self.previous_position.0; let start_register = self.next_register(); - self.advance()?; - self.expect(Token::LeftParenthesis)?; while !self.allow(Token::RightParenthesis)? { @@ -1224,10 +1199,10 @@ impl<'src> Parser<'src> { let end = self.previous_position.1; let to_register = self.next_register(); let argument_count = to_register - start_register; - self.current_is_expression = native_function.r#type().return_type.is_some(); + self.current_is_expression = function.r#type().return_type.is_some(); self.emit_instruction( - Instruction::call_native(to_register, native_function, argument_count), + Instruction::call_native(to_register, function, argument_count), Span(start, end), ); Ok(()) diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index cad4e11..e943346 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -26,53 +26,53 @@ use std::{ ops::{Range, RangeInclusive}, }; -use serde::{ - de::{self, Visitor}, - Deserialize, Deserializer, Serialize, Serializer, -}; +use serde::{Deserialize, Serialize}; use crate::{Chunk, FunctionType, RangeableType, Span, Type, Vm, VmError}; /// Dust value representation /// /// See the [module-level documentation][self] for more. -#[derive(Debug, Eq, PartialEq, PartialOrd, Ord)] +#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub enum Value { - Primitive(Primitive), - Function(Function), - Object(Object), + Concrete(ConcreteValue), + Abstract(AbstractValue), } impl Value { pub fn boolean(value: bool) -> Self { - Value::Primitive(Primitive::Boolean(value)) + Value::Concrete(ConcreteValue::Boolean(value)) } pub fn byte(value: u8) -> Self { - Value::Primitive(Primitive::Byte(value)) + Value::Concrete(ConcreteValue::Byte(value)) } pub fn character(value: char) -> Self { - Value::Primitive(Primitive::Character(value)) + Value::Concrete(ConcreteValue::Character(value)) } pub fn float(value: f64) -> Self { - Value::Primitive(Primitive::Float(value)) + Value::Concrete(ConcreteValue::Float(value)) } pub fn function(body: Chunk, r#type: FunctionType) -> Self { - Value::Function(Function { + Value::Concrete(ConcreteValue::Function(Function { chunk: body, r#type: Type::Function(r#type), - }) + })) } pub fn integer>(into_i64: T) -> Self { - Value::Primitive(Primitive::Integer(into_i64.into())) + Value::Concrete(ConcreteValue::Integer(into_i64.into())) } - pub fn list(start: u8, end: u8, item_type: Type) -> Self { - Value::Object(Object::List { + 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, @@ -80,11 +80,11 @@ impl Value { } pub fn string(to_string: T) -> Self { - Value::Primitive(Primitive::String(to_string.to_string())) + Value::Concrete(ConcreteValue::String(to_string.to_string())) } pub fn as_string(&self) -> Option<&String> { - if let Value::Primitive(Primitive::String(string)) = self { + if let Value::Concrete(ConcreteValue::String(string)) = self { Some(string) } else { None @@ -92,14 +92,13 @@ impl Value { } pub fn is_function(&self) -> bool { - matches!(self, Value::Function(_)) + matches!(self, Value::Concrete(ConcreteValue::Function(_))) } pub fn r#type(&self) -> Type { match self { - Value::Primitive(data) => data.r#type(), - Value::Function(function) => function.r#type().clone(), - Value::Object(Object::List { + Value::Concrete(data) => data.r#type(), + Value::Abstract(AbstractValue::List { start, end, item_type, @@ -116,67 +115,67 @@ impl Value { pub fn add(&self, other: &Value) -> Result { let (left, right) = match (self, other) { - (Value::Primitive(left), Value::Primitive(right)) => (left, right), + (Value::Concrete(left), Value::Concrete(right)) => (left, right), _ => return Err(ValueError::CannotAdd(self.clone(), other.clone())), }; let sum = left .add(right) .ok_or_else(|| ValueError::CannotAdd(self.clone(), other.clone()))?; - Ok(Value::Primitive(sum)) + Ok(Value::Concrete(sum)) } pub fn subtract(&self, other: &Value) -> Result { let (left, right) = match (self, other) { - (Value::Primitive(left), Value::Primitive(right)) => (left, right), + (Value::Concrete(left), Value::Concrete(right)) => (left, right), _ => return Err(ValueError::CannotSubtract(self.clone(), other.clone())), }; let difference = left .subtract(right) .ok_or_else(|| ValueError::CannotSubtract(self.clone(), other.clone()))?; - Ok(Value::Primitive(difference)) + Ok(Value::Concrete(difference)) } pub fn multiply(&self, other: &Value) -> Result { let (left, right) = match (self, other) { - (Value::Primitive(left), Value::Primitive(right)) => (left, right), + (Value::Concrete(left), Value::Concrete(right)) => (left, right), _ => return Err(ValueError::CannotMultiply(self.clone(), other.clone())), }; let product = left .multiply(right) .ok_or_else(|| ValueError::CannotMultiply(self.clone(), other.clone()))?; - Ok(Value::Primitive(product)) + Ok(Value::Concrete(product)) } pub fn divide(&self, other: &Value) -> Result { let (left, right) = match (self, other) { - (Value::Primitive(left), Value::Primitive(right)) => (left, right), + (Value::Concrete(left), Value::Concrete(right)) => (left, right), _ => return Err(ValueError::CannotDivide(self.clone(), other.clone())), }; let quotient = left .divide(right) .ok_or_else(|| ValueError::CannotDivide(self.clone(), other.clone()))?; - Ok(Value::Primitive(quotient)) + Ok(Value::Concrete(quotient)) } pub fn modulo(&self, other: &Value) -> Result { let (left, right) = match (self, other) { - (Value::Primitive(left), Value::Primitive(right)) => (left, right), + (Value::Concrete(left), Value::Concrete(right)) => (left, right), _ => return Err(ValueError::CannotModulo(self.clone(), other.clone())), }; let remainder = left .modulo(right) .ok_or_else(|| ValueError::CannotModulo(self.clone(), other.clone()))?; - Ok(Value::Primitive(remainder)) + Ok(Value::Concrete(remainder)) } pub fn less_than(&self, other: &Value) -> Result { let (left, right) = match (self, other) { - (Value::Primitive(left), Value::Primitive(right)) => (left, right), + (Value::Concrete(left), Value::Concrete(right)) => (left, right), _ => return Err(ValueError::CannotCompare(self.clone(), other.clone())), }; @@ -185,7 +184,7 @@ impl Value { pub fn less_than_or_equal(&self, other: &Value) -> Result { let (left, right) = match (self, other) { - (Value::Primitive(left), Value::Primitive(right)) => (left, right), + (Value::Concrete(left), Value::Concrete(right)) => (left, right), _ => return Err(ValueError::CannotCompare(self.clone(), other.clone())), }; @@ -194,7 +193,7 @@ impl Value { pub fn equal(&self, other: &Value) -> Result { let (left, right) = match (self, other) { - (Value::Primitive(left), Value::Primitive(right)) => (left, right), + (Value::Concrete(left), Value::Concrete(right)) => (left, right), _ => return Err(ValueError::CannotCompare(self.clone(), other.clone())), }; @@ -203,7 +202,7 @@ impl Value { pub fn not_equal(&self, other: &Value) -> Result { let (left, right) = match (self, other) { - (Value::Primitive(left), Value::Primitive(right)) => (left, right), + (Value::Concrete(left), Value::Concrete(right)) => (left, right), _ => return Err(ValueError::CannotCompare(self.clone(), other.clone())), }; @@ -212,42 +211,53 @@ impl Value { pub fn negate(&self) -> Result { let data = match self { - Value::Primitive(data) => data, + Value::Concrete(data) => data, _ => return Err(ValueError::CannotNot(self.clone())), }; data.negate() .ok_or_else(|| ValueError::CannotNot(self.clone())) - .map(Value::Primitive) + .map(Value::Concrete) } pub fn not(&self) -> Result { let data = match self { - Value::Primitive(data) => data, + Value::Concrete(data) => data, _ => return Err(ValueError::CannotNot(self.clone())), }; data.not() .ok_or_else(|| ValueError::CannotNot(self.clone())) - .map(Value::Primitive) + .map(Value::Concrete) } pub fn and(&self, other: &Value) -> Result { let (left, right) = match (self, other) { - (Value::Primitive(left), Value::Primitive(right)) => (left, right), + (Value::Concrete(left), Value::Concrete(right)) => (left, right), _ => return Err(ValueError::CannotAdd(self.clone(), other.clone())), }; left.and(right) .ok_or_else(|| ValueError::CannotAnd(self.clone(), other.clone())) - .map(Value::Primitive) + .map(Value::Concrete) } - pub fn display(&self, vm: &Vm, position: Span) -> Result { + pub fn to_concrete(self, vm: &mut Vm, position: Span) -> Result { match self { - Value::Primitive(primitive) => Ok(primitive.to_string()), - Value::Function(function) => Ok(function.to_string()), - Value::Object(object) => object.display(vm, position), + Value::Concrete(_) => Ok(self), + Value::Abstract(AbstractValue::List { start, end, .. }) => { + let mut items = Vec::new(); + + for register_index in start..end { + let get_value = vm.empty_register(register_index, position); + + if let Ok(value) = get_value { + items.push(value); + } + } + + Ok(Value::Concrete(ConcreteValue::List(items))) + } } } } @@ -305,9 +315,8 @@ impl Clone for Value { log::trace!("Cloning value {self}"); match self { - Value::Primitive(data) => Value::Primitive(data.clone()), - Value::Function(function) => Value::Function(function.clone()), - Value::Object(object) => Value::Object(object.clone()), + Value::Abstract(object) => Value::Abstract(object.clone()), + Value::Concrete(concrete) => Value::Concrete(concrete.clone()), } } } @@ -315,84 +324,43 @@ impl Clone for Value { impl Display for Value { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - Value::Primitive(primitive) => write!(f, "{primitive}"), - Value::Function(function) => write!(f, "{function}"), - Value::Object(object) => write!(f, "{object}"), + Value::Abstract(object) => write!(f, "{object}"), + Value::Concrete(concrete) => write!(f, "{concrete}"), } } } -impl Serialize for Value { - fn serialize(&self, serializer: S) -> Result { - match self { - Value::Primitive(data) => data.serialize(serializer), - Value::Function(function) => function.serialize(serializer), - Value::Object(object) => object.serialize(serializer), - } - } -} - -impl<'de> Deserialize<'de> for Value { - fn deserialize>(deserializer: D) -> Result { - struct ValueVisitor; - - impl<'de> Visitor<'de> for ValueVisitor { - type Value = Value; - - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - formatter.write_str("a value") - } - - fn visit_bool(self, value: bool) -> Result { - Ok(Value::Primitive(Primitive::Boolean(value))) - } - - fn visit_i64(self, value: i64) -> Result { - Ok(Value::Primitive(Primitive::Integer(value))) - } - - fn visit_u64(self, value: u64) -> Result { - Ok(Value::Primitive(Primitive::Integer(value as i64))) - } - - fn visit_f64(self, value: f64) -> Result { - Ok(Value::Primitive(Primitive::Float(value))) - } - - fn visit_str(self, value: &str) -> Result { - Ok(Value::Primitive(Primitive::String(value.to_string()))) - } - - fn visit_string(self, value: String) -> Result { - Ok(Value::Primitive(Primitive::String(value))) - } - } - - deserializer.deserialize_any(ValueVisitor) - } -} - #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub enum Primitive { +pub enum ConcreteValue { Boolean(bool), Byte(u8), Character(char), Float(f64), + Function(Function), Integer(i64), + List(Vec), Range(RangeValue), String(String), } -impl Primitive { +impl ConcreteValue { pub fn r#type(&self) -> Type { match self { - Primitive::Boolean(_) => Type::Boolean, - Primitive::Byte(_) => Type::Byte, - Primitive::Character(_) => Type::Character, - Primitive::Float(_) => Type::Float, - Primitive::Integer(_) => Type::Integer, - Primitive::Range(range) => range.r#type(), - Primitive::String(string) => Type::String { + 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::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::Range(range) => range.r#type(), + ConcreteValue::String(string) => Type::String { length: Some(string.len()), }, } @@ -401,194 +369,194 @@ impl Primitive { pub fn is_rangeable(&self) -> bool { matches!( self, - Primitive::Integer(_) - | Primitive::Float(_) - | Primitive::Character(_) - | Primitive::Byte(_) + ConcreteValue::Integer(_) + | ConcreteValue::Float(_) + | ConcreteValue::Character(_) + | ConcreteValue::Byte(_) ) } - pub fn add(&self, other: &Primitive) -> Option { + pub fn add(&self, other: &ConcreteValue) -> Option { match (self, other) { - (Primitive::Byte(left), Primitive::Byte(right)) => { - Some(Primitive::Byte(left.saturating_add(*right))) + (ConcreteValue::Byte(left), ConcreteValue::Byte(right)) => { + Some(ConcreteValue::Byte(left.saturating_add(*right))) } - (Primitive::Float(left), Primitive::Float(right)) => { - Some(Primitive::Float(left + right)) + (ConcreteValue::Float(left), ConcreteValue::Float(right)) => { + Some(ConcreteValue::Float(left + right)) } - (Primitive::Integer(left), Primitive::Integer(right)) => { - Some(Primitive::Integer(left.saturating_add(*right))) + (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { + Some(ConcreteValue::Integer(left.saturating_add(*right))) } - (Primitive::String(left), Primitive::String(right)) => { - Some(Primitive::String(format!("{}{}", left, right))) + (ConcreteValue::String(left), ConcreteValue::String(right)) => { + Some(ConcreteValue::String(format!("{}{}", left, right))) } _ => None, } } - pub fn subtract(&self, other: &Primitive) -> Option { + pub fn subtract(&self, other: &ConcreteValue) -> Option { match (self, other) { - (Primitive::Byte(left), Primitive::Byte(right)) => { - Some(Primitive::Byte(left.saturating_sub(*right))) + (ConcreteValue::Byte(left), ConcreteValue::Byte(right)) => { + Some(ConcreteValue::Byte(left.saturating_sub(*right))) } - (Primitive::Float(left), Primitive::Float(right)) => { - Some(Primitive::Float(left - right)) + (ConcreteValue::Float(left), ConcreteValue::Float(right)) => { + Some(ConcreteValue::Float(left - right)) } - (Primitive::Integer(left), Primitive::Integer(right)) => { - Some(Primitive::Integer(left.saturating_sub(*right))) + (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { + Some(ConcreteValue::Integer(left.saturating_sub(*right))) } _ => None, } } - pub fn multiply(&self, other: &Primitive) -> Option { + pub fn multiply(&self, other: &ConcreteValue) -> Option { match (self, other) { - (Primitive::Byte(left), Primitive::Byte(right)) => { - Some(Primitive::Byte(left.saturating_mul(*right))) + (ConcreteValue::Byte(left), ConcreteValue::Byte(right)) => { + Some(ConcreteValue::Byte(left.saturating_mul(*right))) } - (Primitive::Float(left), Primitive::Float(right)) => { - Some(Primitive::Float(left * right)) + (ConcreteValue::Float(left), ConcreteValue::Float(right)) => { + Some(ConcreteValue::Float(left * right)) } - (Primitive::Integer(left), Primitive::Integer(right)) => { - Some(Primitive::Integer(left.saturating_mul(*right))) + (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { + Some(ConcreteValue::Integer(left.saturating_mul(*right))) } _ => None, } } - pub fn divide(&self, other: &Primitive) -> Option { + pub fn divide(&self, other: &ConcreteValue) -> Option { match (self, other) { - (Primitive::Byte(left), Primitive::Byte(right)) => { - Some(Primitive::Byte(left.saturating_div(*right))) + (ConcreteValue::Byte(left), ConcreteValue::Byte(right)) => { + Some(ConcreteValue::Byte(left.saturating_div(*right))) } - (Primitive::Float(left), Primitive::Float(right)) => { - Some(Primitive::Float(left / right)) + (ConcreteValue::Float(left), ConcreteValue::Float(right)) => { + Some(ConcreteValue::Float(left / right)) } - (Primitive::Integer(left), Primitive::Integer(right)) => { - Some(Primitive::Integer(left.saturating_div(*right))) + (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { + Some(ConcreteValue::Integer(left.saturating_div(*right))) } _ => None, } } - pub fn modulo(&self, other: &Primitive) -> Option { + pub fn modulo(&self, other: &ConcreteValue) -> Option { match (self, other) { - (Primitive::Float(left), Primitive::Float(right)) => { - Some(Primitive::Float(left % right)) + (ConcreteValue::Float(left), ConcreteValue::Float(right)) => { + Some(ConcreteValue::Float(left % right)) } - (Primitive::Integer(left), Primitive::Integer(right)) => { - Some(Primitive::Integer(left % right)) + (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { + Some(ConcreteValue::Integer(left % right)) } _ => None, } } - pub fn less_than(&self, other: &Primitive) -> Option { + pub fn less_than(&self, other: &ConcreteValue) -> Option { match (self, other) { - (Primitive::Float(left), Primitive::Float(right)) => { - Some(Primitive::Boolean(left < right)) + (ConcreteValue::Float(left), ConcreteValue::Float(right)) => { + Some(ConcreteValue::Boolean(left < right)) } - (Primitive::Integer(left), Primitive::Integer(right)) => { - Some(Primitive::Boolean(left < right)) + (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { + Some(ConcreteValue::Boolean(left < right)) } _ => None, } } - pub fn less_than_or_equal(&self, other: &Primitive) -> Option { + pub fn less_than_or_equal(&self, other: &ConcreteValue) -> Option { match (self, other) { - (Primitive::Float(left), Primitive::Float(right)) => { - Some(Primitive::Boolean(left <= right)) + (ConcreteValue::Float(left), ConcreteValue::Float(right)) => { + Some(ConcreteValue::Boolean(left <= right)) } - (Primitive::Integer(left), Primitive::Integer(right)) => { - Some(Primitive::Boolean(left <= right)) + (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { + Some(ConcreteValue::Boolean(left <= right)) } _ => None, } } - pub fn greater_than(&self, other: &Primitive) -> Option { + pub fn greater_than(&self, other: &ConcreteValue) -> Option { match (self, other) { - (Primitive::Float(left), Primitive::Float(right)) => { - Some(Primitive::Boolean(left > right)) + (ConcreteValue::Float(left), ConcreteValue::Float(right)) => { + Some(ConcreteValue::Boolean(left > right)) } - (Primitive::Integer(left), Primitive::Integer(right)) => { - Some(Primitive::Boolean(left > right)) + (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { + Some(ConcreteValue::Boolean(left > right)) } _ => None, } } - pub fn greater_than_or_equal(&self, other: &Primitive) -> Option { + pub fn greater_than_or_equal(&self, other: &ConcreteValue) -> Option { match (self, other) { - (Primitive::Float(left), Primitive::Float(right)) => { - Some(Primitive::Boolean(left >= right)) + (ConcreteValue::Float(left), ConcreteValue::Float(right)) => { + Some(ConcreteValue::Boolean(left >= right)) } - (Primitive::Integer(left), Primitive::Integer(right)) => { - Some(Primitive::Boolean(left >= right)) + (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => { + Some(ConcreteValue::Boolean(left >= right)) } _ => None, } } - pub fn and(&self, other: &Primitive) -> Option { + pub fn and(&self, other: &ConcreteValue) -> Option { match (self, other) { - (Primitive::Boolean(left), Primitive::Boolean(right)) => { - Some(Primitive::Boolean(*left && *right)) + (ConcreteValue::Boolean(left), ConcreteValue::Boolean(right)) => { + Some(ConcreteValue::Boolean(*left && *right)) } _ => None, } } - pub fn or(&self, other: &Primitive) -> Option { + pub fn or(&self, other: &ConcreteValue) -> Option { match (self, other) { - (Primitive::Boolean(left), Primitive::Boolean(right)) => { - Some(Primitive::Boolean(*left || *right)) + (ConcreteValue::Boolean(left), ConcreteValue::Boolean(right)) => { + Some(ConcreteValue::Boolean(*left || *right)) } _ => None, } } - pub fn is_even(&self) -> Option { + pub fn is_even(&self) -> Option { match self { - Primitive::Integer(integer) => Some(Primitive::Boolean(integer % 2 == 0)), - Primitive::Float(float) => Some(Primitive::Boolean(float % 2.0 == 0.0)), + ConcreteValue::Integer(integer) => Some(ConcreteValue::Boolean(integer % 2 == 0)), + ConcreteValue::Float(float) => Some(ConcreteValue::Boolean(float % 2.0 == 0.0)), _ => None, } } - pub fn is_odd(&self) -> Option { + pub fn is_odd(&self) -> Option { match self { - Primitive::Integer(integer) => Some(Primitive::Boolean(integer % 2 != 0)), - Primitive::Float(float) => Some(Primitive::Boolean(float % 2.0 != 0.0)), + ConcreteValue::Integer(integer) => Some(ConcreteValue::Boolean(integer % 2 != 0)), + ConcreteValue::Float(float) => Some(ConcreteValue::Boolean(float % 2.0 != 0.0)), _ => None, } } - pub fn negate(&self) -> Option { + pub fn negate(&self) -> Option { match self { - Primitive::Byte(value) => Some(Primitive::Byte(!value)), - Primitive::Float(value) => Some(Primitive::Float(-value)), - Primitive::Integer(value) => Some(Primitive::Integer(-value)), + ConcreteValue::Byte(value) => Some(ConcreteValue::Byte(!value)), + ConcreteValue::Float(value) => Some(ConcreteValue::Float(-value)), + ConcreteValue::Integer(value) => Some(ConcreteValue::Integer(-value)), _ => None, } } - pub fn not(&self) -> Option { + pub fn not(&self) -> Option { match self { - Primitive::Boolean(value) => Some(Primitive::Boolean(!value)), + ConcreteValue::Boolean(value) => Some(ConcreteValue::Boolean(!value)), _ => None, } } } -impl Display for Primitive { +impl Display for ConcreteValue { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match self { - Primitive::Boolean(boolean) => write!(f, "{boolean}"), - Primitive::Byte(byte) => write!(f, "0x{byte:02x}"), - Primitive::Character(character) => write!(f, "{character}"), - Primitive::Float(float) => { + 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 { @@ -597,33 +565,49 @@ impl Display for Primitive { Ok(()) } - Primitive::Integer(integer) => write!(f, "{integer}"), - Primitive::Range(range_value) => { + 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}") } - Primitive::String(string) => write!(f, "{string}"), + ConcreteValue::String(string) => write!(f, "{string}"), } } } -impl Eq for Primitive {} +impl Eq for ConcreteValue {} -impl PartialOrd for Primitive { +impl PartialOrd for ConcreteValue { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } -impl Ord for Primitive { +impl Ord for ConcreteValue { fn cmp(&self, other: &Self) -> Ordering { match (self, other) { - (Primitive::Boolean(left), Primitive::Boolean(right)) => left.cmp(right), - (Primitive::Boolean(_), _) => Ordering::Greater, - (Primitive::Byte(left), Primitive::Byte(right)) => left.cmp(right), - (Primitive::Byte(_), _) => Ordering::Greater, - (Primitive::Character(left), Primitive::Character(right)) => left.cmp(right), - (Primitive::Character(_), _) => Ordering::Greater, - (Primitive::Float(left), Primitive::Float(right)) => { + (ConcreteValue::Boolean(left), ConcreteValue::Boolean(right)) => left.cmp(right), + (ConcreteValue::Boolean(_), _) => Ordering::Greater, + (ConcreteValue::Byte(left), ConcreteValue::Byte(right)) => left.cmp(right), + (ConcreteValue::Byte(_), _) => Ordering::Greater, + (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() { @@ -634,13 +618,17 @@ impl Ord for Primitive { left.partial_cmp(right).unwrap() } } - (Primitive::Float(_), _) => Ordering::Greater, - (Primitive::Integer(left), Primitive::Integer(right)) => left.cmp(right), - (Primitive::Integer(_), _) => Ordering::Greater, - (Primitive::Range(left), Primitive::Range(right)) => left.cmp(right), - (Primitive::Range(_), _) => Ordering::Greater, - (Primitive::String(left), Primitive::String(right)) => left.cmp(right), - (Primitive::String(_), _) => Ordering::Greater, + (ConcreteValue::Float(_), _) => Ordering::Greater, + (ConcreteValue::Function(left), ConcreteValue::Function(right)) => left.cmp(right), + (ConcreteValue::Function(_), _) => Ordering::Greater, + (ConcreteValue::Integer(left), ConcreteValue::Integer(right)) => left.cmp(right), + (ConcreteValue::Integer(_), _) => Ordering::Greater, + (ConcreteValue::List(left), ConcreteValue::List(right)) => left.cmp(right), + (ConcreteValue::List(_), _) => Ordering::Greater, + (ConcreteValue::Range(left), ConcreteValue::Range(right)) => left.cmp(right), + (ConcreteValue::Range(_), _) => Ordering::Greater, + (ConcreteValue::String(left), ConcreteValue::String(right)) => left.cmp(right), + (ConcreteValue::String(_), _) => Ordering::Greater, } } } @@ -885,48 +873,16 @@ impl Ord for RangeValue { } } +/// Value representation that can be resolved to a concrete value by the VM. #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] -pub enum Object { +pub enum AbstractValue { List { start: u8, end: u8, item_type: Type }, } -impl Object { - fn display(&self, vm: &Vm, position: Span) -> Result { - match self { - Object::List { start, end, .. } => { - let mut display = String::from("["); - let (start, end) = (*start, *end); - - for register in start..=end { - if register > start { - display.push_str(", "); - } - - let value_display = match vm.get_register(register, position) { - Ok(value) => value.display(vm, position)?, - Err(error) => { - return Err(ValueError::CannotDisplay { - value: Value::Object(self.clone()), - vm_error: Box::new(error), - }) - } - }; - - display.push_str(&value_display); - } - - display.push(']'); - - Ok(display) - } - } - } -} - -impl Display for Object { +impl Display for AbstractValue { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - Object::List { start, end, .. } => { + AbstractValue::List { start, end, .. } => { write!(f, "List [R{}..=R{}]", start, end) } } @@ -938,10 +894,6 @@ pub enum ValueError { CannotAdd(Value, Value), CannotAnd(Value, Value), CannotCompare(Value, Value), - CannotDisplay { - value: Value, - vm_error: Box, - }, CannotDivide(Value, Value), CannotModulo(Value, Value), CannotMultiply(Value, Value), @@ -953,85 +905,36 @@ pub enum ValueError { impl Display for ValueError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let get_value_display = |value: &Value| -> String { - match value { - Value::Primitive(primitive) => primitive.to_string(), - Value::Function(function) => function.to_string(), - Value::Object(_) => "Object".to_string(), - } - }; - match self { ValueError::CannotAdd(left, right) => { - let left_display = get_value_display(left); - let right_display = get_value_display(right); - - write!(f, "Cannot add {} and {}", left_display, right_display) + write!(f, "Cannot add {left} and {right}") } ValueError::CannotAnd(left, right) => { - let left_display = get_value_display(left); - let right_display = get_value_display(right); - - write!( - f, - "Cannot use logical and operation on {} and {}", - left_display, right_display - ) + write!(f, "Cannot use logical AND operation on {left} and {right}") } ValueError::CannotCompare(left, right) => { - let left_display = get_value_display(left); - let right_display = get_value_display(right); - - write!(f, "Cannot compare {} and {}", left_display, right_display) - } - ValueError::CannotDisplay { value, vm_error } => { - let value_display = get_value_display(value); - - write!(f, "Cannot display {}: {:?}", value_display, vm_error) + write!(f, "Cannot compare {left} and {right}") } ValueError::CannotDivide(left, right) => { - let left_display = get_value_display(left); - let right_display = get_value_display(right); - - write!(f, "Cannot divide {} by {}", left_display, right_display) + write!(f, "Cannot divide {left} by {right}") } ValueError::CannotModulo(left, right) => { - let left_display = get_value_display(left); - let right_display = get_value_display(right); - - write!(f, "Cannot modulo {} by {}", left_display, right_display) + write!(f, "Cannot use modulo operation on {left} and {right}") } ValueError::CannotMultiply(left, right) => { - let left_display = get_value_display(left); - let right_display = get_value_display(right); - - write!(f, "Cannot multiply {} by {}", left_display, right_display) + write!(f, "Cannot multiply {left} by {right}") } ValueError::CannotNegate(value) => { - let value_display = get_value_display(value); - - write!(f, "Cannot negate {}", value_display) + write!(f, "Cannot negate {value}") } ValueError::CannotNot(value) => { - let value_display = get_value_display(value); - - write!(f, "Cannot use logical not operation on {}", value_display) + write!(f, "Cannot use logical NOT operation on {value}") } ValueError::CannotSubtract(left, right) => { - let left_display = get_value_display(left); - let right_display = get_value_display(right); - - write!(f, "Cannot subtract {} from {}", right_display, left_display) + write!(f, "Cannot subtract {right} from {left}") } ValueError::CannotOr(left, right) => { - let left_display = get_value_display(left); - let right_display = get_value_display(right); - - write!( - f, - "Cannot use logical or operation on {} and {}", - left_display, right_display - ) + write!(f, "Cannot use logical OR operation on {left} and {right}") } } } diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index feeea2b..308a728 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -2,8 +2,8 @@ use std::{cmp::Ordering, mem::replace}; use crate::{ - parse, value::Primitive, AnnotatedError, Chunk, DustError, FunctionType, Instruction, Local, - NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError, + parse, value::ConcreteValue, AnnotatedError, Chunk, DustError, FunctionType, Instruction, + Local, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError, }; pub fn run(source: &str) -> Result, DustError> { @@ -121,23 +121,17 @@ impl Vm { } Operation::LoadList => { let to_register = instruction.a(); - let first_register = instruction.b(); - let last_register = instruction.c(); - let is_empty = to_register == first_register && first_register == last_register; - let item_type = if is_empty { - Type::Any - } else { - let register_range = (first_register as usize)..=(last_register as usize); - - self.stack[register_range] - .iter() - .find_map(|register| match register { - Register::Value(value) => Some(value.r#type()), - _ => None, - }) - .unwrap_or(Type::Any) - }; - let value = Value::list(first_register, last_register, item_type); + let start_register = instruction.b(); + let item_type = (start_register..to_register) + .find_map(|register_index| { + if let Ok(value) = self.get_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); self.set_register(to_register, value, position)?; } @@ -170,17 +164,6 @@ impl Vm { Operation::SetLocal => { let register = instruction.a(); let local_index = instruction.b(); - let local = self.get_local(local_index, position)?; - - if !local.is_mutable { - return Err(VmError::CannotMutateImmutableLocal { - identifier: self - .chunk - .get_identifier(local.identifier_index) - .unwrap_or_else(|| "unknown".to_string()), - position, - }); - } self.define_local(local_index, register, position)?; } @@ -228,7 +211,7 @@ impl Vm { let register = instruction.a(); let test_value = instruction.c_as_boolean(); let value = self.get_register(register, position)?; - let boolean = if let Value::Primitive(Primitive::Boolean(boolean)) = value { + let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value { *boolean } else { return Err(VmError::ExpectedBoolean { @@ -253,7 +236,7 @@ impl Vm { .equal(right) .map_err(|error| VmError::Value { error, position })?; let boolean = - if let Value::Primitive(Primitive::Boolean(boolean)) = equal_result { + if let Value::Concrete(ConcreteValue::Boolean(boolean)) = equal_result { boolean } else { return Err(VmError::ExpectedBoolean { @@ -288,15 +271,15 @@ impl Vm { let less_result = left .less_than(right) .map_err(|error| VmError::Value { error, position })?; - let boolean = if let Value::Primitive(Primitive::Boolean(boolean)) = less_result - { - boolean - } else { - return Err(VmError::ExpectedBoolean { - found: less_result.clone(), - 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(); if boolean == compare_to { @@ -324,7 +307,7 @@ impl Vm { let less_or_equal_result = left .less_than_or_equal(right) .map_err(|error| VmError::Value { error, position })?; - let boolean = if let Value::Primitive(Primitive::Boolean(boolean)) = + let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = less_or_equal_result { boolean @@ -400,7 +383,8 @@ impl Vm { let function_register = instruction.b(); let argument_count = instruction.c(); let value = self.get_register(function_register, position)?.clone(); - let function = if let Value::Function(function) = value { + let function = if let Value::Concrete(ConcreteValue::Function(function)) = value + { function } else { return Err(VmError::ExpectedFunction { @@ -448,7 +432,9 @@ impl Vm { } if let Some(register) = self.last_assigned_register { - let value = self.empty_register(register, position)?; + let value = self + .empty_register(register, position)? + .to_concrete(&mut self, position)?; return Ok(Some(value)); } else { @@ -623,7 +609,7 @@ impl Vm { } } - fn empty_register(mut self, index: u8, position: Span) -> Result { + pub fn empty_register(&mut self, index: u8, position: Span) -> Result { let index = index as usize; if index >= self.stack.len() { @@ -640,7 +626,12 @@ impl Vm { Ok(value) } Register::Constant(constant_index) => { - let value = self.chunk.take_constants().remove(constant_index as usize); + let constants = &mut self.chunk.constants_mut(); + + constants.push(Value::integer(0)); + + let value = constants.swap_remove(constant_index as usize); + self.chunk.is_poisoned = true; Ok(value) } @@ -649,6 +640,10 @@ impl Vm { } fn read(&mut self, position: Span) -> Result<&(Instruction, Span), VmError> { + if self.chunk.is_poisoned { + return Err(VmError::PoisonedChunk { position }); + } + if self.ip >= self.chunk.len() { self.ip = self.chunk.len() - 1; } @@ -721,59 +716,27 @@ enum Register { #[derive(Debug, Clone, PartialEq)] pub enum VmError { - CannotMutateImmutableLocal { - identifier: String, - position: Span, - }, - EmptyRegister { - index: usize, - position: Span, - }, - ExpectedBoolean { - found: Value, - position: Span, - }, - ExpectedFunction { - found: Value, - position: Span, - }, - ConstantIndexOutOfBounds { - index: usize, - position: Span, - }, - RegisterIndexOutOfBounds { - index: usize, - position: Span, - }, - InstructionIndexOutOfBounds { - index: usize, - position: Span, - }, - LocalIndexOutOfBounds { - index: usize, - position: Span, - }, - InvalidInstruction { - instruction: Instruction, - position: Span, - }, - StackOverflow { - position: Span, - }, - StackUnderflow { - position: Span, - }, - UndefinedVariable { - identifier: String, - position: Span, - }, + // Stack errors + StackOverflow { position: Span }, + StackUnderflow { position: Span }, + + // Register errors + EmptyRegister { index: usize, position: Span }, + RegisterIndexOutOfBounds { index: usize, position: Span }, + + // Chunk errors + ConstantIndexOutOfBounds { index: usize, position: Span }, + InstructionIndexOutOfBounds { index: usize, position: Span }, + LocalIndexOutOfBounds { index: usize, position: Span }, + PoisonedChunk { position: Span }, + + // Execution errors + ExpectedBoolean { found: Value, position: Span }, + ExpectedFunction { found: Value, position: Span }, // Wrappers for foreign errors NativeFunction(NativeFunctionError), - Value { - error: ValueError, - position: Span, - }, + Value { error: ValueError, position: Span }, } impl AnnotatedError for VmError { @@ -783,19 +746,17 @@ impl AnnotatedError for VmError { fn description(&self) -> &'static str { match self { - Self::CannotMutateImmutableLocal { .. } => "Cannot mutate immutable variable", Self::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds", Self::EmptyRegister { .. } => "Empty register", Self::ExpectedBoolean { .. } => "Expected boolean", Self::ExpectedFunction { .. } => "Expected function", - Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds", Self::InstructionIndexOutOfBounds { .. } => "Instruction index out of bounds", - Self::InvalidInstruction { .. } => "Invalid instruction", Self::LocalIndexOutOfBounds { .. } => "Local index out of bounds", + Self::NativeFunction(error) => error.description(), + Self::PoisonedChunk { .. } => "Poisoned chunk", + Self::RegisterIndexOutOfBounds { .. } => "Register index out of bounds", Self::StackOverflow { .. } => "Stack overflow", Self::StackUnderflow { .. } => "Stack underflow", - Self::UndefinedVariable { .. } => "Undefined variable", - Self::NativeFunction(error) => error.description(), Self::Value { .. } => "Value error", } } @@ -805,7 +766,7 @@ impl AnnotatedError for VmError { Self::ConstantIndexOutOfBounds { index, .. } => { Some(format!("Constant C{index} does not exist")) } - Self::EmptyRegister { index, .. } => Some(format!("Register {index} is empty")), + 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")) @@ -816,9 +777,6 @@ impl AnnotatedError for VmError { Self::LocalIndexOutOfBounds { index, .. } => { Some(format!("Local L{index} does not exist")) } - Self::UndefinedVariable { identifier, .. } => { - Some(format!("{identifier} is not in scope")) - } Self::NativeFunction(error) => error.details(), Self::Value { error, .. } => Some(error.to_string()), _ => None, @@ -827,19 +785,17 @@ impl AnnotatedError for VmError { fn position(&self) -> Span { match self { - Self::CannotMutateImmutableLocal { position, .. } => *position, Self::ConstantIndexOutOfBounds { position, .. } => *position, Self::EmptyRegister { position, .. } => *position, Self::ExpectedBoolean { position, .. } => *position, Self::ExpectedFunction { position, .. } => *position, - Self::RegisterIndexOutOfBounds { position, .. } => *position, Self::InstructionIndexOutOfBounds { position, .. } => *position, - Self::InvalidInstruction { position, .. } => *position, Self::LocalIndexOutOfBounds { position, .. } => *position, - Self::StackUnderflow { position } => *position, - Self::StackOverflow { position } => *position, - Self::UndefinedVariable { position, .. } => *position, Self::NativeFunction(error) => error.position(), + Self::PoisonedChunk { position } => *position, + Self::RegisterIndexOutOfBounds { position, .. } => *position, + Self::StackOverflow { position } => *position, + Self::StackUnderflow { position } => *position, Self::Value { position, .. } => *position, } } diff --git a/dust-lang/tests/lists.rs b/dust-lang/tests/lists.rs index 26b1f6a..87eb8f5 100644 --- a/dust-lang/tests/lists.rs +++ b/dust-lang/tests/lists.rs @@ -9,7 +9,7 @@ fn empty_list() { Ok(Chunk::with_data( None, vec![ - (Instruction::load_list(0, 0, 0), Span(0, 2)), + (Instruction::load_list(0, 0), Span(0, 2)), (Instruction::r#return(true), Span(2, 2)), ], vec![], @@ -17,7 +17,7 @@ fn empty_list() { )), ); - assert_eq!(run(source), Ok(Some(Value::list(0, 0, Type::Any)))); + assert_eq!(run(source), Ok(Some(Value::list(vec![])))); } #[test] @@ -32,7 +32,7 @@ fn list() { (Instruction::load_constant(0, 0, false), Span(1, 2)), (Instruction::load_constant(1, 1, false), Span(4, 5)), (Instruction::load_constant(2, 2, false), Span(7, 8)), - (Instruction::load_list(3, 0, 2), Span(0, 9)), + (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),], @@ -40,7 +40,14 @@ fn list() { )), ); - assert_eq!(run(source), Ok(Some(Value::list(0, 2, Type::Integer)))); + assert_eq!( + run(source), + Ok(Some(Value::list([ + Value::integer(1), + Value::integer(2), + Value::integer(3) + ]))) + ); } #[test] @@ -67,7 +74,7 @@ fn list_with_complex_expression() { ), (Instruction::subtract(3, 1, 2), Span(10, 11)), (Instruction::close(1, 3), Span(17, 18)), - (Instruction::load_list(4, 0, 3), Span(0, 18)), + (Instruction::load_list(4, 0), Span(0, 18)), (Instruction::r#return(true), Span(18, 18)), ], vec![ @@ -81,7 +88,13 @@ fn list_with_complex_expression() { )), ); - assert_eq!(run(source), Ok(Some(Value::list(0, 3, Type::Integer)))); + assert_eq!( + run(source), + Ok(Some(Value::list([ + Value::integer(1), + Value::integer(2 + 3 - 4 * 5) + ]))) + ); } #[test] @@ -101,7 +114,7 @@ fn list_with_simple_expression() { Span(6, 7) ), (Instruction::load_constant(2, 3, false), Span(11, 12)), - (Instruction::load_list(3, 0, 2), Span(0, 13)), + (Instruction::load_list(3, 0), Span(0, 13)), (Instruction::r#return(true), Span(13, 13)), ], vec![ @@ -114,5 +127,12 @@ fn list_with_simple_expression() { )), ); - assert_eq!(run(source), Ok(Some(Value::list(0, 2, Type::Integer)))); + assert_eq!( + run(source), + Ok(Some(Value::list([ + Value::integer(1), + Value::integer(2 + 3), + Value::integer(4) + ]))) + ); }