Improve errors
This commit is contained in:
parent
80bf09d807
commit
08a9b265ec
@ -90,11 +90,16 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
||||
let r#type = value.return_type(&self.context)?;
|
||||
|
||||
if let Some(r#type) = r#type {
|
||||
self.context.set_variable_type(
|
||||
self.context
|
||||
.set_variable_type(
|
||||
identifier.inner.clone(),
|
||||
r#type,
|
||||
identifier.position,
|
||||
)?;
|
||||
)
|
||||
.map_err(|error| AnalysisError::ContextError {
|
||||
error,
|
||||
position: identifier.position,
|
||||
})?;
|
||||
} else {
|
||||
return Err(AnalysisError::ExpectedValueFromExpression {
|
||||
expression: value.clone(),
|
||||
@ -114,7 +119,7 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
||||
name: name.inner.clone(),
|
||||
},
|
||||
name.position,
|
||||
)?,
|
||||
),
|
||||
StructDefinition::Tuple { name, items } => {
|
||||
let fields = items.iter().map(|item| item.inner.clone()).collect();
|
||||
|
||||
@ -125,7 +130,7 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
||||
fields,
|
||||
},
|
||||
name.position,
|
||||
)?;
|
||||
)
|
||||
}
|
||||
StructDefinition::Fields { name, fields } => {
|
||||
let fields = fields
|
||||
@ -142,9 +147,13 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
||||
fields,
|
||||
},
|
||||
name.position,
|
||||
)?;
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
.map_err(|error| AnalysisError::ContextError {
|
||||
error,
|
||||
position: struct_definition.position,
|
||||
})?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -172,7 +181,11 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
||||
field_access_expression.inner.as_ref();
|
||||
|
||||
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)?;
|
||||
}
|
||||
Expression::Grouped(expression) => {
|
||||
@ -181,7 +194,11 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
||||
Expression::Identifier(identifier) => {
|
||||
let found = self
|
||||
.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 {
|
||||
return Err(AnalysisError::UndefinedVariable {
|
||||
@ -304,7 +321,11 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
||||
Expression::Struct(struct_expression) => match struct_expression.inner.as_ref() {
|
||||
StructExpression::Fields { name, fields } => {
|
||||
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 {
|
||||
self.analyze_expression(expression)?;
|
||||
@ -376,7 +397,10 @@ impl<'recovered, 'a: 'recovered> Analyzer<'a> {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum AnalysisError {
|
||||
AstError(AstError),
|
||||
ContextError(ContextError),
|
||||
ContextError {
|
||||
error: ContextError,
|
||||
position: Span,
|
||||
},
|
||||
ExpectedBoolean {
|
||||
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 {
|
||||
pub fn position(&self) -> Option<Span> {
|
||||
let position = match self {
|
||||
AnalysisError::AstError(ast_error) => return ast_error.position(),
|
||||
AnalysisError::ContextError(_) => return None,
|
||||
|
||||
pub fn position(&self) -> Span {
|
||||
match self {
|
||||
AnalysisError::AstError(ast_error) => ast_error.position(),
|
||||
AnalysisError::ContextError { position, .. } => *position,
|
||||
AnalysisError::ExpectedBoolean { actual } => actual.position(),
|
||||
AnalysisError::ExpectedIdentifier { actual } => actual.position(),
|
||||
AnalysisError::ExpectedIdentifierOrString { actual } => actual.position(),
|
||||
@ -472,9 +489,7 @@ impl AnalysisError {
|
||||
AnalysisError::UndefinedVariable { identifier } => identifier.position,
|
||||
AnalysisError::UnexpectedIdentifier { identifier } => identifier.position,
|
||||
AnalysisError::UnexectedString { actual } => actual.position(),
|
||||
};
|
||||
|
||||
Some(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -484,7 +499,9 @@ impl Display for AnalysisError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
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, .. } => {
|
||||
write!(f, "Expected boolean, found {}", actual)
|
||||
}
|
||||
|
@ -307,7 +307,14 @@ impl Expression {
|
||||
}
|
||||
}
|
||||
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() {
|
||||
IfExpression::If { .. } => None,
|
||||
IfExpression::IfElse { if_block, .. } => if_block.inner.return_type(context)?,
|
||||
|
@ -62,7 +62,7 @@ impl<T: Display> Display for Node<T> {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum AstError {
|
||||
ContextError(ContextError),
|
||||
ContextError { error: ContextError, position: Span },
|
||||
ExpectedType { position: Span },
|
||||
ExpectedTupleType { position: Span },
|
||||
ExpectedNonEmptyList { position: Span },
|
||||
@ -70,27 +70,23 @@ pub enum AstError {
|
||||
}
|
||||
|
||||
impl AstError {
|
||||
pub fn position(&self) -> Option<Span> {
|
||||
pub fn position(&self) -> Span {
|
||||
match self {
|
||||
AstError::ContextError(_) => None,
|
||||
AstError::ExpectedType { position } => Some(*position),
|
||||
AstError::ExpectedTupleType { position } => Some(*position),
|
||||
AstError::ExpectedNonEmptyList { position } => Some(*position),
|
||||
AstError::ExpectedRangeableType { position } => Some(*position),
|
||||
AstError::ContextError { position, .. } => *position,
|
||||
AstError::ExpectedType { position } => *position,
|
||||
AstError::ExpectedTupleType { position } => *position,
|
||||
AstError::ExpectedNonEmptyList { position } => *position,
|
||||
AstError::ExpectedRangeableType { position } => *position,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ContextError> for AstError {
|
||||
fn from(v: ContextError) -> Self {
|
||||
Self::ContextError(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for AstError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
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::ExpectedTupleType { position } => {
|
||||
write!(f, "Expected a tuple type at {:?}", position)
|
||||
|
@ -14,13 +14,14 @@ use crate::{Identifier, Type, Value};
|
||||
pub enum BuiltInFunction {
|
||||
// String tools
|
||||
ToString,
|
||||
LengthString,
|
||||
|
||||
// Integer and float tools
|
||||
IsEven,
|
||||
IsOdd,
|
||||
|
||||
// List tools
|
||||
Length,
|
||||
LengthList,
|
||||
|
||||
// I/O
|
||||
ReadLine,
|
||||
@ -32,7 +33,8 @@ impl BuiltInFunction {
|
||||
match self {
|
||||
BuiltInFunction::IsEven => "is_even",
|
||||
BuiltInFunction::IsOdd => "is_odd",
|
||||
BuiltInFunction::Length => "length",
|
||||
BuiltInFunction::LengthList => "length",
|
||||
BuiltInFunction::LengthString => "length",
|
||||
BuiltInFunction::ReadLine => "read_line",
|
||||
BuiltInFunction::ToString { .. } => "to_string",
|
||||
BuiltInFunction::WriteLine => "write_line",
|
||||
@ -44,7 +46,8 @@ impl BuiltInFunction {
|
||||
BuiltInFunction::ToString { .. } => None,
|
||||
BuiltInFunction::IsEven => None,
|
||||
BuiltInFunction::IsOdd => None,
|
||||
BuiltInFunction::Length => None,
|
||||
BuiltInFunction::LengthList => None,
|
||||
BuiltInFunction::LengthString => None,
|
||||
BuiltInFunction::ReadLine => None,
|
||||
BuiltInFunction::WriteLine => None,
|
||||
}
|
||||
@ -55,14 +58,15 @@ impl BuiltInFunction {
|
||||
BuiltInFunction::ToString { .. } => Some(vec![("value".into(), Type::Any)]),
|
||||
BuiltInFunction::IsEven => Some(vec![("value".into(), Type::Number)]),
|
||||
BuiltInFunction::IsOdd => Some(vec![("value".into(), Type::Number)]),
|
||||
BuiltInFunction::Length => Some(vec![(
|
||||
BuiltInFunction::LengthList => Some(vec![(
|
||||
"value".into(),
|
||||
Type::ListOf {
|
||||
item_type: Box::new(Type::Any),
|
||||
},
|
||||
)]),
|
||||
BuiltInFunction::LengthString => Some(vec![("value".into(), Type::String)]),
|
||||
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::IsEven => 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::WriteLine => None,
|
||||
}
|
||||
@ -112,13 +117,20 @@ impl BuiltInFunction {
|
||||
Err(BuiltInFunctionError::ExpectedInteger)
|
||||
}
|
||||
}
|
||||
BuiltInFunction::Length => {
|
||||
BuiltInFunction::LengthList => {
|
||||
if let Value::List(list) = &value_arguments.unwrap()[0] {
|
||||
Ok(Some(Value::Integer(list.len() as i64)))
|
||||
} else {
|
||||
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 => {
|
||||
let mut input = String::new();
|
||||
|
||||
|
@ -38,7 +38,7 @@ pub fn core_library<'a>() -> &'a Context {
|
||||
Identifier::new("length"),
|
||||
(
|
||||
ContextData::VariableValue(Value::Function(Function::BuiltIn(
|
||||
BuiltInFunction::Length,
|
||||
BuiltInFunction::LengthList,
|
||||
))),
|
||||
(0, 0),
|
||||
),
|
||||
|
@ -61,12 +61,12 @@ impl<'src> DustError<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn position(&self) -> Option<Span> {
|
||||
pub fn position(&self) -> Span {
|
||||
match self {
|
||||
DustError::Runtime { runtime_error, .. } => runtime_error.position(),
|
||||
DustError::Analysis { analysis_error, .. } => analysis_error.position(),
|
||||
DustError::Parse { parse_error, .. } => Some(parse_error.position()),
|
||||
DustError::Lex { lex_error, .. } => Some(lex_error.position()),
|
||||
DustError::Parse { parse_error, .. } => parse_error.position(),
|
||||
DustError::Lex { lex_error, .. } => lex_error.position(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,25 +79,49 @@ impl<'src> DustError<'src> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report(&self) -> String {
|
||||
let title = self.title();
|
||||
let span = self.position();
|
||||
let label = self.to_string();
|
||||
pub fn primary_error_data(&self) -> (&'static str, Span, String) {
|
||||
(self.title(), self.position(), self.to_string())
|
||||
}
|
||||
|
||||
let message = if let Some(span) = span {
|
||||
Level::Error.title(title).snippet(
|
||||
Snippet::source(self.source())
|
||||
.annotation(Level::Info.span(span.0..span.1).label(&label)),
|
||||
)
|
||||
pub fn secondary_error_data(&self) -> Option<(&'static str, Span, String)> {
|
||||
if let DustError::Runtime { runtime_error, .. } = self {
|
||||
match runtime_error {
|
||||
RuntimeError::Expression { error, .. } => {
|
||||
Some(("Expression error", error.position(), error.to_string()))
|
||||
}
|
||||
RuntimeError::Statement { error, .. } => {
|
||||
Some(("Statement error", error.position(), error.to_string()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
Level::Error
|
||||
.title(title)
|
||||
.snippet(Snippet::source(self.source()))
|
||||
.footer(Level::Info.title("No position information available"))
|
||||
};
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn report(&self) -> String {
|
||||
let mut report = String::new();
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,8 +11,8 @@ use std::{
|
||||
use serde::{de::Visitor, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::{
|
||||
AbstractSyntaxTree, BuiltInFunction, Context, EnumType, FunctionType, Identifier,
|
||||
RangeableType, RuntimeError, StructType, Type, Vm,
|
||||
AbstractSyntaxTree, BuiltInFunction, BuiltInFunctionError, Context, ContextError, EnumType,
|
||||
FunctionType, Identifier, RangeableType, RuntimeError, StructType, Type, Vm,
|
||||
};
|
||||
|
||||
/// Dust value representation
|
||||
@ -232,18 +232,27 @@ impl Value {
|
||||
}
|
||||
|
||||
pub fn get_field(&self, field: &Identifier) -> Option<Value> {
|
||||
if let "to_string" = field.as_str() {
|
||||
return Some(Value::Function(Function::BuiltIn(
|
||||
BuiltInFunction::ToString,
|
||||
)));
|
||||
let built_in_function = match field.as_str() {
|
||||
"to_string" => 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,
|
||||
}
|
||||
|
||||
match self {
|
||||
}
|
||||
_ => {
|
||||
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,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Some(Value::Function(Function::BuiltIn(built_in_function)))
|
||||
}
|
||||
|
||||
pub fn get_index(&self, index: Value) -> Result<Option<Value>, ValueError> {
|
||||
@ -1118,25 +1127,29 @@ impl Function {
|
||||
_type_arguments: Option<Vec<Type>>,
|
||||
value_arguments: Option<Vec<Value>>,
|
||||
context: &Context,
|
||||
) -> Result<Option<Value>, RuntimeError> {
|
||||
) -> Result<Option<Value>, FunctionCallError> {
|
||||
match self {
|
||||
Function::BuiltIn(built_in_function) => built_in_function
|
||||
.call(_type_arguments, value_arguments)
|
||||
.map_err(|error| RuntimeError::BuiltInFunctionError { error }),
|
||||
.map_err(FunctionCallError::BuiltInFunction),
|
||||
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)) =
|
||||
(&r#type.value_parameters, 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);
|
||||
|
||||
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)]
|
||||
pub enum Struct {
|
||||
Unit {
|
||||
|
@ -21,8 +21,8 @@ use crate::{
|
||||
StructDefinition, StructExpression,
|
||||
},
|
||||
core_library, parse, Analyzer, BuiltInFunctionError, Constructor, Context, ContextData,
|
||||
ContextError, DustError, Expression, Function, Identifier, ParseError, StructType, Type, Value,
|
||||
ValueError,
|
||||
ContextError, DustError, Expression, Function, FunctionCallError, Identifier, ParseError,
|
||||
StructType, Type, Value, ValueError,
|
||||
};
|
||||
|
||||
/// Run the source code and return the result.
|
||||
@ -167,14 +167,21 @@ impl Vm {
|
||||
};
|
||||
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)
|
||||
}
|
||||
};
|
||||
|
||||
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 {
|
||||
@ -190,24 +197,27 @@ impl Vm {
|
||||
) -> Result<(), RuntimeError> {
|
||||
match let_statement {
|
||||
LetStatement::Let { identifier, value } => {
|
||||
let value_position = value.position();
|
||||
let position = value.position();
|
||||
let value = self
|
||||
.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(())
|
||||
}
|
||||
LetStatement::LetMut { identifier, value } => {
|
||||
let value_position = value.position();
|
||||
let position = value.position();
|
||||
let mutable_value = self
|
||||
.run_expression(value, collect_garbage)?
|
||||
.expect_value(value_position)?
|
||||
.expect_value(position)?
|
||||
.into_mutable();
|
||||
|
||||
self.context
|
||||
.set_variable_value(identifier.inner, mutable_value)?;
|
||||
.set_variable_value(identifier.inner, mutable_value)
|
||||
.map_err(|error| RuntimeError::ContextError { error, position })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -250,7 +260,7 @@ impl Vm {
|
||||
Expression::Grouped(expression) => {
|
||||
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::List(list_expression) => {
|
||||
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);
|
||||
|
||||
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 {
|
||||
return Ok(Evaluation::Return(Some(value)));
|
||||
@ -296,7 +311,10 @@ impl Vm {
|
||||
return Ok(Evaluation::Constructor(constructor));
|
||||
}
|
||||
|
||||
Err(RuntimeError::UnassociatedIdentifier { identifier })
|
||||
Err(RuntimeError::UnassociatedIdentifier {
|
||||
identifier: identifier.inner,
|
||||
position: identifier.position,
|
||||
})
|
||||
}
|
||||
|
||||
fn run_struct(
|
||||
@ -309,7 +327,10 @@ impl Vm {
|
||||
let StructExpression::Fields { name, fields } = struct_expression;
|
||||
|
||||
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 Constructor::Fields(fields_constructor) = constructor {
|
||||
@ -696,6 +717,7 @@ impl Vm {
|
||||
log::debug!("Running call expression: {call_expression}");
|
||||
|
||||
let CallExpression { invoker, arguments } = call_expression;
|
||||
let invoker_position = invoker.position();
|
||||
|
||||
if let Expression::FieldAccess(field_access) = invoker {
|
||||
let FieldAccessExpression { container, field } = *field_access.inner;
|
||||
@ -738,7 +760,11 @@ impl Vm {
|
||||
|
||||
return function
|
||||
.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();
|
||||
@ -801,6 +827,10 @@ impl Vm {
|
||||
function
|
||||
.call(None, value_arguments, &context)
|
||||
.map(Evaluation::Return)
|
||||
.map_err(|error| RuntimeError::FunctionCall {
|
||||
error,
|
||||
position: invoker_position,
|
||||
})
|
||||
}
|
||||
_ => Err(RuntimeError::ExpectedValueOrConstructor {
|
||||
position: invoker_position,
|
||||
@ -1010,7 +1040,14 @@ impl Evaluation {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum RuntimeError {
|
||||
ContextError(ContextError),
|
||||
ContextError {
|
||||
error: ContextError,
|
||||
position: Span,
|
||||
},
|
||||
FunctionCall {
|
||||
error: FunctionCallError,
|
||||
position: Span,
|
||||
},
|
||||
ParseError(ParseError),
|
||||
Expression {
|
||||
error: Box<RuntimeError>,
|
||||
@ -1030,6 +1067,7 @@ pub enum RuntimeError {
|
||||
// These should be prevented by running the analyzer before the VM
|
||||
BuiltInFunctionError {
|
||||
error: BuiltInFunctionError,
|
||||
position: Span,
|
||||
},
|
||||
EnumVariantNotFound {
|
||||
identifier: Identifier,
|
||||
@ -1086,6 +1124,7 @@ pub enum RuntimeError {
|
||||
},
|
||||
UnassociatedIdentifier {
|
||||
identifier: Identifier,
|
||||
position: Span,
|
||||
},
|
||||
UndefinedType {
|
||||
identifier: Identifier,
|
||||
@ -1099,19 +1138,13 @@ pub enum RuntimeError {
|
||||
},
|
||||
}
|
||||
|
||||
impl From<ContextError> for RuntimeError {
|
||||
fn from(error: ContextError) -> Self {
|
||||
Self::ContextError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeError {
|
||||
pub fn position(&self) -> Option<Span> {
|
||||
let position = match self {
|
||||
Self::ContextError(_) => return None,
|
||||
Self::BuiltInFunctionError { .. } => return None,
|
||||
Self::UnassociatedIdentifier { .. } => return None,
|
||||
|
||||
pub fn position(&self) -> Span {
|
||||
match self {
|
||||
Self::ContextError { position, .. } => *position,
|
||||
Self::BuiltInFunctionError { position, .. } => *position,
|
||||
Self::FunctionCall { position, .. } => *position,
|
||||
Self::UnassociatedIdentifier { position, .. } => *position,
|
||||
Self::ParseError(parse_error) => parse_error.position(),
|
||||
Self::Expression { position, .. } => *position,
|
||||
Self::Statement { position, .. } => *position,
|
||||
@ -1145,9 +1178,7 @@ impl RuntimeError {
|
||||
Self::UndefinedProperty {
|
||||
property_position, ..
|
||||
} => *property_position,
|
||||
};
|
||||
|
||||
Some(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1160,7 +1191,12 @@ impl From<ParseError> for RuntimeError {
|
||||
impl Display for RuntimeError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
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::Expression { error, position } => {
|
||||
write!(
|
||||
@ -1284,7 +1320,7 @@ impl Display for RuntimeError {
|
||||
start_position, end_position
|
||||
)
|
||||
}
|
||||
Self::UnassociatedIdentifier { identifier } => {
|
||||
Self::UnassociatedIdentifier { identifier, .. } => {
|
||||
write!(
|
||||
f,
|
||||
"Identifier \"{identifier}\" is not associated with a value or constructor"
|
||||
|
Loading…
Reference in New Issue
Block a user