diff --git a/dust-lang/src/chunk/disassembler.rs b/dust-lang/src/chunk/disassembler.rs index ac209cd..9a5502c 100644 --- a/dust-lang/src/chunk/disassembler.rs +++ b/dust-lang/src/chunk/disassembler.rs @@ -43,7 +43,7 @@ use std::io::{self, Write}; use colored::{ColoredString, Colorize}; -use crate::{Chunk, Local}; +use crate::{Chunk, Local, Type}; const INSTRUCTION_COLUMNS: [(&str, usize); 4] = [("i", 5), ("POSITION", 12), ("OPERATION", 17), ("INFO", 41)]; @@ -66,11 +66,11 @@ const LOCAL_BORDERS: [&str; 3] = [ "╰─────┴────────────────┴──────────┴───────┴───────╯", ]; -const CONSTANT_COLUMNS: [(&str, usize); 3] = [("i", 5), ("TYPE", 26), ("VALUE", 26)]; +const CONSTANT_COLUMNS: [(&str, usize); 3] = [("i", 11), ("TYPE", 26), ("VALUE", 26)]; const CONSTANT_BORDERS: [&str; 3] = [ - "╭─────┬──────────────────────────┬──────────────────────────╮", - "├─────┼──────────────────────────┼──────────────────────────┤", - "╰─────┴──────────────────────────┴──────────────────────────╯", + "╭───────────┬──────────────────────────┬──────────────────────────╮", + "├───────────┼──────────────────────────┼──────────────────────────┤", + "╰───────────┴──────────────────────────┴──────────────────────────╯", ]; const INDENTATION: &str = "│ "; @@ -314,6 +314,7 @@ impl<'a, W: Write> Disassembler<'a, W> { Local { identifier_index, register_index, + r#type, scope, is_mutable, }, @@ -321,7 +322,7 @@ impl<'a, W: Write> Disassembler<'a, W> { { let identifier_display = self .chunk - .constants + .constant_strings .get(*identifier_index as usize) .map(|value| value.to_string()) .unwrap_or_else(|| "unknown".to_string()); @@ -352,8 +353,9 @@ impl<'a, W: Write> Disassembler<'a, W> { self.write_center_border(&column_name_line)?; self.write_center_border(CONSTANT_BORDERS[1])?; - for (index, value) in self.chunk.constants.iter().enumerate() { - let type_display = value.r#type().to_string(); + for (index, value) in self.chunk.constant_characters.iter().enumerate() { + let index_display = format!("C_CHAR_{index}"); + let type_display = Type::Character.to_string(); let value_display = { let mut value_string = value.to_string(); @@ -363,7 +365,46 @@ impl<'a, W: Write> Disassembler<'a, W> { value_string }; - let constant_display = format!("│{index:^5}│{type_display:^26}│{value_display:^26}│"); + let constant_display = + format!("│{index_display:^11}│{type_display:^26}│{value_display:^26}│"); + + self.write_center_border(&constant_display)?; + } + + for (index, value) in self.chunk.constant_floats.iter().enumerate() { + let index_display = format!("C_FLOAT_{index}"); + let type_display = Type::Float.to_string(); + let value_display = value.to_string(); + let constant_display = + format!("│{index_display:^11}│{type_display:^26}│{value_display:^26}│"); + + self.write_center_border(&constant_display)?; + } + + for (index, value) in self.chunk.constant_integers.iter().enumerate() { + let index_display = format!("C_INT_{index}"); + let type_display = Type::Integer.to_string(); + let value_display = value.to_string(); + let constant_display = + format!("│{index_display:^11}│{type_display:^26}│{value_display:^26}│"); + + self.write_center_border(&constant_display)?; + } + + for (index, value) in self.chunk.constant_strings.iter().enumerate() { + let index_display = format!("C_STR_{index}"); + let type_display = Type::String.to_string(); + let value_display = { + let mut value_string = value.to_string(); + + if value_string.len() > 26 { + value_string = format!("{value_string:.23}..."); + } + + value_string + }; + let constant_display = + format!("│{index_display:^11}│{type_display:^26}│{value_display:^26}│"); self.write_center_border(&constant_display)?; } @@ -415,7 +456,10 @@ impl<'a, W: Write> Disassembler<'a, W> { let info_line = format!( "{} instructions, {} constants, {} locals, returns {}", self.chunk.instructions.len(), - self.chunk.constants.len(), + self.chunk.constant_characters.len() + + self.chunk.constant_floats.len() + + self.chunk.constant_integers.len() + + self.chunk.constant_strings.len(), self.chunk.locals.len(), self.chunk.r#type.return_type ); @@ -431,7 +475,11 @@ impl<'a, W: Write> Disassembler<'a, W> { self.write_local_section()?; } - if !self.chunk.constants.is_empty() { + if !(self.chunk.constant_characters.is_empty() + && self.chunk.constant_floats.is_empty() + && self.chunk.constant_integers.is_empty() + && self.chunk.constant_strings.is_empty()) + { self.write_constant_section()?; } diff --git a/dust-lang/src/chunk/local.rs b/dust-lang/src/chunk/local.rs index 30cf47b..01c1330 100644 --- a/dust-lang/src/chunk/local.rs +++ b/dust-lang/src/chunk/local.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; -use crate::Scope; +use crate::{Scope, Type}; /// Scoped variable. #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] @@ -13,6 +13,9 @@ pub struct Local { /// Index of the register where the variable's value is stored. pub register_index: u16, + /// The variable's type. + pub r#type: Type, + /// Whether the local is mutable. pub is_mutable: bool, @@ -22,10 +25,17 @@ pub struct Local { impl Local { /// Creates a new Local instance. - pub fn new(identifier_index: u16, register_index: u16, is_mutable: bool, scope: Scope) -> Self { + pub fn new( + identifier_index: u16, + register_index: u16, + r#type: Type, + is_mutable: bool, + scope: Scope, + ) -> Self { Self { identifier_index, register_index, + r#type, is_mutable, scope, } diff --git a/dust-lang/src/chunk/mod.rs b/dust-lang/src/chunk/mod.rs index d4fde8c..09237bd 100644 --- a/dust-lang/src/chunk/mod.rs +++ b/dust-lang/src/chunk/mod.rs @@ -49,6 +49,8 @@ pub struct Chunk { pub(crate) prototypes: Vec>, + pub(crate) argument_lists: Vec>, + pub(crate) register_count: usize, pub(crate) prototype_index: u16, } diff --git a/dust-lang/src/compiler/mod.rs b/dust-lang/src/compiler/mod.rs index 81a7c6c..195ab70 100644 --- a/dust-lang/src/compiler/mod.rs +++ b/dust-lang/src/compiler/mod.rs @@ -33,9 +33,9 @@ use std::{mem::replace, sync::Arc}; use optimize::control_flow_register_consolidation; use crate::{ - Chunk, ConcreteValue, DustError, DustString, FunctionType, Instruction, Lexer, Local, - NativeFunction, Operand, Operation, Scope, Span, Token, TokenKind, Type, Value, - instruction::{CallNative, Close, GetLocal, Jump, LoadList, Return, SetLocal, TypeCode}, + Chunk, DustError, DustString, FunctionType, Instruction, Lexer, Local, NativeFunction, Operand, + Operation, Scope, Span, Token, TokenKind, Type, + instruction::{CallNative, Jump, Return, TypeCode}, }; /// Compiles the input and returns a chunk. @@ -86,25 +86,41 @@ pub struct Compiler<'src> { /// The types are discarded after compilation. instructions: Vec<(Instruction, Type, Span)>, - /// Constants that have been compiled. These are assigned to the chunk when [`Compiler::finish`] - /// is called. - constants: Vec, + /// Character constants that have been compiled. These are assigned to the chunk when + /// [`Compiler::finish`] is called. + constant_characters: Vec, + + /// Float constants that have been compiled. These are assigned to the chunk when + /// [`Compiler::finish`] is called. + constant_floats: Vec, + + /// Integer constants that have been compiled. These are assigned to the chunk when + /// [`Compiler::finish`] is called. + constant_integers: Vec, + + /// String constants that have been compiled. These are assigned to the chunk when + /// [`Compiler::finish`] is called. + constant_strings: Vec, /// Block-local variables and their types. The locals are assigned to the chunk when /// [`Compiler::finish`] is called. The types are discarded after compilation. - locals: Vec<(Local, Type)>, + locals: Vec, /// Prototypes that have been compiled. These are assigned to the chunk when /// [`Compiler::finish`] is called. prototypes: Vec>, + /// Argument lists for function calls that have been compiled. These are assigned to the chunk + /// when [`Compiler::finish`] is called. + argument_lists: Vec>, + /// Maximum stack size required by the chunk. This is assigned to the chunk when /// [`Compiler::finish`] is called. stack_size: usize, - /// The first register index that the compiler should use. This is used to avoid reusing the - /// registers that are used for the function's arguments. - minimum_register: u16, + /// Register usage tracker. This is used to determine the next available register of each + /// type. + register_usage: RegisterUsage, /// Index of the current block. This is used to determine the scope of locals and is incremented /// when a new block is entered. @@ -144,12 +160,16 @@ impl<'src> Compiler<'src> { return_type: Type::None, }, instructions: Vec::new(), - constants: Vec::new(), + constant_characters: Vec::new(), + constant_floats: Vec::new(), + constant_integers: Vec::new(), + constant_strings: Vec::new(), locals: Vec::new(), prototypes: Vec::new(), + argument_lists: Vec::new(), stack_size: 0, lexer, - minimum_register: 0, + register_usage: RegisterUsage::default(), block_index: 0, current_scope: Scope::default(), prototype_index: 0, @@ -204,20 +224,19 @@ impl<'src> Compiler<'src> { .into_iter() .map(|(instruction, _, position)| (instruction, position)) .unzip(); - let locals = self - .locals - .into_iter() - .map(|(local, _)| local) - .collect::>(); Chunk { name: self.function_name, r#type: self.r#type, instructions, positions, - constants: self.constants.to_vec(), - locals, + constant_floats: self.constant_floats, + constant_integers: self.constant_integers, + constant_strings: self.constant_strings, + constant_characters: self.constant_characters, + locals: self.locals, prototypes: self.prototypes, + argument_lists: self.argument_lists, register_count: self.stack_size, prototype_index: self.prototype_index, } @@ -227,20 +246,6 @@ impl<'src> Compiler<'src> { matches!(self.current_token, Token::Eof) } - fn next_register(&self) -> u16 { - self.instructions - .iter() - .rev() - .find_map(|(instruction, _, _)| { - if instruction.yields_value() { - Some(instruction.a_field() + 1) - } else { - None - } - }) - .unwrap_or(self.minimum_register) - } - fn advance(&mut self) -> Result<(), CompileError> { if self.is_eof() { return Ok(()); @@ -260,28 +265,13 @@ impl<'src> Compiler<'src> { Ok(()) } - fn get_local(&self, index: u16) -> Result<&(Local, Type), CompileError> { - self.locals - .get(index as usize) - .ok_or(CompileError::UndeclaredVariable { - identifier: format!("#{}", index), - position: self.current_position, - }) - } - fn get_local_index(&self, identifier_text: &str) -> Result { self.locals .iter() .enumerate() .rev() - .find_map(|(index, (local, _))| { - let constant = self.constants.get(local.identifier_index as usize)?; - let identifier = - if let Value::Concrete(ConcreteValue::String(identifier)) = constant { - identifier - } else { - return None; - }; + .find_map(|(index, local)| { + let identifier = self.constant_strings.get(local.identifier_index as usize)?; if identifier == identifier_text { Some(index as u16) @@ -305,39 +295,39 @@ impl<'src> Compiler<'src> { ) -> (u16, u16) { info!("Declaring local {identifier}"); - let identifier = Value::Concrete(ConcreteValue::string(identifier)); - let identifier_index = self.push_or_get_constant(identifier); + let identifier_index = self.push_or_get_constant_string(identifier); let local_index = self.locals.len() as u16; - self.locals.push(( - Local::new(identifier_index, register_index, is_mutable, scope), + self.locals.push(Local::new( + identifier_index, + register_index, r#type, + is_mutable, + scope, )); (local_index, identifier_index) } - fn get_identifier(&self, local_index: u16) -> Option { - self.locals - .get(local_index as usize) - .and_then(|(local, _)| { - self.constants - .get(local.identifier_index as usize) - .map(|value| value.to_string()) - }) + fn get_identifier_for_local(&self, local_index: u16) -> Option { + self.locals.get(local_index as usize).and_then(|local| { + self.constant_strings + .get(local.identifier_index as usize) + .map(|value| value.to_string()) + }) } - fn push_or_get_constant(&mut self, value: Value) -> u16 { + fn push_or_get_constant_string(&mut self, string: &str) -> u16 { if let Some(index) = self - .constants + .constant_strings .iter() - .position(|constant| constant == &value) + .position(|constant| constant == &string) { index as u16 } else { - let index = self.constants.len() as u16; + let index = self.constant_strings.len() as u16; - self.constants.push(value); + self.constant_strings.push(DustString::from(string)); index } @@ -393,44 +383,6 @@ impl<'src> Compiler<'src> { .unwrap_or(Type::None) } - fn get_register_type(&self, register_index: u16) -> Result { - if let Some((_, r#type)) = self - .locals - .iter() - .find(|(local, _)| local.register_index == register_index) - { - return Ok(r#type.clone()); - } - - for (instruction, r#type, _) in &self.instructions { - if !instruction.yields_value() { - continue; - } - - let operation = instruction.operation(); - - if let Operation::LOAD_LIST = operation { - let LoadList { start_register, .. } = LoadList::from(*instruction); - let item_type = self.get_register_type(start_register)?; - - return Ok(Type::List(Box::new(item_type))); - } - - if let Operation::LOAD_SELF = operation { - return Ok(Type::SelfFunction); - } - - if instruction.yields_value() { - return Ok(r#type.clone()); - } - } - - Err(CompileError::CannotResolveRegisterType { - register_index: register_index as usize, - position: self.current_position, - }) - } - /// Updates [`Self::type`] with the given [Type] as `return_type`. /// /// If [`Self::type`] is already set, it will check if the given [Type] is compatible. @@ -466,17 +418,62 @@ impl<'src> Compiler<'src> { self.instructions.push((instruction, r#type, position)); } - fn emit_constant( + fn emit_constant_character( &mut self, - constant: ConcreteValue, + character: char, position: Span, ) -> Result<(), CompileError> { - let r#type = constant.r#type(); - let constant_index = self.push_or_get_constant(Value::Concrete(constant)); - let destination = self.next_register(); + let constant_index = self.constant_characters.len() as u16; + + self.constant_characters.push(character); + + let destination = self.register_usage.next_character_register(); let load_constant = Instruction::load_constant(destination, constant_index, false); - self.emit_instruction(load_constant, r#type, position); + self.emit_instruction(load_constant, Type::Character, position); + + Ok(()) + } + + fn emit_constant_float(&mut self, float: f64, position: Span) -> Result<(), CompileError> { + let constant_index = self.constant_floats.len() as u16; + + self.constant_floats.push(float); + + let destination = self.register_usage.next_float_register(); + let load_constant = Instruction::load_constant(destination, constant_index, false); + + self.emit_instruction(load_constant, Type::Float, position); + + Ok(()) + } + + fn emit_constant_integer(&mut self, integer: i64, position: Span) -> Result<(), CompileError> { + let constant_index = self.constant_integers.len() as u16; + + self.constant_integers.push(integer); + + let destination = self.register_usage.next_integer_register(); + let load_constant = Instruction::load_constant(destination, constant_index, false); + + self.emit_instruction(load_constant, Type::Integer, position); + + Ok(()) + } + + fn emit_constant_string( + &mut self, + string: DustString, + position: Span, + ) -> Result<(), CompileError> { + let constant_index = self.constant_strings.len() as u16; + + self.constant_strings.push(string); + + let destination = self.register_usage.next_string_register(); + let load_constant = Instruction::load_constant(destination, constant_index, false); + + self.emit_instruction(load_constant, Type::String, position); Ok(()) } @@ -488,10 +485,12 @@ impl<'src> Compiler<'src> { self.advance()?; let boolean = text.parse::().unwrap(); - let destination = self.next_register(); - let load_boolean = Instruction::load_boolean(destination, boolean, false); + let boolean_encoded = boolean as u16; + let destination = self.register_usage.next_boolean_register(); + let load_encoded = + Instruction::load_encoded(destination, boolean_encoded, TypeCode::BOOLEAN, false); - self.emit_instruction(load_boolean, Type::Boolean, position); + self.emit_instruction(load_encoded, Type::Boolean, position); Ok(()) } else { @@ -511,9 +510,11 @@ impl<'src> Compiler<'src> { let byte = u8::from_str_radix(&text[2..], 16) .map_err(|error| CompileError::ParseIntError { error, position })?; - let value = ConcreteValue::Byte(byte); + let destination = self.register_usage.next_byte_register(); + let load_encoded = + Instruction::load_encoded(destination, byte as u16, TypeCode::BYTE, true); - self.emit_constant(value, position)?; + self.emit_instruction(load_encoded, Type::Byte, position); Ok(()) } else { @@ -531,9 +532,14 @@ impl<'src> Compiler<'src> { if let Token::Character(character) = self.current_token { self.advance()?; - let value = ConcreteValue::Character(character); + let constant_index = self.constant_characters.len() as u16; - self.emit_constant(value, position)?; + self.constant_characters.push(character); + + let destination = self.register_usage.next_character_register(); + let load_constant = Instruction::load_constant(destination, constant_index, false); + + self.emit_instruction(load_constant, Type::Character, position); Ok(()) } else { @@ -557,9 +563,14 @@ impl<'src> Compiler<'src> { error, position: self.previous_position, })?; - let value = ConcreteValue::Float(float); + let constant_index = self.constant_floats.len() as u16; - self.emit_constant(value, position)?; + self.constant_floats.push(float); + + let destination = self.register_usage.next_float_register(); + let load_constant = Instruction::load_constant(destination, constant_index, false); + + self.emit_instruction(load_constant, Type::Float, position); Ok(()) } else { @@ -577,7 +588,7 @@ impl<'src> Compiler<'src> { if let Token::Integer(text) = self.current_token { self.advance()?; - let mut integer_value = 0_i64; + let mut integer = 0_i64; for digit in text.chars() { let digit = if let Some(digit) = digit.to_digit(10) { @@ -586,12 +597,17 @@ impl<'src> Compiler<'src> { continue; }; - integer_value = integer_value * 10 + digit; + integer = integer * 10 + digit; } - let value = ConcreteValue::Integer(integer_value); + let constant_index = self.constant_integers.len() as u16; - self.emit_constant(value, position)?; + self.constant_integers.push(integer); + + let destination = self.register_usage.next_integer_register(); + let load_constant = Instruction::load_constant(destination, constant_index, false); + + self.emit_instruction(load_constant, Type::Integer, position); Ok(()) } else { @@ -609,9 +625,14 @@ impl<'src> Compiler<'src> { if let Token::String(text) = self.current_token { self.advance()?; - let value = ConcreteValue::string(text); + let constant_index = self.constant_strings.len() as u16; - self.emit_constant(value, position)?; + self.constant_strings.push(DustString::from(text)); + + let destination = self.register_usage.next_string_register(); + let load_constant = Instruction::load_constant(destination, constant_index, false); + + self.emit_instruction(load_constant, Type::String, position); Ok(()) } else { @@ -650,14 +671,22 @@ impl<'src> Compiler<'src> { )) } - let destination = self.next_register(); - let type_code = match previous_type { - Type::Boolean => TypeCode::BOOLEAN, - Type::Byte => TypeCode::BYTE, - Type::Character => TypeCode::CHARACTER, - Type::Float => TypeCode::FLOAT, - Type::Integer => TypeCode::INTEGER, - Type::String => TypeCode::STRING, + let (destination, type_code) = match previous_type { + Type::Boolean => ( + self.register_usage.next_boolean_register(), + TypeCode::BOOLEAN, + ), + Type::Byte => (self.register_usage.next_byte_register(), TypeCode::BYTE), + Type::Character => ( + self.register_usage.next_character_register(), + TypeCode::CHARACTER, + ), + Type::Float => (self.register_usage.next_float_register(), TypeCode::FLOAT), + Type::Integer => ( + self.register_usage.next_integer_register(), + TypeCode::INTEGER, + ), + Type::String => (self.register_usage.next_string_register(), TypeCode::STRING), _ => match operator { Token::Minus => { return Err(CompileError::CannotNegateType { @@ -697,13 +726,8 @@ impl<'src> Compiler<'src> { ) -> Result<(Operand, bool), CompileError> { let (argument, push_back) = match instruction.operation() { Operation::LOAD_CONSTANT => (Operand::Constant(instruction.b_field()), false), - Operation::GET_LOCAL => { - let local_index = instruction.b_field(); - let (local, _) = self.get_local(local_index)?; - - (Operand::Register(local.register_index), false) - } - Operation::LOAD_ENCODED + Operation::POINT + | Operation::LOAD_ENCODED | Operation::LOAD_LIST | Operation::LOAD_SELF | Operation::ADD @@ -749,16 +773,12 @@ impl<'src> Compiler<'src> { position: self.previous_position, })?; let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?; - let left_is_mutable_local = if let Operation::GET_LOCAL = left_instruction.operation() { - let GetLocal { local_index, .. } = GetLocal::from(left_instruction); - - self.locals - .get(local_index as usize) - .map(|(local, _)| local.is_mutable) - .unwrap_or(false) - } else { - false - }; + let left_is_mutable_local = self.locals.iter().any(|local| { + matches!( + left, + Operand::Register(register) if register == local.register_index + ) && local.is_mutable + }); if push_back_left { self.instructions @@ -836,10 +856,26 @@ impl<'src> Compiler<'src> { let destination = if is_assignment { match left { Operand::Register(register) => register, - Operand::Constant(_) => self.next_register(), + Operand::Constant(_) => match left_type { + Type::Boolean => self.register_usage.next_boolean_register(), + Type::Byte => self.register_usage.next_byte_register(), + Type::Character => self.register_usage.next_character_register(), + Type::Float => self.register_usage.next_float_register(), + Type::Integer => self.register_usage.next_integer_register(), + Type::String => self.register_usage.next_string_register(), + _ => unreachable!(), + }, } } else { - self.next_register() + match left_type { + Type::Boolean => self.register_usage.next_boolean_register(), + Type::Byte => self.register_usage.next_byte_register(), + Type::Character => self.register_usage.next_character_register(), + Type::Float => self.register_usage.next_float_register(), + Type::Integer => self.register_usage.next_integer_register(), + Type::String => self.register_usage.next_string_register(), + _ => unreachable!(), + } }; let instruction = match operator { Token::Plus | Token::PlusEqual => { @@ -955,7 +991,7 @@ impl<'src> Compiler<'src> { .push((right_instruction, right_type, right_position)); } - let destination = self.next_register(); + let destination = self.register_usage.next_boolean_register(); let comparison = match operator { Token::DoubleEqual => { Instruction::equal(true, left, left_type_code, right, right_type_code) @@ -989,8 +1025,10 @@ impl<'src> Compiler<'src> { } }; let jump = Instruction::jump(1, true); - let load_true = Instruction::load_boolean(destination, true, true); - let load_false = Instruction::load_boolean(destination, false, false); + let load_true = + Instruction::load_encoded(destination, true as u16, TypeCode::BOOLEAN, true); + let load_false = + Instruction::load_encoded(destination, false as u16, TypeCode::BOOLEAN, false); self.emit_instruction(comparison, Type::Boolean, operator_position); self.emit_instruction(jump, Type::None, operator_position); @@ -1008,11 +1046,7 @@ impl<'src> Compiler<'src> { found: self.previous_token.to_owned(), position: self.previous_position, })?; - let operand_register = if last_instruction.operation() == Operation::GET_LOCAL { - let (local, _) = self.get_local(last_instruction.b_field())?; - - local.register_index - } else if last_instruction.yields_value() { + let operand_register = if last_instruction.yields_value() { let register = last_instruction.a_field(); self.instructions @@ -1020,10 +1054,10 @@ impl<'src> Compiler<'src> { register } else { - self.instructions - .push((last_instruction, last_type, last_position)); - - self.next_register().saturating_sub(1) + return Err(CompileError::ExpectedExpression { + found: self.previous_token.to_owned(), + position: self.previous_position, + }); }; let operator = self.current_token; let operator_position = self.current_position; @@ -1090,7 +1124,7 @@ impl<'src> Compiler<'src> { } else if let Some(native_function) = NativeFunction::from_str(identifier) { return self.parse_call_native(native_function); } else if self.function_name.as_deref() == Some(identifier) && !self.is_main { - let destination = self.next_register(); + let destination = self.register_usage.next_function_register(); let load_self = Instruction::load_self(destination, false); self.emit_instruction(load_self, Type::SelfFunction, start_position); @@ -1103,15 +1137,19 @@ impl<'src> Compiler<'src> { }); }; - let (local, r#type) = self - .get_local(local_index) - .map(|(local, r#type)| (local, r#type.clone()))?; + let local = self + .locals + .get(local_index as usize) + .ok_or(CompileError::UndeclaredVariable { + identifier: identifier.to_string(), + position: start_position, + })? + .clone(); let is_mutable = local.is_mutable; - let local_register_index = local.register_index; if !self.current_scope.contains(&local.scope) { return Err(CompileError::VariableOutOfScope { - identifier: self.get_identifier(local_index).unwrap(), + identifier: self.get_identifier_for_local(local_index).unwrap(), position: start_position, variable_scope: local.scope, access_scope: self.current_scope, @@ -1121,7 +1159,7 @@ impl<'src> Compiler<'src> { if self.allow(Token::Equal)? { if !is_mutable { return Err(CompileError::CannotMutateImmutableVariable { - identifier: self.get_identifier(local_index).unwrap(), + identifier: self.get_identifier_for_local(local_index).unwrap(), position: start_position, }); } @@ -1135,27 +1173,46 @@ impl<'src> Compiler<'src> { { let (math_instruction, _, _) = self.instructions.last_mut().unwrap(); - math_instruction.set_a_field(local_register_index); + math_instruction.set_a_field(local.register_index); } else { - let register = self.next_register() - 1; - let set_local = Instruction::from(SetLocal { - register_index: register, - local_index, - }); + let (register, r#type) = match local.r#type { + Type::Boolean => (self.register_usage.boolean, TypeCode::BOOLEAN), + Type::Byte => (self.register_usage.byte, TypeCode::BYTE), + Type::Character => (self.register_usage.character, TypeCode::CHARACTER), + Type::Float => (self.register_usage.float, TypeCode::FLOAT), + Type::Integer => (self.register_usage.integer, TypeCode::INTEGER), + Type::String => (self.register_usage.string, TypeCode::STRING), + _ => unreachable!(), + }; + let point = Instruction::point(local.register_index, register, r#type); - self.emit_instruction(set_local, Type::None, start_position); + self.emit_instruction(point, Type::None, start_position); } return Ok(()); } - let destination = self.next_register(); - let get_local = Instruction::from(GetLocal { - destination, - local_index, - }); + let (destination, r#type) = match local.r#type { + Type::Boolean => ( + self.register_usage.next_boolean_register(), + TypeCode::BOOLEAN, + ), + Type::Byte => (self.register_usage.next_byte_register(), TypeCode::BYTE), + Type::Character => ( + self.register_usage.next_character_register(), + TypeCode::CHARACTER, + ), + Type::Float => (self.register_usage.next_float_register(), TypeCode::FLOAT), + Type::Integer => ( + self.register_usage.next_integer_register(), + TypeCode::INTEGER, + ), + Type::String => (self.register_usage.next_string_register(), TypeCode::STRING), + _ => unreachable!(), + }; + let point = Instruction::point(destination, local.register_index, r#type); - self.emit_instruction(get_local, r#type, self.previous_position); + self.emit_instruction(point, local.r#type, self.previous_position); Ok(()) } @@ -1197,49 +1254,15 @@ impl<'src> Compiler<'src> { } fn parse_list(&mut self) -> Result<(), CompileError> { - let start = self.current_position.0; - - self.advance()?; - - let start_register = self.next_register(); - let mut item_type = Type::Any; - - while !self.allow(Token::RightBracket)? && !self.is_eof() { - let expected_register = self.next_register(); - - self.parse_expression()?; - - let actual_register = self.next_register() - 1; - - if item_type == Type::Any { - item_type = self.get_last_instruction_type(); - } - - if expected_register < actual_register { - let close = Instruction::from(Close { - from: expected_register, - to: actual_register, - }); - - self.emit_instruction(close, Type::None, self.current_position); - } - - self.allow(Token::Comma)?; - } - - let destination = self.next_register(); - let end = self.previous_position.1; - let load_list = Instruction::load_list(destination, start_register, false); - - self.emit_instruction(load_list, Type::List(Box::new(item_type)), Span(start, end)); - - Ok(()) + todo!() } fn parse_if(&mut self) -> Result<(), CompileError> { self.advance()?; self.parse_expression()?; + let r#type = self.get_last_instruction_type(); + if matches!( self.get_last_operations(), Some([ @@ -1253,7 +1276,15 @@ impl<'src> Compiler<'src> { self.instructions.pop(); self.instructions.pop(); } else { - let operand_register = self.next_register() - 1; + let operand_register = match r#type { + Type::Boolean => self.register_usage.next_boolean_register() - 1, + Type::Byte => self.register_usage.next_byte_register() - 1, + Type::Character => self.register_usage.next_character_register() - 1, + Type::Float => self.register_usage.next_float_register() - 1, + Type::Integer => self.register_usage.next_integer_register() - 1, + Type::String => self.register_usage.next_string_register() - 1, + _ => unreachable!(), + }; let test = Instruction::test(operand_register, true); self.emit_instruction(test, Type::None, self.current_position); @@ -1353,6 +1384,8 @@ impl<'src> Compiler<'src> { self.parse_expression()?; + let r#type = self.get_last_instruction_type(); + if matches!( self.get_last_operations(), Some([ @@ -1366,7 +1399,15 @@ impl<'src> Compiler<'src> { self.instructions.pop(); self.instructions.pop(); } else { - let operand_register = self.next_register() - 1; + let operand_register = match r#type { + Type::Boolean => self.register_usage.next_boolean_register() - 1, + Type::Byte => self.register_usage.next_byte_register() - 1, + Type::Character => self.register_usage.next_character_register() - 1, + Type::Float => self.register_usage.next_float_register() - 1, + Type::Integer => self.register_usage.next_integer_register() - 1, + Type::String => self.register_usage.next_string_register() - 1, + _ => unreachable!(), + }; let test = Instruction::test(operand_register, true); self.emit_instruction(test, Type::None, self.current_position); @@ -1399,38 +1440,61 @@ impl<'src> Compiler<'src> { fn parse_call_native(&mut self, function: NativeFunction) -> Result<(), CompileError> { let start = self.previous_position.0; - let start_register = self.next_register(); + let mut register_list = Vec::new(); self.expect(Token::LeftParenthesis)?; + let mut argument_index = 0; + while !self.allow(Token::RightParenthesis)? { - let expected_register = self.next_register(); + let argument_type = function + .r#type() + .value_parameters + .get(argument_index) + .map(|(_, r#type)| r#type) + .unwrap() // TODO: Throw error if the function does not have enough parameters + .clone(); self.parse_expression()?; - let actual_register = self.next_register() - 1; - let registers_to_close = actual_register - expected_register; + // TODO: Check if the argument type matches the expected type - if registers_to_close > 0 { - let close = Instruction::from(Close { - from: expected_register, - to: actual_register, - }); + let argument_register = match argument_type { + Type::Boolean => self.register_usage.boolean, + Type::Byte => self.register_usage.byte, + Type::Character => self.register_usage.character, + Type::Float => self.register_usage.float, + Type::Integer => self.register_usage.integer, + Type::String => self.register_usage.string, + _ => unreachable!(), + }; - self.emit_instruction(close, Type::None, self.current_position); - } + register_list.push(argument_register); + + argument_index += 1; self.allow(Token::Comma)?; } let end = self.previous_position.1; - let destination = self.next_register(); - let argument_count = destination - start_register; let return_type = function.r#type().return_type; + let destination = match return_type { + Type::Boolean => self.register_usage.next_boolean_register(), + Type::Byte => self.register_usage.next_byte_register(), + Type::Character => self.register_usage.next_character_register(), + Type::Float => self.register_usage.next_float_register(), + Type::Integer => self.register_usage.next_integer_register(), + Type::String => self.register_usage.next_string_register(), + _ => unreachable!(), + }; + let argument_list_index = self.argument_lists.len() as u16; + + self.argument_lists.push(register_list); + let call_native = Instruction::from(CallNative { destination, function, - argument_count, + argument_list_index, }); self.emit_instruction(call_native, return_type, Span(start, end)); @@ -1493,7 +1557,15 @@ impl<'src> Compiler<'src> { true }; let end = self.current_position.1; - let return_register = self.next_register() - 1; + let return_register = match self.r#type.return_type { + Type::Boolean => self.register_usage.boolean, + Type::Byte => self.register_usage.byte, + Type::Character => self.register_usage.character, + Type::Float => self.register_usage.float, + Type::Integer => self.register_usage.integer, + Type::String => self.register_usage.string, + _ => unreachable!(), + }; let r#return = Instruction::from(Return { should_return_value, return_register, @@ -1603,17 +1675,24 @@ impl<'src> Compiler<'src> { self.expect(Token::Equal)?; self.parse_expression()?; - let register_index = self.next_register() - 1; - let r#type = if let Some(r#type) = explicit_type { - r#type - } else { - self.get_register_type(register_index)? + let actual_type = self.get_last_instruction_type(); + + // TODO: Check if the actual type matches the explicit type + + let register_index = match actual_type { + Type::Boolean => self.register_usage.boolean, + Type::Byte => self.register_usage.byte, + Type::Character => self.register_usage.character, + Type::Float => self.register_usage.float, + Type::Integer => self.register_usage.integer, + Type::String => self.register_usage.string, + _ => unreachable!(), }; self.declare_local( identifier, register_index, - r#type, + actual_type, is_mutable, self.current_scope, ); @@ -1673,7 +1752,15 @@ impl<'src> Compiler<'src> { function_compiler.advance()?; - let local_register_index = function_compiler.next_register(); + let local_register_index = match r#type { + Type::Boolean => function_compiler.register_usage.next_boolean_register(), + Type::Byte => function_compiler.register_usage.next_byte_register(), + Type::Character => function_compiler.register_usage.next_character_register(), + Type::Float => function_compiler.register_usage.next_float_register(), + Type::Integer => function_compiler.register_usage.next_integer_register(), + Type::String => function_compiler.register_usage.next_string_register(), + _ => unreachable!(), + }; let (_, identifier_index) = function_compiler.declare_local( parameter, local_register_index, @@ -1684,8 +1771,6 @@ impl<'src> Compiler<'src> { value_parameters.push((identifier_index, r#type)); function_compiler.allow(Token::Comma)?; - - function_compiler.minimum_register += 1; } let return_type = if function_compiler.allow(Token::ArrowThin)? { @@ -1722,7 +1807,7 @@ impl<'src> Compiler<'src> { let function_end = function_compiler.previous_position.1; let prototype_index = function_compiler.prototype_index; let chunk = function_compiler.finish(); - let destination = self.next_register(); + let destination = self.register_usage.next_function_register(); self.prototypes.push(Arc::new(chunk)); @@ -1785,32 +1870,34 @@ impl<'src> Compiler<'src> { }; let is_recursive = last_instruction_type == &Type::SelfFunction; + let mut argument_list = Vec::new(); let mut argument_count = 0; while !self.allow(Token::RightParenthesis)? { - let expected_register = self.next_register(); - self.parse_expression()?; - let actual_register = self.next_register() - 1; - let registers_to_close = (actual_register - expected_register).saturating_sub(1); + let argument_type = self.get_last_instruction_type(); + let argument_register = match argument_type { + Type::Boolean => self.register_usage.next_boolean_register(), + Type::Byte => self.register_usage.next_byte_register(), + Type::Character => self.register_usage.next_character_register(), + Type::Float => self.register_usage.next_float_register(), + Type::Integer => self.register_usage.next_integer_register(), + Type::String => self.register_usage.next_string_register(), + _ => unreachable!(), + }; - if registers_to_close > 0 { - let close = Instruction::from(Close { - from: expected_register, - to: actual_register, - }); + argument_list.push(argument_register); - self.emit_instruction(close, Type::None, self.current_position); - } - - argument_count += registers_to_close + 1; + argument_count += 1; self.allow(Token::Comma)?; } + self.argument_lists.push(argument_list); + let end = self.current_position.1; - let destination = self.next_register(); + let destination = self.register_usage.next_function_register(); let call = Instruction::call(destination, function_register, argument_count, is_recursive); self.emit_instruction(call, function_return_type, Span(start, end)); @@ -1862,3 +1949,90 @@ impl<'src> Compiler<'src> { Ok(()) } } + +#[derive(Debug, Default)] +struct RegisterUsage { + boolean: u16, + byte: u16, + character: u16, + float: u16, + integer: u16, + string: u16, + list: u16, + function: u16, + argument: u16, +} + +impl RegisterUsage { + fn next_boolean_register(&mut self) -> u16 { + let register = self.boolean; + + self.boolean += 1; + + register + } + + fn next_byte_register(&mut self) -> u16 { + let register = self.byte; + + self.byte += 1; + + register + } + + fn next_character_register(&mut self) -> u16 { + let register = self.character; + + self.character += 1; + + register + } + + fn next_float_register(&mut self) -> u16 { + let register = self.float; + + self.float += 1; + + register + } + + fn next_integer_register(&mut self) -> u16 { + let register = self.integer; + + self.integer += 1; + + register + } + + fn next_string_register(&mut self) -> u16 { + let register = self.string; + + self.string += 1; + + register + } + + fn next_list_register(&mut self) -> u16 { + let register = self.list; + + self.list += 1; + + register + } + + fn next_function_register(&mut self) -> u16 { + let register = self.function; + + self.function += 1; + + register + } + + fn next_argument_register(&mut self) -> u16 { + let register = self.argument; + + self.argument += 1; + + register + } +} diff --git a/dust-lang/src/compiler/optimize.rs b/dust-lang/src/compiler/optimize.rs index f60ad82..124459b 100644 --- a/dust-lang/src/compiler/optimize.rs +++ b/dust-lang/src/compiler/optimize.rs @@ -28,8 +28,8 @@ use crate::{Compiler, Instruction, Operation}; /// The instructions must be in the following order: /// - `TEST` or any of the `EQUAL`, `LESS` or `LESS_EQUAL` instructions /// - `JUMP` -/// - `LOAD_BOOLEAN` or `LOAD_CONSTANT` -/// - `LOAD_BOOLEAN` or `LOAD_CONSTANT` +/// - `LOAD_ENCODED` +/// - `LOAD_ENCODED` /// /// This optimization was taken from `A No-Frills Introduction to Lua 5.1 VM Instructions` by /// Kein-Hong Man. @@ -39,8 +39,8 @@ pub fn control_flow_register_consolidation(compiler: &mut Compiler) { Some([ Operation::TEST | Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL, Operation::JUMP, - Operation::LOAD_ENCODED | Operation::LOAD_CONSTANT, - Operation::LOAD_ENCODED | Operation::LOAD_CONSTANT, + Operation::LOAD_ENCODED, + Operation::LOAD_ENCODED, ]) ) { return; @@ -51,14 +51,19 @@ pub fn control_flow_register_consolidation(compiler: &mut Compiler) { let first_loader_index = compiler.instructions.len() - 2; let (first_loader, _, _) = &mut compiler.instructions.get_mut(first_loader_index).unwrap(); let first_loader_destination = first_loader.a_field(); - *first_loader = - Instruction::load_boolean(first_loader.a_field(), first_loader.b_field() != 0, true); + *first_loader = Instruction::load_encoded( + first_loader.a_field(), + first_loader.b_field(), + first_loader.b_type(), + true, + ); let second_loader_index = compiler.instructions.len() - 1; let (second_loader, _, _) = &mut compiler.instructions.get_mut(second_loader_index).unwrap(); - *second_loader = Instruction::load_boolean( + *second_loader = Instruction::load_encoded( first_loader_destination, - second_loader.b_field() != 0, + second_loader.b_field(), + second_loader.b_type(), false, ); } diff --git a/dust-lang/src/instruction/call_native.rs b/dust-lang/src/instruction/call_native.rs index f66fac4..c21edb4 100644 --- a/dust-lang/src/instruction/call_native.rs +++ b/dust-lang/src/instruction/call_native.rs @@ -7,18 +7,19 @@ use super::InstructionBuilder; pub struct CallNative { pub destination: u16, pub function: NativeFunction, - pub argument_count: u16, + pub argument_list_index: u16, } impl From for CallNative { fn from(instruction: Instruction) -> Self { let destination = instruction.a_field(); let function = NativeFunction::from(instruction.b_field()); + let argument_list_index = instruction.c_field(); CallNative { destination, function, - argument_count: instruction.c_field(), + argument_list_index, } } } @@ -28,7 +29,7 @@ impl From for Instruction { let operation = Operation::CALL_NATIVE; let a_field = call_native.destination; let b_field = call_native.function as u16; - let c_field = call_native.argument_count; + let c_field = call_native.argument_list_index; InstructionBuilder { operation, @@ -46,21 +47,9 @@ impl Display for CallNative { let CallNative { destination, function, - argument_count, + .. } = self; - let arguments_start = destination.saturating_sub(*argument_count); - let arguments_end = arguments_start + argument_count; - if function.returns_value() { - write!(f, "R{destination} = ")?; - } - - match argument_count { - 0 => { - write!(f, "{function}()") - } - 1 => write!(f, "{function}(R{arguments_start})"), - _ => write!(f, "{function}(R{arguments_start}..R{arguments_end})"), - } + write!(f, "R{destination} = {function}(..)") } } diff --git a/dust-lang/src/instruction/get_local.rs b/dust-lang/src/instruction/get_local.rs deleted file mode 100644 index 8aba567..0000000 --- a/dust-lang/src/instruction/get_local.rs +++ /dev/null @@ -1,49 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use crate::{Instruction, Operation}; - -use super::InstructionBuilder; - -pub struct GetLocal { - pub destination: u16, - pub local_index: u16, -} - -impl From for GetLocal { - fn from(instruction: Instruction) -> Self { - let destination = instruction.a_field(); - let local_index = instruction.b_field(); - - GetLocal { - destination, - local_index, - } - } -} - -impl From for Instruction { - fn from(get_local: GetLocal) -> Self { - let operation = Operation::GET_LOCAL; - let a_field = get_local.destination; - let b_field = get_local.local_index; - - InstructionBuilder { - operation, - a_field, - b_field, - ..Default::default() - } - .build() - } -} - -impl Display for GetLocal { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let GetLocal { - destination, - local_index, - } = self; - - write!(f, "R{destination} = L{local_index}") - } -} diff --git a/dust-lang/src/instruction/mod.rs b/dust-lang/src/instruction/mod.rs index b846010..06458ed 100644 --- a/dust-lang/src/instruction/mod.rs +++ b/dust-lang/src/instruction/mod.rs @@ -99,7 +99,6 @@ mod call_native; mod close; mod divide; mod equal; -mod get_local; mod jump; mod less; mod less_equal; @@ -115,7 +114,6 @@ mod not; mod operation; mod point; mod r#return; -mod set_local; mod subtract; mod test; mod test_set; @@ -127,7 +125,6 @@ pub use call_native::CallNative; pub use close::Close; pub use divide::Divide; pub use equal::Equal; -pub use get_local::GetLocal; pub use jump::Jump; pub use less::Less; pub use less_equal::LessEqual; @@ -143,7 +140,6 @@ pub use not::Not; pub use operation::Operation; pub use point::Point; pub use r#return::Return; -pub use set_local::SetLocal; pub use subtract::Subtract; pub use test::Test; pub use test_set::TestSet; @@ -154,6 +150,7 @@ pub use type_code::TypeCode; use crate::NativeFunction; +#[derive(Clone, Copy, Debug)] pub struct InstructionBuilder { pub operation: Operation, pub a_field: u16, @@ -182,6 +179,22 @@ impl InstructionBuilder { } } +impl From<&Instruction> for InstructionBuilder { + fn from(instruction: &Instruction) -> Self { + InstructionBuilder { + operation: instruction.operation(), + a_field: instruction.a_field(), + b_field: instruction.b_field(), + c_field: instruction.c_field(), + d_field: instruction.d_field(), + b_is_constant: instruction.b_is_constant(), + c_is_constant: instruction.c_is_constant(), + b_type: instruction.b_type(), + c_type: instruction.c_type(), + } + } +} + impl Default for InstructionBuilder { fn default() -> Self { InstructionBuilder { @@ -259,18 +272,28 @@ impl Instruction { self.0 = (bits as u64) << 63; } - pub fn point(from: u16, to: u16) -> Instruction { - Instruction::from(Point { from, to }) + pub fn point(destination: u16, to: u16, r#type: TypeCode) -> Instruction { + Instruction::from(Point { + destination, + to, + r#type, + }) } pub fn close(from: u16, to: u16) -> Instruction { Instruction::from(Close { from, to }) } - pub fn load_boolean(destination: u16, value: bool, jump_next: bool) -> Instruction { - Instruction::from(LoadBoolean { + pub fn load_encoded( + destination: u16, + value: u16, + r#type: TypeCode, + jump_next: bool, + ) -> Instruction { + Instruction::from(LoadEncoded { destination, value, + r#type, jump_next, }) } @@ -306,20 +329,6 @@ impl Instruction { }) } - pub fn get_local(destination: u16, local_index: u16) -> Instruction { - Instruction::from(GetLocal { - destination, - local_index, - }) - } - - pub fn set_local(register: u16, local_index: u16) -> Instruction { - Instruction::from(SetLocal { - local_index, - register_index: register, - }) - } - pub fn add( destination: u16, left: Operand, @@ -502,12 +511,12 @@ impl Instruction { pub fn call_native( destination: u16, function: NativeFunction, - argument_count: u16, + argument_list_index: u16, ) -> Instruction { Instruction::from(CallNative { destination, function, - argument_count, + argument_list_index, }) } @@ -532,7 +541,6 @@ impl Instruction { Operation::LOAD_ENCODED | Operation::LOAD_LIST | Operation::LOAD_SELF - | Operation::GET_LOCAL | Operation::ADD | Operation::SUBTRACT | Operation::MULTIPLY @@ -588,7 +596,6 @@ impl Instruction { | Operation::LOAD_FUNCTION | Operation::LOAD_LIST | Operation::LOAD_SELF - | Operation::GET_LOCAL | Operation::ADD | Operation::SUBTRACT | Operation::MULTIPLY @@ -603,7 +610,6 @@ impl Instruction { function.returns_value() } Operation::CLOSE - | Operation::SET_LOCAL | Operation::EQUAL | Operation::LESS | Operation::LESS_EQUAL @@ -621,13 +627,11 @@ impl Instruction { match operation { Operation::POINT => Point::from(*self).to_string(), Operation::CLOSE => Close::from(*self).to_string(), - Operation::LOAD_ENCODED => LoadBoolean::from(*self).to_string(), + Operation::LOAD_ENCODED => LoadEncoded::from(*self).to_string(), Operation::LOAD_CONSTANT => LoadConstant::from(*self).to_string(), Operation::LOAD_FUNCTION => LoadFunction::from(*self).to_string(), Operation::LOAD_LIST => LoadList::from(*self).to_string(), Operation::LOAD_SELF => LoadSelf::from(*self).to_string(), - Operation::GET_LOCAL => GetLocal::from(*self).to_string(), - Operation::SET_LOCAL => SetLocal::from(*self).to_string(), Operation::ADD => Add::from(*self).to_string(), Operation::SUBTRACT => Subtract::from(*self).to_string(), Operation::MULTIPLY => Multiply::from(*self).to_string(), diff --git a/dust-lang/src/instruction/operation.rs b/dust-lang/src/instruction/operation.rs index 78d88d8..9832b6d 100644 --- a/dust-lang/src/instruction/operation.rs +++ b/dust-lang/src/instruction/operation.rs @@ -20,37 +20,33 @@ impl Operation { pub const LOAD_LIST: Operation = Operation(5); pub const LOAD_SELF: Operation = Operation(6); - // Locals - pub const GET_LOCAL: Operation = Operation(7); - pub const SET_LOCAL: Operation = Operation(8); - // Arithmetic - pub const ADD: Operation = Operation(9); - pub const SUBTRACT: Operation = Operation(10); - pub const MULTIPLY: Operation = Operation(11); - pub const DIVIDE: Operation = Operation(12); - pub const MODULO: Operation = Operation(13); + pub const ADD: Operation = Operation(7); + pub const SUBTRACT: Operation = Operation(8); + pub const MULTIPLY: Operation = Operation(9); + pub const DIVIDE: Operation = Operation(10); + pub const MODULO: Operation = Operation(11); // Comparison - pub const EQUAL: Operation = Operation(14); - pub const LESS: Operation = Operation(15); - pub const LESS_EQUAL: Operation = Operation(16); + pub const EQUAL: Operation = Operation(12); + pub const LESS: Operation = Operation(13); + pub const LESS_EQUAL: Operation = Operation(14); // Unary operations - pub const NEGATE: Operation = Operation(17); - pub const NOT: Operation = Operation(18); + pub const NEGATE: Operation = Operation(15); + pub const NOT: Operation = Operation(16); // Logical operations - pub const TEST: Operation = Operation(19); - pub const TEST_SET: Operation = Operation(20); + pub const TEST: Operation = Operation(17); + pub const TEST_SET: Operation = Operation(18); // Function calls - pub const CALL: Operation = Operation(21); - pub const CALL_NATIVE: Operation = Operation(22); + pub const CALL: Operation = Operation(19); + pub const CALL_NATIVE: Operation = Operation(20); // Control flow - pub const JUMP: Operation = Operation(23); - pub const RETURN: Operation = Operation(24); + pub const JUMP: Operation = Operation(21); + pub const RETURN: Operation = Operation(22); } impl Operation { @@ -63,8 +59,6 @@ impl Operation { Self::LOAD_FUNCTION => "LOAD_FUNCTION", Self::LOAD_LIST => "LOAD_LIST", Self::LOAD_SELF => "LOAD_SELF", - Self::GET_LOCAL => "GET_LOCAL", - Self::SET_LOCAL => "SET_LOCAL", Self::ADD => "ADD", Self::SUBTRACT => "SUBTRACT", Self::MULTIPLY => "MULTIPLY", diff --git a/dust-lang/src/instruction/point.rs b/dust-lang/src/instruction/point.rs index eb6e459..e0e2d8a 100644 --- a/dust-lang/src/instruction/point.rs +++ b/dust-lang/src/instruction/point.rs @@ -2,18 +2,20 @@ use std::fmt::{self, Display, Formatter}; use crate::{Instruction, Operation}; -use super::InstructionBuilder; +use super::{InstructionBuilder, TypeCode}; pub struct Point { - pub from: u16, + pub destination: u16, pub to: u16, + pub r#type: TypeCode, } impl From for Point { fn from(instruction: Instruction) -> Self { Point { - from: instruction.b_field(), - to: instruction.c_field(), + destination: instruction.a_field(), + to: instruction.b_field(), + r#type: instruction.b_type(), } } } @@ -21,13 +23,15 @@ impl From for Point { impl From for Instruction { fn from(r#move: Point) -> Self { let operation = Operation::POINT; - let b_field = r#move.from; - let c_field = r#move.to; + let a_field = r#move.destination; + let b_field = r#move.to; + let b_type = r#move.r#type; InstructionBuilder { operation, + a_field, b_field, - c_field, + b_type, ..Default::default() } .build() @@ -36,8 +40,20 @@ impl From for Instruction { impl Display for Point { fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let Point { from, to } = self; + let Point { + destination, + to, + r#type, + } = self; - write!(f, "{from} -> {to}") + match *r#type { + TypeCode::BOOLEAN => write!(f, "R_BOOL_{destination} = R_BOOL_{to}"), + TypeCode::BYTE => write!(f, "R_BYTE_{destination} = R_BYTE_{to}"), + TypeCode::CHARACTER => write!(f, "R_CHAR_{destination} = R_CHAR_{to}"), + TypeCode::FLOAT => write!(f, "R_FLOAT_{destination} = R_FLOAT_{to}"), + TypeCode::INTEGER => write!(f, "R_INT_{destination} = R_INT_{to}"), + TypeCode::STRING => write!(f, "R_STR_{destination} = R_STR_{to}"), + unsupported => unsupported.panic_from_unsupported_code(), + } } } diff --git a/dust-lang/src/instruction/set_local.rs b/dust-lang/src/instruction/set_local.rs deleted file mode 100644 index 57b48a2..0000000 --- a/dust-lang/src/instruction/set_local.rs +++ /dev/null @@ -1,49 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use crate::{Instruction, Operation}; - -use super::InstructionBuilder; - -pub struct SetLocal { - pub register_index: u16, - pub local_index: u16, -} - -impl From for SetLocal { - fn from(instruction: Instruction) -> Self { - let register_index = instruction.b_field(); - let local_index = instruction.c_field(); - - SetLocal { - register_index, - local_index, - } - } -} - -impl From for Instruction { - fn from(set_local: SetLocal) -> Self { - let operation = Operation::SET_LOCAL; - let b_field = set_local.register_index; - let c_field = set_local.local_index; - - InstructionBuilder { - operation, - b_field, - c_field, - ..Default::default() - } - .build() - } -} - -impl Display for SetLocal { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - let SetLocal { - register_index, - local_index, - } = self; - - write!(f, "L{local_index} = R{register_index}") - } -} diff --git a/dust-lang/src/instruction/type_code.rs b/dust-lang/src/instruction/type_code.rs index 4420e3f..9a44cf4 100644 --- a/dust-lang/src/instruction/type_code.rs +++ b/dust-lang/src/instruction/type_code.rs @@ -4,12 +4,13 @@ use std::fmt::Display; pub struct TypeCode(pub u8); impl TypeCode { - pub const BOOLEAN: TypeCode = TypeCode(0); - pub const BYTE: TypeCode = TypeCode(1); - pub const CHARACTER: TypeCode = TypeCode(2); - pub const FLOAT: TypeCode = TypeCode(3); - pub const INTEGER: TypeCode = TypeCode(4); - pub const STRING: TypeCode = TypeCode(5); + pub const NONE: TypeCode = TypeCode(0); + pub const BOOLEAN: TypeCode = TypeCode(1); + pub const BYTE: TypeCode = TypeCode(2); + pub const CHARACTER: TypeCode = TypeCode(3); + pub const FLOAT: TypeCode = TypeCode(4); + pub const INTEGER: TypeCode = TypeCode(5); + pub const STRING: TypeCode = TypeCode(6); pub fn panic_from_unknown_code(self) -> ! { panic!("Unknown type code: {}", self.0); diff --git a/dust-lang/src/native_function/assert.rs b/dust-lang/src/native_function/assert.rs index 3cd7045..7ae8f4a 100644 --- a/dust-lang/src/native_function/assert.rs +++ b/dust-lang/src/native_function/assert.rs @@ -1,22 +1,5 @@ use std::{ops::Range, panic}; -use crate::vm::ThreadData; +use crate::{instruction::InstructionBuilder, vm::Thread}; -pub fn panic(data: &mut ThreadData, _: u16, argument_range: Range) -> bool { - let position = data.current_position(); - let mut message = format!("Dust panic at {position}!"); - - for register_index in argument_range { - let value_option = data.open_register_allow_empty_unchecked(register_index); - let value = match value_option { - Some(value) => value, - None => continue, - }; - let string = value.display(data); - - message.push_str(&string); - message.push('\n'); - } - - panic!("{}", message) -} +pub fn panic(instruction: InstructionBuilder, thread: &mut Thread) {} diff --git a/dust-lang/src/native_function/io.rs b/dust-lang/src/native_function/io.rs index 89bf2b7..d259d16 100644 --- a/dust-lang/src/native_function/io.rs +++ b/dust-lang/src/native_function/io.rs @@ -2,57 +2,13 @@ use std::io::{Write, stdin, stdout}; use std::ops::Range; use crate::{ - ConcreteValue, Value, - vm::{Register, ThreadData, get_next_action}, + Value, + instruction::InstructionBuilder, + vm::{Register, Thread}, }; -pub fn read_line(data: &mut ThreadData, destination: u16, _argument_range: Range) -> bool { - let mut buffer = String::new(); +pub fn read_line(instruction: InstructionBuilder, thread: &mut Thread) {} - if stdin().read_line(&mut buffer).is_ok() { - let length = buffer.len(); +pub fn write(instruction: InstructionBuilder, thread: &mut Thread) {} - buffer.truncate(length.saturating_sub(1)); - - let register = Register::Value(Value::Concrete(ConcreteValue::string(buffer))); - - data.set_register(destination, register); - } - - data.next_action = get_next_action(data); - - false -} - -pub fn write(data: &mut ThreadData, _: u16, argument_range: Range) -> bool { - let mut stdout = stdout(); - - for register_index in argument_range { - if let Some(value) = data.open_register_allow_empty_unchecked(register_index) { - let string = value.display(data); - let _ = stdout.write(string.as_bytes()); - } - } - - let _ = stdout.flush(); - data.next_action = get_next_action(data); - - false -} - -pub fn write_line(data: &mut ThreadData, _: u16, argument_range: Range) -> bool { - let mut stdout = stdout().lock(); - - for register_index in argument_range { - if let Some(value) = data.open_register_allow_empty_unchecked(register_index) { - let string = value.display(data); - let _ = stdout.write(string.as_bytes()); - let _ = stdout.write(b"\n"); - } - } - - let _ = stdout.flush(); - data.next_action = get_next_action(data); - - false -} +pub fn write_line(instruction: InstructionBuilder, thread: &mut Thread) {} diff --git a/dust-lang/src/native_function/mod.rs b/dust-lang/src/native_function/mod.rs index 1926143..e77dd73 100644 --- a/dust-lang/src/native_function/mod.rs +++ b/dust-lang/src/native_function/mod.rs @@ -17,7 +17,9 @@ use std::{ use serde::{Deserialize, Serialize}; -use crate::{AnnotatedError, FunctionType, Span, Type, vm::ThreadData}; +use crate::{ + AnnotatedError, FunctionType, Span, Type, instruction::InstructionBuilder, vm::Thread, +}; macro_rules! define_native_function { ($(($name:ident, $bytes:literal, $str:expr, $type:expr, $function:expr)),*) => { @@ -34,13 +36,12 @@ macro_rules! define_native_function { impl NativeFunction { pub fn call( &self, - data: &mut ThreadData, - destination: u16, - argument_range: Range, - ) -> bool { + instruction: InstructionBuilder, + thread: &mut Thread, + ) { match self { $( - NativeFunction::$name => $function(data, destination, argument_range), + NativeFunction::$name => $function(instruction, thread), )* } } diff --git a/dust-lang/src/native_function/random.rs b/dust-lang/src/native_function/random.rs index 1eae46a..377ed3f 100644 --- a/dust-lang/src/native_function/random.rs +++ b/dust-lang/src/native_function/random.rs @@ -2,39 +2,6 @@ use std::ops::Range; use rand::Rng; -use crate::{ - Value, - vm::{Register, ThreadData, get_next_action}, -}; +use crate::{instruction::InstructionBuilder, vm::Thread}; -pub fn random_int(data: &mut ThreadData, destination: u16, argument_range: Range) -> bool { - let mut argument_range_iter = argument_range.into_iter(); - let (min, max) = { - let mut min = None; - - loop { - let register_index = argument_range_iter - .next() - .unwrap_or_else(|| panic!("No argument was passed to \"random_int\"")); - let value_option = data.open_register_allow_empty_unchecked(register_index); - - if let Some(argument) = value_option { - if let Some(integer) = argument.as_integer() { - if min.is_none() { - min = Some(integer); - } else { - break (min, integer); - } - } - } - } - }; - - let random_integer = rand::thread_rng().gen_range(min.unwrap()..max); - - data.set_register(destination, Register::Value(Value::integer(random_integer))); - - data.next_action = get_next_action(data); - - false -} +pub fn random_int(instruction: InstructionBuilder, thread: &mut Thread) {} diff --git a/dust-lang/src/native_function/string.rs b/dust-lang/src/native_function/string.rs index 9e3546c..3271dec 100644 --- a/dust-lang/src/native_function/string.rs +++ b/dust-lang/src/native_function/string.rs @@ -1,18 +1,9 @@ use std::ops::Range; use crate::{ - ConcreteValue, Value, - vm::{Register, ThreadData, get_next_action}, + Value, + instruction::InstructionBuilder, + vm::{Register, Thread}, }; -pub fn to_string(data: &mut ThreadData, destination: u16, argument_range: Range) -> bool { - let argument_value = data.open_register_unchecked(argument_range.start); - let argument_string = argument_value.display(data); - let register = Register::Value(Value::Concrete(ConcreteValue::string(argument_string))); - - data.set_register(destination, register); - - data.next_action = get_next_action(data); - - false -} +pub fn to_string(instruction: InstructionBuilder, thread: &mut Thread) {} diff --git a/dust-lang/src/native_function/thread.rs b/dust-lang/src/native_function/thread.rs index 4b0cf85..82f45c3 100644 --- a/dust-lang/src/native_function/thread.rs +++ b/dust-lang/src/native_function/thread.rs @@ -5,60 +5,6 @@ use std::{ use tracing::{Level, info, span}; -use crate::{ - DustString, - vm::{Thread, ThreadData, get_next_action}, -}; +use crate::{DustString, instruction::InstructionBuilder, vm::Thread}; -fn start_thread(data: &mut ThreadData, argument_range: Range) -> JoinHandle<()> { - let mut argument_range_iter = argument_range.into_iter(); - let function_argument = { - loop { - let register_index = argument_range_iter - .next() - .unwrap_or_else(|| panic!("No argument was passed to \"spawn\"")); - let value_option = data.open_register_allow_empty_unchecked(register_index); - - if let Some(argument) = value_option { - break argument; - } - } - }; - let function = function_argument.as_function().unwrap(); - let prototype_index = function.prototype_index as usize; - let current_call = data.call_stack.last_unchecked(); - let prototype = current_call.chunk.prototypes[prototype_index].clone(); - - info!( - "Spawning thread for \"{}\"", - function - .name - .as_ref() - .cloned() - .unwrap_or_else(|| DustString::from("anonymous")) - ); - - let thread_name = prototype - .name - .as_ref() - .map(|name| name.to_string()) - .unwrap_or_else(|| "anonymous".to_string()); - let mut thread = Thread::new(prototype); - - Builder::new() - .name(thread_name) - .spawn(move || { - let span = span!(Level::INFO, "Spawned thread"); - let _enter = span.enter(); - - thread.run(); - }) - .expect("Critical VM Error: Failed to spawn thread") -} - -pub fn spawn(data: &mut ThreadData, _: u16, argument_range: Range) -> bool { - let _ = start_thread(data, argument_range); - data.next_action = get_next_action(data); - - false -} +pub fn spawn(instruction: InstructionBuilder, thread: &mut Thread) {} diff --git a/dust-lang/src/value/abstract_list.rs b/dust-lang/src/value/abstract_list.rs deleted file mode 100644 index f678d77..0000000 --- a/dust-lang/src/value/abstract_list.rs +++ /dev/null @@ -1,45 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use crate::{vm::ThreadData, Pointer, Type}; - -use super::DustString; - -#[derive(Clone, Debug, PartialEq, PartialOrd)] -pub struct AbstractList { - pub item_type: Type, - pub item_pointers: Vec, -} - -impl AbstractList { - pub fn display(&self, data: &ThreadData) -> DustString { - let mut display = DustString::new(); - - display.push('['); - - for (i, item) in self.item_pointers.iter().enumerate() { - if i > 0 { - display.push_str(", "); - } - - let item_display = data.follow_pointer_unchecked(*item).display(data); - - display.push_str(&item_display); - } - - display.push(']'); - - display - } -} - -impl Display for AbstractList { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "[")?; - - for pointer in &self.item_pointers { - write!(f, "{}", pointer)?; - } - - write!(f, "]") - } -} diff --git a/dust-lang/src/value/concrete_value.rs b/dust-lang/src/value/concrete_value.rs deleted file mode 100644 index f923d2a..0000000 --- a/dust-lang/src/value/concrete_value.rs +++ /dev/null @@ -1,341 +0,0 @@ -use std::fmt::{self, Display, Formatter}; - -use serde::{Deserialize, Serialize}; -use smartstring::{LazyCompact, SmartString}; -use tracing::trace; - -use crate::{Type, Value, ValueError}; - -use super::RangeValue; - -pub type DustString = SmartString; - -#[derive(Debug, PartialEq, PartialOrd, Serialize, Deserialize)] -pub enum ConcreteValue { - Boolean(bool), - Byte(u8), - Character(char), - Float(f64), - Integer(i64), - List(Vec), - Range(RangeValue), - String(DustString), -} - -impl ConcreteValue { - pub fn to_value(self) -> Value { - Value::Concrete(self) - } - - pub fn list>>(into_list: T) -> Self { - ConcreteValue::List(into_list.into()) - } - - pub fn string>>(to_string: T) -> Self { - ConcreteValue::String(to_string.into()) - } - - pub fn as_string(&self) -> Option<&DustString> { - if let ConcreteValue::String(string) = self { - Some(string) - } else { - None - } - } - - pub fn display(&self) -> DustString { - DustString::from(self.to_string()) - } - - 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::Integer(_) => Type::Integer, - ConcreteValue::List(list) => { - let item_type = list.first().map_or(Type::Any, |item| item.r#type()); - - Type::List(Box::new(item_type)) - } - ConcreteValue::Range(range) => range.r#type(), - ConcreteValue::String(_) => Type::String, - } - } - - pub fn add(&self, other: &Self) -> ConcreteValue { - use ConcreteValue::*; - - match (self, other) { - (Byte(left), Byte(right)) => { - let sum = left.saturating_add(*right); - - Byte(sum) - } - (Character(left), Character(right)) => { - let mut concatenated = DustString::new(); - - concatenated.push(*left); - concatenated.push(*right); - - String(concatenated) - } - (Character(left), String(right)) => { - let mut concatenated = DustString::new(); - - concatenated.push(*left); - concatenated.push_str(right); - - String(concatenated) - } - (Float(left), Float(right)) => { - let sum = left + right; - - Float(sum) - } - (Integer(left), Integer(right)) => { - let sum = left.saturating_add(*right); - - Integer(sum) - } - (String(left), Character(right)) => { - let concatenated = format!("{}{}", left, right); - - String(DustString::from(concatenated)) - } - (String(left), String(right)) => { - let concatenated = format!("{}{}", left, right); - - String(DustString::from(concatenated)) - } - _ => panic!( - "{}", - ValueError::CannotAdd( - Value::Concrete(self.clone()), - Value::Concrete(other.clone()) - ) - ), - } - } - - pub fn subtract(&self, other: &Self) -> ConcreteValue { - use ConcreteValue::*; - - 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)), - _ => panic!( - "{}", - ValueError::CannotSubtract( - Value::Concrete(self.clone()), - Value::Concrete(other.clone()) - ) - ), - } - } - - 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().to_value(), - other.clone().to_value(), - )) - } - }; - - 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().to_value(), - other.clone().to_value(), - )) - } - }; - - 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().to_value(), - other.clone().to_value(), - )) - } - }; - - Ok(product) - } - - pub fn negate(&self) -> ConcreteValue { - use ConcreteValue::*; - - 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()), - _ => panic!("{}", ValueError::CannotNegate(self.clone().to_value())), - } - } - - pub fn not(&self) -> Result { - use ConcreteValue::*; - - let not = match self { - Boolean(value) => ConcreteValue::Boolean(!value), - _ => return Err(ValueError::CannotNot(self.clone().to_value())), - }; - - Ok(not) - } - - pub fn equals(&self, other: &ConcreteValue) -> bool { - use ConcreteValue::*; - - match (self, other) { - (Boolean(left), Boolean(right)) => left == right, - (Byte(left), Byte(right)) => left == right, - (Character(left), Character(right)) => left == right, - (Float(left), Float(right)) => left == right, - (Integer(left), Integer(right)) => left == right, - (List(left), List(right)) => left == right, - (Range(left), Range(right)) => left == right, - (String(left), String(right)) => left == right, - _ => { - panic!( - "{}", - ValueError::CannotCompare( - Value::Concrete(self.clone()), - Value::Concrete(other.clone()) - ) - ) - } - } - } - - 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), - (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().to_value(), - other.clone().to_value(), - )) - } - }; - - Ok(less_than) - } - - pub fn less_than_or_equals(&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), - (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().to_value(), - other.clone().to_value(), - )) - } - }; - - Ok(less_than_or_equal) - } -} - -impl Clone for ConcreteValue { - fn clone(&self) -> Self { - trace!("Cloning concrete value {}", self); - - match self { - 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::Integer(integer) => ConcreteValue::Integer(*integer), - ConcreteValue::List(list) => ConcreteValue::List(list.clone()), - ConcreteValue::Range(range) => ConcreteValue::Range(*range), - ConcreteValue::String(string) => ConcreteValue::String(string.clone()), - } - } -} - -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}")?; - - if float.fract() == 0.0 { - write!(f, ".0")?; - } - - Ok(()) - } - ConcreteValue::Integer(integer) => write!(f, "{integer}"), - ConcreteValue::List(list) => { - write!(f, "[")?; - - for (index, item) in list.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}"), - } - } -} diff --git a/dust-lang/src/vm/action.rs b/dust-lang/src/vm/action.rs index 009b6f0..a23b810 100644 --- a/dust-lang/src/vm/action.rs +++ b/dust-lang/src/vm/action.rs @@ -1,44 +1,54 @@ use tracing::trace; use crate::{ - AbstractList, ConcreteValue, Instruction, Operand, Type, Value, + Instruction, Operand, Type, Value, instruction::{ - Add, Call, CallNative, Close, Divide, Equal, GetLocal, Jump, Less, LessEqual, LoadConstant, - LoadEncoded, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, Point, - Return, SetLocal, Subtract, Test, TestSet, TypeCode, + Add, Call, CallNative, Close, Divide, Equal, InstructionBuilder, Jump, Less, LessEqual, + LoadConstant, LoadEncoded, LoadFunction, LoadList, LoadSelf, Modulo, Multiply, Negate, Not, + Point, Return, Subtract, Test, TestSet, TypeCode, }, vm::CallFrame, }; -use super::{Pointer, Register, thread::ThreadData}; +use super::{Register, Thread, call_frame::Pointer}; -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Action { - pub logic: RunnerLogic, - pub instruction: Instruction, +pub struct ActionSequence { + pub actions: Vec, } -impl From for Action { - fn from(instruction: Instruction) -> Self { - let operation = instruction.operation(); - let logic = RUNNER_LOGIC_TABLE[operation.0 as usize]; +impl ActionSequence { + pub fn new<'a, T: IntoIterator>(instructions: T) -> Self { + let actions = instructions.into_iter().map(Action::from).collect(); + + Self { actions } + } +} + +#[derive(Clone, Copy, Debug)] +pub struct Action { + pub logic: ActionLogic, + pub instruction: InstructionBuilder, +} + +impl From<&Instruction> for Action { + fn from(instruction: &Instruction) -> Self { + let instruction = InstructionBuilder::from(instruction); + let logic = RUNNER_LOGIC_TABLE[instruction.operation.0 as usize]; Action { logic, instruction } } } -pub type RunnerLogic = fn(Instruction, &mut Thread) -> bool; +pub type ActionLogic = fn(InstructionBuilder, &mut Thread); -pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [ +pub const RUNNER_LOGIC_TABLE: [ActionLogic; 23] = [ point, close, - load_boolean, + load_encoded, load_constant, load_function, load_list, load_self, - get_local, - set_local, add, subtract, multiply, @@ -57,793 +67,81 @@ pub const RUNNER_LOGIC_TABLE: [RunnerLogic; 25] = [ r#return, ]; -pub(crate) fn get_next_action(data: &mut ThreadData) -> Action { - let current_call = data.call_stack.last_mut_unchecked(); - let instruction = current_call.chunk.instructions[current_call.ip]; - let operation = instruction.operation(); - let logic = RUNNER_LOGIC_TABLE[operation.0 as usize]; +pub fn point(instruction: InstructionBuilder, thread: &mut Thread) { + let destination = instruction.a_field as usize; + let to = instruction.b_field as usize; + let r#type = instruction.b_type; - current_call.ip += 1; - - Action { logic, instruction } -} - -pub fn point(instruction: Instruction, data: &mut ThreadData) -> bool { - let Point { from, to } = instruction.into(); - let from_register = data.get_register_unchecked(from); - let from_register_is_empty = matches!(from_register, Register::Empty); - - if !from_register_is_empty { - let register = Register::Pointer(Pointer::Register(to)); - - data.set_register(from, register); + match r#type { + TypeCode::BOOLEAN => { + thread.set_boolean_register(destination, Register::Pointer(Pointer::Register(to))); + } + TypeCode::BYTE => { + thread.set_byte_register(destination, Register::Pointer(Pointer::Register(to))); + } + TypeCode::CHARACTER => { + thread.set_character_register(destination, Register::Pointer(Pointer::Register(to))); + } + TypeCode::FLOAT => { + thread.set_float_register(destination, Register::Pointer(Pointer::Register(to))); + } + TypeCode::INTEGER => { + thread.set_integer_register(destination, Register::Pointer(Pointer::Register(to))); + } + TypeCode::STRING => { + thread.set_string_register(destination, Register::Pointer(Pointer::Register(to))); + } + unsupported => unsupported.panic_from_unsupported_code(), } - - data.next_action = get_next_action(data); - - false } -pub fn close(instruction: Instruction, data: &mut ThreadData) -> bool { - let Close { from, to } = instruction.into(); +pub fn close(instruction: InstructionBuilder, thread: &mut Thread) {} - for register_index in from..to { - data.set_register(register_index, Register::Empty); - } +pub fn load_encoded(instruction: InstructionBuilder, thread: &mut Thread) {} - data.next_action = get_next_action(data); +pub fn load_constant(instruction: InstructionBuilder, thread: &mut Thread) {} - false -} +pub fn load_list(instruction: InstructionBuilder, thread: &mut Thread) {} -pub fn load_boolean(instruction: Instruction, data: &mut ThreadData) -> bool { - let LoadEncoded { - destination, - value, - jump_next, - } = instruction.into(); - let boolean = Value::Concrete(ConcreteValue::Boolean(value)); - let register = Register::Value(boolean); +pub fn load_function(instruction: InstructionBuilder, thread: &mut Thread) {} - data.set_register(destination, register); +pub fn load_self(instruction: InstructionBuilder, thread: &mut Thread) {} - if jump_next { - let current_call = data.call_stack.last_mut_unchecked(); +pub fn get_local(instruction: InstructionBuilder, thread: &mut Thread) {} - current_call.ip += 1; - } +pub fn set_local(instruction: InstructionBuilder, thread: &mut Thread) {} - data.next_action = get_next_action(data); +pub fn add(instruction: InstructionBuilder, thread: &mut Thread) {} - false -} +pub fn subtract(instruction: InstructionBuilder, thread: &mut Thread) {} -pub fn load_constant(instruction: Instruction, data: &mut ThreadData) -> bool { - let LoadConstant { - destination, - constant_index, - jump_next, - } = instruction.into(); - let register = Register::Pointer(Pointer::Constant(constant_index)); +pub fn multiply(instruction: InstructionBuilder, thread: &mut Thread) {} - trace!("Load constant {constant_index} into R{destination}"); +pub fn divide(instruction: InstructionBuilder, thread: &mut Thread) {} - data.set_register(destination, register); +pub fn modulo(instruction: InstructionBuilder, thread: &mut Thread) {} - if jump_next { - let current_call = data.call_stack.last_mut_unchecked(); +pub fn test(instruction: InstructionBuilder, thread: &mut Thread) {} - current_call.ip += 1; - } +pub fn test_set(instruction: InstructionBuilder, thread: &mut Thread) {} - data.next_action = get_next_action(data); +pub fn equal(instruction: InstructionBuilder, thread: &mut Thread) {} - false -} +pub fn less(instruction: InstructionBuilder, thread: &mut Thread) {} -pub fn load_list(instruction: Instruction, data: &mut ThreadData) -> bool { - let LoadList { - destination, - start_register, - jump_next, - } = instruction.into(); - let mut item_pointers = Vec::with_capacity((destination - start_register) as usize); - let mut item_type = Type::Any; +pub fn less_equal(instruction: InstructionBuilder, thread: &mut Thread) {} - for register_index in start_register..destination { - match data.get_register_unchecked(register_index) { - Register::Empty => continue, - Register::Value(value) => { - if item_type == Type::Any { - item_type = value.r#type(); - } - } - Register::Pointer(pointer) => { - if item_type == Type::Any { - item_type = data.follow_pointer_unchecked(*pointer).r#type(); - } - } - } +pub fn negate(instruction: InstructionBuilder, thread: &mut Thread) {} - let pointer = Pointer::Register(register_index); +pub fn not(instruction: InstructionBuilder, thread: &mut Thread) {} - item_pointers.push(pointer); - } +pub fn jump(instruction: InstructionBuilder, thread: &mut Thread) {} - let list_value = Value::AbstractList(AbstractList { - item_type, - item_pointers, - }); - let register = Register::Value(list_value); +pub fn call(instruction: InstructionBuilder, thread: &mut Thread) {} - data.set_register(destination, register); +pub fn call_native(instruction: InstructionBuilder, thread: &mut Thread) {} - data.next_action = get_next_action(data); - - false -} - -pub fn load_function(instruction: Instruction, data: &mut ThreadData) -> bool { - let LoadFunction { - destination, - prototype_index, - jump_next, - } = instruction.into(); - let prototype_index = prototype_index as usize; - let current_call = data.call_stack.last_mut_unchecked(); - let prototype = ¤t_call.chunk.prototypes[prototype_index]; - let function = prototype.as_function(); - let register = Register::Value(Value::Function(function)); - - data.set_register(destination, register); - - data.next_action = get_next_action(data); - - false -} - -pub fn load_self(instruction: Instruction, data: &mut ThreadData) -> bool { - let LoadSelf { - destination, - jump_next, - } = instruction.into(); - let current_call = data.call_stack.last_mut_unchecked(); - let prototype = ¤t_call.chunk; - let function = prototype.as_function(); - let register = Register::Value(Value::Function(function)); - - data.set_register(destination, register); - - data.next_action = get_next_action(data); - - false -} - -pub fn get_local(instruction: Instruction, data: &mut ThreadData) -> bool { - let GetLocal { - destination, - local_index, - } = instruction.into(); - let local_register_index = data.get_local_register(local_index); - let register = Register::Pointer(Pointer::Register(local_register_index)); - - data.set_register(destination, register); - - data.next_action = get_next_action(data); - - false -} - -pub fn set_local(instruction: Instruction, data: &mut ThreadData) -> bool { - let SetLocal { - register_index, - local_index, - } = instruction.into(); - let local_register_index = data.get_local_register(local_index); - let register = Register::Pointer(Pointer::Register(register_index)); - - data.set_register(local_register_index, register); - - data.next_action = get_next_action(data); - - false -} - -pub fn add(instruction: Instruction, data: &mut ThreadData) -> bool { - let Add { - destination, - left, - left_type, - right, - right_type, - } = instruction.into(); - let sum = match (left_type, right_type) { - (TypeCode::INTEGER, TypeCode::INTEGER) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_integer() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_integer() - .unwrap_unchecked() - }; - - ConcreteValue::Integer(left + right) - } - (TypeCode::FLOAT, TypeCode::FLOAT) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_float() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_float() - .unwrap_unchecked() - }; - - ConcreteValue::Float(left + right) - } - _ => panic!("VM Error: Cannot add values"), - }; - let register = Register::Value(Value::Concrete(sum)); - - data.set_register(destination, register); - - data.next_action = get_next_action(data); - - false -} - -pub fn subtract(instruction: Instruction, data: &mut ThreadData) -> bool { - let Subtract { - destination, - left, - left_type, - right, - right_type, - } = instruction.into(); - let difference = match (left_type, right_type) { - (TypeCode::INTEGER, TypeCode::INTEGER) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_integer() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_integer() - .unwrap_unchecked() - }; - - ConcreteValue::Integer(left - right) - } - (TypeCode::FLOAT, TypeCode::FLOAT) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_float() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_float() - .unwrap_unchecked() - }; - - ConcreteValue::Float(left - right) - } - _ => panic!("VM Error: Cannot subtract values"), - }; - let register = Register::Value(Value::Concrete(difference)); - - data.set_register(destination, register); - - data.next_action = get_next_action(data); - - false -} - -pub fn multiply(instruction: Instruction, data: &mut ThreadData) -> bool { - let Multiply { - destination, - left, - left_type, - right, - right_type, - } = instruction.into(); - let product = match (left_type, right_type) { - (TypeCode::INTEGER, TypeCode::INTEGER) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_integer() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_integer() - .unwrap_unchecked() - }; - - ConcreteValue::Integer(left * right) - } - (TypeCode::FLOAT, TypeCode::FLOAT) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_float() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_float() - .unwrap_unchecked() - }; - - ConcreteValue::Float(left * right) - } - _ => panic!("VM Error: Cannot multiply values"), - }; - let register = Register::Value(Value::Concrete(product)); - - data.set_register(destination, register); - - data.next_action = get_next_action(data); - - false -} - -pub fn divide(instruction: Instruction, data: &mut ThreadData) -> bool { - let Divide { - destination, - left, - left_type, - right, - right_type, - } = instruction.into(); - let quotient = match (left_type, right_type) { - (TypeCode::INTEGER, TypeCode::INTEGER) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_integer() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_integer() - .unwrap_unchecked() - }; - - ConcreteValue::Integer(left / right) - } - (TypeCode::FLOAT, TypeCode::FLOAT) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_float() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_float() - .unwrap_unchecked() - }; - - ConcreteValue::Float(left / right) - } - _ => panic!("VM Error: Cannot divide values"), - }; - let register = Register::Value(Value::Concrete(quotient)); - - data.set_register(destination, register); - - data.next_action = get_next_action(data); - - false -} - -pub fn modulo(instruction: Instruction, data: &mut ThreadData) -> bool { - let Modulo { - destination, - left, - left_type, - right, - right_type, - } = instruction.into(); - let remainder = match (left_type, right_type) { - (TypeCode::INTEGER, TypeCode::INTEGER) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_integer() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_integer() - .unwrap_unchecked() - }; - - ConcreteValue::Integer(left % right) - } - _ => panic!("VM Error: Cannot modulo values"), - }; - let register = Register::Value(Value::Concrete(remainder)); - - data.set_register(destination, register); - - data.next_action = get_next_action(data); - - false -} - -pub fn test(instruction: Instruction, data: &mut ThreadData) -> bool { - let Test { - operand_register, - test_value, - } = instruction.into(); - let value = data.open_register_unchecked(operand_register); - let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value { - *boolean - } else { - panic!("VM Error: Expected boolean value for TEST operation",); - }; - - if boolean == test_value { - let current_call = data.call_stack.last_mut_unchecked(); - - current_call.ip += 1; - } - - data.next_action = get_next_action(data); - - false -} - -pub fn test_set(instruction: Instruction, data: &mut ThreadData) -> bool { - let TestSet { - destination, - argument, - test_value, - } = instruction.into(); - let value = data.get_argument_unchecked(argument); - let boolean = if let Value::Concrete(ConcreteValue::Boolean(boolean)) = value { - *boolean - } else { - panic!("VM Error: Expected boolean value for TEST_SET operation",); - }; - - if boolean == test_value { - } else { - let pointer = match argument { - Operand::Constant(constant_index) => Pointer::Constant(constant_index), - Operand::Register(register_index) => Pointer::Register(register_index), - }; - let register = Register::Pointer(pointer); - - data.set_register(destination, register); - } - - data.next_action = get_next_action(data); - - false -} - -pub fn equal(instruction: Instruction, data: &mut ThreadData) -> bool { - let Equal { - comparator, - left, - left_type, - right, - right_type, - } = instruction.into(); - let is_equal = match (left_type, right_type) { - (TypeCode::INTEGER, TypeCode::INTEGER) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_integer() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_integer() - .unwrap_unchecked() - }; - - left == right - } - (TypeCode::FLOAT, TypeCode::FLOAT) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_float() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_float() - .unwrap_unchecked() - }; - - left == right - } - (TypeCode::BOOLEAN, TypeCode::BOOLEAN) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_boolean() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_boolean() - .unwrap_unchecked() - }; - - left == right - } - (TypeCode::STRING, TypeCode::STRING) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_string() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_string() - .unwrap_unchecked() - }; - - left == right - } - _ => panic!("VM Error: Cannot compare values"), - }; - - if is_equal == comparator { - let current_call = data.call_stack.last_mut_unchecked(); - - current_call.ip += 1; - } - - data.next_action = get_next_action(data); - - false -} - -pub fn less(instruction: Instruction, data: &mut ThreadData) -> bool { - let Less { - comparator, - left, - left_type, - right, - right_type, - } = instruction.into(); - let is_less = match (left_type, right_type) { - (TypeCode::INTEGER, TypeCode::INTEGER) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_integer() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_integer() - .unwrap_unchecked() - }; - - left < right - } - (TypeCode::FLOAT, TypeCode::FLOAT) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_float() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_float() - .unwrap_unchecked() - }; - - left < right - } - _ => panic!("VM Error: Cannot compare values"), - }; - - if is_less == comparator { - let current_call = data.call_stack.last_mut_unchecked(); - - current_call.ip += 1; - } - - data.next_action = get_next_action(data); - - false -} - -pub fn less_equal(instruction: Instruction, data: &mut ThreadData) -> bool { - let LessEqual { - comparator, - left, - left_type, - right, - right_type, - } = instruction.into(); - let is_less_or_equal = match (left_type, right_type) { - (TypeCode::INTEGER, TypeCode::INTEGER) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_integer() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_integer() - .unwrap_unchecked() - }; - - left <= right - } - (TypeCode::FLOAT, TypeCode::FLOAT) => { - let left = unsafe { - data.get_argument_unchecked(left) - .as_float() - .unwrap_unchecked() - }; - let right = unsafe { - data.get_argument_unchecked(right) - .as_float() - .unwrap_unchecked() - }; - - left <= right - } - _ => panic!("VM Error: Cannot compare values"), - }; - - if is_less_or_equal == comparator { - let current_call = data.call_stack.last_mut_unchecked(); - - current_call.ip += 1; - } - - data.next_action = get_next_action(data); - - false -} - -pub fn negate(instruction: Instruction, data: &mut ThreadData) -> bool { - let Negate { - destination, - argument, - argument_type, - } = instruction.into(); - let argument = data.get_argument_unchecked(argument); - let negated = argument.negate(); - let register = Register::Value(negated); - - data.set_register(destination, register); - - data.next_action = get_next_action(data); - - false -} - -pub fn not(instruction: Instruction, data: &mut ThreadData) -> bool { - let Not { - destination, - argument, - } = instruction.into(); - let argument = data.get_argument_unchecked(argument); - let not = match argument { - Value::Concrete(ConcreteValue::Boolean(boolean)) => ConcreteValue::Boolean(!boolean), - _ => panic!("VM Error: Expected boolean value for NOT operation"), - }; - let register = Register::Value(Value::Concrete(not)); - - data.set_register(destination, register); - - data.next_action = get_next_action(data); - - false -} - -pub fn jump(instruction: Instruction, data: &mut ThreadData) -> bool { - let Jump { - offset, - is_positive, - } = instruction.into(); - let offset = offset as usize; - let current_call = data.call_stack.last_mut_unchecked(); - - if is_positive { - current_call.ip += offset; - } else { - current_call.ip -= offset + 1; - } - - data.next_action = get_next_action(data); - - false -} - -pub fn call(instruction: Instruction, data: &mut ThreadData) -> bool { - let Call { - destination: return_register, - function_register, - argument_count, - is_recursive, - } = instruction.into(); - let current_call = data.call_stack.last_unchecked(); - let first_argument_register = return_register - argument_count; - let prototype = if is_recursive { - current_call.chunk.clone() - } else { - let function = data - .open_register_unchecked(function_register) - .as_function() - .unwrap(); - - current_call.chunk.prototypes[function.prototype_index as usize].clone() - }; - let mut next_call = CallFrame::new(prototype, return_register); - let mut argument_index = 0; - - for register_index in first_argument_register..return_register { - let value_option = data.open_register_allow_empty_unchecked(register_index); - let argument = if let Some(value) = value_option { - value.clone() - } else { - continue; - }; - - next_call.registers[argument_index] = Register::Value(argument); - argument_index += 1; - } - - data.call_stack.push(next_call); - - data.next_action = get_next_action(data); - - false -} - -pub fn call_native(instruction: Instruction, data: &mut ThreadData) -> bool { - let CallNative { - destination, - function, - argument_count, - } = instruction.into(); - let first_argument_index = destination - argument_count; - let argument_range = first_argument_index..destination; - - function.call(data, destination, argument_range) -} - -pub fn r#return(instruction: Instruction, data: &mut ThreadData) -> bool { - trace!("Returning with call stack:\n{}", data.call_stack); - - let Return { - should_return_value, - return_register, - } = instruction.into(); - let (destination, return_value) = if data.call_stack.len() == 1 { - if should_return_value { - data.return_value_index = Some(return_register); - }; - - return true; - } else { - let return_value = data.empty_register_or_clone_constant_unchecked(return_register); - let destination = data.call_stack.pop_unchecked().return_register; - - (destination, return_value) - }; - - if should_return_value { - data.set_register(destination, Register::Value(return_value)); - } - - data.next_action = get_next_action(data); - - false -} +pub fn r#return(instruction: InstructionBuilder, thread: &mut Thread) {} #[cfg(test)] mod tests { @@ -852,15 +150,14 @@ mod tests { use super::*; - const ALL_OPERATIONS: [(Operation, RunnerLogic); 24] = [ + const ALL_OPERATIONS: [(Operation, ActionLogic); 23] = [ (Operation::POINT, point), (Operation::CLOSE, close), - (Operation::LOAD_ENCODED, load_boolean), + (Operation::LOAD_ENCODED, load_encoded), (Operation::LOAD_CONSTANT, load_constant), (Operation::LOAD_LIST, load_list), + (Operation::LOAD_FUNCTION, load_function), (Operation::LOAD_SELF, load_self), - (Operation::GET_LOCAL, get_local), - (Operation::SET_LOCAL, set_local), (Operation::ADD, add), (Operation::SUBTRACT, subtract), (Operation::MULTIPLY, multiply), diff --git a/dust-lang/src/vm/call_frame.rs b/dust-lang/src/vm/call_frame.rs index a1ea6f0..04fa8ea 100644 --- a/dust-lang/src/vm/call_frame.rs +++ b/dust-lang/src/vm/call_frame.rs @@ -5,7 +5,7 @@ use std::{ use smallvec::{SmallVec, smallvec}; -use crate::{Chunk, DustString}; +use crate::{Chunk, DustString, instruction::TypeCode}; #[derive(Debug)] pub struct CallFrame { @@ -69,5 +69,11 @@ impl RegisterTable { pub enum Register { Empty, Value(T), - Pointer(*const T), + Pointer(Pointer), +} + +#[derive(Debug, Clone, Copy)] +pub enum Pointer { + Register(usize), + Constant(usize), } diff --git a/dust-lang/src/vm/mod.rs b/dust-lang/src/vm/mod.rs index b2cf91d..533c78c 100644 --- a/dust-lang/src/vm/mod.rs +++ b/dust-lang/src/vm/mod.rs @@ -10,7 +10,6 @@ use std::{ }; pub use action::Action; -pub(crate) use action::get_next_action; pub use call_frame::{CallFrame, Register, RegisterTable}; pub use thread::Thread; @@ -50,7 +49,7 @@ impl Vm { Builder::new() .name(thread_name) .spawn(move || { - let mut main_thread = Thread::new(main_chunk); + let main_thread = Thread::new(main_chunk); let value_option = main_thread.run(); let _ = tx.send(value_option); }) diff --git a/dust-lang/src/vm/thread.rs b/dust-lang/src/vm/thread.rs index ff1b0f9..0022d09 100644 --- a/dust-lang/src/vm/thread.rs +++ b/dust-lang/src/vm/thread.rs @@ -1,16 +1,18 @@ use std::{sync::Arc, thread::JoinHandle}; -use tracing::{info, trace}; +use tracing::{info, span}; -use crate::{Chunk, DustString, Span, Value, vm::CallFrame}; +use crate::{ + Chunk, DustString, Span, Value, + vm::{CallFrame, action::ActionSequence}, +}; -use super::{Action, Register}; +use super::{Register, call_frame::Pointer}; pub struct Thread { chunk: Arc, call_stack: Vec, - next_action: Action, - return_value: Option, + return_value: Option>, spawned_threads: Vec>, } @@ -22,18 +24,18 @@ impl Thread { call_stack.push(main_call); - let first_action = Action::from(*chunk.instructions.first().unwrap()); - Thread { chunk, call_stack, - next_action: first_action, return_value: None, spawned_threads: Vec::new(), } } - pub fn run(&mut self) -> Option { + pub fn run(mut self) -> Option { + let span = span!(tracing::Level::INFO, "Thread"); + let _ = span.enter(); + info!( "Starting thread with {}", self.chunk @@ -42,17 +44,20 @@ impl Thread { .unwrap_or_else(|| DustString::from("anonymous")) ); + let actions = ActionSequence::new(&self.chunk.instructions); + loop { - trace!("Instruction: {}", self.next_action.instruction); + let ip = self.current_frame().ip; + let next_action = if cfg!(debug_assertions) { + actions.actions.get(ip).unwrap() + } else { + unsafe { actions.actions.get_unchecked(ip) } + }; - let should_end = (self.next_action.logic)(self.next_action.instruction, self); + (next_action.logic)(next_action.instruction, &mut self); - if should_end { - self.spawned_threads.into_iter().for_each(|join_handle| { - let _ = join_handle.join(); - }); - - return self.return_value.take(); + if let Some(return_value) = self.return_value { + return return_value; } } } @@ -105,7 +110,7 @@ impl Thread { match register { Register::Value(boolean) => *boolean, - Register::Pointer(pointer) => unsafe { **pointer }, + Register::Pointer(pointer) => self.follow_pointer_to_boolean(*pointer), Register::Empty => panic!("Attempted to get a boolean from an empty register"), } } @@ -147,7 +152,7 @@ impl Thread { match register { Register::Value(byte) => *byte, - Register::Pointer(pointer) => unsafe { **pointer }, + Register::Pointer(pointer) => self.follow_pointer_to_byte(*pointer), Register::Empty => panic!("Attempted to get a byte from an empty register"), } } @@ -189,7 +194,7 @@ impl Thread { match register { Register::Value(character) => *character, - Register::Pointer(pointer) => unsafe { **pointer }, + Register::Pointer(pointer) => self.follow_pointer_to_character(*pointer), Register::Empty => panic!("Attempted to get a character from an empty register"), } } @@ -231,11 +236,30 @@ impl Thread { match register { Register::Value(float) => *float, - Register::Pointer(pointer) => unsafe { **pointer }, + Register::Pointer(pointer) => self.follow_pointer_to_float(*pointer), Register::Empty => panic!("Attempted to get a float from an empty register"), } } + pub fn set_float_register(&mut self, index: usize, new_register: Register) { + let old_register = if cfg!(debug_assertions) { + self.current_frame_mut() + .registers + .floats + .get_mut(index as usize) + .unwrap() + } else { + unsafe { + self.current_frame_mut() + .registers + .floats + .get_unchecked_mut(index as usize) + } + }; + + *old_register = new_register; + } + pub fn get_integer_register(&self, index: usize) -> i64 { let register = if cfg!(debug_assertions) { self.current_frame() @@ -254,7 +278,7 @@ impl Thread { match register { Register::Value(integer) => *integer, - Register::Pointer(pointer) => unsafe { **pointer }, + Register::Pointer(pointer) => self.follow_pointer_to_integer(*pointer), Register::Empty => panic!("Attempted to get an integer from an empty register"), } } @@ -296,13 +320,7 @@ impl Thread { match register { Register::Value(string) => string, - Register::Pointer(pointer) => { - if cfg!(debug_assertions) { - unsafe { pointer.as_ref().unwrap() } - } else { - unsafe { pointer.as_ref().unwrap_unchecked() } - } - } + Register::Pointer(pointer) => self.follow_pointer_to_string(*pointer), Register::Empty => panic!("Attempted to get a string from an empty register"), } } @@ -325,4 +343,108 @@ impl Thread { *old_register = new_register; } + + pub fn follow_pointer_to_boolean(&self, pointer: Pointer) -> bool { + match pointer { + Pointer::Register(register_index) => self.get_boolean_register(register_index), + Pointer::Constant(_) => { + panic!("Attempted to access boolean from a constant pointer") + } + } + } + + pub fn follow_pointer_to_byte(&self, pointer: Pointer) -> u8 { + match pointer { + Pointer::Register(register_index) => self.get_byte_register(register_index), + Pointer::Constant(_) => { + panic!("Attempted to access byte from a constant pointer") + } + } + } + + pub fn follow_pointer_to_character(&self, pointer: Pointer) -> char { + match pointer { + Pointer::Register(register_index) => self.get_character_register(register_index), + Pointer::Constant(constant_index) => { + if cfg!(debug_assertions) { + *self + .chunk + .constant_characters + .get(constant_index as usize) + .unwrap() + } else { + unsafe { + *self + .chunk + .constant_characters + .get_unchecked(constant_index as usize) + } + } + } + } + } + + pub fn follow_pointer_to_float(&self, pointer: Pointer) -> f64 { + match pointer { + Pointer::Register(register_index) => self.get_float_register(register_index), + Pointer::Constant(constant_index) => { + if cfg!(debug_assertions) { + *self + .chunk + .constant_floats + .get(constant_index as usize) + .unwrap() + } else { + unsafe { + *self + .chunk + .constant_floats + .get_unchecked(constant_index as usize) + } + } + } + } + } + + pub fn follow_pointer_to_integer(&self, pointer: Pointer) -> i64 { + match pointer { + Pointer::Register(register_index) => self.get_integer_register(register_index), + Pointer::Constant(constant_index) => { + if cfg!(debug_assertions) { + *self + .chunk + .constant_integers + .get(constant_index as usize) + .unwrap() + } else { + unsafe { + *self + .chunk + .constant_integers + .get_unchecked(constant_index as usize) + } + } + } + } + } + + pub fn follow_pointer_to_string(&self, pointer: Pointer) -> &DustString { + match pointer { + Pointer::Register(register_index) => self.get_string_register(register_index), + Pointer::Constant(constant_index) => { + if cfg!(debug_assertions) { + self.chunk + .constant_strings + .get(constant_index as usize) + .unwrap() + } else { + unsafe { + self.chunk + .constant_strings + .get_unchecked(constant_index as usize) + } + } + } + } + } } diff --git a/dust-lang/tests/logic/and.rs b/dust-lang/tests/logic/and.rs index 68f9d6a..0bce420 100644 --- a/dust-lang/tests/logic/and.rs +++ b/dust-lang/tests/logic/and.rs @@ -15,10 +15,10 @@ fn true_and_true() { return_type: Type::Boolean, }, smallvec![ - Instruction::load_boolean(0, true, false), + Instruction::load_encoded(0, true, false), Instruction::test(0, true), Instruction::jump(1, true), - Instruction::load_boolean(1, true, false), + Instruction::load_encoded(1, true, false), Instruction::r#return(true), ], smallvec![ @@ -51,10 +51,10 @@ fn false_and_false() { return_type: Type::Boolean, }, smallvec![ - Instruction::load_boolean(0, false, false), + Instruction::load_encoded(0, false, false), Instruction::test(0, true), Instruction::jump(1, true), - Instruction::load_boolean(1, false, false), + Instruction::load_encoded(1, false, false), Instruction::r#return(true), ], smallvec![ @@ -87,10 +87,10 @@ fn false_and_true() { return_type: Type::Boolean, }, smallvec![ - Instruction::load_boolean(0, false, false), + Instruction::load_encoded(0, false, false), Instruction::test(0, true), Instruction::jump(1, true), - Instruction::load_boolean(1, true, false), + Instruction::load_encoded(1, true, false), Instruction::r#return(true), ], smallvec![ @@ -123,10 +123,10 @@ fn true_and_false() { return_type: Type::Boolean, }, smallvec![ - Instruction::load_boolean(0, true, false), + Instruction::load_encoded(0, true, false), Instruction::test(0, true), Instruction::jump(1, true), - Instruction::load_boolean(1, false, false), + Instruction::load_encoded(1, false, false), Instruction::r#return(true), ], smallvec![ diff --git a/dust-lang/tests/logic/and_and.rs b/dust-lang/tests/logic/and_and.rs index 53c38ba..a0807d2 100644 --- a/dust-lang/tests/logic/and_and.rs +++ b/dust-lang/tests/logic/and_and.rs @@ -15,13 +15,13 @@ fn true_and_true_and_true() { return_type: Type::Boolean, }, smallvec![ - Instruction::load_boolean(0, true, false), + Instruction::load_encoded(0, true, false), Instruction::test(0, true), Instruction::jump(1, true), - Instruction::load_boolean(1, true, false), + Instruction::load_encoded(1, true, false), Instruction::test(1, true), Instruction::jump(1, true), - Instruction::load_boolean(2, true, false), + Instruction::load_encoded(2, true, false), Instruction::r#return(true), ], smallvec![ diff --git a/dust-lang/tests/logic/or.rs b/dust-lang/tests/logic/or.rs index 0886f23..87eb26e 100644 --- a/dust-lang/tests/logic/or.rs +++ b/dust-lang/tests/logic/or.rs @@ -15,10 +15,10 @@ fn true_or_false() { return_type: Type::Boolean, }, smallvec![ - Instruction::load_boolean(0, true, false), + Instruction::load_encoded(0, true, false), Instruction::test(0, false), Instruction::jump(1, true), - Instruction::load_boolean(1, false, false), + Instruction::load_encoded(1, false, false), Instruction::r#return(true), ], smallvec![ diff --git a/dust-lang/tests/unary_operations.rs b/dust-lang/tests/unary_operations.rs index 3b44024..129f441 100644 --- a/dust-lang/tests/unary_operations.rs +++ b/dust-lang/tests/unary_operations.rs @@ -39,7 +39,7 @@ fn not() { return_type: Type::Boolean, }, vec![ - (Instruction::load_boolean(0, true, false), Span(1, 5)), + (Instruction::load_encoded(0, true, false), Span(1, 5)), (Instruction::not(1, Operand::Register(0)), Span(0, 1)), (Instruction::r#return(true), Span(5, 5)), ],