Begin improving errors
This commit is contained in:
parent
3a97ba76a0
commit
1750132ed8
@ -7,7 +7,7 @@ use super::{AbstractTree, Action, Identifier, Statement, Type, WithPosition};
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct Assignment {
|
pub struct Assignment {
|
||||||
identifier: Identifier,
|
identifier: WithPosition<Identifier>,
|
||||||
r#type: Option<WithPosition<Type>>,
|
r#type: Option<WithPosition<Type>>,
|
||||||
operator: AssignmentOperator,
|
operator: AssignmentOperator,
|
||||||
statement: Box<WithPosition<Statement>>,
|
statement: Box<WithPosition<Statement>>,
|
||||||
@ -22,7 +22,7 @@ pub enum AssignmentOperator {
|
|||||||
|
|
||||||
impl Assignment {
|
impl Assignment {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
identifier: Identifier,
|
identifier: WithPosition<Identifier>,
|
||||||
r#type: Option<WithPosition<Type>>,
|
r#type: Option<WithPosition<Type>>,
|
||||||
operator: AssignmentOperator,
|
operator: AssignmentOperator,
|
||||||
statement: WithPosition<Statement>,
|
statement: WithPosition<Statement>,
|
||||||
@ -57,12 +57,12 @@ impl AbstractTree for Assignment {
|
|||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
context.set_type(self.identifier.clone(), expected_type.clone())?;
|
context.set_type(self.identifier.node.clone(), expected_type.clone())?;
|
||||||
} else {
|
} else {
|
||||||
context.set_type(self.identifier.clone(), statement_type)?;
|
context.set_type(self.identifier.node.clone(), statement_type)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.identifier.validate(context)?;
|
self.identifier.node.validate(context)?;
|
||||||
self.statement.node.validate(context)?;
|
self.statement.node.validate(context)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -77,27 +77,33 @@ impl AbstractTree for Assignment {
|
|||||||
|
|
||||||
match self.operator {
|
match self.operator {
|
||||||
AssignmentOperator::Assign => {
|
AssignmentOperator::Assign => {
|
||||||
context.set_value(self.identifier, value)?;
|
context.set_value(self.identifier.node, value)?;
|
||||||
}
|
}
|
||||||
AssignmentOperator::AddAssign => {
|
AssignmentOperator::AddAssign => {
|
||||||
if let Some(previous_value) = context.get_value(&self.identifier)? {
|
if let Some(previous_value) = context.get_value(&self.identifier.node)? {
|
||||||
let new_value = previous_value.add(&value)?;
|
let new_value = previous_value.add(&value)?;
|
||||||
|
|
||||||
context.set_value(self.identifier, new_value)?;
|
context.set_value(self.identifier.node, new_value)?;
|
||||||
} else {
|
} else {
|
||||||
return Err(RuntimeError::ValidationFailure(
|
return Err(RuntimeError::ValidationFailure(
|
||||||
ValidationError::VariableNotFound(self.identifier),
|
ValidationError::VariableNotFound {
|
||||||
|
identifier: self.identifier.node,
|
||||||
|
position: self.identifier.position,
|
||||||
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AssignmentOperator::SubAssign => {
|
AssignmentOperator::SubAssign => {
|
||||||
if let Some(previous_value) = context.get_value(&self.identifier)? {
|
if let Some(previous_value) = context.get_value(&self.identifier.node)? {
|
||||||
let new_value = previous_value.subtract(&value)?;
|
let new_value = previous_value.subtract(&value)?;
|
||||||
|
|
||||||
context.set_value(self.identifier, new_value)?;
|
context.set_value(self.identifier.node, new_value)?;
|
||||||
} else {
|
} else {
|
||||||
return Err(RuntimeError::ValidationFailure(
|
return Err(RuntimeError::ValidationFailure(
|
||||||
ValidationError::VariableNotFound(self.identifier),
|
ValidationError::VariableNotFound {
|
||||||
|
identifier: self.identifier.node,
|
||||||
|
position: self.identifier.position,
|
||||||
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -122,7 +128,7 @@ mod tests {
|
|||||||
let context = Context::new();
|
let context = Context::new();
|
||||||
|
|
||||||
Assignment::new(
|
Assignment::new(
|
||||||
Identifier::new("foobar"),
|
Identifier::new("foobar").with_position((0, 0)),
|
||||||
None,
|
None,
|
||||||
AssignmentOperator::Assign,
|
AssignmentOperator::Assign,
|
||||||
Statement::Expression(Expression::Value(ValueNode::Integer(42))).with_position((0, 0)),
|
Statement::Expression(Expression::Value(ValueNode::Integer(42))).with_position((0, 0)),
|
||||||
@ -145,7 +151,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Assignment::new(
|
Assignment::new(
|
||||||
Identifier::new("foobar"),
|
Identifier::new("foobar").with_position((0, 0)),
|
||||||
None,
|
None,
|
||||||
AssignmentOperator::AddAssign,
|
AssignmentOperator::AddAssign,
|
||||||
Statement::Expression(Expression::Value(ValueNode::Integer(41))).with_position((0, 0)),
|
Statement::Expression(Expression::Value(ValueNode::Integer(41))).with_position((0, 0)),
|
||||||
@ -168,7 +174,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
Assignment::new(
|
Assignment::new(
|
||||||
Identifier::new("foobar"),
|
Identifier::new("foobar").with_position((0, 0)),
|
||||||
None,
|
None,
|
||||||
AssignmentOperator::SubAssign,
|
AssignmentOperator::SubAssign,
|
||||||
Statement::Expression(Expression::Value(ValueNode::Integer(1))).with_position((0, 0)),
|
Statement::Expression(Expression::Value(ValueNode::Integer(1))).with_position((0, 0)),
|
||||||
@ -185,7 +191,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn type_check() {
|
fn type_check() {
|
||||||
let validation = Assignment::new(
|
let validation = Assignment::new(
|
||||||
Identifier::new("foobar"),
|
Identifier::new("foobar").with_position((0, 0)),
|
||||||
Some(WithPosition {
|
Some(WithPosition {
|
||||||
node: Type::Boolean,
|
node: Type::Boolean,
|
||||||
position: (0, 0).into(),
|
position: (0, 0).into(),
|
||||||
|
@ -52,7 +52,14 @@ impl AbstractTree for FunctionCall {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
fn run(self, context: &Context) -> Result<Action, RuntimeError> {
|
||||||
let value = self.function.node.run(context)?.as_return_value()?;
|
let action = self.function.node.run(context)?;
|
||||||
|
let value = if let Action::Return(value) = action {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
return Err(RuntimeError::ValidationFailure(
|
||||||
|
ValidationError::InterpreterExpectedReturn(self.function.position),
|
||||||
|
));
|
||||||
|
};
|
||||||
let function = if let ValueInner::Function(function) = value.inner().as_ref() {
|
let function = if let ValueInner::Function(function) = value.inner().as_ref() {
|
||||||
function
|
function
|
||||||
} else {
|
} else {
|
||||||
@ -66,7 +73,14 @@ impl AbstractTree for FunctionCall {
|
|||||||
let mut arguments = Vec::with_capacity(self.arguments.len());
|
let mut arguments = Vec::with_capacity(self.arguments.len());
|
||||||
|
|
||||||
for expression in self.arguments {
|
for expression in self.arguments {
|
||||||
let value = expression.node.run(context)?.as_return_value()?;
|
let action = expression.node.run(context)?;
|
||||||
|
let value = if let Action::Return(value) = action {
|
||||||
|
value
|
||||||
|
} else {
|
||||||
|
return Err(RuntimeError::ValidationFailure(
|
||||||
|
ValidationError::InterpreterExpectedReturn(expression.position),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
arguments.push(value);
|
arguments.push(value);
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,10 @@ impl AbstractTree for Identifier {
|
|||||||
if let Some(r#type) = context.get_type(self)? {
|
if let Some(r#type) = context.get_type(self)? {
|
||||||
Ok(r#type)
|
Ok(r#type)
|
||||||
} else {
|
} else {
|
||||||
Err(ValidationError::VariableNotFound(self.clone()))
|
Err(ValidationError::VariableNotFound {
|
||||||
|
identifier: todo!(),
|
||||||
|
position: todo!(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,7 +39,10 @@ impl AbstractTree for Identifier {
|
|||||||
if context.contains(self)? {
|
if context.contains(self)? {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(ValidationError::VariableNotFound(self.clone()))
|
Err(ValidationError::VariableNotFound {
|
||||||
|
identifier: todo!(),
|
||||||
|
position: todo!(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +53,10 @@ impl AbstractTree for Identifier {
|
|||||||
Ok(action)
|
Ok(action)
|
||||||
} else {
|
} else {
|
||||||
Err(RuntimeError::ValidationFailure(
|
Err(RuntimeError::ValidationFailure(
|
||||||
ValidationError::VariableNotFound(self.clone()),
|
ValidationError::VariableNotFound {
|
||||||
|
identifier: todo!(),
|
||||||
|
position: todo!(),
|
||||||
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,9 +54,10 @@ impl AbstractTree for ListIndex {
|
|||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(ValidationError::CannotIndexWith {
|
Err(ValidationError::CannotIndexWith {
|
||||||
collection_type: todo!(),
|
collection_type: left_type,
|
||||||
index_type: todo!(),
|
collection_position: self.left.position,
|
||||||
position: todo!(),
|
index_type: right_type,
|
||||||
|
index_position: self.right.position,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,9 +83,10 @@ impl AbstractTree for ListIndex {
|
|||||||
} else {
|
} else {
|
||||||
Err(RuntimeError::ValidationFailure(
|
Err(RuntimeError::ValidationFailure(
|
||||||
ValidationError::CannotIndexWith {
|
ValidationError::CannotIndexWith {
|
||||||
collection_type: todo!(),
|
collection_type: left_value.r#type(),
|
||||||
index_type: todo!(),
|
collection_position: self.left.position,
|
||||||
position: todo!(),
|
index_type: right_value.r#type(),
|
||||||
|
index_position: self.right.position,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -72,8 +72,9 @@ impl AbstractTree for MapIndex {
|
|||||||
|
|
||||||
Err(ValidationError::CannotIndexWith {
|
Err(ValidationError::CannotIndexWith {
|
||||||
collection_type: left_type,
|
collection_type: left_type,
|
||||||
|
collection_position: todo!(),
|
||||||
index_type: self.right.node.expected_type(_context)?,
|
index_type: self.right.node.expected_type(_context)?,
|
||||||
position: self.right.position,
|
index_position: self.right.position,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,8 +92,9 @@ impl AbstractTree for MapIndex {
|
|||||||
} else {
|
} else {
|
||||||
Err(ValidationError::CannotIndexWith {
|
Err(ValidationError::CannotIndexWith {
|
||||||
collection_type: left_type,
|
collection_type: left_type,
|
||||||
|
collection_position: self.left.position,
|
||||||
index_type: self.right.node.expected_type(context)?,
|
index_type: self.right.node.expected_type(context)?,
|
||||||
position: self.right.position,
|
index_position: self.right.position,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,8 +115,9 @@ impl AbstractTree for MapIndex {
|
|||||||
Err(RuntimeError::ValidationFailure(
|
Err(RuntimeError::ValidationFailure(
|
||||||
ValidationError::CannotIndexWith {
|
ValidationError::CannotIndexWith {
|
||||||
collection_type: collection.r#type(),
|
collection_type: collection.r#type(),
|
||||||
|
collection_position: todo!(),
|
||||||
index_type: self.right.node.expected_type(_context)?,
|
index_type: self.right.node.expected_type(_context)?,
|
||||||
position: self.right.position,
|
index_position: self.right.position,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -80,13 +80,3 @@ pub enum Action {
|
|||||||
Break,
|
Break,
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Action {
|
|
||||||
pub fn as_return_value(self) -> Result<Value, ValidationError> {
|
|
||||||
if let Action::Return(value) = self {
|
|
||||||
Ok(value)
|
|
||||||
} else {
|
|
||||||
Err(ValidationError::InterpreterExpectedReturn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
179
src/error.rs
179
src/error.rs
@ -1,6 +1,6 @@
|
|||||||
use std::{io, ops::Range, sync::PoisonError};
|
use std::{io, ops::Range, sync::PoisonError};
|
||||||
|
|
||||||
use ariadne::{Color, Fmt, Label, ReportBuilder};
|
use ariadne::{Color, Fmt, Label, Report, ReportBuilder, ReportKind};
|
||||||
use chumsky::{prelude::Rich, span::Span};
|
use chumsky::{prelude::Rich, span::Span};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -29,61 +29,93 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
pub fn build_report(
|
pub fn build_report<'a>(self) -> ReportBuilder<'a, (&'a str, Range<usize>)> {
|
||||||
self,
|
let (mut builder, validation_error) = match &self {
|
||||||
mut builder: ReportBuilder<'_, Range<usize>>,
|
|
||||||
) -> ReportBuilder<'_, Range<usize>> {
|
|
||||||
let type_color = Color::Green;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Error::Parse { expected, span } => {
|
Error::Parse { expected, span } => {
|
||||||
let message = match expected.as_str() {
|
let message = if expected.is_empty() {
|
||||||
"" => "Invalid character.".to_string(),
|
"Invalid token.".to_string()
|
||||||
expected => format!("Expected {expected}."),
|
} else {
|
||||||
|
format!("Expected {expected}.")
|
||||||
};
|
};
|
||||||
|
|
||||||
builder = builder.with_note("Parsing error.");
|
(
|
||||||
|
Report::build(
|
||||||
builder.add_label(Label::new(span.0..span.1).with_message(message));
|
ReportKind::Custom("Parsing Error", Color::White),
|
||||||
|
"input",
|
||||||
|
span.1,
|
||||||
|
)
|
||||||
|
.with_label(
|
||||||
|
Label::new(("input", span.0..span.1))
|
||||||
|
.with_message(message)
|
||||||
|
.with_color(Color::Red),
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Error::Lex { expected, span } => {
|
Error::Lex { expected, span } => {
|
||||||
let message = match expected.as_str() {
|
let message = if expected.is_empty() {
|
||||||
"" => "Invalid character.".to_string(),
|
"Invalid token.".to_string()
|
||||||
expected => format!("Expected {expected}."),
|
} else {
|
||||||
|
format!("Expected {expected}.")
|
||||||
};
|
};
|
||||||
|
|
||||||
builder = builder.with_note("Lexing error.");
|
(
|
||||||
|
Report::build(
|
||||||
builder.add_label(Label::new(span.0..span.1).with_message(message));
|
ReportKind::Custom("Dust Error", Color::White),
|
||||||
|
"input",
|
||||||
|
span.1,
|
||||||
|
)
|
||||||
|
.with_label(
|
||||||
|
Label::new(("input", span.0..span.1))
|
||||||
|
.with_message(message)
|
||||||
|
.with_color(Color::Red),
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Error::Runtime { error, position } => match error {
|
Error::Runtime { error, position } => (
|
||||||
RuntimeError::RwLockPoison(_) => todo!(),
|
Report::build(
|
||||||
RuntimeError::ValidationFailure(validation_error) => {
|
ReportKind::Custom("Dust Error", Color::White),
|
||||||
builder =
|
"input",
|
||||||
Error::Validation {
|
position.1,
|
||||||
error: validation_error,
|
),
|
||||||
position,
|
if let RuntimeError::ValidationFailure(validation_error) = error {
|
||||||
}
|
Some(validation_error)
|
||||||
.build_report(builder.with_note(
|
} else {
|
||||||
"The interpreter failed to catch this error during validation.",
|
None
|
||||||
));
|
|
||||||
}
|
|
||||||
RuntimeError::Io(_) => todo!(),
|
|
||||||
},
|
},
|
||||||
Error::Validation { error, position } => match error {
|
),
|
||||||
|
Error::Validation { error, position } => (
|
||||||
|
Report::build(
|
||||||
|
ReportKind::Custom("Dust Error", Color::White),
|
||||||
|
"input",
|
||||||
|
position.1,
|
||||||
|
),
|
||||||
|
Some(error),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let type_color = Color::Green;
|
||||||
|
|
||||||
|
if let Some(validation_error) = validation_error {
|
||||||
|
match validation_error {
|
||||||
ValidationError::ExpectedBoolean { actual, position } => {
|
ValidationError::ExpectedBoolean { actual, position } => {
|
||||||
builder.add_label(Label::new(position.0..position.1).with_message(format!(
|
builder.add_label(Label::new(("input", position.0..position.1)).with_message(
|
||||||
|
format!(
|
||||||
"Expected {} but got {}.",
|
"Expected {} but got {}.",
|
||||||
"boolean".fg(type_color),
|
"boolean".fg(type_color),
|
||||||
actual.fg(type_color)
|
actual.fg(type_color)
|
||||||
)));
|
),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
ValidationError::ExpectedIntegerOrFloat => {
|
ValidationError::ExpectedIntegerOrFloat(position) => {
|
||||||
builder.add_label(Label::new(position.0..position.1).with_message(format!(
|
builder.add_label(Label::new(("input", position.0..position.1)).with_message(
|
||||||
|
format!(
|
||||||
"Expected {} or {}.",
|
"Expected {} or {}.",
|
||||||
"integer".fg(type_color),
|
"integer".fg(type_color),
|
||||||
"float".fg(type_color)
|
"float".fg(type_color)
|
||||||
)));
|
),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
ValidationError::RwLockPoison(_) => todo!(),
|
ValidationError::RwLockPoison(_) => todo!(),
|
||||||
ValidationError::TypeCheck {
|
ValidationError::TypeCheck {
|
||||||
@ -94,39 +126,54 @@ impl Error {
|
|||||||
let TypeConflict { actual, expected } = conflict;
|
let TypeConflict { actual, expected } = conflict;
|
||||||
|
|
||||||
builder.add_labels([
|
builder.add_labels([
|
||||||
Label::new(expected_postion.0..expected_postion.1).with_message(format!(
|
Label::new(("input", expected_postion.0..expected_postion.1)).with_message(
|
||||||
"Type {} established here.",
|
format!("Type {} established here.", expected.fg(type_color)),
|
||||||
expected.fg(type_color)
|
),
|
||||||
)),
|
Label::new(("input", actual_position.0..actual_position.1))
|
||||||
Label::new(actual_position.0..actual_position.1)
|
|
||||||
.with_message(format!("Got type {} here.", actual.fg(type_color))),
|
.with_message(format!("Got type {} here.", actual.fg(type_color))),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
ValidationError::VariableNotFound(identifier) => {
|
ValidationError::VariableNotFound {
|
||||||
|
identifier,
|
||||||
|
position,
|
||||||
|
} => {
|
||||||
builder.add_label(
|
builder.add_label(
|
||||||
Label::new(position.0..position.1)
|
Label::new(("input", position.0..position.1))
|
||||||
.with_message(format!("The variable {identifier} does not exist."))
|
.with_message(format!("The variable {identifier} does not exist."))
|
||||||
.with_priority(1),
|
.with_priority(1),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
ValidationError::CannotIndex { r#type, position } => builder.add_label(
|
ValidationError::CannotIndex { r#type, position } => builder.add_label(
|
||||||
Label::new(position.0..position.1)
|
Label::new(("input", position.0..position.1))
|
||||||
.with_message(format!("Cannot index into a {}.", r#type.fg(type_color))),
|
.with_message(format!("Cannot index into a {}.", r#type.fg(type_color))),
|
||||||
),
|
),
|
||||||
ValidationError::CannotIndexWith {
|
ValidationError::CannotIndexWith {
|
||||||
collection_type,
|
collection_type,
|
||||||
|
collection_position,
|
||||||
index_type,
|
index_type,
|
||||||
position,
|
index_position,
|
||||||
} => builder.add_label(Label::new(position.0..position.1).with_message(format!(
|
} => {
|
||||||
"Cannot index into a {} with a {}.",
|
builder = builder.with_message(format!(
|
||||||
|
"Cannot index into {} with {}.",
|
||||||
|
collection_type.clone().fg(type_color),
|
||||||
|
index_type.clone().fg(type_color)
|
||||||
|
));
|
||||||
|
|
||||||
|
builder.add_labels([
|
||||||
|
Label::new(("input", collection_position.0..collection_position.1))
|
||||||
|
.with_message(format!(
|
||||||
|
"This has type {}.",
|
||||||
collection_type.fg(type_color),
|
collection_type.fg(type_color),
|
||||||
index_type.fg(type_color)
|
)),
|
||||||
))),
|
Label::new(("input", index_position.0..index_position.1))
|
||||||
ValidationError::InterpreterExpectedReturn => todo!(),
|
.with_message(format!("This has type {}.", index_type.fg(type_color),)),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
ValidationError::InterpreterExpectedReturn(_) => todo!(),
|
||||||
ValidationError::ExpectedFunction { .. } => todo!(),
|
ValidationError::ExpectedFunction { .. } => todo!(),
|
||||||
ValidationError::ExpectedValue => todo!(),
|
ValidationError::ExpectedValue(_) => todo!(),
|
||||||
ValidationError::PropertyNotFound(_) => todo!(),
|
ValidationError::PropertyNotFound { .. } => todo!(),
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builder
|
builder
|
||||||
@ -197,8 +244,9 @@ pub enum ValidationError {
|
|||||||
},
|
},
|
||||||
CannotIndexWith {
|
CannotIndexWith {
|
||||||
collection_type: Type,
|
collection_type: Type,
|
||||||
|
collection_position: SourcePosition,
|
||||||
index_type: Type,
|
index_type: Type,
|
||||||
position: SourcePosition,
|
index_position: SourcePosition,
|
||||||
},
|
},
|
||||||
ExpectedBoolean {
|
ExpectedBoolean {
|
||||||
actual: Type,
|
actual: Type,
|
||||||
@ -208,9 +256,9 @@ pub enum ValidationError {
|
|||||||
actual: Type,
|
actual: Type,
|
||||||
position: SourcePosition,
|
position: SourcePosition,
|
||||||
},
|
},
|
||||||
ExpectedIntegerOrFloat,
|
ExpectedIntegerOrFloat(SourcePosition),
|
||||||
ExpectedValue,
|
ExpectedValue(SourcePosition),
|
||||||
InterpreterExpectedReturn,
|
InterpreterExpectedReturn(SourcePosition),
|
||||||
RwLockPoison(RwLockPoisonError),
|
RwLockPoison(RwLockPoisonError),
|
||||||
TypeCheck {
|
TypeCheck {
|
||||||
/// The mismatch that caused the error.
|
/// The mismatch that caused the error.
|
||||||
@ -222,8 +270,15 @@ pub enum ValidationError {
|
|||||||
/// The position of the item that gave the "expected" type.
|
/// The position of the item that gave the "expected" type.
|
||||||
expected_position: SourcePosition,
|
expected_position: SourcePosition,
|
||||||
},
|
},
|
||||||
VariableNotFound(Identifier),
|
VariableNotFound {
|
||||||
PropertyNotFound(Identifier),
|
identifier: Identifier,
|
||||||
|
position: SourcePosition,
|
||||||
|
},
|
||||||
|
|
||||||
|
PropertyNotFound {
|
||||||
|
identifier: Identifier,
|
||||||
|
position: SourcePosition,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RwLockPoisonError> for ValidationError {
|
impl From<RwLockPoisonError> for ValidationError {
|
||||||
|
63
src/main.rs
63
src/main.rs
@ -1,12 +1,13 @@
|
|||||||
//! Command line interface for the dust programming language.
|
//! Command line interface for the dust programming language.
|
||||||
|
|
||||||
use ariadne::{Color, Report, ReportKind, Source};
|
use ariadne::{sources, Color, Label, Report, ReportKind, Source};
|
||||||
|
use chumsky::span::SimpleSpan;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
|
||||||
use std::{fs::read_to_string, io::Write};
|
use std::{fs::read_to_string, io::Write, ops::Range};
|
||||||
|
|
||||||
use dust_lang::{context::Context, Interpreter};
|
use dust_lang::{context::Context, error::Error, Interpreter};
|
||||||
|
|
||||||
/// Command-line arguments to be parsed.
|
/// Command-line arguments to be parsed.
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
@ -53,17 +54,63 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(errors) => {
|
Err(errors) => {
|
||||||
let mut report_builder =
|
|
||||||
Report::build(ReportKind::Custom("Dust Error", Color::White), (), 5);
|
|
||||||
|
|
||||||
for error in errors {
|
for error in errors {
|
||||||
report_builder = error.build_report(report_builder);
|
let mut report_builder = match &error {
|
||||||
|
Error::Parse { expected, span } => {
|
||||||
|
let message = if expected.is_empty() {
|
||||||
|
"Invalid token.".to_string()
|
||||||
|
} else {
|
||||||
|
format!("Expected {expected}.")
|
||||||
|
};
|
||||||
|
|
||||||
|
Report::build(
|
||||||
|
ReportKind::Custom("Parsing Error", Color::White),
|
||||||
|
"input",
|
||||||
|
span.1,
|
||||||
|
)
|
||||||
|
.with_label(
|
||||||
|
Label::new(("input", span.0..span.1))
|
||||||
|
.with_message(message)
|
||||||
|
.with_color(Color::Red),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
Error::Lex { expected, span } => {
|
||||||
|
let message = if expected.is_empty() {
|
||||||
|
"Invalid token.".to_string()
|
||||||
|
} else {
|
||||||
|
format!("Expected {expected}.")
|
||||||
|
};
|
||||||
|
|
||||||
|
Report::build(
|
||||||
|
ReportKind::Custom("Dust Error", Color::White),
|
||||||
|
"input",
|
||||||
|
span.1,
|
||||||
|
)
|
||||||
|
.with_label(
|
||||||
|
Label::new(("input", span.0..span.1))
|
||||||
|
.with_message(message)
|
||||||
|
.with_color(Color::Red),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Error::Runtime { error, position } => Report::build(
|
||||||
|
ReportKind::Custom("Dust Error", Color::White),
|
||||||
|
"input",
|
||||||
|
position.1,
|
||||||
|
),
|
||||||
|
Error::Validation { error, position } => Report::build(
|
||||||
|
ReportKind::Custom("Dust Error", Color::White),
|
||||||
|
"input",
|
||||||
|
position.1,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
report_builder = error.build_report(report_builder);
|
||||||
|
|
||||||
report_builder
|
report_builder
|
||||||
.finish()
|
.finish()
|
||||||
.eprint(Source::from(source))
|
.eprint(sources([("input", &source)]))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,10 @@ pub fn parser<'src>() -> DustParser<'src> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let positioned_identifier = identifier
|
||||||
|
.clone()
|
||||||
|
.map_with(|identifier, state| identifier.with_position(state.span()));
|
||||||
|
|
||||||
let basic_value = select! {
|
let basic_value = select! {
|
||||||
Token::Boolean(boolean) => ValueNode::Boolean(boolean),
|
Token::Boolean(boolean) => ValueNode::Boolean(boolean),
|
||||||
Token::Float(float) => ValueNode::Float(float),
|
Token::Float(float) => ValueNode::Float(float),
|
||||||
@ -360,7 +364,7 @@ pub fn parser<'src>() -> DustParser<'src> {
|
|||||||
let r#break = just(Token::Keyword("break"))
|
let r#break = just(Token::Keyword("break"))
|
||||||
.map_with(|_, state| Statement::Break.with_position(state.span()));
|
.map_with(|_, state| Statement::Break.with_position(state.span()));
|
||||||
|
|
||||||
let assignment = identifier
|
let assignment = positioned_identifier
|
||||||
.clone()
|
.clone()
|
||||||
.then(type_specification.clone().or_not())
|
.then(type_specification.clone().or_not())
|
||||||
.then(choice((
|
.then(choice((
|
||||||
|
Loading…
Reference in New Issue
Block a user