Compare commits
2 Commits
fa2ce8a0bf
...
77134e5292
Author | SHA1 | Date | |
---|---|---|---|
77134e5292 | |||
4805a53269 |
77
Cargo.lock
generated
77
Cargo.lock
generated
@ -11,6 +11,16 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "annotate-snippets"
|
||||
version = "0.11.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24e35ed54e5ea7997c14ed4c70ba043478db1112e98263b3b035907aa197d991"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.13"
|
||||
@ -27,9 +37,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "1.0.6"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
|
||||
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
@ -65,6 +75,46 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "colorchoice"
|
||||
version = "1.0.0"
|
||||
@ -100,6 +150,7 @@ checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||
name = "dust-lang"
|
||||
version = "0.5.0"
|
||||
dependencies = [
|
||||
"annotate-snippets",
|
||||
"env_logger",
|
||||
"rand",
|
||||
"rayon",
|
||||
@ -110,6 +161,10 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "dust-shell"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"dust-lang",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
@ -151,6 +206,12 @@ dependencies = [
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
@ -321,6 +382,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.53"
|
||||
@ -338,6 +405,12 @@ version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
|
@ -9,6 +9,7 @@ readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
annotate-snippets = "0.11.4"
|
||||
env_logger = "0.11.3"
|
||||
rand = "0.8.5"
|
||||
rayon = "1.9.0"
|
||||
|
@ -5,7 +5,11 @@
|
||||
/// hash map of variables:
|
||||
/// - `analyze` convenience function
|
||||
/// - `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};
|
||||
|
||||
@ -81,11 +85,13 @@ impl<'a> Analyzer<'a> {
|
||||
(Some(Type::Integer), _) | (Some(Type::Float), _) | (Some(Type::String), _) => {
|
||||
return Err(AnalyzerError::ExpectedIntegerFloatOrString {
|
||||
actual: right.as_ref().clone(),
|
||||
position: right.position,
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
return Err(AnalyzerError::ExpectedIntegerFloatOrString {
|
||||
actual: left.as_ref().clone(),
|
||||
position: left.position,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -99,6 +105,7 @@ impl<'a> Analyzer<'a> {
|
||||
} else {
|
||||
return Err(AnalyzerError::ExpectedIdentifier {
|
||||
actual: left.as_ref().clone(),
|
||||
position: left.position,
|
||||
});
|
||||
}
|
||||
|
||||
@ -112,12 +119,14 @@ impl<'a> Analyzer<'a> {
|
||||
} else {
|
||||
return Err(AnalyzerError::ExpectedIdentifier {
|
||||
actual: function.as_ref().clone(),
|
||||
position: function.position,
|
||||
});
|
||||
}
|
||||
}
|
||||
Statement::Identifier(_) => {
|
||||
return Err(AnalyzerError::UnexpectedIdentifier {
|
||||
identifier: node.clone(),
|
||||
position: node.position,
|
||||
});
|
||||
}
|
||||
Statement::List(statements) => {
|
||||
@ -132,6 +141,7 @@ impl<'a> Analyzer<'a> {
|
||||
} else {
|
||||
return Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||
actual: left.as_ref().clone(),
|
||||
position: left.position,
|
||||
});
|
||||
}
|
||||
|
||||
@ -141,6 +151,7 @@ impl<'a> Analyzer<'a> {
|
||||
} else {
|
||||
return Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||
actual: right.as_ref().clone(),
|
||||
position: right.position,
|
||||
});
|
||||
}
|
||||
|
||||
@ -155,6 +166,7 @@ impl<'a> Analyzer<'a> {
|
||||
} else {
|
||||
return Err(AnalyzerError::ExpectedIdentifierOrValue {
|
||||
actual: left.as_ref().clone(),
|
||||
position: left.position,
|
||||
});
|
||||
}
|
||||
|
||||
@ -164,6 +176,7 @@ impl<'a> Analyzer<'a> {
|
||||
} else {
|
||||
return Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||
actual: left.as_ref().clone(),
|
||||
position: left.position,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -179,13 +192,43 @@ impl<'a> Analyzer<'a> {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum AnalyzerError {
|
||||
ExpectedBoolean { actual: Node },
|
||||
ExpectedFunction { position: Span },
|
||||
ExpectedIdentifier { actual: Node },
|
||||
ExpectedIdentifierOrValue { actual: Node },
|
||||
ExpectedIntegerOrFloat { actual: Node },
|
||||
ExpectedIntegerFloatOrString { actual: Node },
|
||||
UnexpectedIdentifier { identifier: Node },
|
||||
ExpectedBoolean { actual: Node, position: Span },
|
||||
ExpectedFunction { actual: Node, position: Span },
|
||||
ExpectedIdentifier { actual: Node, position: Span },
|
||||
ExpectedIdentifierOrValue { actual: Node, position: Span },
|
||||
ExpectedIntegerOrFloat { actual: Node, position: Span },
|
||||
ExpectedIntegerFloatOrString { actual: Node, position: Span },
|
||||
UnexpectedIdentifier { identifier: Node, position: Span },
|
||||
}
|
||||
|
||||
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)]
|
||||
@ -212,7 +255,8 @@ mod tests {
|
||||
assert_eq!(
|
||||
analyzer.analyze(),
|
||||
Err(AnalyzerError::ExpectedIntegerFloatOrString {
|
||||
actual: Node::new(Statement::Constant(Value::float(1.0)), (1, 2))
|
||||
actual: Node::new(Statement::Constant(Value::float(1.0)), (1, 2)),
|
||||
position: (1, 2)
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -235,7 +279,8 @@ mod tests {
|
||||
assert_eq!(
|
||||
analyzer.analyze(),
|
||||
Err(AnalyzerError::ExpectedIntegerFloatOrString {
|
||||
actual: Node::new(Statement::Constant(Value::boolean(true)), (0, 1))
|
||||
actual: Node::new(Statement::Constant(Value::boolean(true)), (0, 1)),
|
||||
position: (0, 1)
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -265,7 +310,8 @@ mod tests {
|
||||
assert_eq!(
|
||||
analyzer.analyze(),
|
||||
Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||
actual: Node::new(Statement::Constant(Value::boolean(true)), (0, 1))
|
||||
actual: Node::new(Statement::Constant(Value::boolean(true)), (0, 1)),
|
||||
position: (0, 1)
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -294,7 +340,8 @@ mod tests {
|
||||
assert_eq!(
|
||||
analyzer.analyze(),
|
||||
Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||
actual: Node::new(Statement::Constant(Value::boolean(true)), (0, 1))
|
||||
actual: Node::new(Statement::Constant(Value::boolean(true)), (0, 1)),
|
||||
position: (0, 1)
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -320,7 +367,8 @@ mod tests {
|
||||
assert_eq!(
|
||||
analyzer.analyze(),
|
||||
Err(AnalyzerError::ExpectedIntegerOrFloat {
|
||||
actual: Node::new(Statement::Constant(Value::boolean(false)), (1, 2))
|
||||
actual: Node::new(Statement::Constant(Value::boolean(false)), (1, 2)),
|
||||
position: (1, 2)
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -343,7 +391,8 @@ mod tests {
|
||||
assert_eq!(
|
||||
analyzer.analyze(),
|
||||
Err(AnalyzerError::ExpectedIdentifier {
|
||||
actual: Node::new(Statement::Constant(Value::integer(1)), (0, 1))
|
||||
actual: Node::new(Statement::Constant(Value::integer(1)), (0, 1)),
|
||||
position: (0, 1)
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -363,7 +412,8 @@ mod tests {
|
||||
assert_eq!(
|
||||
analyzer.analyze(),
|
||||
Err(AnalyzerError::UnexpectedIdentifier {
|
||||
identifier: Node::new(Statement::Identifier(Identifier::new("x")), (0, 1))
|
||||
identifier: Node::new(Statement::Identifier(Identifier::new("x")), (0, 1)),
|
||||
position: (0, 1)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt::{self, Display, Formatter},
|
||||
io::{self, stdin},
|
||||
};
|
||||
@ -139,3 +140,17 @@ impl From<io::Error> for BuiltInFunctionError {
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
67
dust-lang/src/dust_error.rs
Normal file
67
dust-lang/src/dust_error.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use annotate_snippets::{Level, Renderer, Snippet};
|
||||
use std::{error::Error, fmt::Display};
|
||||
|
||||
use crate::{AnalyzerError, VmError};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct DustError<'src> {
|
||||
vm_error: VmError,
|
||||
source: &'src str,
|
||||
}
|
||||
|
||||
impl<'src> DustError<'src> {
|
||||
pub fn new(vm_error: VmError, source: &'src str) -> Self {
|
||||
Self { vm_error, source }
|
||||
}
|
||||
|
||||
pub fn report(&self) -> String {
|
||||
let title = match &self.vm_error {
|
||||
VmError::AnaylyzerError(_) => "Analyzer error",
|
||||
VmError::ParseError(_) => "Parse error",
|
||||
VmError::ValueError(_) => "Value error",
|
||||
VmError::BuiltInFunctionCallError(_) => "Runtime error",
|
||||
_ => "Analysis Failure",
|
||||
};
|
||||
let span = match &self.vm_error {
|
||||
VmError::AnaylyzerError(analyzer_error) => match analyzer_error {
|
||||
AnalyzerError::ExpectedBoolean { position, .. } => position,
|
||||
AnalyzerError::ExpectedFunction { position, .. } => position,
|
||||
AnalyzerError::ExpectedIdentifier { position, .. } => position,
|
||||
AnalyzerError::ExpectedIdentifierOrValue { position, .. } => position,
|
||||
AnalyzerError::ExpectedIntegerOrFloat { position, .. } => position,
|
||||
AnalyzerError::ExpectedIntegerFloatOrString { position, .. } => position,
|
||||
AnalyzerError::UnexpectedIdentifier { position, .. } => position,
|
||||
},
|
||||
VmError::ParseError(_) => todo!(),
|
||||
VmError::ValueError(_) => todo!(),
|
||||
VmError::BuiltInFunctionCallError(_) => todo!(),
|
||||
VmError::ExpectedIdentifier { position } => position,
|
||||
VmError::ExpectedIdentifierOrInteger { position } => position,
|
||||
VmError::ExpectedInteger { position } => position,
|
||||
VmError::ExpectedFunction { position, .. } => position,
|
||||
VmError::ExpectedList { position } => position,
|
||||
VmError::ExpectedValue { position } => position,
|
||||
};
|
||||
let label = self.vm_error.to_string();
|
||||
|
||||
let message = Level::Error.title(title).snippet(
|
||||
Snippet::source(self.source).annotation(Level::Info.span(span.0..span.1).label(&label)),
|
||||
);
|
||||
|
||||
let renderer = Renderer::styled();
|
||||
|
||||
format!("{}", renderer.render(message))
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
@ -3,7 +3,11 @@
|
||||
//! This module provides two lexing options:
|
||||
//! - [`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
|
||||
use std::num::{ParseFloatError, ParseIntError};
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt::{self, Display, Formatter},
|
||||
num::{ParseFloatError, ParseIntError},
|
||||
};
|
||||
|
||||
use crate::{Span, Token};
|
||||
|
||||
@ -284,6 +288,28 @@ pub enum LexError {
|
||||
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 {
|
||||
fn from(error: std::num::ParseFloatError) -> Self {
|
||||
Self::FloatError(error)
|
||||
|
@ -8,6 +8,7 @@
|
||||
pub mod abstract_tree;
|
||||
pub mod analyzer;
|
||||
pub mod built_in_function;
|
||||
pub mod dust_error;
|
||||
pub mod identifier;
|
||||
pub mod lex;
|
||||
pub mod parse;
|
||||
@ -19,6 +20,7 @@ pub mod vm;
|
||||
pub use abstract_tree::{AbstractSyntaxTree, Node, Statement};
|
||||
pub use analyzer::{analyze, Analyzer, AnalyzerError};
|
||||
pub use built_in_function::{BuiltInFunction, BuiltInFunctionError};
|
||||
pub use dust_error::DustError;
|
||||
pub use identifier::Identifier;
|
||||
pub use lex::{lex, LexError, Lexer};
|
||||
pub use parse::{parse, ParseError, Parser};
|
||||
|
@ -3,7 +3,11 @@
|
||||
/// This module provides two parsing options:
|
||||
/// - `parse` convenience function
|
||||
/// - `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::{
|
||||
built_in_function::BuiltInFunction, token::TokenOwned, AbstractSyntaxTree, Identifier,
|
||||
@ -231,7 +235,7 @@ impl<'src> Parser<'src> {
|
||||
} else {
|
||||
Err(ParseError::ExpectedClosingParenthesis {
|
||||
actual: self.current.0.to_owned(),
|
||||
span: self.current.1,
|
||||
position: self.current.1,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -261,7 +265,7 @@ impl<'src> Parser<'src> {
|
||||
} else {
|
||||
return Err(ParseError::ExpectedClosingSquareBrace {
|
||||
actual: self.current.0.to_owned(),
|
||||
span: self.current.1,
|
||||
position: self.current.1,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -286,7 +290,7 @@ impl<'src> Parser<'src> {
|
||||
} else {
|
||||
return Err(ParseError::ExpectedOpeningParenthesis {
|
||||
actual: self.current.0.to_owned(),
|
||||
span: self.current.1,
|
||||
position: self.current.1,
|
||||
});
|
||||
}
|
||||
|
||||
@ -312,7 +316,7 @@ impl<'src> Parser<'src> {
|
||||
} else {
|
||||
return Err(ParseError::ExpectedClosingParenthesis {
|
||||
actual: self.current.0.to_owned(),
|
||||
span: self.current.1,
|
||||
position: self.current.1,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -345,12 +349,39 @@ impl<'src> Parser<'src> {
|
||||
pub enum ParseError {
|
||||
LexError(LexError),
|
||||
|
||||
ExpectedClosingParenthesis { actual: TokenOwned, span: Span },
|
||||
ExpectedClosingSquareBrace { actual: TokenOwned, span: Span },
|
||||
ExpectedOpeningParenthesis { actual: TokenOwned, span: Span },
|
||||
ExpectedClosingParenthesis { actual: TokenOwned, position: Span },
|
||||
ExpectedClosingSquareBrace { actual: TokenOwned, position: Span },
|
||||
ExpectedOpeningParenthesis { actual: TokenOwned, position: Span },
|
||||
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, .. } => {
|
||||
write!(f, "Expected closing parenthesis, found {actual}",)
|
||||
}
|
||||
Self::ExpectedClosingSquareBrace { actual, .. } => {
|
||||
write!(f, "Expected closing square brace, found {actual}",)
|
||||
}
|
||||
Self::ExpectedOpeningParenthesis { actual, .. } => {
|
||||
write!(f, "Expected opening parenthesis, found {actual}",)
|
||||
}
|
||||
Self::UnexpectedToken(actual) => write!(f, "Unexpected token {actual}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<LexError> for ParseError {
|
||||
fn from(v: LexError) -> Self {
|
||||
Self::LexError(v)
|
||||
|
@ -121,3 +121,30 @@ pub enum TokenOwned {
|
||||
RightSquareBrace,
|
||||
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, "]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::{BTreeMap, HashMap},
|
||||
error::Error,
|
||||
fmt::{self, Display, Formatter},
|
||||
ops::Range,
|
||||
sync::Arc,
|
||||
@ -656,3 +657,20 @@ pub enum ValueError {
|
||||
IndexOutOfBounds { value: Value, index: i64 },
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
use std::collections::HashMap;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
error::Error,
|
||||
fmt::{self, Display, Formatter},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
parse, AbstractSyntaxTree, Analyzer, AnalyzerError, BuiltInFunctionError, Identifier, Node,
|
||||
@ -134,9 +138,10 @@ impl Vm {
|
||||
let function = if let Some(function) = function_value.as_function() {
|
||||
function
|
||||
} else {
|
||||
return Err(VmError::AnaylyzerError(AnalyzerError::ExpectedFunction {
|
||||
return Err(VmError::ExpectedFunction {
|
||||
actual: function_value,
|
||||
position: function_position,
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
let value_parameters = if let Some(value_nodes) = value_parameter_nodes {
|
||||
@ -245,17 +250,18 @@ pub enum VmError {
|
||||
|
||||
// Anaylsis Failures
|
||||
// These should be prevented by running the analyzer before the VM
|
||||
BuiltInFunctionCallFailed(BuiltInFunctionError),
|
||||
BuiltInFunctionCallError(BuiltInFunctionError),
|
||||
ExpectedIdentifier { position: Span },
|
||||
ExpectedIdentifierOrInteger { position: Span },
|
||||
ExpectedInteger { position: Span },
|
||||
ExpectedFunction { actual: Value, position: Span },
|
||||
ExpectedList { position: Span },
|
||||
ExpectedValue { position: Span },
|
||||
}
|
||||
|
||||
impl From<BuiltInFunctionError> for VmError {
|
||||
fn from(v: BuiltInFunctionError) -> Self {
|
||||
Self::BuiltInFunctionCallFailed(v)
|
||||
Self::BuiltInFunctionCallError(v)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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::BuiltInFunctionCallError(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::BuiltInFunctionCallError(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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -8,3 +8,5 @@ readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.14", features = ["derive"] }
|
||||
dust-lang = { path = "../dust-lang" }
|
||||
|
@ -1,3 +1,38 @@
|
||||
fn main() {
|
||||
println!("Hello, world!");
|
||||
use std::{collections::HashMap, fs::read_to_string};
|
||||
|
||||
use clap::Parser;
|
||||
use dust_lang::{run, DustError, Identifier, Value};
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Cli {
|
||||
#[arg(short, long)]
|
||||
command: Option<String>,
|
||||
|
||||
path: Option<String>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Cli::parse();
|
||||
let mut variables = HashMap::new();
|
||||
|
||||
if let Some(command) = &args.command {
|
||||
run_and_display_errors(command, &mut variables);
|
||||
} else if let Some(path) = &args.path {
|
||||
let source = read_to_string(path).expect("Failed to read file");
|
||||
|
||||
run_and_display_errors(&source, &mut variables)
|
||||
} else {
|
||||
panic!("No command or path provided");
|
||||
};
|
||||
}
|
||||
|
||||
fn run_and_display_errors(source: &str, variables: &mut HashMap<Identifier, Value>) {
|
||||
match run(source, variables) {
|
||||
Ok(return_value) => {
|
||||
if let Some(value) = return_value {
|
||||
println!("{}", value);
|
||||
}
|
||||
}
|
||||
Err(error) => eprintln!("{}", DustError::new(error, source).report()),
|
||||
}
|
||||
}
|
||||
|
1
rust-toolchain
Normal file
1
rust-toolchain
Normal file
@ -0,0 +1 @@
|
||||
nightly
|
Loading…
Reference in New Issue
Block a user