From 8af8e48ebdc7df20ed64241285f08cb451e2b830 Mon Sep 17 00:00:00 2001 From: Jeff Date: Sat, 9 Nov 2024 21:40:33 -0500 Subject: [PATCH] Begin large refactor to enhance type handling --- dust-lang/src/chunk.rs | 87 ++----- dust-lang/src/compiler.rs | 408 ++++++++++++++++++++++--------- dust-lang/src/disassembler.rs | 50 ++-- dust-lang/src/instruction.rs | 47 ++-- dust-lang/src/lib.rs | 2 +- dust-lang/src/native_function.rs | 14 +- dust-lang/src/optimizer.rs | 95 ++++--- dust-lang/src/type.rs | 14 +- dust-lang/src/vm.rs | 11 +- dust-lang/tests/control_flow.rs | 51 ++-- dust-lang/tests/functions.rs | 24 +- dust-lang/tests/logic.rs | 4 +- dust-lang/tests/loops.rs | 4 +- dust-lang/tests/math.rs | 8 +- dust-lang/tests/scopes.rs | 28 +-- dust-lang/tests/variables.rs | 4 +- 16 files changed, 479 insertions(+), 372 deletions(-) diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 54249ff..80982a8 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -11,7 +11,7 @@ use std::{ use serde::{Deserialize, Serialize}; -use crate::{Disassembler, Instruction, Operation, Span, Type, Value}; +use crate::{Disassembler, Instruction, Span, Type, Value}; /// In-memory representation of a Dust program or function. /// @@ -24,6 +24,7 @@ pub struct Chunk { constants: Vec, locals: Vec, + return_type: Type, current_scope: Scope, block_index: u8, } @@ -35,6 +36,7 @@ impl Chunk { instructions: Vec::new(), constants: Vec::new(), locals: Vec::new(), + return_type: Type::None, current_scope: Scope::default(), block_index: 0, } @@ -51,6 +53,7 @@ impl Chunk { instructions, constants, locals, + return_type: Type::None, current_scope: Scope::default(), block_index: 0, } @@ -172,78 +175,26 @@ impl Chunk { } } - pub fn get_constant_type(&self, constant_index: u8) -> Option { + pub fn get_constant_type(&self, constant_index: u8) -> Result { self.constants .get(constant_index as usize) .map(|value| value.r#type()) - } - - pub fn get_local_type(&self, local_index: u8) -> Option { - self.locals.get(local_index as usize)?.r#type.clone() - } - - pub fn get_register_type(&self, register_index: u8) -> Option { - self.instructions - .iter() - .enumerate() - .find_map(|(index, (instruction, _))| { - if let Operation::LoadList = instruction.operation() { - if instruction.a() == register_index { - let mut length = (instruction.c() - instruction.b() + 1) as usize; - let mut item_type = Type::Any; - let distance_to_end = self.len() - index; - - for (instruction, _) in self - .instructions() - .iter() - .rev() - .skip(distance_to_end) - .take(length) - { - if let Operation::Close = instruction.operation() { - length -= (instruction.c() - instruction.b()) as usize; - } else if let Type::Any = item_type { - item_type = instruction.yielded_type(self).unwrap_or(Type::Any); - } - } - - return Some(Type::List { - item_type: Box::new(item_type), - length, - }); - } - } - - if instruction.yields_value() && instruction.a() == register_index { - instruction.yielded_type(self) - } else { - None - } + .ok_or(ChunkError::ConstantIndexOutOfBounds { + index: constant_index as usize, }) } - pub fn return_type(&self) -> Option { - let returns_value = self - .instructions() - .last() - .map(|(instruction, _)| { - debug_assert!(matches!(instruction.operation(), Operation::Return)); - - instruction.b_as_boolean() + pub fn get_local_type(&self, local_index: u8) -> Result<&Type, ChunkError> { + self.locals + .get(local_index as usize) + .map(|local| &local.r#type) + .ok_or(ChunkError::LocalIndexOutOfBounds { + index: local_index as usize, }) - .unwrap_or(false); + } - if returns_value { - self.instructions.iter().rev().find_map(|(instruction, _)| { - if instruction.yields_value() { - instruction.yielded_type(self) - } else { - None - } - }) - } else { - None - } + pub fn return_type(&self) -> &Type { + &self.return_type } pub fn disassembler(&self) -> Disassembler { @@ -288,7 +239,7 @@ pub struct Local { pub identifier_index: u8, /// The expected type of the local's value. - pub r#type: Option, + pub r#type: Type, /// Whether the local is mutable. pub is_mutable: bool, @@ -299,7 +250,7 @@ pub struct Local { impl Local { /// Creates a new Local instance. - pub fn new(identifier_index: u8, r#type: Option, mutable: bool, scope: Scope) -> Self { + pub fn new(identifier_index: u8, r#type: Type, mutable: bool, scope: Scope) -> Self { Self { identifier_index, r#type, @@ -350,7 +301,6 @@ pub enum ChunkError { ConstantIndexOutOfBounds { index: usize }, InstructionIndexOutOfBounds { index: usize }, LocalIndexOutOfBounds { index: usize }, - PoisonedChunk, } impl Display for ChunkError { @@ -365,7 +315,6 @@ impl Display for ChunkError { ChunkError::LocalIndexOutOfBounds { index } => { write!(f, "Local index {} out of bounds", index) } - ChunkError::PoisonedChunk => write!(f, "Chunk is poisoned"), } } } diff --git a/dust-lang/src/compiler.rs b/dust-lang/src/compiler.rs index ead2731..0fc2a0f 100644 --- a/dust-lang/src/compiler.rs +++ b/dust-lang/src/compiler.rs @@ -13,9 +13,9 @@ use std::{ use colored::Colorize; use crate::{ - optimize, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Instruction, LexError, - Lexer, Local, NativeFunction, Operation, Scope, Span, Token, TokenKind, TokenOwned, Type, - Value, + AnnotatedError, Chunk, ChunkError, DustError, FunctionType, Instruction, LexError, Lexer, + Local, NativeFunction, Operation, Optimizer, Scope, Span, Token, TokenKind, TokenOwned, Type, + TypeConflict, Value, }; /// Compiles the input and returns a chunk. @@ -51,7 +51,7 @@ pub struct Compiler<'src> { local_definitions: Vec, optimization_count: usize, - previous_is_expression: bool, + previous_expression_type: Type, minimum_register: u8, current_token: Token<'src>, @@ -77,7 +77,7 @@ impl<'src> Compiler<'src> { lexer, local_definitions: Vec::new(), optimization_count: 0, - previous_is_expression: false, + previous_expression_type: Type::None, minimum_register: 0, current_token, current_position, @@ -167,10 +167,10 @@ impl<'src> Compiler<'src> { }) } - pub fn declare_local( + fn declare_local( &mut self, identifier: &str, - r#type: Option, + r#type: Type, is_mutable: bool, scope: Scope, register_index: u8, @@ -230,35 +230,27 @@ impl<'src> Compiler<'src> { }) } - fn get_last_value_operation(&self) -> Option { - self.chunk - .instructions() - .iter() - .last() - .map(|(instruction, _)| instruction.operation()) - } + fn get_last_operations(&self) -> Option<[Operation; COUNT]> { + let mut n_operations = [Operation::Return; COUNT]; - fn get_last_instructions(&self) -> Option<[Operation; COUNT]> { - let mut operations = [Operation::Return; COUNT]; - - for (index, (instruction, _)) in self - .chunk - .instructions() - .iter() - .rev() - .take(COUNT) - .enumerate() - { - operations[index] = instruction.operation(); + for (nth, operation) in n_operations.iter_mut().rev().zip( + self.chunk + .instructions() + .iter() + .rev() + .map(|(instruction, _)| instruction.operation()), + ) { + *nth = operation; } - Some(operations) + Some(n_operations) } fn get_last_jumpable_mut(&mut self) -> Option<&mut Instruction> { self.chunk .instructions_mut() .iter_mut() + .rev() .find_map(|(instruction, _)| { if let Operation::LoadBoolean | Operation::LoadConstant = instruction.operation() { Some(instruction) @@ -268,6 +260,101 @@ impl<'src> Compiler<'src> { }) } + pub fn get_instruction_type(&self, instruction: &Instruction) -> Result { + use Operation::*; + + match instruction.operation() { + Add | Divide | Modulo | Multiply | Subtract => { + if instruction.b_is_constant() { + self.chunk + .get_constant_type(instruction.b()) + .map_err(|error| CompileError::Chunk { + error, + position: self.current_position, + }) + } else { + self.get_register_type(instruction.b()) + } + } + LoadBoolean | Not => Ok(Type::Boolean), + Negate => { + if instruction.b_is_constant() { + self.chunk + .get_constant_type(instruction.b()) + .map_err(|error| CompileError::Chunk { + error, + position: self.current_position, + }) + } else { + self.get_register_type(instruction.b()) + } + } + LoadConstant => self + .chunk + .get_constant_type(instruction.b()) + .map_err(|error| CompileError::Chunk { + error, + position: self.current_position, + }), + LoadList => self.get_register_type(instruction.a()), + GetLocal => self + .chunk + .get_local_type(instruction.b()) + .cloned() + .map_err(|error| CompileError::Chunk { + error, + position: self.current_position, + }), + CallNative => { + let native_function = NativeFunction::from(instruction.b()); + + Ok(*native_function.r#type().return_type) + } + _ => Ok(Type::None), + } + } + + pub fn get_register_type(&self, register_index: u8) -> Result { + for (index, (instruction, _)) in self.chunk.instructions().iter().enumerate() { + if let Operation::LoadList = instruction.operation() { + if instruction.a() == register_index { + let mut length = (instruction.c() - instruction.b() + 1) as usize; + let mut item_type = Type::Any; + let distance_to_end = self.chunk.len() - index; + + for (instruction, _) in self + .chunk + .instructions() + .iter() + .rev() + .skip(distance_to_end) + .take(length) + { + if let Operation::Close = instruction.operation() { + length -= (instruction.c() - instruction.b()) as usize; + } else if let Type::Any = item_type { + item_type = self.get_instruction_type(instruction)?; + } + } + + return Ok(Type::List { + item_type: Box::new(item_type), + length, + }); + } + } + + if instruction.yields_value() && instruction.a() == register_index { + return self.get_instruction_type(instruction); + } + } + + Err(CompileError::CannotResolveRegisterType { + register_index: register_index as usize, + position: self.current_position, + }) + } + fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), CompileError> { let constant_index = self.chunk.push_or_get_constant(value); let register = self.next_register(); @@ -294,7 +381,7 @@ impl<'src> Compiler<'src> { position, ); - self.previous_is_expression = true; + self.previous_expression_type = Type::Boolean; Ok(()) } else { @@ -318,7 +405,7 @@ impl<'src> Compiler<'src> { self.emit_constant(value, position)?; - self.previous_is_expression = true; + self.previous_expression_type = Type::Byte; Ok(()) } else { @@ -340,7 +427,7 @@ impl<'src> Compiler<'src> { self.emit_constant(value, position)?; - self.previous_is_expression = true; + self.previous_expression_type = Type::Character; Ok(()) } else { @@ -368,7 +455,7 @@ impl<'src> Compiler<'src> { self.emit_constant(value, position)?; - self.previous_is_expression = true; + self.previous_expression_type = Type::Float; Ok(()) } else { @@ -396,7 +483,7 @@ impl<'src> Compiler<'src> { self.emit_constant(value, position)?; - self.previous_is_expression = true; + self.previous_expression_type = Type::Integer; Ok(()) } else { @@ -418,7 +505,9 @@ impl<'src> Compiler<'src> { self.emit_constant(value, position)?; - self.previous_is_expression = true; + self.previous_expression_type = Type::String { + length: Some(text.len()), + }; Ok(()) } else { @@ -435,8 +524,6 @@ impl<'src> Compiler<'src> { self.parse_expression()?; self.expect(Token::RightParenthesis)?; - self.previous_is_expression = true; - Ok(()) } @@ -486,7 +573,9 @@ impl<'src> Compiler<'src> { self.emit_instruction(instruction, operator_position); - self.previous_is_expression = true; + if let TokenKind::Bang = operator.kind() { + self.previous_expression_type = Type::Boolean; + } Ok(()) } @@ -640,17 +729,17 @@ impl<'src> Compiler<'src> { | Token::SlashEqual | Token::PercentEqual = operator { - self.previous_is_expression = false; + self.previous_expression_type = Type::None; } else { - self.previous_is_expression = true; + self.previous_expression_type = self.get_instruction_type(&left_instruction)?; } Ok(()) } fn parse_comparison_binary(&mut self) -> Result<(), CompileError> { - if let Some(Operation::Equal | Operation::Less | Operation::LessEqual) = - self.get_last_value_operation() + if let Some([Operation::Equal | Operation::Less | Operation::LessEqual, _, _, _]) = + self.get_last_operations() { return Err(CompileError::CannotChainComparison { position: self.current_position, @@ -726,7 +815,6 @@ impl<'src> Compiler<'src> { let register = self.next_register(); self.emit_instruction(instruction, operator_position); - self.emit_instruction(Instruction::jump(1, true), operator_position); self.emit_instruction( Instruction::load_boolean(register, true, true), @@ -737,7 +825,7 @@ impl<'src> Compiler<'src> { operator_position, ); - self.previous_is_expression = true; + self.previous_expression_type = Type::Boolean; Ok(()) } @@ -770,7 +858,7 @@ impl<'src> Compiler<'src> { self.emit_instruction(Instruction::jump(jump_distance, true), operator_position); self.parse_sub_expression(&rule.precedence)?; - self.previous_is_expression = true; + self.previous_expression_type = Type::Boolean; Ok(()) } @@ -797,9 +885,9 @@ impl<'src> Compiler<'src> { let scope = self.chunk.current_scope(); self.emit_instruction(Instruction::load_self(register), start_position); - self.declare_local(identifier, None, false, scope, register); + self.declare_local(identifier, Type::SelfChunk, false, scope, register); - self.previous_is_expression = true; + self.previous_expression_type = Type::SelfChunk; return Ok(()); } else { @@ -842,18 +930,29 @@ impl<'src> Compiler<'src> { start_position, ); - self.previous_is_expression = false; - } else { - let register = self.next_register(); + self.previous_expression_type = Type::None; - self.emit_instruction( - Instruction::get_local(register, local_index), - self.previous_position, - ); + let mut optimizer = Optimizer::new(self.chunk.instructions_mut()); + let optimized = Optimizer::optimize_set_local(&mut optimizer); - self.previous_is_expression = true; + if optimized { + self.optimization_count += 1; + } + + return Ok(()); } + let register = self.next_register(); + + self.emit_instruction( + Instruction::get_local(register, local_index), + self.previous_position, + ); + + let local = self.get_local(local_index)?; + + self.previous_expression_type = local.r#type.clone(); + Ok(()) } @@ -895,12 +994,23 @@ impl<'src> Compiler<'src> { 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()?; + if expected_register > start_register { + if let Err(conflict) = item_type.check(&self.previous_expression_type) { + return Err(CompileError::ListItemTypeConflict { + conflict, + position: self.previous_position, + }); + } + } + + item_type = self.previous_expression_type.clone(); let actual_register = self.next_register() - 1; if expected_register < actual_register { @@ -921,7 +1031,10 @@ impl<'src> Compiler<'src> { Span(start, end), ); - self.previous_is_expression = true; + self.previous_expression_type = Type::List { + item_type: Box::new(item_type), + length: (to_register - start_register) as usize, + }; Ok(()) } @@ -931,12 +1044,12 @@ impl<'src> Compiler<'src> { self.parse_expression()?; if matches!( - self.get_last_instructions(), + self.get_last_operations(), Some([ - Operation::LoadBoolean, - Operation::LoadBoolean, + Operation::Equal | Operation::Less | Operation::LessEqual, Operation::Jump, - Operation::Equal | Operation::Less | Operation::LessEqual + Operation::LoadBoolean, + Operation::LoadBoolean, ]) ) { self.chunk.instructions_mut().pop(); @@ -959,24 +1072,10 @@ impl<'src> Compiler<'src> { let if_block_end = self.chunk.len(); let mut if_block_distance = (if_block_end - if_block_start) as u8; - - let if_block_is_expression = self - .chunk - .instructions() - .iter() - .rev() - .find_map(|(instruction, _)| { - if !matches!(instruction.operation(), Operation::Jump | Operation::Move) { - Some(instruction.yields_value()) - } else { - None - } - }) - .unwrap_or(false); - + let if_block_type = self.previous_expression_type.clone(); let if_last_register = self.next_register().saturating_sub(1); - if let Token::Else = self.current_token { + let has_else_statement = if let Token::Else = self.current_token { self.advance()?; if let Token::LeftBrace = self.current_token { @@ -988,18 +1087,29 @@ impl<'src> Compiler<'src> { position: self.current_position, }); } - } else { - self.previous_is_expression = false; - } - self.previous_is_expression = if_block_is_expression && self.previous_is_expression; + true + } else if self.previous_expression_type != Type::None { + return Err(CompileError::IfMissingElse { + position: Span(if_block_start_position.0, self.current_position.1), + }); + } else { + false + }; let else_block_end = self.chunk.len(); let else_block_distance = (else_block_end - if_block_end) as u8; + if let Err(conflict) = if_block_type.check(&self.previous_expression_type) { + return Err(CompileError::IfElseBranchMismatch { + conflict, + position: Span(if_block_start_position.0, self.current_position.1), + }); + } + match else_block_distance { 0 => {} - 1 => { + 1 if !has_else_statement => { if let Some(skippable) = self.get_last_jumpable_mut() { skippable.set_c_to_boolean(true); } else { @@ -1014,6 +1124,7 @@ impl<'src> Compiler<'src> { ); } } + 1 => {} 2.. => { if_block_distance += 1; @@ -1036,13 +1147,12 @@ impl<'src> Compiler<'src> { ); if self.chunk.len() >= 4 { - let possible_comparison_statement = { - let start = self.chunk.len() - 4; + let mut optimizer = Optimizer::new(self.chunk.instructions_mut()); + let optimized = optimizer.optimize_comparison(); - &mut self.chunk.instructions_mut()[start..] - }; - - self.optimization_count += optimize(possible_comparison_statement); + if optimized { + self.optimization_count += 1 + } } let else_last_register = self.next_register().saturating_sub(1); @@ -1065,12 +1175,12 @@ impl<'src> Compiler<'src> { self.parse_expression()?; if matches!( - self.get_last_instructions(), + self.get_last_operations(), Some([ - Operation::LoadBoolean, - Operation::LoadBoolean, + Operation::Equal | Operation::Less | Operation::LessEqual, Operation::Jump, - Operation::Equal | Operation::Less | Operation::LessEqual + Operation::LoadBoolean, + Operation::LoadBoolean, ],) ) { self.chunk.instructions_mut().pop(); @@ -1097,7 +1207,7 @@ impl<'src> Compiler<'src> { self.emit_instruction(jump_back, self.current_position); - self.previous_is_expression = false; + self.previous_expression_type = Type::None; Ok(()) } @@ -1128,7 +1238,8 @@ impl<'src> Compiler<'src> { let end = self.previous_position.1; let to_register = self.next_register(); let argument_count = to_register - start_register; - self.previous_is_expression = function.r#type().return_type.is_some(); + + self.previous_expression_type = Type::Function(function.r#type()); self.emit_instruction( Instruction::call_native(to_register, function, argument_count), @@ -1154,7 +1265,7 @@ impl<'src> Compiler<'src> { fn parse_expression(&mut self) -> Result<(), CompileError> { self.parse(Precedence::None)?; - if !self.previous_is_expression || self.chunk.is_empty() { + if self.previous_expression_type == Type::None || self.chunk.is_empty() { return Err(CompileError::ExpectedExpression { found: self.previous_token.to_owned(), position: self.current_position, @@ -1185,7 +1296,7 @@ impl<'src> Compiler<'src> { self.emit_instruction(Instruction::r#return(has_return_value), Span(start, end)); - self.previous_is_expression = false; + self.previous_expression_type = Type::None; Ok(()) } @@ -1195,7 +1306,7 @@ impl<'src> Compiler<'src> { self.emit_instruction(Instruction::r#return(false), self.current_position); } else { self.emit_instruction( - Instruction::r#return(self.previous_is_expression), + Instruction::r#return(self.previous_expression_type != Type::None), self.current_position, ); } @@ -1220,11 +1331,11 @@ impl<'src> Compiler<'src> { position, }); }; - let r#type = if self.allow(Token::Colon)? { - let r#type = self.parse_type_from(self.current_token, self.current_position)?; - + let explicit_type = if self.allow(Token::Colon)? { self.advance()?; + let r#type = self.parse_type_from(self.current_token, self.current_position)?; + Some(r#type) } else { None @@ -1233,7 +1344,12 @@ impl<'src> Compiler<'src> { self.expect(Token::Equal)?; self.parse_expression()?; - let register = self.next_register().saturating_sub(1); + let register = self.next_register() - 1; + let r#type = if let Some(r#type) = explicit_type { + r#type + } else { + self.get_register_type(register)? + }; let (local_index, _) = self.declare_local(identifier, r#type, is_mutable, scope, register); self.emit_instruction( @@ -1241,7 +1357,7 @@ impl<'src> Compiler<'src> { position, ); - self.previous_is_expression = false; + self.previous_expression_type = Type::None; Ok(()) } @@ -1292,7 +1408,7 @@ impl<'src> Compiler<'src> { let scope = function_compiler.chunk.current_scope(); let (_, identifier_index) = function_compiler.declare_local( parameter, - Some(r#type.clone()), + r#type.clone(), is_mutable, scope, register, @@ -1319,9 +1435,9 @@ impl<'src> Compiler<'src> { function_compiler.advance()?; - Some(Box::new(r#type)) + Box::new(r#type) } else { - None + Box::new(Type::None) }; function_compiler.expect(Token::LeftBrace)?; @@ -1347,7 +1463,7 @@ impl<'src> Compiler<'src> { let scope = self.chunk.current_scope(); let (local_index, _) = self.declare_local( identifier, - Some(Type::Function(function_type)), + Type::Function(function_type), false, scope, register, @@ -1359,11 +1475,11 @@ impl<'src> Compiler<'src> { identifier_position, ); - self.previous_is_expression = false; + self.previous_expression_type = Type::None; } else { self.emit_constant(function, Span(function_start, function_end))?; - self.previous_is_expression = true; + self.previous_expression_type = Type::Function(function_type); } Ok(()) @@ -1387,6 +1503,15 @@ impl<'src> Compiler<'src> { } let function_register = last_instruction.a(); + let function_return_type = + if let Type::Function(function_type) = self.get_register_type(function_register)? { + *function_type.return_type + } else { + return Err(CompileError::ExpectedFunction { + found: self.previous_token.to_owned(), + position: self.previous_position, + }); + }; let start = self.current_position.0; self.advance()?; @@ -1417,7 +1542,7 @@ impl<'src> Compiler<'src> { Span(start, end), ); - self.previous_is_expression = true; + self.previous_expression_type = function_return_type; Ok(()) } @@ -1425,7 +1550,7 @@ impl<'src> Compiler<'src> { fn parse_semicolon(&mut self) -> Result<(), CompileError> { self.advance()?; - self.previous_is_expression = false; + self.previous_expression_type = Type::None; Ok(()) } @@ -1817,6 +1942,10 @@ pub enum CompileError { found: TokenOwned, position: Span, }, + ExpectedFunction { + found: TokenOwned, + position: Span, + }, InvalidAssignmentTarget { found: TokenOwned, position: Span, @@ -1845,6 +1974,27 @@ pub enum CompileError { position: Span, }, + // Type errors + CannotResolveRegisterType { + register_index: usize, + position: Span, + }, + CannotResolveVariableType { + identifier: String, + position: Span, + }, + IfElseBranchMismatch { + conflict: TypeConflict, + position: Span, + }, + IfMissingElse { + position: Span, + }, + ListItemTypeConflict { + conflict: TypeConflict, + position: Span, + }, + // Wrappers around foreign errors Chunk { error: ChunkError, @@ -1868,15 +2018,21 @@ impl AnnotatedError for CompileError { fn description(&self) -> &'static str { match self { - Self::CannotChainComparison { .. } => "Cannot chain comparison", + Self::CannotChainComparison { .. } => "Cannot chain comparison operations", Self::CannotMutateImmutableVariable { .. } => "Cannot mutate immutable variable", + Self::CannotResolveRegisterType { .. } => "Cannot resolve register type", + Self::CannotResolveVariableType { .. } => "Cannot resolve type", Self::Chunk { .. } => "Chunk error", Self::ExpectedExpression { .. } => "Expected an expression", + Self::ExpectedFunction { .. } => "Expected a function", Self::ExpectedMutableVariable { .. } => "Expected a mutable variable", Self::ExpectedToken { .. } => "Expected a specific token", Self::ExpectedTokenMultiple { .. } => "Expected one of multiple tokens", + Self::IfElseBranchMismatch { .. } => "Type mismatch in if/else branches", + Self::IfMissingElse { .. } => "If statement missing else branch", Self::InvalidAssignmentTarget { .. } => "Invalid assignment target", Self::Lex(error) => error.description(), + Self::ListItemTypeConflict { .. } => "List item type conflict", Self::ParseFloatError { .. } => "Failed to parse float", Self::ParseIntError { .. } => "Failed to parse integer", Self::UndeclaredVariable { .. } => "Undeclared variable", @@ -1887,14 +2043,14 @@ impl AnnotatedError for CompileError { fn details(&self) -> Option { match self { - Self::CannotChainComparison { .. } => { - Some("Cannot chain comparison operations".to_string()) - } Self::CannotMutateImmutableVariable { identifier, .. } => { - Some(format!("Cannot mutate immutable variable {identifier}")) + Some(format!("{identifier} is immutable")) } Self::Chunk { error, .. } => Some(error.to_string()), Self::ExpectedExpression { found, .. } => Some(format!("Found {found}")), + Self::ExpectedFunction { found, .. } => { + Some(format!("Expected \"{found}\" to be a function")) + } Self::ExpectedToken { expected, found, .. } => Some(format!("Expected {expected} but found {found}")), @@ -1919,23 +2075,31 @@ impl AnnotatedError for CompileError { Some(details) } - Self::ExpectedMutableVariable { found, .. } => { - Some(format!("Expected mutable variable, found {found}")) - } + Self::ExpectedMutableVariable { found, .. } => Some(format!("Found {found}")), + Self::IfElseBranchMismatch { + conflict: TypeConflict { expected, actual }, + .. + } => Some( + format!("This if block evaluates to type \"{expected}\" but the else block evaluates to \"{actual}\"") + ), + Self::IfMissingElse { .. } => Some( + "This \"if\" expression evaluates to a value but is missing an else block" + .to_string(), + ), Self::InvalidAssignmentTarget { found, .. } => { - Some(format!("Invalid assignment target, found {found}")) + Some(format!("Cannot assign to {found}")) } Self::Lex(error) => error.details(), - Self::ParseFloatError { error, .. } => Some(error.to_string()), Self::ParseIntError { error, .. } => Some(error.to_string()), Self::UndeclaredVariable { identifier, .. } => { - Some(format!("Undeclared variable {identifier}")) + Some(format!("{identifier} has not been declared")) } Self::UnexpectedReturn { .. } => None, Self::VariableOutOfScope { identifier, .. } => { - Some(format!("Variable {identifier} is out of scope")) + Some(format!("{identifier} is out of scope")) } + _ => None, } } @@ -1943,13 +2107,19 @@ impl AnnotatedError for CompileError { match self { Self::CannotChainComparison { position } => *position, Self::CannotMutateImmutableVariable { position, .. } => *position, + Self::CannotResolveRegisterType { position, .. } => *position, + Self::CannotResolveVariableType { position, .. } => *position, Self::Chunk { position, .. } => *position, Self::ExpectedExpression { position, .. } => *position, + Self::ExpectedFunction { position, .. } => *position, Self::ExpectedMutableVariable { position, .. } => *position, Self::ExpectedToken { position, .. } => *position, Self::ExpectedTokenMultiple { position, .. } => *position, + Self::IfElseBranchMismatch { position, .. } => *position, + Self::IfMissingElse { position } => *position, Self::InvalidAssignmentTarget { position, .. } => *position, Self::Lex(error) => error.position(), + Self::ListItemTypeConflict { position, .. } => *position, Self::ParseFloatError { position, .. } => *position, Self::ParseIntError { position, .. } => *position, Self::UndeclaredVariable { position, .. } => *position, diff --git a/dust-lang/src/disassembler.rs b/dust-lang/src/disassembler.rs index 9f17b39..9a0bf8c 100644 --- a/dust-lang/src/disassembler.rs +++ b/dust-lang/src/disassembler.rs @@ -64,8 +64,8 @@ const CONSTANT_HEADER: [&str; 4] = [ const LOCAL_HEADER: [&str; 4] = [ "Locals", "------", - " i IDENTIFIER TYPE MUTABLE SCOPE REGISTER", - "--- ---------- ---------------- ------- ------- --------", + " i IDENTIFIER TYPE MUTABLE SCOPE ", + "--- ---------- ---------------- ------- -------", ]; /// Builder that constructs a human-readable representation of a chunk. @@ -222,7 +222,16 @@ impl<'a> Disassembler<'a> { .map(|identifier| identifier.to_string()) .unwrap_or_else(|| { current_exe() - .map(|path| path.to_string_lossy().to_string()) + .map(|path| { + let path_string = path.to_string_lossy(); + let file_name = path_string + .split('/') + .last() + .map(|slice| slice.to_string()) + .unwrap_or(path_string.to_string()); + + file_name + }) .unwrap_or("Chunk Disassembly".to_string()) }); @@ -245,10 +254,7 @@ impl<'a> Disassembler<'a> { self.chunk.len(), self.chunk.constants().len(), self.chunk.locals().len(), - self.chunk - .return_type() - .map(|r#type| r#type.to_string()) - .unwrap_or("none".to_string()) + self.chunk.return_type() ); self.push(&info_line, true, false, true, true); @@ -262,23 +268,10 @@ impl<'a> Disassembler<'a> { let bytecode = format!("{:02X}", u32::from(instruction)); let operation = instruction.operation().to_string(); let info = instruction.disassembly_info(self.chunk); - let type_display = instruction - .yielded_type(self.chunk) - .map(|r#type| { - let type_string = r#type.to_string(); - - if type_string.len() > 16 { - format!("{type_string:.13}...") - } else { - type_string - } - }) - .unwrap_or(String::with_capacity(0)); let position = position.to_string(); - let instruction_display = format!( - "{index:^3} {bytecode:>8} {operation:13} {info:^20} {type_display:^16} {position:10}" - ); + let instruction_display = + format!("{index:^3} {bytecode:>8} {operation:13} {info:^20} {position:10}"); self.push_details(&instruction_display); } @@ -305,18 +298,7 @@ impl<'a> Disassembler<'a> { .get(*identifier_index as usize) .map(|value| value.to_string()) .unwrap_or_else(|| "unknown".to_string()); - let type_display = r#type - .as_ref() - .map(|r#type| { - let type_string = r#type.to_string(); - - if type_string.len() > 16 { - format!("{type_string:.13}...") - } else { - type_string - } - }) - .unwrap_or("unknown".to_string()); + let type_display = r#type.to_string(); let local_display = format!( "{index:^3} {identifier_display:10} {type_display:16} {mutable:7} {scope:7}" ); diff --git a/dust-lang/src/instruction.rs b/dust-lang/src/instruction.rs index 2bcef0d..ff3fa8e 100644 --- a/dust-lang/src/instruction.rs +++ b/dust-lang/src/instruction.rs @@ -362,6 +362,18 @@ impl Instruction { self } + pub fn returns_or_panics(&self) -> bool { + match self.operation() { + Operation::Return => true, + Operation::CallNative => { + let native_function = NativeFunction::from(self.b()); + + matches!(native_function, NativeFunction::Panic) + } + _ => false, + } + } + pub fn yields_value(&self) -> bool { match self.operation() { Operation::Add @@ -380,43 +392,12 @@ impl Instruction { Operation::CallNative => { let native_function = NativeFunction::from(self.b()); - native_function.r#type().return_type.is_some() + *native_function.r#type().return_type != Type::None } _ => false, } } - pub fn yielded_type(&self, chunk: &Chunk) -> Option { - use Operation::*; - - match self.operation() { - Add | Divide | Modulo | Multiply | Subtract => { - if self.b_is_constant() { - chunk.get_constant_type(self.b()) - } else { - chunk.get_register_type(self.b()) - } - } - LoadBoolean | Not => Some(Type::Boolean), - Negate => { - if self.b_is_constant() { - chunk.get_constant_type(self.b()) - } else { - chunk.get_register_type(self.b()) - } - } - LoadConstant => chunk.get_constant_type(self.b()), - LoadList => chunk.get_register_type(self.a()), - GetLocal => chunk.get_local_type(self.b()), - CallNative => { - let native_function = NativeFunction::from(self.b()); - - native_function.r#type().return_type.map(|boxed| *boxed) - } - _ => None, - } - } - pub fn disassembly_info(&self, chunk: &Chunk) -> String { let format_arguments = || { let first_argument = if self.b_is_constant() { @@ -629,7 +610,7 @@ impl Instruction { let mut output = String::new(); let native_function_name = native_function.as_str(); - if native_function.r#type().return_type.is_some() { + if *native_function.r#type().return_type != Type::None { output.push_str(&format!("R{} = {}(", to_register, native_function_name)); } else { output.push_str(&format!("{}(", native_function_name)); diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 6640c84..bd13096 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -24,7 +24,7 @@ pub use crate::instruction::Instruction; pub use crate::lexer::{lex, LexError, Lexer}; pub use crate::native_function::{NativeFunction, NativeFunctionError}; pub use crate::operation::Operation; -pub use crate::optimizer::{optimize, Optimizer}; +pub use crate::optimizer::Optimizer; pub use crate::r#type::{EnumType, FunctionType, RangeableType, StructType, Type, TypeConflict}; pub use crate::token::{output_token_list, Token, TokenKind, TokenOwned}; pub use crate::value::{ConcreteValue, Function, Value, ValueError}; diff --git a/dust-lang/src/native_function.rs b/dust-lang/src/native_function.rs index 7a34a1e..7eec7a3 100644 --- a/dust-lang/src/native_function.rs +++ b/dust-lang/src/native_function.rs @@ -56,7 +56,7 @@ macro_rules! define_native_function { pub fn returns_value(&self) -> bool { match self { $( - NativeFunction::$name => $type.return_type.is_some(), + NativeFunction::$name => *$type.return_type != Type::None, )* } } @@ -100,7 +100,7 @@ define_native_function! { FunctionType { type_parameters: None, value_parameters: None, - return_type: None + return_type: Box::new(Type::None) } ), // (AssertEqual, 1_u8, "assert_equal", false), @@ -112,7 +112,7 @@ define_native_function! { FunctionType { type_parameters: None, value_parameters: None, - return_type: Some(Box::new(Type::Any)) + return_type: Box::new(Type::None) } ), @@ -128,7 +128,7 @@ define_native_function! { FunctionType { type_parameters: None, value_parameters: Some(vec![(0, Type::Any)]), - return_type: Some(Box::new(Type::String { length: None })) + return_type: Box::new(Type::String { length: None }) } ), @@ -188,7 +188,7 @@ define_native_function! { FunctionType { type_parameters: None, value_parameters: None, - return_type: Some(Box::new(Type::String { length: None })) + return_type: Box::new(Type::String { length: None }) } ), // (ReadTo, 51_u8, "read_to", false), @@ -203,7 +203,7 @@ define_native_function! { FunctionType { type_parameters: None, value_parameters: Some(vec![(0, Type::String { length: None })]), - return_type: None + return_type: Box::new(Type::None) } ), // (WriteFile, 56_u8, "write_file", false), @@ -214,7 +214,7 @@ define_native_function! { FunctionType { type_parameters: None, value_parameters: Some(vec![(0, Type::String { length: None })]), - return_type: None + return_type: Box::new(Type::None) } ) diff --git a/dust-lang/src/optimizer.rs b/dust-lang/src/optimizer.rs index 373b8fd..c97d4b3 100644 --- a/dust-lang/src/optimizer.rs +++ b/dust-lang/src/optimizer.rs @@ -1,34 +1,41 @@ -//! Tools used by the compiler to optimize a chunk's bytecode. -use std::{iter::Map, slice::Iter}; +//! Tool used by the compiler to optimize a chunk's bytecode. use crate::{Instruction, Operation, Span}; -type MapToOperation = fn(&(Instruction, Span)) -> Operation; - -type OperationIter<'iter> = Map, MapToOperation>; - -/// Performs optimizations on a subset of instructions. -pub fn optimize(instructions: &mut [(Instruction, Span)]) -> usize { - Optimizer::new(instructions).optimize() -} - /// An instruction optimizer that mutably borrows instructions from a chunk. #[derive(Debug, Eq, PartialEq, PartialOrd, Ord)] -pub struct Optimizer<'chunk> { - instructions: &'chunk mut [(Instruction, Span)], +pub struct Optimizer<'a> { + instructions: &'a mut Vec<(Instruction, Span)>, } -impl<'chunk> Optimizer<'chunk> { +impl<'a> Optimizer<'a> { /// Creates a new optimizer with a mutable reference to some of a chunk's instructions. - pub fn new(instructions: &'chunk mut [(Instruction, Span)]) -> Self { + pub fn new(instructions: &'a mut Vec<(Instruction, Span)>) -> Self { Self { instructions } } - /// Potentially mutates the instructions to optimize them. - pub fn optimize(&mut self) -> usize { - let mut optimizations = 0; - - if matches!( + /// Optimizes a comparison operation. + /// + /// Comparison instructions (which are always followed by a JUMP) can be optimized when the + /// next instructions are two constant or boolean loaders. The first loader is set to skip an + /// instruction if it is run while the second loader is modified to use the first's register. + /// This makes the following two code snippets compile to the same bytecode: + /// + /// ```dust + /// 4 == 4 + /// ``` + /// + /// ```dust + /// if 4 == 4 { true } else { false } + /// ``` + /// + /// The instructions must be in the following order: + /// - `Operation::Equal | Operation::Less | Operation::LessEqual` + /// - `Operation::Jump` + /// - `Operation::LoadBoolean | Operation::LoadConstant` + /// - `Operation::LoadBoolean | Operation::LoadConstant` + pub fn optimize_comparison(&mut self) -> bool { + if !matches!( self.get_operations(), Some([ Operation::Equal | Operation::Less | Operation::LessEqual, @@ -37,22 +44,9 @@ impl<'chunk> Optimizer<'chunk> { Operation::LoadBoolean | Operation::LoadConstant, ]) ) { - self.optimize_comparison(); - - optimizations += 1; + return false; } - optimizations - } - - /// Optimizes a comparison operation. - /// - /// The instructions must be in the following order: - /// - `Operation::Equal | Operation::Less | Operation::LessEqual` - /// - `Operation::Jump` - /// - `Operation::LoadBoolean | Operation::LoadConstant` - /// - `Operation::LoadBoolean | Operation::LoadConstant` - fn optimize_comparison(&mut self) { log::debug!("Optimizing comparison"); let first_loader_register = { @@ -72,12 +66,30 @@ impl<'chunk> Optimizer<'chunk> { second_loader_new.set_c_to_boolean(second_loader.c_is_constant()); *second_loader = second_loader_new; + + true } - fn operations_iter(&self) -> OperationIter { - self.instructions - .iter() - .map(|(instruction, _)| instruction.operation()) + pub fn optimize_set_local(&mut self) -> bool { + if !matches!( + self.get_operations(), + Some([ + Operation::Add + | Operation::Subtract + | Operation::Multiply + | Operation::Divide + | Operation::Modulo, + Operation::SetLocal, + ]) + ) { + return false; + } + + log::debug!("Optimizing set local"); + + self.instructions.pop(); + + true } fn get_operations(&self) -> Option<[Operation; COUNT]> { @@ -87,7 +99,12 @@ impl<'chunk> Optimizer<'chunk> { let mut n_operations = [Operation::Return; COUNT]; - for (nth, operation) in n_operations.iter_mut().zip(self.operations_iter()) { + for (nth, operation) in n_operations.iter_mut().rev().zip( + self.instructions + .iter() + .rev() + .map(|(instruction, _)| instruction.operation()), + ) { *nth = operation; } diff --git a/dust-lang/src/type.rs b/dust-lang/src/type.rs index 41a7ff0..5af1af9 100644 --- a/dust-lang/src/type.rs +++ b/dust-lang/src/type.rs @@ -33,10 +33,12 @@ pub enum Type { Map { pairs: HashMap, }, + None, Number, Range { r#type: RangeableType, }, + SelfChunk, String { length: Option, }, @@ -257,8 +259,10 @@ impl Display for Type { write!(f, "}}") } + Type::None => write!(f, "none"), Type::Number => write!(f, "num"), Type::Range { r#type } => write!(f, "{type} range"), + Type::SelfChunk => write!(f, "self"), Type::String { .. } => write!(f, "str"), Type::Struct(struct_type) => write!(f, "{struct_type}"), Type::Tuple { fields } => { @@ -343,12 +347,16 @@ impl Ord for Type { left_pairs.iter().cmp(right_pairs.iter()) } (Type::Map { .. }, _) => Ordering::Greater, + (Type::None, Type::None) => Ordering::Equal, + (Type::None, _) => Ordering::Greater, (Type::Number, Type::Number) => Ordering::Equal, (Type::Number, _) => Ordering::Greater, (Type::Range { r#type: left_type }, Type::Range { r#type: right_type }) => { left_type.cmp(right_type) } (Type::Range { .. }, _) => Ordering::Greater, + (Type::SelfChunk, Type::SelfChunk) => Ordering::Equal, + (Type::SelfChunk, _) => Ordering::Greater, (Type::String { length: left }, Type::String { length: right }) => left.cmp(right), (Type::String { .. }, _) => Ordering::Greater, (Type::Struct(left_struct), Type::Struct(right_struct)) => { @@ -366,7 +374,7 @@ impl Ord for Type { pub struct FunctionType { pub type_parameters: Option>, pub value_parameters: Option>, - pub return_type: Option>, + pub return_type: Box, } impl Display for FunctionType { @@ -401,8 +409,8 @@ impl Display for FunctionType { write!(f, ")")?; - if let Some(return_type) = &self.return_type { - write!(f, " -> {return_type}")?; + if *self.return_type != Type::None { + write!(f, " -> {}", self.return_type)?; } Ok(()) diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 2490e05..888aa5f 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -7,8 +7,7 @@ use std::{ use crate::{ compile, value::ConcreteValue, AnnotatedError, Chunk, ChunkError, DustError, FunctionType, - Instruction, Local, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, - ValueError, + Instruction, NativeFunction, NativeFunctionError, Operation, Span, Type, Value, ValueError, }; pub fn run(source: &str) -> Result, DustError> { @@ -164,7 +163,7 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { FunctionType { type_parameters: None, value_parameters: None, - return_type: None, + return_type: Box::new(Type::None), }, ); @@ -595,12 +594,6 @@ impl<'chunk, 'parent> Vm<'chunk, 'parent> { Ok(()) } - fn get_local(&self, local_index: u8, position: Span) -> Result<&Local, VmError> { - self.chunk - .get_local(local_index) - .map_err(|error| VmError::Chunk { error, position }) - } - fn get_instruction( &self, index: usize, diff --git a/dust-lang/tests/control_flow.rs b/dust-lang/tests/control_flow.rs index df38e89..d92e643 100644 --- a/dust-lang/tests/control_flow.rs +++ b/dust-lang/tests/control_flow.rs @@ -23,15 +23,7 @@ fn equality_assignment_long() { (Instruction::r#return(true), Span(44, 44)), ], vec![Value::integer(4), Value::string("a")], - vec![Local::new( - 1, - None, - false, - Scope { - depth: 0, - block_index: 0 - }, - )] + vec![Local::new(1, Type::Boolean, false, Scope::default(),)] )), ); @@ -61,7 +53,7 @@ fn equality_assignment_short() { (Instruction::r#return(true), Span(16, 16)), ], vec![Value::integer(4), Value::string("a")], - vec![Local::new(1, None, false, Scope::default())] + vec![Local::new(1, Type::Boolean, false, Scope::default())] )), ); @@ -119,7 +111,7 @@ fn if_else_assigment_false() { Value::integer(42), Value::string("a") ], - vec![Local::new(5, None, false, Scope::default())] + vec![Local::new(5, Type::Integer, false, Scope::default())] )), ); @@ -177,7 +169,7 @@ fn if_else_assigment_true() { Value::integer(42), Value::string("a") ], - vec![Local::new(5, None, false, Scope::default())] + vec![Local::new(5, Type::Integer, false, Scope::default())] )), ); @@ -302,7 +294,7 @@ fn if_else_complex() { #[test] fn if_else_false() { - let source = "if 1 == 2 { panic() } else { 42 }"; + let source = "if 1 == 2 { panic(); 0 } else { 42 }"; assert_eq!( compile(source), @@ -334,7 +326,7 @@ fn if_else_false() { #[test] fn if_else_true() { - let source = "if 1 == 1 { 42 } else { panic() }"; + let source = "if 1 == 1 { 42 } else { panic(); 0 }"; assert_eq!( compile(source), @@ -366,7 +358,7 @@ fn if_else_true() { #[test] fn if_false() { - let source = "if 1 == 2 { 2 }"; + let source = "if 1 == 2 { panic() }"; assert_eq!( compile(source), @@ -380,8 +372,11 @@ fn if_false() { Span(5, 7) ), (Instruction::jump(1, true), Span(10, 11)), - (Instruction::load_constant(0, 1, false), Span(12, 13)), - (Instruction::r#return(false), Span(15, 15)) + ( + Instruction::call_native(0, NativeFunction::Panic, 0), + Span(12, 19) + ), + (Instruction::r#return(false), Span(21, 21)) ], vec![Value::integer(1), Value::integer(2)], vec![] @@ -393,7 +388,7 @@ fn if_false() { #[test] fn if_true() { - let source = "if 1 == 1 { 2 }"; + let source = "if 1 == 1 { panic() }"; assert_eq!( compile(source), @@ -407,13 +402,25 @@ fn if_true() { Span(5, 7) ), (Instruction::jump(1, true), Span(10, 11)), - (Instruction::load_constant(0, 1, false), Span(12, 13)), - (Instruction::r#return(false), Span(15, 15)) + ( + Instruction::call_native(0, NativeFunction::Panic, 0), + Span(12, 19) + ), + (Instruction::r#return(false), Span(21, 21)) ], - vec![Value::integer(1), Value::integer(2)], + vec![Value::integer(1)], vec![] )), ); - assert_eq!(run(source), Ok(None)); + assert_eq!( + run(source), + Err(DustError::Runtime { + error: VmError::NativeFunction(NativeFunctionError::Panic { + message: None, + position: Span(12, 19) + }), + source + }) + ); } diff --git a/dust-lang/tests/functions.rs b/dust-lang/tests/functions.rs index 916f1d0..d32db21 100644 --- a/dust-lang/tests/functions.rs +++ b/dust-lang/tests/functions.rs @@ -15,14 +15,14 @@ fn function() { ], vec![Value::string("a"), Value::string("b"),], vec![ - Local::new(0, Some(Type::Integer), false, Scope::default()), - Local::new(1, Some(Type::Integer), false, Scope::default()) + Local::new(0, Type::Integer, false, Scope::default()), + Local::new(1, Type::Integer, false, Scope::default()) ] ), FunctionType { type_parameters: None, value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]), - return_type: Some(Box::new(Type::Integer)), + return_type: Box::new(Type::Integer), } ))) ); @@ -53,14 +53,14 @@ fn function_call() { ], vec![Value::string("a"), Value::string("b"),], vec![ - Local::new(0, Some(Type::Integer), false, Scope::default()), - Local::new(1, Some(Type::Integer), false, Scope::default()) + Local::new(0, Type::Integer, false, Scope::default()), + Local::new(1, Type::Integer, false, Scope::default()) ] ), FunctionType { type_parameters: None, value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]), - return_type: Some(Box::new(Type::Integer)), + return_type: Box::new(Type::Integer), } ), Value::integer(1), @@ -97,24 +97,24 @@ fn function_declaration() { ], vec![Value::string("a"), Value::string("b")], vec![ - Local::new(0, Some(Type::Integer), false, Scope::default()), - Local::new(1, Some(Type::Integer), false, Scope::default()) + Local::new(0, Type::Integer, false, Scope::default()), + Local::new(1, Type::Integer, false, Scope::default()) ] ), FunctionType { type_parameters: None, value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]), - return_type: Some(Box::new(Type::Integer)), + return_type: Box::new(Type::Integer), }, ) ], vec![Local::new( 0, - Some(Type::Function(FunctionType { + Type::Function(FunctionType { type_parameters: None, value_parameters: Some(vec![(0, Type::Integer), (1, Type::Integer)]), - return_type: Some(Box::new(Type::Integer)), - })), + return_type: Box::new(Type::Integer), + }), false, Scope::default(), )], diff --git a/dust-lang/tests/logic.rs b/dust-lang/tests/logic.rs index 718a228..0a418c6 100644 --- a/dust-lang/tests/logic.rs +++ b/dust-lang/tests/logic.rs @@ -67,8 +67,8 @@ fn variable_and() { ], vec![Value::string("a"), Value::string("b"),], vec![ - Local::new(0, None, false, Scope::default()), - Local::new(1, None, false, Scope::default()), + Local::new(0, Type::Boolean, false, Scope::default()), + Local::new(1, Type::Boolean, false, Scope::default()), ] )) ); diff --git a/dust-lang/tests/loops.rs b/dust-lang/tests/loops.rs index 48072a0..df01134 100644 --- a/dust-lang/tests/loops.rs +++ b/dust-lang/tests/loops.rs @@ -16,7 +16,7 @@ fn r#while() { Span(23, 24) ), (Instruction::jump(2, true), Span(41, 42)), - (*Instruction::add(0, 0, 3).set_c_is_constant(), Span(39, 40)), + (*Instruction::add(0, 0, 3).set_c_is_constant(), Span(35, 36)), (Instruction::jump(3, false), Span(41, 42)), (Instruction::get_local(1, 0), Span(41, 42)), (Instruction::r#return(true), Span(42, 42)), @@ -27,7 +27,7 @@ fn r#while() { Value::integer(5), Value::integer(1), ], - vec![Local::new(1, None, true, Scope::default())] + vec![Local::new(1, Type::Integer, true, Scope::default())] )), ); diff --git a/dust-lang/tests/math.rs b/dust-lang/tests/math.rs index 38bc743..a3297b8 100644 --- a/dust-lang/tests/math.rs +++ b/dust-lang/tests/math.rs @@ -41,7 +41,7 @@ fn add_assign() { (Instruction::r#return(true), Span(24, 24)) ], vec![Value::integer(1), Value::string("a"), Value::integer(2)], - vec![Local::new(1, None, true, Scope::default())] + vec![Local::new(1, Type::Integer, true, Scope::default())] )) ); @@ -124,7 +124,7 @@ fn divide_assign() { (Instruction::r#return(true), Span(24, 24)) ], vec![Value::integer(2), Value::string("a")], - vec![Local::new(1, None, true, Scope::default())] + vec![Local::new(1, Type::Integer, true, Scope::default())] )) ); @@ -233,7 +233,7 @@ fn multiply_assign() { (Instruction::r#return(true), Span(23, 23)) ], vec![Value::integer(2), Value::string("a"), Value::integer(3)], - vec![Local::new(1, None, true, Scope::default())] + vec![Local::new(1, Type::Integer, true, Scope::default())] )) ); @@ -300,7 +300,7 @@ fn subtract_assign() { (Instruction::r#return(true), Span(25, 25)), ], vec![Value::integer(42), Value::string("x"), Value::integer(2)], - vec![Local::new(1, None, true, Scope::default())] + vec![Local::new(1, Type::Integer, true, Scope::default())] )), ); diff --git a/dust-lang/tests/scopes.rs b/dust-lang/tests/scopes.rs index 122b92f..079e9f5 100644 --- a/dust-lang/tests/scopes.rs +++ b/dust-lang/tests/scopes.rs @@ -55,11 +55,11 @@ fn block_scope() { Value::string("e"), ], vec![ - Local::new(1, None, false, Scope::new(0, 0)), - Local::new(3, None, false, Scope::new(1, 1)), - Local::new(5, None, false, Scope::new(2, 2)), - Local::new(7, None, false, Scope::new(1, 1)), - Local::new(8, None, false, Scope::new(0, 0)), + Local::new(1, Type::Integer, false, Scope::new(0, 0)), + Local::new(3, Type::Integer, false, Scope::new(1, 1)), + Local::new(5, Type::Integer, false, Scope::new(2, 2)), + Local::new(7, Type::Integer, false, Scope::new(1, 1)), + Local::new(8, Type::Integer, false, Scope::new(0, 0)), ] )), ); @@ -127,15 +127,15 @@ fn multiple_block_scopes() { Value::string("e"), ], vec![ - Local::new(1, None, false, Scope::new(0, 0)), - Local::new(3, None, false, Scope::new(1, 1)), - Local::new(5, None, false, Scope::new(2, 2)), - Local::new(7, None, false, Scope::new(1, 1)), - Local::new(8, None, false, Scope::new(0, 0)), - Local::new(3, None, false, Scope::new(1, 3)), - Local::new(5, None, false, Scope::new(2, 4)), - Local::new(7, None, false, Scope::new(1, 3)), - Local::new(9, None, false, Scope::new(0, 0)), + Local::new(1, Type::Integer, false, Scope::new(0, 0)), + Local::new(3, Type::Integer, false, Scope::new(1, 1)), + Local::new(5, Type::Integer, false, Scope::new(2, 2)), + Local::new(7, Type::Integer, false, Scope::new(1, 1)), + Local::new(8, Type::Integer, false, Scope::new(0, 0)), + Local::new(3, Type::Integer, false, Scope::new(1, 3)), + Local::new(5, Type::Integer, false, Scope::new(2, 4)), + Local::new(7, Type::Integer, false, Scope::new(1, 3)), + Local::new(9, Type::Integer, false, Scope::new(0, 0)), ] )), ); diff --git a/dust-lang/tests/variables.rs b/dust-lang/tests/variables.rs index 5b28426..2ee5a51 100644 --- a/dust-lang/tests/variables.rs +++ b/dust-lang/tests/variables.rs @@ -14,7 +14,7 @@ fn define_local() { (Instruction::r#return(false), Span(11, 11)) ], vec![Value::integer(42), Value::string("x")], - vec![Local::new(1, None, false, Scope::default())] + vec![Local::new(1, Type::Integer, false, Scope::default())] )), ); @@ -55,7 +55,7 @@ fn set_local() { (Instruction::r#return(true), Span(25, 25)), ], vec![Value::integer(41), Value::string("x"), Value::integer(42)], - vec![Local::new(1, None, true, Scope::default())] + vec![Local::new(1, Type::Integer, true, Scope::default())] )), );