diff --git a/dust-lang/src/chunk.rs b/dust-lang/src/chunk.rs index 751ab9c..ff23333 100644 --- a/dust-lang/src/chunk.rs +++ b/dust-lang/src/chunk.rs @@ -3,7 +3,10 @@ use std::fmt::{self, Debug, Display, Formatter}; use colored::Colorize; use serde::{Deserialize, Serialize}; -use crate::{AnnotatedError, Identifier, Instruction, Operation, Span, Value}; +use crate::{ + value::ValueKind, AnnotatedError, Function, Identifier, Instruction, Operation, Span, Type, + Value, +}; #[derive(Clone, PartialOrd, Ord, Serialize, Deserialize)] pub struct Chunk { @@ -214,18 +217,20 @@ impl Chunk { pub fn declare_local( &mut self, identifier: Identifier, - mutable: bool, + r#type: Option, + is_mutable: bool, register_index: u8, position: Span, ) -> Result { let starting_length = self.locals.len(); if starting_length + 1 > (u8::MAX as usize) { - Err(ChunkError::IdentifierOverflow { position }) + Err(ChunkError::LocalOverflow { position }) } else { self.locals.push(Local::new( identifier, - mutable, + r#type, + is_mutable, self.scope_depth, Some(register_index), )); @@ -308,6 +313,7 @@ impl PartialEq for Chunk { #[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Local { pub identifier: Identifier, + pub r#type: Option, pub is_mutable: bool, pub depth: usize, pub register_index: Option, @@ -316,12 +322,14 @@ pub struct Local { impl Local { pub fn new( identifier: Identifier, + r#type: Option, mutable: bool, depth: usize, register_index: Option, ) -> Self { Self { identifier, + r#type, is_mutable: mutable, depth, register_index, @@ -334,6 +342,7 @@ pub struct ChunkDisassembler<'a> { chunk: &'a Chunk, width: Option, styled: bool, + indent: usize, } impl<'a> ChunkDisassembler<'a> { @@ -357,8 +366,8 @@ impl<'a> ChunkDisassembler<'a> { "", "Locals", "------", - "INDEX IDENTIFIER MUTABLE DEPTH REGISTER", - "----- ---------- ------- ----- --------", + "INDEX IDENTIFIER TYPE MUTABLE DEPTH REGISTER", + "----- ---------- -------- ------- ----- --------", ]; /// The default width of the disassembly output. To correctly align the output, this should @@ -375,6 +384,7 @@ impl<'a> ChunkDisassembler<'a> { chunk, width: None, styled: false, + indent: 0, } } @@ -390,6 +400,12 @@ impl<'a> ChunkDisassembler<'a> { self } + pub fn indent(&mut self, indent: usize) -> &mut Self { + self.indent = indent; + + self + } + pub fn disassemble(&self) -> String { let width = self.width.unwrap_or_else(Self::default_width); let center = |line: &str| format!("{line:^width$}\n"); @@ -402,9 +418,16 @@ impl<'a> ChunkDisassembler<'a> { }; let mut disassembly = String::with_capacity(self.predict_length()); + let mut push_line = |line: &str| { + for _ in 0..self.indent { + disassembly.push_str(" "); + } + + disassembly.push_str(line); + }; let name_line = style(center(self.name)); - disassembly.push_str(&name_line); + push_line(&name_line); let info_line = center(&format!( "{} instructions, {} constants, {} locals", @@ -420,10 +443,10 @@ impl<'a> ChunkDisassembler<'a> { } }; - disassembly.push_str(&styled_info_line); + push_line(&styled_info_line); for line in Self::INSTRUCTION_HEADER { - disassembly.push_str(&style(center(line))); + push_line(&style(center(line))); } for (index, (instruction, position)) in self.chunk.instructions.iter().enumerate() { @@ -454,11 +477,11 @@ impl<'a> ChunkDisassembler<'a> { "{index:<5} {bytecode:<08X} {operation:15} {info:25} {jump_offset:8} {position:8}" ); - disassembly.push_str(¢er(&instruction_display)); + push_line(¢er(&instruction_display)); } for line in Self::CONSTANT_HEADER { - disassembly.push_str(&style(center(line))); + push_line(&style(center(line))); } for (index, value_option) in self.chunk.constants.iter().enumerate() { @@ -474,17 +497,34 @@ impl<'a> ChunkDisassembler<'a> { format!("{index:<5} {value_display: value_data.as_function().map(|function| function.body()), + Value::Reference(arc) => arc.as_function().map(|function| function.body()), + Value::Mutable(arc) => todo!(), + }) { + let mut function_disassembler = chunk.disassembler("function"); + + function_disassembler + .styled(self.styled) + .indent(self.indent + 1); + + let function_disassembly = function_disassembler.disassemble(); + + push_line(&function_disassembly); + } } for line in Self::LOCAL_HEADER { - disassembly.push_str(&style(center(line))); + push_line(&style(center(line))); } for ( index, Local { identifier, + r#type, depth, register_index, is_mutable: mutable, @@ -496,11 +536,15 @@ impl<'a> ChunkDisassembler<'a> { .map(|value| value.to_string()) .unwrap_or_else(|| "empty".to_string()); let identifier_display = identifier.as_str(); + let type_display = r#type + .as_ref() + .map(|r#type| r#type.to_string()) + .unwrap_or("unknown".to_string()); let local_display = format!( - "{index:<5} {identifier_display:10} {mutable:7} {depth:<5} {register_display:8}" + "{index:<5} {identifier_display:10} {type_display:8} {mutable:7} {depth:<5} {register_display:8}" ); - disassembly.push_str(¢er(&local_display)); + push_line(¢er(&local_display)); } let expected_length = self.predict_length(); @@ -573,7 +617,7 @@ pub enum ChunkError { index: usize, position: Span, }, - IdentifierOverflow { + LocalOverflow { position: Span, }, IdentifierNotFound { @@ -594,8 +638,8 @@ impl AnnotatedError for ChunkError { ChunkError::ConstantOverflow { .. } => "Constant overflow", ChunkError::ConstantIndexOutOfBounds { .. } => "Constant index out of bounds", ChunkError::InstructionUnderflow { .. } => "Instruction underflow", - ChunkError::LocalIndexOutOfBounds { .. } => "Identifier index out of bounds", - ChunkError::IdentifierOverflow { .. } => "Identifier overflow", + ChunkError::LocalIndexOutOfBounds { .. } => "Local index out of bounds", + ChunkError::LocalOverflow { .. } => "Local overflow", ChunkError::IdentifierNotFound { .. } => "Identifier not found", } } @@ -611,13 +655,13 @@ impl AnnotatedError for ChunkError { } ChunkError::InstructionUnderflow { .. } => None, ChunkError::LocalIndexOutOfBounds { index, .. } => { - Some(format!("Identifier index: {}", index)) + Some(format!("Local index: {}", index)) } ChunkError::IdentifierNotFound { identifier, .. } => { Some(format!("Identifier: {}", identifier)) } - ChunkError::IdentifierOverflow { .. } => Some("Identifier overflow".to_string()), - ChunkError::ConstantOverflow { .. } => Some("Constant overflow".to_string()), + ChunkError::LocalOverflow { .. } => None, + ChunkError::ConstantOverflow { .. } => None, } } @@ -629,7 +673,7 @@ impl AnnotatedError for ChunkError { ChunkError::IdentifierNotFound { position, .. } => *position, ChunkError::InstructionUnderflow { position, .. } => *position, ChunkError::LocalIndexOutOfBounds { position, .. } => *position, - ChunkError::IdentifierOverflow { position, .. } => *position, + ChunkError::LocalOverflow { position, .. } => *position, ChunkError::ConstantOverflow { position, .. } => *position, } } diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index 54268bf..d1bed95 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -93,6 +93,10 @@ impl<'src> Lexer<'src> { } } + pub fn skip_to(&mut self, position: usize) { + self.position = position; + } + /// Produce the next token. pub fn next_token(&mut self) -> Result<(Token<'src>, Span), LexError> { self.skip_whitespace(); @@ -539,6 +543,7 @@ impl<'src> Lexer<'src> { "loop" => Token::Loop, "map" => Token::Map, "mut" => Token::Mut, + "str" => Token::Str, "struct" => Token::Struct, "true" => Token::Boolean("true"), "while" => Token::While, diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index b15a197..6a15202 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -8,7 +8,7 @@ use colored::Colorize; use crate::{ AnnotatedError, Chunk, ChunkError, DustError, Identifier, Instruction, LexError, Lexer, - Operation, Span, Token, TokenKind, TokenOwned, Value, + Operation, Span, Token, TokenKind, TokenOwned, Type, Value, }; pub fn parse(source: &str) -> Result { @@ -17,7 +17,11 @@ pub fn parse(source: &str) -> Result { while !parser.is_eof() { parser - .parse_statement(true) + .parse_statement(Allowed { + assignment: true, + explicit_return: false, + implicit_return: true, + }) .map_err(|error| DustError::Parse { error, source })?; } @@ -39,9 +43,8 @@ impl<'src> Parser<'src> { pub fn new(mut lexer: Lexer<'src>) -> Result { let (current_token, current_position) = lexer.next_token()?; - log::info!("Begin chunk"); log::info!( - "{} at {}", + "Begin chunk with {} at {}", current_token.to_string().bold(), current_position.to_string() ); @@ -81,7 +84,7 @@ impl<'src> Parser<'src> { } } - fn decrement_register(&mut self) -> Result<(), ParseError> { + fn _decrement_register(&mut self) -> Result<(), ParseError> { let current = self.current_register; if current == 0 { @@ -140,8 +143,7 @@ impl<'src> Parser<'src> { self.chunk.push_instruction(instruction, position); } - fn emit_constant(&mut self, value: Value) -> Result<(), ParseError> { - let position = self.previous_position; + fn emit_constant(&mut self, value: Value, position: Span) -> Result<(), ParseError> { let constant_index = self.chunk.push_constant(value, position)?; self.emit_instruction( @@ -152,11 +154,7 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_boolean( - &mut self, - _allow_assignment: bool, - _allow_return: bool, - ) -> Result<(), ParseError> { + fn parse_boolean(&mut self, _: Allowed) -> Result<(), ParseError> { let boolean_text = if let Token::Boolean(text) = self.current_token { text } else { @@ -171,7 +169,6 @@ impl<'src> Parser<'src> { let boolean = boolean_text.parse::().unwrap(); self.advance()?; - self.emit_instruction( Instruction::load_boolean(self.current_register, boolean, false), position, @@ -180,11 +177,9 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_byte( - &mut self, - _allow_assignment: bool, - _allow_return: bool, - ) -> Result<(), ParseError> { + fn parse_byte(&mut self, _: Allowed) -> Result<(), ParseError> { + let position = self.current_position; + if let Token::Byte(text) = self.current_token { self.advance()?; @@ -195,33 +190,29 @@ impl<'src> Parser<'src> { })?; let value = Value::byte(byte); - self.emit_constant(value)?; + self.emit_constant(value, position)?; } Ok(()) } - fn parse_character( - &mut self, - _allow_assignment: bool, - _allow_return: bool, - ) -> Result<(), ParseError> { + fn parse_character(&mut self, _: Allowed) -> Result<(), ParseError> { + let position = self.current_position; + if let Token::Character(character) = self.current_token { self.advance()?; let value = Value::character(character); - self.emit_constant(value)?; + self.emit_constant(value, position)?; } Ok(()) } - fn parse_float( - &mut self, - _allow_assignment: bool, - _allow_return: bool, - ) -> Result<(), ParseError> { + fn parse_float(&mut self, _: Allowed) -> Result<(), ParseError> { + let position = self.current_position; + if let Token::Float(text) = self.current_token { self.advance()?; @@ -233,17 +224,15 @@ impl<'src> Parser<'src> { })?; let value = Value::float(float); - self.emit_constant(value)?; + self.emit_constant(value, position)?; } Ok(()) } - fn parse_integer( - &mut self, - _allow_assignment: bool, - _allow_return: bool, - ) -> Result<(), ParseError> { + fn parse_integer(&mut self, _: Allowed) -> Result<(), ParseError> { + let position = self.current_position; + if let Token::Integer(text) = self.current_token { self.advance()?; @@ -255,43 +244,33 @@ impl<'src> Parser<'src> { })?; let value = Value::integer(integer); - self.emit_constant(value)?; + self.emit_constant(value, position)?; } Ok(()) } - fn parse_string( - &mut self, - _allow_assignment: bool, - _allow_return: bool, - ) -> Result<(), ParseError> { + fn parse_string(&mut self, _: Allowed) -> Result<(), ParseError> { + let position = self.current_position; + if let Token::String(text) = self.current_token { self.advance()?; let value = Value::string(text); - self.emit_constant(value)?; + self.emit_constant(value, position)?; } Ok(()) } - fn parse_grouped( - &mut self, - _allow_assignment: bool, - _allow_return: bool, - ) -> Result<(), ParseError> { + fn parse_grouped(&mut self, _: Allowed) -> Result<(), ParseError> { self.allow(TokenKind::LeftParenthesis)?; self.parse(Precedence::Assignment)?; // Do not allow assignment self.expect(TokenKind::RightParenthesis) } - fn parse_unary( - &mut self, - _allow_assignment: bool, - _allow_return: bool, - ) -> Result<(), ParseError> { + fn parse_unary(&mut self, _: Allowed) -> Result<(), ParseError> { let operator = self.current_token; let operator_position = self.current_position; @@ -428,6 +407,7 @@ impl<'src> Parser<'src> { current }; + let mut new_instruction = match operator.kind() { TokenKind::Plus => Instruction::add(register, left, right), TokenKind::PlusEqual => Instruction::add(register, left, right), @@ -605,11 +585,7 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_variable( - &mut self, - allow_assignment: bool, - _allow_return: bool, - ) -> Result<(), ParseError> { + fn parse_variable(&mut self, allowed: Allowed) -> Result<(), ParseError> { let token = self.current_token; let start_position = self.current_position; @@ -617,7 +593,7 @@ impl<'src> Parser<'src> { let local_index = self.parse_identifier_from(token, start_position)?; - if allow_assignment && self.allow(TokenKind::Equal)? { + if allowed.assignment && self.allow(TokenKind::Equal)? { let is_mutable = self .chunk .get_local(local_index, start_position)? @@ -687,16 +663,31 @@ impl<'src> Parser<'src> { } } - fn parse_block( - &mut self, - _allow_assignment: bool, - _allow_return: bool, - ) -> Result<(), ParseError> { - self.expect(TokenKind::LeftCurlyBrace)?; + fn parse_type_from(&mut self, token: Token, position: Span) -> Result { + match token { + Token::Bool => Ok(Type::Boolean), + Token::FloatKeyword => Ok(Type::Float), + Token::Int => Ok(Type::Integer), + Token::Str => Ok(Type::String { length: None }), + _ => Err(ParseError::ExpectedTokenMultiple { + expected: &[ + TokenKind::Bool, + TokenKind::FloatKeyword, + TokenKind::Int, + TokenKind::Str, + ], + found: self.current_token.to_owned(), + position, + }), + } + } + + fn parse_block(&mut self, allowed: Allowed) -> Result<(), ParseError> { + self.advance()?; self.chunk.begin_scope(); while !self.allow(TokenKind::RightCurlyBrace)? && !self.is_eof() { - self.parse_statement(_allow_return)?; + self.parse_statement(allowed)?; } self.chunk.end_scope(); @@ -704,11 +695,7 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_list( - &mut self, - _allow_assignment: bool, - _allow_return: bool, - ) -> Result<(), ParseError> { + fn parse_list(&mut self, _: Allowed) -> Result<(), ParseError> { let start = self.current_position.0; self.advance()?; @@ -745,7 +732,8 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_if(&mut self, allow_assignment: bool, allow_return: bool) -> Result<(), ParseError> { + fn parse_if(&mut self, mut allowed: Allowed) -> Result<(), ParseError> { + allowed.implicit_return = false; let length = self.chunk.len(); self.advance()?; @@ -762,7 +750,7 @@ impl<'src> Parser<'src> { } if let Token::LeftCurlyBrace = self.current_token { - self.parse_block(allow_assignment, allow_return)?; + self.parse_block(allowed)?; } let last_operation = self.chunk.get_last_operation()?; @@ -780,28 +768,25 @@ impl<'src> Parser<'src> { if self.allow(TokenKind::Else)? { if let Token::If = self.current_token { - self.parse_if(allow_assignment, allow_return)?; + self.parse_if(allowed)?; } if let Token::LeftCurlyBrace = self.current_token { - self.parse_block(allow_assignment, allow_return)?; + self.parse_block(allowed)?; } } Ok(()) } - fn parse_while( - &mut self, - allow_assignment: bool, - allow_return: bool, - ) -> Result<(), ParseError> { + fn parse_while(&mut self, mut allowed: Allowed) -> Result<(), ParseError> { self.advance()?; + allowed.implicit_return = false; let jump_start = self.chunk.len(); self.parse(Precedence::Assignment)?; // Do not allow assignment - self.parse_block(allow_assignment, allow_return)?; + self.parse_block(allowed)?; let jump_end = self.chunk.len(); let jump_distance = jump_end.abs_diff(jump_start) as u8; @@ -847,20 +832,27 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_statement(&mut self, allow_return: bool) -> Result<(), ParseError> { + fn parse_statement(&mut self, allowed: Allowed) -> Result<(), ParseError> { match self.current_token { Token::Let => { - self.parse_let_statement(true, allow_return)?; + self.parse_let_statement(Allowed { + assignment: true, + explicit_return: false, + implicit_return: false, + })?; self.allow(TokenKind::Semicolon)?; } Token::LeftCurlyBrace => { - self.parse_block(true, true)?; + self.parse_block(allowed)?; self.allow(TokenKind::Semicolon)?; } _ => { self.parse_expression()?; - if !self.allow(TokenKind::Semicolon)? && self.is_eof() { + if allowed.implicit_return + && !self.allow(TokenKind::Semicolon)? + && (self.is_eof() || matches!(self.current_token, Token::RightCurlyBrace)) + { self.emit_instruction(Instruction::r#return(), self.current_position); } } @@ -869,19 +861,15 @@ impl<'src> Parser<'src> { Ok(()) } - fn parse_let_statement( - &mut self, - allow_assignment: bool, - _allow_return: bool, - ) -> Result<(), ParseError> { - if !allow_assignment { + fn parse_let_statement(&mut self, allowed: Allowed) -> Result<(), ParseError> { + if !allowed.assignment { return Err(ParseError::ExpectedExpression { found: self.current_token.to_owned(), position: self.current_position, }); } - self.allow(TokenKind::Let)?; + self.expect(TokenKind::Let)?; let is_mutable = self.allow(TokenKind::Mut)?; let position = self.current_position; @@ -896,40 +884,104 @@ impl<'src> Parser<'src> { position: self.current_position, }); }; + let explicit_type = if self.allow(TokenKind::Colon)? { + Some(self.parse_type_from(self.current_token, self.current_position)?) + } else { + None + }; + + let register = self.current_register; self.expect(TokenKind::Equal)?; self.parse_expression()?; - self.increment_register()?; + + if self.current_register == register { + self.increment_register()?; + } let (previous_instruction, previous_position) = self.chunk.get_last_instruction()?; - let register = previous_instruction.a(); - let local_index = - self.chunk - .declare_local(identifier, is_mutable, register, *previous_position)?; - self.emit_instruction( - Instruction::define_local(register, local_index, is_mutable), - position, - ); - self.allow(TokenKind::Semicolon)?; + if let Operation::LoadConstant | Operation::LoadBoolean | Operation::Add = + previous_instruction.operation() + { + let register = previous_instruction.a(); + let local_index = self.chunk.declare_local( + identifier, + explicit_type, + is_mutable, + register, + *previous_position, + )?; + + self.emit_instruction( + Instruction::define_local(register, local_index, is_mutable), + position, + ); + } Ok(()) } - fn parse_function( - &mut self, - _allow_assignment: bool, - _allow_return: bool, - ) -> Result<(), ParseError> { - self.advance()?; - - self.expect(TokenKind::LeftParenthesis)?; - + fn parse_function(&mut self, _: Allowed) -> Result<(), ParseError> { + let function_start = self.current_position.0; let mut function_parser = Parser::new(self.lexer)?; - self.expect(TokenKind::RightParenthesis)?; + function_parser.expect(TokenKind::LeftParenthesis)?; - function_parser.parse_block(false, true)?; + while function_parser.current_token != Token::RightParenthesis { + let start = function_parser.current_position.0; + let is_mutable = function_parser.allow(TokenKind::Mut)?; + let parameter = if let Token::Identifier(text) = function_parser.current_token { + function_parser.advance()?; + + Identifier::new(text) + } else { + return Err(ParseError::ExpectedToken { + expected: TokenKind::Identifier, + found: function_parser.current_token.to_owned(), + position: function_parser.current_position, + }); + }; + + function_parser.expect(TokenKind::Colon)?; + + let r#type = function_parser.parse_type_from( + function_parser.current_token, + function_parser.current_position, + )?; + + function_parser.advance()?; + + let end = function_parser.current_position.1; + let local_index = function_parser.chunk.declare_local( + parameter, + Some(r#type), + is_mutable, + function_parser.current_register, + Span(start, end), + )?; + + function_parser.chunk.define_local( + local_index, + function_parser.current_register, + Span(start, end), + )?; + function_parser.increment_register()?; + function_parser.allow(TokenKind::Comma)?; + } + + function_parser.advance()?; + function_parser.expect(TokenKind::LeftCurlyBrace)?; + + while function_parser.current_token != Token::RightCurlyBrace { + function_parser.parse_statement(Allowed { + assignment: true, + explicit_return: true, + implicit_return: true, + })?; + } + + function_parser.advance()?; self.previous_token = function_parser.previous_token; self.previous_position = function_parser.previous_position; @@ -937,8 +989,10 @@ impl<'src> Parser<'src> { self.current_position = function_parser.current_position; let function = Value::function(function_parser.take_chunk()); + let function_end = self.current_position.1; - self.emit_constant(function)?; + self.lexer.skip_to(function_end); + self.emit_constant(function, Span(function_start, function_end))?; Ok(()) } @@ -954,7 +1008,14 @@ impl<'src> Parser<'src> { self.current_token.to_string().bold(), ); - prefix_parser(self, allow_assignment, allow_return)?; + prefix_parser( + self, + Allowed { + assignment: true, + explicit_return: allow_return, + implicit_return: allow_return, + }, + )?; parsed = true; } @@ -1056,7 +1117,14 @@ impl Display for Precedence { } } -type PrefixFunction<'a> = fn(&mut Parser<'a>, bool, bool) -> Result<(), ParseError>; +#[derive(Debug, Clone, Copy)] +struct Allowed { + assignment: bool, + explicit_return: bool, + implicit_return: bool, +} + +type PrefixFunction<'a> = fn(&mut Parser<'a>, Allowed) -> Result<(), ParseError>; type InfixFunction<'a> = fn(&mut Parser<'a>) -> Result<(), ParseError>; #[derive(Debug, Clone, Copy)] @@ -1374,19 +1442,19 @@ impl AnnotatedError for ParseError { fn details(&self) -> Option { match self { Self::CannotMutateImmutableVariable { identifier, .. } => { - Some(format!("Cannot mutate immutable variable \"{identifier}\"")) + Some(format!("Cannot mutate immutable variable {identifier}")) } - Self::ExpectedExpression { found, .. } => Some(format!("Found \"{found}\"")), + Self::ExpectedExpression { found, .. } => Some(format!("Found {found}")), Self::ExpectedToken { expected, found, .. - } => Some(format!("Expected \"{expected}\", found \"{found}\"")), + } => Some(format!("Expected {expected} but found {found}")), Self::ExpectedTokenMultiple { expected, found, .. } => { let mut details = String::from("Expected"); for (index, token) in expected.iter().enumerate() { - details.push_str(&format!(" \"{token}\"")); + details.push_str(&format!(" {token}")); if index < expected.len() - 2 { details.push_str(", "); @@ -1397,18 +1465,18 @@ impl AnnotatedError for ParseError { } } - details.push_str(&format!(" found \"{found}\"")); + details.push_str(&format!(" but found {found}")); Some(details) } Self::ExpectedMutableVariable { found, .. } => { - Some(format!("Expected mutable variable, found \"{found}\"")) + Some(format!("Expected mutable variable, found {found}")) } Self::InvalidAssignmentTarget { found, .. } => { - Some(format!("Invalid assignment target, found \"{found}\"")) + Some(format!("Invalid assignment target, found {found}")) } Self::UndefinedVariable { identifier, .. } => { - Some(format!("Undefined variable \"{identifier}\"")) + Some(format!("Undefined variable {identifier}")) } Self::RegisterOverflow { .. } => None, Self::RegisterUnderflow { .. } => None, @@ -1430,10 +1498,10 @@ impl AnnotatedError for ParseError { Self::UndefinedVariable { position, .. } => *position, Self::RegisterOverflow { position } => *position, Self::RegisterUnderflow { position } => *position, - Self::Chunk(error) => error.position(), - Self::Lex(error) => error.position(), Self::ParseFloatError { position, .. } => *position, Self::ParseIntError { position, .. } => *position, + Self::Chunk(error) => error.position(), + Self::Lex(error) => error.position(), } } } diff --git a/dust-lang/src/value.rs b/dust-lang/src/value.rs index bcba5e2..5108982 100644 --- a/dust-lang/src/value.rs +++ b/dust-lang/src/value.rs @@ -45,7 +45,7 @@ use serde::{ Deserialize, Deserializer, Serialize, Serializer, }; -use crate::{Chunk, EnumType, FunctionType, Identifier, RangeableType, StructType, Type}; +use crate::{Chunk, EnumType, Identifier, RangeableType, StructType, Type}; /// Dust value representation /// @@ -1063,6 +1063,14 @@ impl ValueData { } } + pub fn as_function(&self) -> Option<&Function> { + if let ValueData::Function(function) = self { + Some(function) + } else { + None + } + } + pub fn is_rangeable(&self) -> bool { matches!( self, @@ -1452,6 +1460,10 @@ pub struct Function { } impl Function { + pub fn body(&self) -> &Chunk { + &self.body + } + pub fn call( self, _type_arguments: Option>, diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index 43e9df5..d52034b 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -482,7 +482,7 @@ impl Vm { } } - fn pop(&mut self, position: Span) -> Result { + fn _pop(&mut self, position: Span) -> Result { if let Some(register) = self.register_stack.pop() { let value = register.ok_or(VmError::EmptyRegister { index: self.register_stack.len().saturating_sub(1), diff --git a/dust-lang/tests/expressions.rs b/dust-lang/tests/expressions.rs index f792d73..2b5d2b1 100644 --- a/dust-lang/tests/expressions.rs +++ b/dust-lang/tests/expressions.rs @@ -39,7 +39,7 @@ fn add_assign() { (Instruction::r#return(), Span(24, 24)) ], vec![Value::integer(1), Value::integer(2)], - vec![Local::new(Identifier::new("a"), true, 0, Some(0))] + vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))] )) ); @@ -105,11 +105,11 @@ fn block_scope() { Value::integer(1) ], vec![ - Local::new(Identifier::new("a"), false, 0, Some(0)), - Local::new(Identifier::new("b"), false, 1, Some(1)), - Local::new(Identifier::new("c"), false, 2, Some(2)), - Local::new(Identifier::new("d"), false, 1, Some(3)), - Local::new(Identifier::new("e"), false, 0, Some(4)), + Local::new(Identifier::new("a"), None, false, 0, Some(0)), + Local::new(Identifier::new("b"), None, false, 1, Some(1)), + Local::new(Identifier::new("c"), None, false, 2, Some(2)), + Local::new(Identifier::new("d"), None, false, 1, Some(3)), + Local::new(Identifier::new("e"), None, false, 0, Some(4)), ] )), ); @@ -148,7 +148,7 @@ fn define_local() { (Instruction::define_local(0, 0, false), Span(4, 5)), ], vec![Value::integer(42)], - vec![Local::new(Identifier::new("x"), false, 0, Some(0))] + vec![Local::new(Identifier::new("x"), None, false, 0, Some(0))] )), ); @@ -197,7 +197,7 @@ fn divide_assign() { (Instruction::r#return(), Span(24, 24)) ], vec![Value::integer(2), Value::integer(2)], - vec![Local::new(Identifier::new("a"), true, 0, Some(0))] + vec![Local::new(Identifier::new("a"), None, true, 0, Some(0))] )) ); @@ -261,7 +261,7 @@ fn equality_assignment_long() { (Instruction::r#return(), Span(44, 44)), ], vec![Value::integer(4), Value::integer(4)], - vec![Local::new(Identifier::new("a"), false, 0, Some(0))] + vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))] )), ); @@ -290,7 +290,7 @@ fn equality_assignment_short() { (Instruction::r#return(), Span(17, 17)), ], vec![Value::integer(4), Value::integer(4)], - vec![Local::new(Identifier::new("a"), false, 0, Some(0))] + vec![Local::new(Identifier::new("a"), None, false, 0, Some(0))] )), ); @@ -686,7 +686,7 @@ fn multiply_assign() { (Instruction::r#return(), Span(23, 23)) ], vec![Value::integer(2), Value::integer(3)], - vec![Local::new(Identifier::new("a"), true, 0, Some(0)),] + vec![Local::new(Identifier::new("a"), None, true, 0, Some(0)),] )) ); @@ -825,7 +825,7 @@ fn set_local() { (Instruction::r#return(), Span(25, 25)), ], vec![Value::integer(41), Value::integer(42)], - vec![Local::new(Identifier::new("x"), true, 0, Some(0)),] + vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),] )), ); @@ -874,7 +874,7 @@ fn subtract_assign() { (Instruction::r#return(), Span(25, 25)), ], vec![Value::integer(42), Value::integer(2)], - vec![Local::new(Identifier::new("x"), true, 0, Some(0)),] + vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),] )), ); @@ -901,8 +901,8 @@ fn variable_and() { ], vec![], vec![ - Local::new(Identifier::new("a"), false, 0, Some(0)), - Local::new(Identifier::new("b"), false, 0, Some(1)), + Local::new(Identifier::new("a"), None, false, 0, Some(0)), + Local::new(Identifier::new("b"), None, false, 0, Some(1)), ] )) ); @@ -931,7 +931,7 @@ fn r#while() { (Instruction::r#return(), Span(42, 42)), ], vec![Value::integer(0), Value::integer(5), Value::integer(1),], - vec![Local::new(Identifier::new("x"), true, 0, Some(0)),] + vec![Local::new(Identifier::new("x"), None, true, 0, Some(0)),] )), );