Refine error reports
This commit is contained in:
parent
004b7be27a
commit
7263507e84
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -384,6 +384,7 @@ dependencies = [
|
|||||||
name = "dust-shell"
|
name = "dust-shell"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ariadne",
|
||||||
"clap",
|
"clap",
|
||||||
"colored",
|
"colored",
|
||||||
"dust-lang",
|
"dust-lang",
|
||||||
|
@ -144,11 +144,11 @@ impl Display for Type {
|
|||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Type::Any => write!(f, "any"),
|
Type::Any => write!(f, "any"),
|
||||||
Type::Boolean => write!(f, "boolean"),
|
Type::Boolean => write!(f, "bool"),
|
||||||
Type::Float => write!(f, "float"),
|
Type::Float => write!(f, "float"),
|
||||||
Type::Integer => write!(f, "integer"),
|
Type::Integer => write!(f, "int"),
|
||||||
Type::List => write!(f, "list"),
|
Type::List => write!(f, "list"),
|
||||||
Type::ListOf(item_type) => write!(f, "list of {item_type}"),
|
Type::ListOf(item_type) => write!(f, "list({item_type})"),
|
||||||
Type::ListExact(item_types) => {
|
Type::ListExact(item_types) => {
|
||||||
write!(f, "[")?;
|
write!(f, "[")?;
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ impl Display for Type {
|
|||||||
Type::Map => write!(f, "map"),
|
Type::Map => write!(f, "map"),
|
||||||
Type::None => write!(f, "none"),
|
Type::None => write!(f, "none"),
|
||||||
Type::Range => write!(f, "range"),
|
Type::Range => write!(f, "range"),
|
||||||
Type::String => write!(f, "string"),
|
Type::String => write!(f, "str"),
|
||||||
Type::Function {
|
Type::Function {
|
||||||
parameter_types,
|
parameter_types,
|
||||||
return_type,
|
return_type,
|
||||||
|
@ -5,12 +5,10 @@ use std::{
|
|||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use rand::{thread_rng, Rng};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
abstract_tree::{Action, Identifier, Type, WithPosition},
|
abstract_tree::{Action, Type},
|
||||||
context::Context,
|
context::Context,
|
||||||
error::{RuntimeError, ValidationError},
|
error::RuntimeError,
|
||||||
value::ValueInner,
|
value::ValueInner,
|
||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
@ -18,6 +16,7 @@ use crate::{
|
|||||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub enum BuiltInFunction {
|
pub enum BuiltInFunction {
|
||||||
ReadLine,
|
ReadLine,
|
||||||
|
Sleep,
|
||||||
WriteLine,
|
WriteLine,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,6 +24,7 @@ impl BuiltInFunction {
|
|||||||
pub fn name(&self) -> &'static str {
|
pub fn name(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
BuiltInFunction::ReadLine => todo!(),
|
BuiltInFunction::ReadLine => todo!(),
|
||||||
|
BuiltInFunction::Sleep => todo!(),
|
||||||
BuiltInFunction::WriteLine => todo!(),
|
BuiltInFunction::WriteLine => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,32 +47,6 @@ impl BuiltInFunction {
|
|||||||
|
|
||||||
pub fn call(&self, arguments: Vec<Value>, context: &Context) -> Result<Action, RuntimeError> {
|
pub fn call(&self, arguments: Vec<Value>, context: &Context) -> Result<Action, RuntimeError> {
|
||||||
match self {
|
match self {
|
||||||
BuiltInFunction::ReadLine => {
|
|
||||||
let string = arguments.get(0).unwrap();
|
|
||||||
|
|
||||||
if let ValueInner::String(_string) = string.inner().as_ref() {
|
|
||||||
// let integer = string.parse();
|
|
||||||
|
|
||||||
todo!()
|
|
||||||
|
|
||||||
// Ok(Action::Return(Value::integer(integer)))
|
|
||||||
} else {
|
|
||||||
let mut actual = Vec::with_capacity(arguments.len());
|
|
||||||
|
|
||||||
for value in arguments {
|
|
||||||
let r#type = value.r#type(context)?;
|
|
||||||
|
|
||||||
actual.push(r#type);
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(RuntimeError::ValidationFailure(
|
|
||||||
ValidationError::WrongArguments {
|
|
||||||
expected: vec![Type::String],
|
|
||||||
actual,
|
|
||||||
},
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// "INT_RANDOM_RANGE" => {
|
// "INT_RANDOM_RANGE" => {
|
||||||
// let range = arguments.get(0).unwrap();
|
// let range = arguments.get(0).unwrap();
|
||||||
|
|
||||||
@ -91,18 +65,18 @@ impl BuiltInFunction {
|
|||||||
|
|
||||||
Ok(Action::Return(Value::string(input)))
|
Ok(Action::Return(Value::string(input)))
|
||||||
}
|
}
|
||||||
|
BuiltInFunction::Sleep => {
|
||||||
|
if let ValueInner::Integer(milliseconds) = arguments[0].inner().as_ref() {
|
||||||
|
thread::sleep(Duration::from_millis(*milliseconds as u64));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Action::None)
|
||||||
|
}
|
||||||
BuiltInFunction::WriteLine => {
|
BuiltInFunction::WriteLine => {
|
||||||
println!("{}", arguments[0]);
|
println!("{}", arguments[0]);
|
||||||
|
|
||||||
Ok(Action::None)
|
Ok(Action::None)
|
||||||
}
|
}
|
||||||
// "SLEEP" => {
|
|
||||||
// if let ValueInner::Integer(milliseconds) = arguments[0].inner().as_ref() {
|
|
||||||
// thread::sleep(Duration::from_millis(*milliseconds as u64));
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Ok(Action::None)
|
|
||||||
// }
|
|
||||||
_ => {
|
_ => {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::{io, sync::PoisonError};
|
use std::{fmt::Debug, hash::Hash, io, ops::Range, sync::PoisonError};
|
||||||
|
|
||||||
use ariadne::{sources, Color, Fmt, Label, Report, ReportKind};
|
use ariadne::{Color, Fmt, Label, Report, ReportKind};
|
||||||
use chumsky::{prelude::Rich, span::Span};
|
use chumsky::{prelude::Rich, span::Span};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -31,7 +31,10 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
pub fn build_report(self, source: &str) -> Result<Vec<u8>, io::Error> {
|
pub fn build_report<'id, Id: Debug + Hash + Eq + Clone>(
|
||||||
|
self,
|
||||||
|
source_id: Id,
|
||||||
|
) -> Result<Report<'id, (Id, Range<usize>)>, io::Error> {
|
||||||
let (mut builder, validation_error, error_position) = match self {
|
let (mut builder, validation_error, error_position) = match self {
|
||||||
Error::Parse {
|
Error::Parse {
|
||||||
expected,
|
expected,
|
||||||
@ -47,12 +50,12 @@ impl Error {
|
|||||||
(
|
(
|
||||||
Report::build(
|
Report::build(
|
||||||
ReportKind::Custom("Parsing Error", Color::Yellow),
|
ReportKind::Custom("Parsing Error", Color::Yellow),
|
||||||
"input",
|
source_id.clone(),
|
||||||
span.1,
|
span.1,
|
||||||
)
|
)
|
||||||
.with_message(description)
|
.with_message(description)
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new(("input", span.0..span.1))
|
Label::new((source_id.clone(), span.0..span.1))
|
||||||
.with_message(reason)
|
.with_message(reason)
|
||||||
.with_color(Color::Red),
|
.with_color(Color::Red),
|
||||||
),
|
),
|
||||||
@ -74,12 +77,12 @@ impl Error {
|
|||||||
(
|
(
|
||||||
Report::build(
|
Report::build(
|
||||||
ReportKind::Custom("Lexing Error", Color::Yellow),
|
ReportKind::Custom("Lexing Error", Color::Yellow),
|
||||||
"input",
|
source_id.clone(),
|
||||||
span.1,
|
span.1,
|
||||||
)
|
)
|
||||||
.with_message(description)
|
.with_message(description)
|
||||||
.with_label(
|
.with_label(
|
||||||
Label::new(("input", span.0..span.1))
|
Label::new((source_id.clone(), span.0..span.1))
|
||||||
.with_message(reason)
|
.with_message(reason)
|
||||||
.with_color(Color::Red),
|
.with_color(Color::Red),
|
||||||
),
|
),
|
||||||
@ -90,7 +93,7 @@ impl Error {
|
|||||||
Error::Runtime { error, position } => (
|
Error::Runtime { error, position } => (
|
||||||
Report::build(
|
Report::build(
|
||||||
ReportKind::Custom("Runtime Error", Color::Red),
|
ReportKind::Custom("Runtime Error", Color::Red),
|
||||||
"input",
|
source_id.clone(),
|
||||||
position.1,
|
position.1,
|
||||||
)
|
)
|
||||||
.with_message("An error occured that forced the program to exit.")
|
.with_message("An error occured that forced the program to exit.")
|
||||||
@ -110,7 +113,7 @@ impl Error {
|
|||||||
Error::Validation { error, position } => (
|
Error::Validation { error, position } => (
|
||||||
Report::build(
|
Report::build(
|
||||||
ReportKind::Custom("Validation Error", Color::Magenta),
|
ReportKind::Custom("Validation Error", Color::Magenta),
|
||||||
"input",
|
source_id.clone(),
|
||||||
position.1,
|
position.1,
|
||||||
)
|
)
|
||||||
.with_message("The syntax is valid but this code is not sound.")
|
.with_message("The syntax is valid but this code is not sound.")
|
||||||
@ -126,22 +129,22 @@ impl Error {
|
|||||||
if let Some(validation_error) = validation_error {
|
if let Some(validation_error) = validation_error {
|
||||||
match validation_error {
|
match validation_error {
|
||||||
ValidationError::ExpectedBoolean { actual, position } => {
|
ValidationError::ExpectedBoolean { actual, position } => {
|
||||||
builder.add_label(Label::new(("input", position.0..position.1)).with_message(
|
builder.add_label(
|
||||||
format!(
|
Label::new((source_id, 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(position) => {
|
ValidationError::ExpectedIntegerOrFloat(position) => {
|
||||||
builder.add_label(Label::new(("input", position.0..position.1)).with_message(
|
builder.add_label(
|
||||||
format!(
|
Label::new((source_id, 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 {
|
||||||
@ -154,15 +157,17 @@ impl Error {
|
|||||||
builder = builder.with_message("A type conflict was found.");
|
builder = builder.with_message("A type conflict was found.");
|
||||||
|
|
||||||
builder.add_labels([
|
builder.add_labels([
|
||||||
Label::new(("input", expected_postion.0..expected_postion.1)).with_message(
|
Label::new((source_id.clone(), expected_postion.0..expected_postion.1))
|
||||||
format!("Type {} established here.", expected.fg(type_color)),
|
.with_message(format!(
|
||||||
),
|
"Type {} established here.",
|
||||||
Label::new(("input", actual_position.0..actual_position.1))
|
expected.fg(type_color)
|
||||||
|
)),
|
||||||
|
Label::new((source_id, 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) => builder.add_label(
|
ValidationError::VariableNotFound(identifier) => builder.add_label(
|
||||||
Label::new(("input", error_position.0..error_position.1)).with_message(
|
Label::new((source_id, error_position.0..error_position.1)).with_message(
|
||||||
format!(
|
format!(
|
||||||
"Variable {} does not exist in this context.",
|
"Variable {} does not exist in this context.",
|
||||||
identifier.fg(identifier_color)
|
identifier.fg(identifier_color)
|
||||||
@ -170,7 +175,7 @@ impl Error {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
ValidationError::CannotIndex { r#type, position } => builder.add_label(
|
ValidationError::CannotIndex { r#type, position } => builder.add_label(
|
||||||
Label::new(("input", position.0..position.1))
|
Label::new((source_id, 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 {
|
||||||
@ -186,12 +191,14 @@ impl Error {
|
|||||||
));
|
));
|
||||||
|
|
||||||
builder.add_labels([
|
builder.add_labels([
|
||||||
Label::new(("input", collection_position.0..collection_position.1))
|
Label::new((
|
||||||
.with_message(format!(
|
source_id.clone(),
|
||||||
"This has type {}.",
|
collection_position.0..collection_position.1,
|
||||||
collection_type.fg(type_color),
|
))
|
||||||
)),
|
.with_message(
|
||||||
Label::new(("input", index_position.0..index_position.1))
|
format!("This has type {}.", collection_type.fg(type_color),),
|
||||||
|
),
|
||||||
|
Label::new((source_id, index_position.0..index_position.1))
|
||||||
.with_message(format!("This has type {}.", index_type.fg(type_color),)),
|
.with_message(format!("This has type {}.", index_type.fg(type_color),)),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@ -203,13 +210,7 @@ impl Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut output = Vec::new();
|
Ok(builder.finish())
|
||||||
|
|
||||||
builder
|
|
||||||
.finish()
|
|
||||||
.write_for_stdout(sources([("input", source)]), &mut output)?;
|
|
||||||
|
|
||||||
Ok(output)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ impl<'src> Display for Token<'src> {
|
|||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum BuiltInIdentifier {
|
pub enum BuiltInIdentifier {
|
||||||
ReadLine,
|
ReadLine,
|
||||||
|
Sleep,
|
||||||
WriteLine,
|
WriteLine,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,6 +47,7 @@ impl Display for BuiltInIdentifier {
|
|||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
BuiltInIdentifier::ReadLine => write!(f, "__READ_LINE__"),
|
BuiltInIdentifier::ReadLine => write!(f, "__READ_LINE__"),
|
||||||
|
BuiltInIdentifier::Sleep => write!(f, "__SLEEP__"),
|
||||||
BuiltInIdentifier::WriteLine => write!(f, "__WRITE_LINE__"),
|
BuiltInIdentifier::WriteLine => write!(f, "__WRITE_LINE__"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,6 +304,7 @@ pub fn lexer<'src>() -> impl Parser<
|
|||||||
|
|
||||||
let built_in_identifier = choice((
|
let built_in_identifier = choice((
|
||||||
just("__READ_LINE__").to(BuiltInIdentifier::ReadLine),
|
just("__READ_LINE__").to(BuiltInIdentifier::ReadLine),
|
||||||
|
just("__SLEEP__").to(BuiltInIdentifier::Sleep),
|
||||||
just("__WRITE_LINE__").to(BuiltInIdentifier::WriteLine),
|
just("__WRITE_LINE__").to(BuiltInIdentifier::WriteLine),
|
||||||
))
|
))
|
||||||
.map(Token::BuiltInIdentifier);
|
.map(Token::BuiltInIdentifier);
|
||||||
|
@ -236,10 +236,8 @@ pub fn parser<'src>() -> impl Parser<
|
|||||||
let built_in_function = {
|
let built_in_function = {
|
||||||
select! {
|
select! {
|
||||||
Token::BuiltInIdentifier(built_in_identifier) => {
|
Token::BuiltInIdentifier(built_in_identifier) => {
|
||||||
match built_in_identifier {
|
match built_in_identifier {BuiltInIdentifier::ReadLine=>BuiltInFunction::ReadLine,BuiltInIdentifier::WriteLine=>BuiltInFunction::WriteLine,
|
||||||
BuiltInIdentifier::ReadLine => BuiltInFunction::ReadLine,
|
BuiltInIdentifier::Sleep => BuiltInFunction::Sleep, }
|
||||||
BuiltInIdentifier::WriteLine => BuiltInFunction::WriteLine,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ readme.workspace = true
|
|||||||
repository.workspace = true
|
repository.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
ariadne = "0.4.0"
|
||||||
clap = { version = "4.5.3", features = ["derive"] }
|
clap = { version = "4.5.3", features = ["derive"] }
|
||||||
colored = "2.1.0"
|
colored = "2.1.0"
|
||||||
dust-lang = { path = "../dust-lang" }
|
dust-lang = { path = "../dust-lang" }
|
||||||
|
@ -5,6 +5,7 @@ use std::{
|
|||||||
process::Command,
|
process::Command,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use ariadne::sources;
|
||||||
use dust_lang::{
|
use dust_lang::{
|
||||||
context::{Context, ValueData},
|
context::{Context, ValueData},
|
||||||
*,
|
*,
|
||||||
@ -87,9 +88,11 @@ pub fn run_shell(context: Context) {
|
|||||||
Ok(None) => {}
|
Ok(None) => {}
|
||||||
Err(errors) => {
|
Err(errors) => {
|
||||||
for error in errors {
|
for error in errors {
|
||||||
let report = error.build_report(&buffer).unwrap();
|
let report = error.build_report(&"input").unwrap();
|
||||||
|
|
||||||
stderr().write_all(&report).unwrap();
|
report
|
||||||
|
.write_for_stdout(sources([(&"input", buffer.clone())]), stderr())
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
218
dust-shell/src/error.rs
Normal file
218
dust-shell/src/error.rs
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
use ariadne::{Color, Fmt, Label, Report, ReportKind};
|
||||||
|
use dust_lang::error::{Error as DustError, RuntimeError, TypeConflict, ValidationError};
|
||||||
|
use std::{fmt::Debug, io, ops::Range, path::Path, rc::Rc};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
Dust {
|
||||||
|
errors: Vec<dust_lang::error::Error>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn build_reports<'id>(
|
||||||
|
self,
|
||||||
|
source_id: Rc<String>,
|
||||||
|
source: &str,
|
||||||
|
) -> Result<Vec<Report<'id, (Rc<String>, Range<usize>)>>, io::Error> {
|
||||||
|
if let Error::Dust { errors } = self {
|
||||||
|
let mut reports = Vec::new();
|
||||||
|
|
||||||
|
for error in errors {
|
||||||
|
let (mut builder, validation_error, error_position) = match error {
|
||||||
|
DustError::Parse {
|
||||||
|
expected,
|
||||||
|
span,
|
||||||
|
reason,
|
||||||
|
} => {
|
||||||
|
let description = if expected.is_empty() {
|
||||||
|
"Invalid token.".to_string()
|
||||||
|
} else {
|
||||||
|
format!("Expected {expected}.")
|
||||||
|
};
|
||||||
|
|
||||||
|
(
|
||||||
|
Report::build(
|
||||||
|
ReportKind::Custom("Parsing Error", Color::Yellow),
|
||||||
|
source_id.clone(),
|
||||||
|
span.1,
|
||||||
|
)
|
||||||
|
.with_message(description)
|
||||||
|
.with_label(
|
||||||
|
Label::new((source_id.clone(), span.0..span.1))
|
||||||
|
.with_message(reason)
|
||||||
|
.with_color(Color::Red),
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
span.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DustError::Lex {
|
||||||
|
expected,
|
||||||
|
span,
|
||||||
|
reason,
|
||||||
|
} => {
|
||||||
|
let description = if expected.is_empty() {
|
||||||
|
"Invalid character.".to_string()
|
||||||
|
} else {
|
||||||
|
format!("Expected {expected}.")
|
||||||
|
};
|
||||||
|
|
||||||
|
(
|
||||||
|
Report::build(
|
||||||
|
ReportKind::Custom("Lexing Error", Color::Yellow),
|
||||||
|
source_id.clone(),
|
||||||
|
span.1,
|
||||||
|
)
|
||||||
|
.with_message(description)
|
||||||
|
.with_label(
|
||||||
|
Label::new((source_id.clone(), span.0..span.1))
|
||||||
|
.with_message(reason)
|
||||||
|
.with_color(Color::Red),
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
span.into(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DustError::Runtime { error, position } => (
|
||||||
|
Report::build(
|
||||||
|
ReportKind::Custom("Runtime Error", Color::Red),
|
||||||
|
source_id.clone(),
|
||||||
|
position.1,
|
||||||
|
)
|
||||||
|
.with_message("An error occured that forced the program to exit.")
|
||||||
|
.with_note(
|
||||||
|
"There may be unexpected side-effects because the program could not finish.",
|
||||||
|
)
|
||||||
|
.with_help(
|
||||||
|
"This is the interpreter's fault. Please submit a bug with this error message.",
|
||||||
|
),
|
||||||
|
if let RuntimeError::ValidationFailure(validation_error) = error {
|
||||||
|
Some(validation_error)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
position,
|
||||||
|
),
|
||||||
|
DustError::Validation { error, position } => (
|
||||||
|
Report::build(
|
||||||
|
ReportKind::Custom("Validation Error", Color::Magenta),
|
||||||
|
source_id.clone(),
|
||||||
|
position.1,
|
||||||
|
)
|
||||||
|
.with_message("The syntax is valid but this code is not sound.")
|
||||||
|
.with_note("This error was detected by the interpreter before running the code."),
|
||||||
|
Some(error),
|
||||||
|
position,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let type_color = Color::Green;
|
||||||
|
let identifier_color = Color::Blue;
|
||||||
|
|
||||||
|
if let Some(validation_error) = validation_error {
|
||||||
|
match validation_error {
|
||||||
|
ValidationError::ExpectedBoolean { actual, position } => {
|
||||||
|
builder.add_label(
|
||||||
|
Label::new((source_id.clone(), position.0..position.1))
|
||||||
|
.with_message(format!(
|
||||||
|
"Expected {} but got {}.",
|
||||||
|
"boolean".fg(type_color),
|
||||||
|
actual.fg(type_color)
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ValidationError::ExpectedIntegerOrFloat(position) => {
|
||||||
|
builder.add_label(
|
||||||
|
Label::new((source_id.clone(), position.0..position.1))
|
||||||
|
.with_message(format!(
|
||||||
|
"Expected {} or {}.",
|
||||||
|
"integer".fg(type_color),
|
||||||
|
"float".fg(type_color)
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ValidationError::RwLockPoison(_) => todo!(),
|
||||||
|
ValidationError::TypeCheck {
|
||||||
|
conflict,
|
||||||
|
actual_position,
|
||||||
|
expected_position: expected_postion,
|
||||||
|
} => {
|
||||||
|
let TypeConflict { actual, expected } = conflict;
|
||||||
|
|
||||||
|
builder = builder.with_message("A type conflict was found.");
|
||||||
|
|
||||||
|
builder.add_labels([
|
||||||
|
Label::new((
|
||||||
|
source_id.clone(),
|
||||||
|
expected_postion.0..expected_postion.1,
|
||||||
|
))
|
||||||
|
.with_message(format!(
|
||||||
|
"Type {} established here.",
|
||||||
|
expected.fg(type_color)
|
||||||
|
)),
|
||||||
|
Label::new((
|
||||||
|
source_id.clone(),
|
||||||
|
actual_position.0..actual_position.1,
|
||||||
|
))
|
||||||
|
.with_message(format!("Got type {} here.", actual.fg(type_color))),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
ValidationError::VariableNotFound(identifier) => builder.add_label(
|
||||||
|
Label::new((source_id.clone(), error_position.0..error_position.1))
|
||||||
|
.with_message(format!(
|
||||||
|
"Variable {} does not exist in this context.",
|
||||||
|
identifier.fg(identifier_color)
|
||||||
|
)),
|
||||||
|
),
|
||||||
|
ValidationError::CannotIndex { r#type, position } => builder.add_label(
|
||||||
|
Label::new((source_id.clone(), position.0..position.1)).with_message(
|
||||||
|
format!("Cannot index into a {}.", r#type.fg(type_color)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ValidationError::CannotIndexWith {
|
||||||
|
collection_type,
|
||||||
|
collection_position,
|
||||||
|
index_type,
|
||||||
|
index_position,
|
||||||
|
} => {
|
||||||
|
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((
|
||||||
|
source_id.clone(),
|
||||||
|
collection_position.0..collection_position.1,
|
||||||
|
))
|
||||||
|
.with_message(format!(
|
||||||
|
"This has type {}.",
|
||||||
|
collection_type.fg(type_color),
|
||||||
|
)),
|
||||||
|
Label::new((source_id.clone(), index_position.0..index_position.1))
|
||||||
|
.with_message(format!(
|
||||||
|
"This has type {}.",
|
||||||
|
index_type.fg(type_color),
|
||||||
|
)),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
ValidationError::InterpreterExpectedReturn(_) => todo!(),
|
||||||
|
ValidationError::ExpectedFunction { .. } => todo!(),
|
||||||
|
ValidationError::ExpectedValue(_) => todo!(),
|
||||||
|
ValidationError::PropertyNotFound { .. } => todo!(),
|
||||||
|
ValidationError::WrongArguments { .. } => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let report = builder.finish();
|
||||||
|
|
||||||
|
reports.push(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(reports);
|
||||||
|
} else {
|
||||||
|
return Ok(Vec::with_capacity(0));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,18 @@
|
|||||||
//! Command line interface for the dust programming language.
|
//! Command line interface for the dust programming language.
|
||||||
mod cli;
|
mod cli;
|
||||||
|
mod error;
|
||||||
|
|
||||||
|
use ariadne::sources;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use cli::run_shell;
|
use cli::run_shell;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
|
use error::Error;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::read_to_string,
|
fs::read_to_string,
|
||||||
io::{stderr, Write},
|
io::{stderr, Write},
|
||||||
|
path::Path,
|
||||||
|
rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use dust_lang::{context::Context, interpret};
|
use dust_lang::{context::Context, interpret};
|
||||||
@ -37,11 +42,10 @@ fn main() {
|
|||||||
|
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
let context = Context::new();
|
let context = Context::new();
|
||||||
|
let (source, source_id) = if let Some(path) = args.path {
|
||||||
let source = if let Some(path) = &args.path {
|
(read_to_string(&path).unwrap(), Rc::new(path))
|
||||||
read_to_string(path).unwrap()
|
|
||||||
} else if let Some(command) = args.command {
|
} else if let Some(command) = args.command {
|
||||||
command
|
(command, Rc::new("input".to_string()))
|
||||||
} else {
|
} else {
|
||||||
return run_shell(context);
|
return run_shell(context);
|
||||||
};
|
};
|
||||||
@ -55,10 +59,14 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(errors) => {
|
Err(errors) => {
|
||||||
for error in errors {
|
let reports = Error::Dust { errors }
|
||||||
let report = error.build_report(&source).unwrap();
|
.build_reports(source_id.clone(), &source)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
stderr().write_all(&report).unwrap();
|
for report in reports {
|
||||||
|
report
|
||||||
|
.write_for_stdout(sources([(source_id.clone(), source.clone())]), stderr())
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
std/thread.ds
Normal file
5
std/thread.ds
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
thread = {
|
||||||
|
sleep = (milliseconds: int) : none {
|
||||||
|
__SLEEP__()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user