Improve errors

This commit is contained in:
Jeff 2024-08-20 17:01:30 -04:00
parent 80bf09d807
commit 08a9b265ec
8 changed files with 242 additions and 120 deletions

View File

@ -90,11 +90,16 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
let r#type = value.return_type(&self.context)?; let r#type = value.return_type(&self.context)?;
if let Some(r#type) = r#type { if let Some(r#type) = r#type {
self.context.set_variable_type( self.context
identifier.inner.clone(), .set_variable_type(
r#type, identifier.inner.clone(),
identifier.position, r#type,
)?; identifier.position,
)
.map_err(|error| AnalysisError::ContextError {
error,
position: identifier.position,
})?;
} else { } else {
return Err(AnalysisError::ExpectedValueFromExpression { return Err(AnalysisError::ExpectedValueFromExpression {
expression: value.clone(), expression: value.clone(),
@ -114,7 +119,7 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
name: name.inner.clone(), name: name.inner.clone(),
}, },
name.position, name.position,
)?, ),
StructDefinition::Tuple { name, items } => { StructDefinition::Tuple { name, items } => {
let fields = items.iter().map(|item| item.inner.clone()).collect(); let fields = items.iter().map(|item| item.inner.clone()).collect();
@ -125,7 +130,7 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
fields, fields,
}, },
name.position, name.position,
)?; )
} }
StructDefinition::Fields { name, fields } => { StructDefinition::Fields { name, fields } => {
let fields = fields let fields = fields
@ -142,9 +147,13 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
fields, fields,
}, },
name.position, name.position,
)?; )
} }
}, }
.map_err(|error| AnalysisError::ContextError {
error,
position: struct_definition.position,
})?,
} }
Ok(()) Ok(())
@ -172,7 +181,11 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
field_access_expression.inner.as_ref(); field_access_expression.inner.as_ref();
self.context self.context
.update_last_position(&field.inner, field.position)?; .update_last_position(&field.inner, field.position)
.map_err(|error| AnalysisError::ContextError {
error,
position: field.position,
})?;
self.analyze_expression(container)?; self.analyze_expression(container)?;
} }
Expression::Grouped(expression) => { Expression::Grouped(expression) => {
@ -181,7 +194,11 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
Expression::Identifier(identifier) => { Expression::Identifier(identifier) => {
let found = self let found = self
.context .context
.update_last_position(&identifier.inner, identifier.position)?; .update_last_position(&identifier.inner, identifier.position)
.map_err(|error| AnalysisError::ContextError {
error,
position: identifier.position,
})?;
if !found { if !found {
return Err(AnalysisError::UndefinedVariable { return Err(AnalysisError::UndefinedVariable {
@ -304,7 +321,11 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
Expression::Struct(struct_expression) => match struct_expression.inner.as_ref() { Expression::Struct(struct_expression) => match struct_expression.inner.as_ref() {
StructExpression::Fields { name, fields } => { StructExpression::Fields { name, fields } => {
self.context self.context
.update_last_position(&name.inner, name.position)?; .update_last_position(&name.inner, name.position)
.map_err(|error| AnalysisError::ContextError {
error,
position: name.position,
});
for (_, expression) in fields { for (_, expression) in fields {
self.analyze_expression(expression)?; self.analyze_expression(expression)?;
@ -376,7 +397,10 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum AnalysisError { pub enum AnalysisError {
AstError(AstError), AstError(AstError),
ContextError(ContextError), ContextError {
error: ContextError,
position: Span,
},
ExpectedBoolean { ExpectedBoolean {
actual: Statement, actual: Statement,
}, },
@ -442,18 +466,11 @@ impl From<AstError> for AnalysisError {
} }
} }
impl From<ContextError> for AnalysisError {
fn from(context_error: ContextError) -> Self {
Self::ContextError(context_error)
}
}
impl AnalysisError { impl AnalysisError {
pub fn position(&self) -> Option<Span> { pub fn position(&self) -> Span {
let position = match self { match self {
AnalysisError::AstError(ast_error) => return ast_error.position(), AnalysisError::AstError(ast_error) => ast_error.position(),
AnalysisError::ContextError(_) => return None, AnalysisError::ContextError { position, .. } => *position,
AnalysisError::ExpectedBoolean { actual } => actual.position(), AnalysisError::ExpectedBoolean { actual } => actual.position(),
AnalysisError::ExpectedIdentifier { actual } => actual.position(), AnalysisError::ExpectedIdentifier { actual } => actual.position(),
AnalysisError::ExpectedIdentifierOrString { actual } => actual.position(), AnalysisError::ExpectedIdentifierOrString { actual } => actual.position(),
@ -472,9 +489,7 @@ impl AnalysisError {
AnalysisError::UndefinedVariable { identifier } => identifier.position, AnalysisError::UndefinedVariable { identifier } => identifier.position,
AnalysisError::UnexpectedIdentifier { identifier } => identifier.position, AnalysisError::UnexpectedIdentifier { identifier } => identifier.position,
AnalysisError::UnexectedString { actual } => actual.position(), AnalysisError::UnexectedString { actual } => actual.position(),
}; }
Some(position)
} }
} }
@ -484,7 +499,9 @@ impl Display for AnalysisError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
AnalysisError::AstError(ast_error) => write!(f, "{}", ast_error), AnalysisError::AstError(ast_error) => write!(f, "{}", ast_error),
AnalysisError::ContextError(context_error) => write!(f, "{}", context_error), Self::ContextError { error, position } => {
write!(f, "Context error at {:?}: {}", position, error)
}
AnalysisError::ExpectedBoolean { actual, .. } => { AnalysisError::ExpectedBoolean { actual, .. } => {
write!(f, "Expected boolean, found {}", actual) write!(f, "Expected boolean, found {}", actual)
} }

View File

@ -307,7 +307,14 @@ impl Expression {
} }
} }
Expression::Grouped(expression) => expression.inner.return_type(context)?, Expression::Grouped(expression) => expression.inner.return_type(context)?,
Expression::Identifier(identifier) => context.get_type(&identifier.inner)?, Expression::Identifier(identifier) => {
context
.get_type(&identifier.inner)
.map_err(|error| AstError::ContextError {
error,
position: identifier.position,
})?
}
Expression::If(if_expression) => match if_expression.inner.as_ref() { Expression::If(if_expression) => match if_expression.inner.as_ref() {
IfExpression::If { .. } => None, IfExpression::If { .. } => None,
IfExpression::IfElse { if_block, .. } => if_block.inner.return_type(context)?, IfExpression::IfElse { if_block, .. } => if_block.inner.return_type(context)?,

View File

@ -62,7 +62,7 @@ impl<T: Display> Display for Node<T> {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum AstError { pub enum AstError {
ContextError(ContextError), ContextError { error: ContextError, position: Span },
ExpectedType { position: Span }, ExpectedType { position: Span },
ExpectedTupleType { position: Span }, ExpectedTupleType { position: Span },
ExpectedNonEmptyList { position: Span }, ExpectedNonEmptyList { position: Span },
@ -70,27 +70,23 @@ pub enum AstError {
} }
impl AstError { impl AstError {
pub fn position(&self) -> Option<Span> { pub fn position(&self) -> Span {
match self { match self {
AstError::ContextError(_) => None, AstError::ContextError { position, .. } => *position,
AstError::ExpectedType { position } => Some(*position), AstError::ExpectedType { position } => *position,
AstError::ExpectedTupleType { position } => Some(*position), AstError::ExpectedTupleType { position } => *position,
AstError::ExpectedNonEmptyList { position } => Some(*position), AstError::ExpectedNonEmptyList { position } => *position,
AstError::ExpectedRangeableType { position } => Some(*position), AstError::ExpectedRangeableType { position } => *position,
} }
} }
} }
impl From<ContextError> for AstError {
fn from(v: ContextError) -> Self {
Self::ContextError(v)
}
}
impl Display for AstError { impl Display for AstError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
AstError::ContextError(error) => write!(f, "{}", error), AstError::ContextError { error, position } => {
write!(f, "Context error at {:?}: {}", position, error)
}
AstError::ExpectedType { position } => write!(f, "Expected a type at {:?}", position), AstError::ExpectedType { position } => write!(f, "Expected a type at {:?}", position),
AstError::ExpectedTupleType { position } => { AstError::ExpectedTupleType { position } => {
write!(f, "Expected a tuple type at {:?}", position) write!(f, "Expected a tuple type at {:?}", position)

View File

@ -14,13 +14,14 @@ use crate::{Identifier, Type, Value};
pub enum BuiltInFunction { pub enum BuiltInFunction {
// String tools // String tools
ToString, ToString,
LengthString,
// Integer and float tools // Integer and float tools
IsEven, IsEven,
IsOdd, IsOdd,
// List tools // List tools
Length, LengthList,
// I/O // I/O
ReadLine, ReadLine,
@ -32,7 +33,8 @@ impl BuiltInFunction {
match self { match self {
BuiltInFunction::IsEven => "is_even", BuiltInFunction::IsEven => "is_even",
BuiltInFunction::IsOdd => "is_odd", BuiltInFunction::IsOdd => "is_odd",
BuiltInFunction::Length => "length", BuiltInFunction::LengthList => "length",
BuiltInFunction::LengthString => "length",
BuiltInFunction::ReadLine => "read_line", BuiltInFunction::ReadLine => "read_line",
BuiltInFunction::ToString { .. } => "to_string", BuiltInFunction::ToString { .. } => "to_string",
BuiltInFunction::WriteLine => "write_line", BuiltInFunction::WriteLine => "write_line",
@ -44,7 +46,8 @@ impl BuiltInFunction {
BuiltInFunction::ToString { .. } => None, BuiltInFunction::ToString { .. } => None,
BuiltInFunction::IsEven => None, BuiltInFunction::IsEven => None,
BuiltInFunction::IsOdd => None, BuiltInFunction::IsOdd => None,
BuiltInFunction::Length => None, BuiltInFunction::LengthList => None,
BuiltInFunction::LengthString => None,
BuiltInFunction::ReadLine => None, BuiltInFunction::ReadLine => None,
BuiltInFunction::WriteLine => None, BuiltInFunction::WriteLine => None,
} }
@ -55,14 +58,15 @@ impl BuiltInFunction {
BuiltInFunction::ToString { .. } => Some(vec![("value".into(), Type::Any)]), BuiltInFunction::ToString { .. } => Some(vec![("value".into(), Type::Any)]),
BuiltInFunction::IsEven => Some(vec![("value".into(), Type::Number)]), BuiltInFunction::IsEven => Some(vec![("value".into(), Type::Number)]),
BuiltInFunction::IsOdd => Some(vec![("value".into(), Type::Number)]), BuiltInFunction::IsOdd => Some(vec![("value".into(), Type::Number)]),
BuiltInFunction::Length => Some(vec![( BuiltInFunction::LengthList => Some(vec![(
"value".into(), "value".into(),
Type::ListOf { Type::ListOf {
item_type: Box::new(Type::Any), item_type: Box::new(Type::Any),
}, },
)]), )]),
BuiltInFunction::LengthString => Some(vec![("value".into(), Type::String)]),
BuiltInFunction::ReadLine => None, BuiltInFunction::ReadLine => None,
BuiltInFunction::WriteLine => Some(vec![("output".into(), Type::Any)]), BuiltInFunction::WriteLine => Some(vec![("value".into(), Type::Any)]),
} }
} }
@ -71,7 +75,8 @@ impl BuiltInFunction {
BuiltInFunction::ToString { .. } => Some(Type::String), BuiltInFunction::ToString { .. } => Some(Type::String),
BuiltInFunction::IsEven => Some(Type::Boolean), BuiltInFunction::IsEven => Some(Type::Boolean),
BuiltInFunction::IsOdd => Some(Type::Boolean), BuiltInFunction::IsOdd => Some(Type::Boolean),
BuiltInFunction::Length => Some(Type::Number), BuiltInFunction::LengthList => Some(Type::Number),
BuiltInFunction::LengthString => Some(Type::Number),
BuiltInFunction::ReadLine => Some(Type::String), BuiltInFunction::ReadLine => Some(Type::String),
BuiltInFunction::WriteLine => None, BuiltInFunction::WriteLine => None,
} }
@ -112,13 +117,20 @@ impl BuiltInFunction {
Err(BuiltInFunctionError::ExpectedInteger) Err(BuiltInFunctionError::ExpectedInteger)
} }
} }
BuiltInFunction::Length => { BuiltInFunction::LengthList => {
if let Value::List(list) = &value_arguments.unwrap()[0] { if let Value::List(list) = &value_arguments.unwrap()[0] {
Ok(Some(Value::Integer(list.len() as i64))) Ok(Some(Value::Integer(list.len() as i64)))
} else { } else {
Err(BuiltInFunctionError::ExpectedList) Err(BuiltInFunctionError::ExpectedList)
} }
} }
BuiltInFunction::LengthString => {
if let Value::String(string) = &value_arguments.unwrap()[0] {
Ok(Some(Value::Integer(string.len() as i64)))
} else {
Err(BuiltInFunctionError::ExpectedString)
}
}
BuiltInFunction::ReadLine => { BuiltInFunction::ReadLine => {
let mut input = String::new(); let mut input = String::new();

View File

@ -38,7 +38,7 @@ pub fn core_library<'a>() -> &'a Context {
Identifier::new("length"), Identifier::new("length"),
( (
ContextData::VariableValue(Value::Function(Function::BuiltIn( ContextData::VariableValue(Value::Function(Function::BuiltIn(
BuiltInFunction::Length, BuiltInFunction::LengthList,
))), ))),
(0, 0), (0, 0),
), ),

View File

@ -61,12 +61,12 @@ impl<'src> DustError<'src> {
} }
} }
pub fn position(&self) -> Option<Span> { pub fn position(&self) -> Span {
match self { match self {
DustError::Runtime { runtime_error, .. } => runtime_error.position(), DustError::Runtime { runtime_error, .. } => runtime_error.position(),
DustError::Analysis { analysis_error, .. } => analysis_error.position(), DustError::Analysis { analysis_error, .. } => analysis_error.position(),
DustError::Parse { parse_error, .. } => Some(parse_error.position()), DustError::Parse { parse_error, .. } => parse_error.position(),
DustError::Lex { lex_error, .. } => Some(lex_error.position()), DustError::Lex { lex_error, .. } => lex_error.position(),
} }
} }
@ -79,25 +79,49 @@ impl<'src> DustError<'src> {
} }
} }
pub fn report(&self) -> String { pub fn primary_error_data(&self) -> (&'static str, Span, String) {
let title = self.title(); (self.title(), self.position(), self.to_string())
let span = self.position(); }
let label = self.to_string();
let message = if let Some(span) = span { pub fn secondary_error_data(&self) -> Option<(&'static str, Span, String)> {
Level::Error.title(title).snippet( if let DustError::Runtime { runtime_error, .. } = self {
Snippet::source(self.source()) match runtime_error {
.annotation(Level::Info.span(span.0..span.1).label(&label)), RuntimeError::Expression { error, .. } => {
) Some(("Expression error", error.position(), error.to_string()))
}
RuntimeError::Statement { error, .. } => {
Some(("Statement error", error.position(), error.to_string()))
}
_ => None,
}
} else { } else {
Level::Error None
.title(title) }
.snippet(Snippet::source(self.source())) }
.footer(Level::Info.title("No position information available"))
}; pub fn report(&self) -> String {
let mut report = String::new();
let renderer = Renderer::styled(); let renderer = Renderer::styled();
format!("{}", renderer.render(message)) let (title, span, label) = self.primary_error_data();
let message = Level::Error.title(title).snippet(
Snippet::source(self.source())
.annotation(Level::Info.span(span.0..span.1).label(&label)),
);
report.push_str(&format!("{}", renderer.render(message)));
if let Some((title, span, label)) = self.secondary_error_data() {
let message = Level::Error.title(title).snippet(
Snippet::source(self.source())
.annotation(Level::Info.span(span.0..span.1).label(&label)),
);
report.push_str(&format!("{}", renderer.render(message)));
}
report
} }
} }

View File

@ -11,8 +11,8 @@ use std::{
use serde::{de::Visitor, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer}; use serde::{de::Visitor, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
use crate::{ use crate::{
AbstractSyntaxTree, BuiltInFunction, Context, EnumType, FunctionType, Identifier, AbstractSyntaxTree, BuiltInFunction, BuiltInFunctionError, Context, ContextError, EnumType,
RangeableType, RuntimeError, StructType, Type, Vm, FunctionType, Identifier, RangeableType, RuntimeError, StructType, Type, Vm,
}; };
/// Dust value representation /// Dust value representation
@ -232,18 +232,27 @@ impl Value {
} }
pub fn get_field(&self, field: &Identifier) -> Option<Value> { pub fn get_field(&self, field: &Identifier) -> Option<Value> {
if let "to_string" = field.as_str() { let built_in_function = match field.as_str() {
return Some(Value::Function(Function::BuiltIn( "to_string" => BuiltInFunction::ToString,
BuiltInFunction::ToString, "length" => {
))); return match self {
} Value::List(values) => Some(Value::Integer(values.len() as i64)),
Value::String(string) => Some(Value::Integer(string.len() as i64)),
Value::Map(map) => Some(Value::Integer(map.len() as i64)),
_ => None,
}
}
_ => {
return match self {
Value::Mutable(inner) => inner.read().unwrap().get_field(field),
Value::Struct(Struct::Fields { fields, .. }) => fields.get(field).cloned(),
Value::Map(pairs) => pairs.get(field).cloned(),
_ => None,
};
}
};
match self { Some(Value::Function(Function::BuiltIn(built_in_function)))
Value::Mutable(inner) => inner.read().unwrap().get_field(field),
Value::Struct(Struct::Fields { fields, .. }) => fields.get(field).cloned(),
Value::Map(pairs) => pairs.get(field).cloned(),
_ => None,
}
} }
pub fn get_index(&self, index: Value) -> Result<Option<Value>, ValueError> { pub fn get_index(&self, index: Value) -> Result<Option<Value>, ValueError> {
@ -1118,25 +1127,29 @@ impl Function {
_type_arguments: Option<Vec<Type>>, _type_arguments: Option<Vec<Type>>,
value_arguments: Option<Vec<Value>>, value_arguments: Option<Vec<Value>>,
context: &Context, context: &Context,
) -> Result<Option<Value>, RuntimeError> { ) -> Result<Option<Value>, FunctionCallError> {
match self { match self {
Function::BuiltIn(built_in_function) => built_in_function Function::BuiltIn(built_in_function) => built_in_function
.call(_type_arguments, value_arguments) .call(_type_arguments, value_arguments)
.map_err(|error| RuntimeError::BuiltInFunctionError { error }), .map_err(FunctionCallError::BuiltInFunction),
Function::Parsed { r#type, body, .. } => { Function::Parsed { r#type, body, .. } => {
let new_context = Context::with_data_from(context)?; let new_context =
Context::with_data_from(context).map_err(FunctionCallError::Context)?;
if let (Some(value_parameters), Some(value_arguments)) = if let (Some(value_parameters), Some(value_arguments)) =
(&r#type.value_parameters, value_arguments) (&r#type.value_parameters, value_arguments)
{ {
for ((identifier, _), value) in value_parameters.iter().zip(value_arguments) { for ((identifier, _), value) in value_parameters.iter().zip(value_arguments) {
new_context.set_variable_value(identifier.clone(), value)?; new_context
.set_variable_value(identifier.clone(), value)
.map_err(FunctionCallError::Context)?;
} }
} }
let mut vm = Vm::new(body, new_context); let mut vm = Vm::new(body, new_context);
vm.run() vm.run()
.map_err(|error| FunctionCallError::Runtime(Box::new(error)))
} }
} }
} }
@ -1187,6 +1200,23 @@ impl Display for Function {
} }
} }
#[derive(Clone, Debug, PartialEq)]
pub enum FunctionCallError {
BuiltInFunction(BuiltInFunctionError),
Context(ContextError),
Runtime(Box<RuntimeError>),
}
impl Display for FunctionCallError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
FunctionCallError::BuiltInFunction(error) => write!(f, "{}", error),
FunctionCallError::Context(error) => write!(f, "{}", error),
FunctionCallError::Runtime(error) => write!(f, "{}", error),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum Struct { pub enum Struct {
Unit { Unit {

View File

@ -21,8 +21,8 @@ use crate::{
StructDefinition, StructExpression, StructDefinition, StructExpression,
}, },
core_library, parse, Analyzer, BuiltInFunctionError, Constructor, Context, ContextData, core_library, parse, Analyzer, BuiltInFunctionError, Constructor, Context, ContextData,
ContextError, DustError, Expression, Function, Identifier, ParseError, StructType, Type, Value, ContextError, DustError, Expression, Function, FunctionCallError, Identifier, ParseError,
ValueError, StructType, Type, Value, ValueError,
}; };
/// Run the source code and return the result. /// Run the source code and return the result.
@ -167,14 +167,21 @@ impl Vm {
}; };
let constructor = struct_type.constructor(); let constructor = struct_type.constructor();
self.context.set_constructor(name, constructor)?; self.context
.set_constructor(name, constructor)
.map_err(|error| RuntimeError::ContextError {
error,
position: struct_definition.position,
})?;
Ok(None) Ok(None)
} }
}; };
if collect_garbage { if collect_garbage {
self.context.collect_garbage(position.1)?; self.context
.collect_garbage(position.1)
.map_err(|error| RuntimeError::ContextError { error, position })?;
} }
result.map_err(|error| RuntimeError::Statement { result.map_err(|error| RuntimeError::Statement {
@ -190,24 +197,27 @@ impl Vm {
) -> Result<(), RuntimeError> { ) -> Result<(), RuntimeError> {
match let_statement { match let_statement {
LetStatement::Let { identifier, value } => { LetStatement::Let { identifier, value } => {
let value_position = value.position(); let position = value.position();
let value = self let value = self
.run_expression(value, collect_garbage)? .run_expression(value, collect_garbage)?
.expect_value(value_position)?; .expect_value(position)?;
self.context.set_variable_value(identifier.inner, value)?; self.context
.set_variable_value(identifier.inner, value)
.map_err(|error| RuntimeError::ContextError { error, position })?;
Ok(()) Ok(())
} }
LetStatement::LetMut { identifier, value } => { LetStatement::LetMut { identifier, value } => {
let value_position = value.position(); let position = value.position();
let mutable_value = self let mutable_value = self
.run_expression(value, collect_garbage)? .run_expression(value, collect_garbage)?
.expect_value(value_position)? .expect_value(position)?
.into_mutable(); .into_mutable();
self.context self.context
.set_variable_value(identifier.inner, mutable_value)?; .set_variable_value(identifier.inner, mutable_value)
.map_err(|error| RuntimeError::ContextError { error, position })?;
Ok(()) Ok(())
} }
@ -250,7 +260,7 @@ impl Vm {
Expression::Grouped(expression) => { Expression::Grouped(expression) => {
self.run_expression(*expression.inner, collect_garbage) self.run_expression(*expression.inner, collect_garbage)
} }
Expression::Identifier(identifier) => self.run_identifier(identifier.inner), Expression::Identifier(identifier) => self.run_identifier(identifier),
Expression::If(if_expression) => self.run_if(*if_expression.inner, collect_garbage), Expression::If(if_expression) => self.run_if(*if_expression.inner, collect_garbage),
Expression::List(list_expression) => { Expression::List(list_expression) => {
self.run_list(*list_expression.inner, collect_garbage) self.run_list(*list_expression.inner, collect_garbage)
@ -279,10 +289,15 @@ impl Vm {
}) })
} }
fn run_identifier(&self, identifier: Identifier) -> Result<Evaluation, RuntimeError> { fn run_identifier(&self, identifier: Node<Identifier>) -> Result<Evaluation, RuntimeError> {
log::debug!("Running identifier: {}", identifier); log::debug!("Running identifier: {}", identifier);
let get_data = self.context.get_data(&identifier)?; let get_data = self.context.get_data(&identifier.inner).map_err(|error| {
RuntimeError::ContextError {
error,
position: identifier.position,
}
})?;
if let Some(ContextData::VariableValue(value)) = get_data { if let Some(ContextData::VariableValue(value)) = get_data {
return Ok(Evaluation::Return(Some(value))); return Ok(Evaluation::Return(Some(value)));
@ -296,7 +311,10 @@ impl Vm {
return Ok(Evaluation::Constructor(constructor)); return Ok(Evaluation::Constructor(constructor));
} }
Err(RuntimeError::UnassociatedIdentifier { identifier }) Err(RuntimeError::UnassociatedIdentifier {
identifier: identifier.inner,
position: identifier.position,
})
} }
fn run_struct( fn run_struct(
@ -309,7 +327,10 @@ impl Vm {
let StructExpression::Fields { name, fields } = struct_expression; let StructExpression::Fields { name, fields } = struct_expression;
let position = name.position; let position = name.position;
let constructor = self.context.get_constructor(&name.inner)?; let constructor = self
.context
.get_constructor(&name.inner)
.map_err(|error| RuntimeError::ContextError { error, position })?;
if let Some(constructor) = constructor { if let Some(constructor) = constructor {
if let Constructor::Fields(fields_constructor) = constructor { if let Constructor::Fields(fields_constructor) = constructor {
@ -696,6 +717,7 @@ impl Vm {
log::debug!("Running call expression: {call_expression}"); log::debug!("Running call expression: {call_expression}");
let CallExpression { invoker, arguments } = call_expression; let CallExpression { invoker, arguments } = call_expression;
let invoker_position = invoker.position();
if let Expression::FieldAccess(field_access) = invoker { if let Expression::FieldAccess(field_access) = invoker {
let FieldAccessExpression { container, field } = *field_access.inner; let FieldAccessExpression { container, field } = *field_access.inner;
@ -738,7 +760,11 @@ impl Vm {
return function return function
.call(None, Some(value_arguments), &context) .call(None, Some(value_arguments), &context)
.map(Evaluation::Return); .map(Evaluation::Return)
.map_err(|error| RuntimeError::FunctionCall {
error,
position: invoker_position,
});
} }
let invoker_position = invoker.position(); let invoker_position = invoker.position();
@ -801,6 +827,10 @@ impl Vm {
function function
.call(None, value_arguments, &context) .call(None, value_arguments, &context)
.map(Evaluation::Return) .map(Evaluation::Return)
.map_err(|error| RuntimeError::FunctionCall {
error,
position: invoker_position,
})
} }
_ => Err(RuntimeError::ExpectedValueOrConstructor { _ => Err(RuntimeError::ExpectedValueOrConstructor {
position: invoker_position, position: invoker_position,
@ -1010,7 +1040,14 @@ impl Evaluation {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum RuntimeError { pub enum RuntimeError {
ContextError(ContextError), ContextError {
error: ContextError,
position: Span,
},
FunctionCall {
error: FunctionCallError,
position: Span,
},
ParseError(ParseError), ParseError(ParseError),
Expression { Expression {
error: Box<RuntimeError>, error: Box<RuntimeError>,
@ -1030,6 +1067,7 @@ pub enum RuntimeError {
// These should be prevented by running the analyzer before the VM // These should be prevented by running the analyzer before the VM
BuiltInFunctionError { BuiltInFunctionError {
error: BuiltInFunctionError, error: BuiltInFunctionError,
position: Span,
}, },
EnumVariantNotFound { EnumVariantNotFound {
identifier: Identifier, identifier: Identifier,
@ -1086,6 +1124,7 @@ pub enum RuntimeError {
}, },
UnassociatedIdentifier { UnassociatedIdentifier {
identifier: Identifier, identifier: Identifier,
position: Span,
}, },
UndefinedType { UndefinedType {
identifier: Identifier, identifier: Identifier,
@ -1099,19 +1138,13 @@ pub enum RuntimeError {
}, },
} }
impl From<ContextError> for RuntimeError {
fn from(error: ContextError) -> Self {
Self::ContextError(error)
}
}
impl RuntimeError { impl RuntimeError {
pub fn position(&self) -> Option<Span> { pub fn position(&self) -> Span {
let position = match self { match self {
Self::ContextError(_) => return None, Self::ContextError { position, .. } => *position,
Self::BuiltInFunctionError { .. } => return None, Self::BuiltInFunctionError { position, .. } => *position,
Self::UnassociatedIdentifier { .. } => return None, Self::FunctionCall { position, .. } => *position,
Self::UnassociatedIdentifier { position, .. } => *position,
Self::ParseError(parse_error) => parse_error.position(), Self::ParseError(parse_error) => parse_error.position(),
Self::Expression { position, .. } => *position, Self::Expression { position, .. } => *position,
Self::Statement { position, .. } => *position, Self::Statement { position, .. } => *position,
@ -1145,9 +1178,7 @@ impl RuntimeError {
Self::UndefinedProperty { Self::UndefinedProperty {
property_position, .. property_position, ..
} => *property_position, } => *property_position,
}; }
Some(position)
} }
} }
@ -1160,7 +1191,12 @@ impl From<ParseError> for RuntimeError {
impl Display for RuntimeError { impl Display for RuntimeError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self { match self {
Self::ContextError(context_error) => write!(f, "{}", context_error), Self::ContextError { error, position } => {
write!(f, "Context error at {:?}: {}", position, error)
}
Self::FunctionCall { error, position } => {
write!(f, "Function call error at {:?}: {}", position, error)
}
Self::ParseError(parse_error) => write!(f, "{}", parse_error), Self::ParseError(parse_error) => write!(f, "{}", parse_error),
Self::Expression { error, position } => { Self::Expression { error, position } => {
write!( write!(
@ -1284,7 +1320,7 @@ impl Display for RuntimeError {
start_position, end_position start_position, end_position
) )
} }
Self::UnassociatedIdentifier { identifier } => { Self::UnassociatedIdentifier { identifier, .. } => {
write!( write!(
f, f,
"Identifier \"{identifier}\" is not associated with a value or constructor" "Identifier \"{identifier}\" is not associated with a value or constructor"