From e84bb2ea708637a7f5ce7ca9247d34447bd3e2a4 Mon Sep 17 00:00:00 2001 From: Jeff Date: Fri, 23 Aug 2024 05:54:58 -0400 Subject: [PATCH] Improve errors --- dust-lang/src/analyzer.rs | 61 +++++++++++++++------------------ dust-lang/src/ast/expression.rs | 16 ++++----- dust-lang/src/lexer.rs | 11 +++--- dust-lang/src/parser.rs | 21 ++++++++++-- dust-lang/src/token.rs | 5 +-- dust-lang/src/vm.rs | 9 ++++- 6 files changed, 68 insertions(+), 55 deletions(-) diff --git a/dust-lang/src/analyzer.rs b/dust-lang/src/analyzer.rs index f5ef596..f2d410a 100644 --- a/dust-lang/src/analyzer.rs +++ b/dust-lang/src/analyzer.rs @@ -102,9 +102,8 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> { position: identifier.position, })?; } else { - return Err(AnalysisError::ExpectedValueFromExpression { - expression: value.clone(), - found_type: r#type, + return Err(AnalysisError::LetExpectedValueFromStatement { + actual: value.clone(), }); } @@ -234,7 +233,6 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> { } else { return Err(AnalysisError::ExpectedValueFromExpression { expression: index.clone(), - found_type: None, }); }; @@ -313,7 +311,6 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> { if list_type.is_none() { return Err(AnalysisError::ExpectedValueFromExpression { expression: list.clone(), - found_type: list_type, }); } } @@ -361,14 +358,12 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> { if expected_type.is_none() { return Err(AnalysisError::ExpectedValueFromExpression { expression: assignee.clone(), - found_type: expected_type, }); } if actual_type.is_none() { return Err(AnalysisError::ExpectedValueFromExpression { expression: modifier.clone(), - found_type: actual_type, }); } @@ -399,14 +394,12 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> { if left_type.is_none() { return Err(AnalysisError::ExpectedValueFromExpression { expression: left.clone(), - found_type: left_type, }); } if right_type.is_none() { return Err(AnalysisError::ExpectedValueFromExpression { expression: right.clone(), - found_type: right_type, }); } @@ -428,14 +421,12 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> { if left_type.is_none() { return Err(AnalysisError::ExpectedValueFromExpression { expression: left.clone(), - found_type: left_type, }); } if right_type.is_none() { return Err(AnalysisError::ExpectedValueFromExpression { expression: right.clone(), - found_type: right_type, }); } @@ -557,12 +548,11 @@ pub enum AnalysisError { ExpectedIdentifierOrString { actual: Expression, }, - ExpectedValueFromStatement { - actual: Statement, + LetExpectedValueFromStatement { + actual: Expression, }, ExpectedValueFromExpression { expression: Expression, - found_type: Option, }, ExpectedValueArgumentCount { expected: usize, @@ -623,9 +613,9 @@ impl AnalysisError { AnalysisError::ExpectedIdentifier { actual } => actual.position(), AnalysisError::ExpectedIdentifierOrString { actual } => actual.position(), AnalysisError::ExpectedValueFromExpression { expression, .. } => expression.position(), - AnalysisError::ExpectedValueFromStatement { actual } => actual.position(), AnalysisError::ExpectedValueArgumentCount { position, .. } => *position, AnalysisError::IndexOutOfBounds { index, .. } => index.position(), + AnalysisError::LetExpectedValueFromStatement { actual } => actual.position(), AnalysisError::NegativeIndex { index, .. } => index.position(), AnalysisError::TypeConflict { actual_expression, .. @@ -653,7 +643,7 @@ impl Display for AnalysisError { } => { write!( f, - "Expected type {:?}, found {:?} in {}", + "Expected type {}, found {} in {}", expected, actual, actual_expression ) } @@ -662,11 +652,19 @@ impl Display for AnalysisError { actual, actual_expression, } => { - write!( - f, - "Expected one of {:?}, found {:?} in {}", - expected, actual, actual_expression - ) + write!(f, "Expected ")?; + + for (i, expected_type) in expected.iter().enumerate() { + if i == expected.len() - 1 { + write!(f, "or ")?; + } else if i > 0 { + write!(f, ", ")?; + } + + write!(f, "{}", expected_type)?; + } + + write!(f, ", found {} in {}", actual, actual_expression) } AnalysisError::ExpectedIdentifier { actual, .. } => { @@ -675,18 +673,8 @@ impl Display for AnalysisError { AnalysisError::ExpectedIdentifierOrString { actual } => { write!(f, "Expected identifier or string, found {}", actual) } - AnalysisError::ExpectedValueFromExpression { - expression, - found_type, - } => { - write!( - f, - "Expected {} to produce a value, found {:?}", - expression, found_type - ) - } - AnalysisError::ExpectedValueFromStatement { actual, .. } => { - write!(f, "Expected statement to produce a value, found {}", actual) + AnalysisError::ExpectedValueFromExpression { expression } => { + write!(f, "Expected {} to produce a value", expression) } AnalysisError::ExpectedValueArgumentCount { expected, actual, .. @@ -701,6 +689,13 @@ impl Display for AnalysisError { "Index {} out of bounds for list {} with length {}", index_value, list, length ), + AnalysisError::LetExpectedValueFromStatement { actual, .. } => { + write!( + f, + "Cannot assign to nothing. This expression should produce a value, but {} does not", + actual + ) + } AnalysisError::NegativeIndex { list, index_value, .. } => write!(f, "Negative index {} for list {}", index_value, list), diff --git a/dust-lang/src/ast/expression.rs b/dust-lang/src/ast/expression.rs index 54f747c..be8946f 100644 --- a/dust-lang/src/ast/expression.rs +++ b/dust-lang/src/ast/expression.rs @@ -681,7 +681,7 @@ impl Display for PrimitiveValueExpression { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { PrimitiveValueExpression::Boolean(boolean) => write!(f, "{boolean}"), - PrimitiveValueExpression::Character(character) => write!(f, "{character}"), + PrimitiveValueExpression::Character(character) => write!(f, "'{character}'"), PrimitiveValueExpression::Float(float) => write!(f, "{float}"), PrimitiveValueExpression::Integer(integer) => write!(f, "{integer}"), } @@ -772,7 +772,7 @@ impl Display for LiteralExpression { LiteralExpression::Primitive(primitive) => { write!(f, "{primitive}") } - LiteralExpression::String(string) => write!(f, "{string}"), + LiteralExpression::String(string) => write!(f, "\"{string}\""), } } } @@ -1065,27 +1065,27 @@ impl Display for BlockExpression { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { BlockExpression::Async(statements) => { - writeln!(f, "async {{ ")?; + write!(f, "async {{ ")?; for (i, statement) in statements.iter().enumerate() { if i > 0 { - writeln!(f, " ")?; + write!(f, " ")?; } - writeln!(f, "{}", statement)?; + write!(f, "{}", statement)?; } write!(f, " }}") } BlockExpression::Sync(statements) => { - writeln!(f, "{{ ")?; + write!(f, "{{ ")?; for (i, statement) in statements.iter().enumerate() { if i > 0 { - writeln!(f, " ")?; + write!(f, " ")?; } - writeln!(f, "{}", statement)?; + write!(f, "{}", statement)?; } write!(f, " }}") diff --git a/dust-lang/src/lexer.rs b/dust-lang/src/lexer.rs index 241c4f0..a26d684 100644 --- a/dust-lang/src/lexer.rs +++ b/dust-lang/src/lexer.rs @@ -3,10 +3,7 @@ //! This module provides two lexing options: //! - [`lex`], which lexes the entire input and returns a vector of tokens and their positions //! - [`Lexer`], which lexes the input a token at a time -use std::{ - error::Error, - fmt::{self, Display, Formatter}, -}; +use std::fmt::{self, Display, Formatter}; use crate::{ast::Span, Token}; @@ -131,7 +128,7 @@ impl<'src> Lexer<'src> { } } 'a'..='z' | 'A'..='Z' => self.lex_alphanumeric()?, - '"' => self.lex_string('"')?, + '"' => self.lex_string()?, '\'' => { self.position += 1; @@ -465,13 +462,13 @@ impl<'src> Lexer<'src> { Ok((token, (start_pos, self.position))) } - fn lex_string(&mut self, delimiter: char) -> Result<(Token<'src>, Span), LexError> { + fn lex_string(&mut self) -> Result<(Token<'src>, Span), LexError> { let start_pos = self.position; self.next_char(); while let Some(c) = self.peek_char() { - if c == delimiter { + if c == '"' { self.next_char(); break; } else { diff --git a/dust-lang/src/parser.rs b/dust-lang/src/parser.rs index e233c57..b8a11aa 100644 --- a/dust-lang/src/parser.rs +++ b/dust-lang/src/parser.rs @@ -4,7 +4,6 @@ //! - `parse` convenience function //! - `Parser` struct, which parses the input a statement at a time use std::{ - error::Error, fmt::{self, Display, Formatter}, num::{ParseFloatError, ParseIntError}, str::ParseBoolError, @@ -436,6 +435,13 @@ impl<'src> Parser<'src> { Ok(Expression::r#break(expression_option, position)) } + Token::Character(character) => { + self.next_token()?; + + let expression = Expression::literal(character, start_position); + + Ok(expression) + } Token::Float(text) => { self.next_token()?; @@ -1158,11 +1164,22 @@ impl Display for ParseError { #[cfg(test)] mod tests { - use crate::{Identifier, Type}; use super::*; + #[test] + fn character_literal() { + let source = "'a'"; + + assert_eq!( + parse(source), + Ok(AbstractSyntaxTree::with_statements([ + Statement::Expression(Expression::literal('a', (0, 3))) + ])) + ); + } + #[test] fn break_loop() { let source = "loop { break; }"; diff --git a/dust-lang/src/token.rs b/dust-lang/src/token.rs index 02285e3..69a543c 100644 --- a/dust-lang/src/token.rs +++ b/dust-lang/src/token.rs @@ -1,8 +1,5 @@ //! Token and TokenOwned types. -use std::{ - borrow::Borrow, - fmt::{self, Display, Formatter}, -}; +use std::fmt::{self, Display, Formatter}; use serde::{Deserialize, Serialize}; diff --git a/dust-lang/src/vm.rs b/dust-lang/src/vm.rs index b0f670c..105726b 100644 --- a/dust-lang/src/vm.rs +++ b/dust-lang/src/vm.rs @@ -1402,11 +1402,18 @@ mod tests { use super::*; + #[test] + fn character() { + let input = "'a'"; + + assert_eq!(run(input), Ok(Some(Value::character('a')))); + } + #[test] fn break_loop() { let input = "let mut x = 0; loop { x += 1; if x == 10 { break; } } x"; - assert_eq!(run(input), Ok(Some(Value::mutable(Value::integer(10))))); + assert_eq!(run(input), Ok(Some(Value::integer(10)))); } #[test]