Improve errors
This commit is contained in:
parent
0d0a2d2237
commit
e84bb2ea70
@ -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<Type>,
|
||||
},
|
||||
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),
|
||||
|
@ -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, " }}")
|
||||
|
@ -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 {
|
||||
|
@ -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; }";
|
||||
|
@ -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};
|
||||
|
||||
|
@ -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]
|
||||
|
Loading…
Reference in New Issue
Block a user