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)?;
|
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)
|
||||||
}
|
}
|
||||||
|
@ -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)?,
|
||||||
|
@ -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)
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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),
|
||||||
),
|
),
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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"
|
||||||
|
Loading…
Reference in New Issue
Block a user