Begin expanding errors

This commit is contained in:
Jeff 2024-08-08 20:58:56 -04:00
parent fa2ce8a0bf
commit 4805a53269
12 changed files with 340 additions and 11 deletions

60
Cargo.lock generated
View File

@ -27,9 +27,9 @@ dependencies = [
[[package]] [[package]]
name = "anstyle" name = "anstyle"
version = "1.0.6" version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]] [[package]]
name = "anstyle-parse" name = "anstyle-parse"
@ -65,6 +65,46 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.0" version = "1.0.0"
@ -110,6 +150,10 @@ dependencies = [
[[package]] [[package]]
name = "dust-shell" name = "dust-shell"
version = "0.1.0" version = "0.1.0"
dependencies = [
"clap",
"dust-lang",
]
[[package]] [[package]]
name = "either" name = "either"
@ -151,6 +195,12 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "humantime" name = "humantime"
version = "2.1.0" version = "2.1.0"
@ -321,6 +371,12 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.53" version = "2.0.53"

View File

@ -5,7 +5,11 @@
/// hash map of variables: /// hash map of variables:
/// - `analyze` convenience function /// - `analyze` convenience function
/// - `Analyzer` struct /// - `Analyzer` struct
use std::collections::HashMap; use std::{
collections::HashMap,
error::Error,
fmt::{self, Display, Formatter},
};
use crate::{AbstractSyntaxTree, BuiltInFunction, Identifier, Node, Span, Statement, Type, Value}; use crate::{AbstractSyntaxTree, BuiltInFunction, Identifier, Node, Span, Statement, Type, Value};
@ -180,7 +184,7 @@ impl<'a> Analyzer<'a> {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum AnalyzerError { pub enum AnalyzerError {
ExpectedBoolean { actual: Node }, ExpectedBoolean { actual: Node },
ExpectedFunction { position: Span }, ExpectedFunction { actual: Node },
ExpectedIdentifier { actual: Node }, ExpectedIdentifier { actual: Node },
ExpectedIdentifierOrValue { actual: Node }, ExpectedIdentifierOrValue { actual: Node },
ExpectedIntegerOrFloat { actual: Node }, ExpectedIntegerOrFloat { actual: Node },
@ -188,6 +192,36 @@ pub enum AnalyzerError {
UnexpectedIdentifier { identifier: Node }, UnexpectedIdentifier { identifier: Node },
} }
impl Error for AnalyzerError {}
impl Display for AnalyzerError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
AnalyzerError::ExpectedBoolean { actual } => {
write!(f, "Expected boolean, found {}", actual)
}
AnalyzerError::ExpectedFunction { actual } => {
write!(f, "Expected function, found {}", actual)
}
AnalyzerError::ExpectedIdentifier { actual } => {
write!(f, "Expected identifier, found {}", actual)
}
AnalyzerError::ExpectedIdentifierOrValue { actual } => {
write!(f, "Expected identifier or value, found {}", actual)
}
AnalyzerError::ExpectedIntegerOrFloat { actual } => {
write!(f, "Expected integer or float, found {}", actual)
}
AnalyzerError::ExpectedIntegerFloatOrString { actual } => {
write!(f, "Expected integer, float, or string, found {}", actual)
}
AnalyzerError::UnexpectedIdentifier { identifier } => {
write!(f, "Unexpected identifier {}", identifier)
}
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{BuiltInFunction, Identifier, Value}; use crate::{BuiltInFunction, Identifier, Value};

View File

@ -1,4 +1,5 @@
use std::{ use std::{
error::Error,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
io::{self, stdin}, io::{self, stdin},
}; };
@ -139,3 +140,17 @@ impl From<io::Error> for BuiltInFunctionError {
Self::Io(error.kind()) Self::Io(error.kind())
} }
} }
impl Error for BuiltInFunctionError {}
impl Display for BuiltInFunctionError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
BuiltInFunctionError::ExpectedInteger => write!(f, "Expected an integer"),
BuiltInFunctionError::Io(error_kind) => write!(f, "I/O error: {}", error_kind),
BuiltInFunctionError::WrongNumberOfValueArguments => {
write!(f, "Wrong number of value arguments")
}
}
}
}

View File

@ -0,0 +1,21 @@
use std::{error::Error, fmt::Display};
use crate::VmError;
#[derive(Debug, Clone, PartialEq)]
pub struct DustError<'src> {
vm_error: VmError,
source: &'src str,
}
impl Error for DustError<'_> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.vm_error)
}
}
impl Display for DustError<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}\n{}", self.vm_error, self.source)
}
}

View File

@ -3,7 +3,11 @@
//! This module provides two lexing options: //! This module provides two lexing options:
//! - [`lex`], which lexes the entire input and returns a vector of tokens and their positions //! - [`lex`], which lexes the entire input and returns a vector of tokens and their positions
//! - [`Lexer`], which lexes the input a token at a time //! - [`Lexer`], which lexes the input a token at a time
use std::num::{ParseFloatError, ParseIntError}; use std::{
error::Error,
fmt::{self, Display, Formatter},
num::{ParseFloatError, ParseIntError},
};
use crate::{Span, Token}; use crate::{Span, Token};
@ -284,6 +288,28 @@ pub enum LexError {
IntegerError(ParseIntError), IntegerError(ParseIntError),
} }
impl Error for LexError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::FloatError(parse_float_error) => Some(parse_float_error),
Self::IntegerError(parse_int_error) => Some(parse_int_error),
}
}
}
impl Display for LexError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::FloatError(parse_float_error) => {
write!(f, "Failed to parse float: {}", parse_float_error)
}
Self::IntegerError(parse_int_error) => {
write!(f, "Failed to parse integer: {}", parse_int_error)
}
}
}
}
impl From<ParseFloatError> for LexError { impl From<ParseFloatError> for LexError {
fn from(error: std::num::ParseFloatError) -> Self { fn from(error: std::num::ParseFloatError) -> Self {
Self::FloatError(error) Self::FloatError(error)

View File

@ -8,6 +8,7 @@
pub mod abstract_tree; pub mod abstract_tree;
pub mod analyzer; pub mod analyzer;
pub mod built_in_function; pub mod built_in_function;
pub mod dust_error;
pub mod identifier; pub mod identifier;
pub mod lex; pub mod lex;
pub mod parse; pub mod parse;

View File

@ -3,7 +3,11 @@
/// This module provides two parsing options: /// This module provides two parsing options:
/// - `parse` convenience function /// - `parse` convenience function
/// - `Parser` struct, which parses the input a statement at a time /// - `Parser` struct, which parses the input a statement at a time
use std::collections::VecDeque; use std::{
collections::VecDeque,
error::Error,
fmt::{self, Display, Formatter},
};
use crate::{ use crate::{
built_in_function::BuiltInFunction, token::TokenOwned, AbstractSyntaxTree, Identifier, built_in_function::BuiltInFunction, token::TokenOwned, AbstractSyntaxTree, Identifier,
@ -351,6 +355,39 @@ pub enum ParseError {
UnexpectedToken(TokenOwned), UnexpectedToken(TokenOwned),
} }
impl Error for ParseError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::LexError(error) => Some(error),
_ => None,
}
}
}
impl Display for ParseError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::LexError(error) => write!(f, "{}", error),
Self::ExpectedClosingParenthesis { actual, span } => write!(
f,
"Expected closing parenthesis, found {} at {:?}",
actual, span
),
Self::ExpectedClosingSquareBrace { actual, span } => write!(
f,
"Expected closing square brace, found {:?} at {:?}",
actual, span
),
Self::ExpectedOpeningParenthesis { actual, span } => write!(
f,
"Expected opening parenthesis, found {:?} at {:?}",
actual, span
),
Self::UnexpectedToken(actual) => write!(f, "Unexpected token {:?}", actual),
}
}
}
impl From<LexError> for ParseError { impl From<LexError> for ParseError {
fn from(v: LexError) -> Self { fn from(v: LexError) -> Self {
Self::LexError(v) Self::LexError(v)

View File

@ -121,3 +121,30 @@ pub enum TokenOwned {
RightSquareBrace, RightSquareBrace,
Star, Star,
} }
impl Display for TokenOwned {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
TokenOwned::Eof => write!(f, "EOF"),
TokenOwned::Identifier(text) => write!(f, "{text}"),
TokenOwned::Boolean(boolean) => write!(f, "{boolean}"),
TokenOwned::Float(float) => write!(f, "{float}"),
TokenOwned::Integer(integer) => write!(f, "{integer}"),
TokenOwned::String(string) => write!(f, "{string}"),
TokenOwned::IsEven => write!(f, "is_even"),
TokenOwned::IsOdd => write!(f, "is_odd"),
TokenOwned::Length => write!(f, "length"),
TokenOwned::ReadLine => write!(f, "read_line"),
TokenOwned::WriteLine => write!(f, "write_line"),
TokenOwned::Comma => write!(f, ","),
TokenOwned::Dot => write!(f, "."),
TokenOwned::Equal => write!(f, "="),
TokenOwned::Plus => write!(f, "+"),
TokenOwned::Star => write!(f, "*"),
TokenOwned::LeftParenthesis => write!(f, "("),
TokenOwned::RightParenthesis => write!(f, ")"),
TokenOwned::LeftSquareBrace => write!(f, "["),
TokenOwned::RightSquareBrace => write!(f, "]"),
}
}
}

View File

@ -2,6 +2,7 @@
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
collections::{BTreeMap, HashMap}, collections::{BTreeMap, HashMap},
error::Error,
fmt::{self, Display, Formatter}, fmt::{self, Display, Formatter},
ops::Range, ops::Range,
sync::Arc, sync::Arc,
@ -656,3 +657,20 @@ pub enum ValueError {
IndexOutOfBounds { value: Value, index: i64 }, IndexOutOfBounds { value: Value, index: i64 },
ExpectedList(Value), ExpectedList(Value),
} }
impl Error for ValueError {}
impl Display for ValueError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
ValueError::CannotAdd(left, right) => write!(f, "Cannot add {} and {}", left, right),
ValueError::PropertyNotFound { value, property } => {
write!(f, "{} does not have a property named {}", value, property)
}
ValueError::IndexOutOfBounds { value, index } => {
write!(f, "{} does not have an index of {}", value, index)
}
ValueError::ExpectedList(value) => write!(f, "{} is not a list", value),
}
}
}

View File

@ -1,4 +1,8 @@
use std::collections::HashMap; use std::{
collections::HashMap,
error::Error,
fmt::{self, Display, Formatter},
};
use crate::{ use crate::{
parse, AbstractSyntaxTree, Analyzer, AnalyzerError, BuiltInFunctionError, Identifier, Node, parse, AbstractSyntaxTree, Analyzer, AnalyzerError, BuiltInFunctionError, Identifier, Node,
@ -134,9 +138,10 @@ impl Vm {
let function = if let Some(function) = function_value.as_function() { let function = if let Some(function) = function_value.as_function() {
function function
} else { } else {
return Err(VmError::AnaylyzerError(AnalyzerError::ExpectedFunction { return Err(VmError::ExpectedFunction {
actual: function_value,
position: function_position, position: function_position,
})); });
}; };
let value_parameters = if let Some(value_nodes) = value_parameter_nodes { let value_parameters = if let Some(value_nodes) = value_parameter_nodes {
@ -249,6 +254,7 @@ pub enum VmError {
ExpectedIdentifier { position: Span }, ExpectedIdentifier { position: Span },
ExpectedIdentifierOrInteger { position: Span }, ExpectedIdentifierOrInteger { position: Span },
ExpectedInteger { position: Span }, ExpectedInteger { position: Span },
ExpectedFunction { actual: Value, position: Span },
ExpectedList { position: Span }, ExpectedList { position: Span },
ExpectedValue { position: Span }, ExpectedValue { position: Span },
} }
@ -277,6 +283,59 @@ impl From<ValueError> for VmError {
} }
} }
impl Error for VmError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::AnaylyzerError(analyzer_error) => Some(analyzer_error),
Self::ParseError(parse_error) => Some(parse_error),
Self::ValueError(value_error) => Some(value_error),
Self::BuiltInFunctionCallFailed(built_in_function_error) => {
Some(built_in_function_error)
}
_ => None,
}
}
}
impl Display for VmError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::AnaylyzerError(analyzer_error) => write!(f, "{}", analyzer_error),
Self::ParseError(parse_error) => write!(f, "{}", parse_error),
Self::ValueError(value_error) => write!(f, "{}", value_error),
Self::BuiltInFunctionCallFailed(built_in_function_error) => {
write!(f, "{}", built_in_function_error)
}
Self::ExpectedFunction { actual, position } => {
write!(
f,
"Expected a function, but got: {} at position: {:?}",
actual, position
)
}
Self::ExpectedIdentifier { position } => {
write!(f, "Expected an identifier at position: {:?}", position)
}
Self::ExpectedIdentifierOrInteger { position } => {
write!(
f,
"Expected an identifier or integer at position: {:?}",
position
)
}
Self::ExpectedInteger { position } => {
write!(f, "Expected an integer at position: {:?}", position)
}
Self::ExpectedList { position } => {
write!(f, "Expected a list at position: {:?}", position)
}
Self::ExpectedValue { position } => {
write!(f, "Expected a value at position: {:?}", position)
}
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -8,3 +8,5 @@ readme.workspace = true
repository.workspace = true repository.workspace = true
[dependencies] [dependencies]
clap = { version = "4.5.14", features = ["derive"] }
dust-lang = { path = "../dust-lang" }

View File

@ -1,3 +1,36 @@
fn main() { use std::{collections::HashMap, fs::read_to_string};
println!("Hello, world!");
use clap::Parser;
use dust_lang::run;
#[derive(Parser)]
struct Cli {
#[arg(short, long)]
command: Option<String>,
path: Option<String>,
}
fn main() {
let args = Cli::parse();
let mut variables = HashMap::new();
let result = if let Some(command) = &args.command {
run(command, &mut variables)
} else if let Some(path) = &args.path {
let content = read_to_string(path).unwrap();
run(&content, &mut variables)
} else {
panic!("No command or path provided");
};
match result {
Ok(return_value) => {
if let Some(value) = return_value {
println!("{}", value);
}
}
Err(error) => eprintln!("{}", error),
}
} }