Pass all tests
This commit is contained in:
parent
7dfc026be5
commit
2b797c19f7
104
Cargo.lock
generated
104
Cargo.lock
generated
@ -115,7 +115,6 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd002a6223f12c7a95cdd4b1cb3a0149d22d37f7a9ecdb2cb691a071fe236c29"
|
||||
dependencies = [
|
||||
"concolor",
|
||||
"unicode-width",
|
||||
"yansi",
|
||||
]
|
||||
@ -262,26 +261,6 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concolor"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b946244a988c390a94667ae0e3958411fa40cc46ea496a929b263d883f5f9c3"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"concolor-query",
|
||||
"is-terminal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concolor-query"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf"
|
||||
dependencies = [
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
@ -537,12 +516,6 @@ version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "home"
|
||||
version = "0.5.9"
|
||||
@ -591,17 +564,6 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.1"
|
||||
@ -1456,15 +1418,6 @@ dependencies = [
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
@ -1483,21 +1436,6 @@ dependencies = [
|
||||
"windows-targets 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
@ -1528,12 +1466,6 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.52.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
@ -1546,12 +1478,6 @@ version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
@ -1564,12 +1490,6 @@ version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
@ -1582,12 +1502,6 @@ version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
@ -1600,12 +1514,6 @@ version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
@ -1618,12 +1526,6 @@ version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
@ -1636,12 +1538,6 @@ version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
|
@ -9,7 +9,7 @@ readme.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[dependencies]
|
||||
ariadne = { version = "0.4.0", features = ["auto-color"] }
|
||||
ariadne = "0.4.0"
|
||||
chumsky = { version = "1.0.0-alpha.6", features = ["pratt", "label"] }
|
||||
clap = { version = "4.5.2", features = ["derive"] }
|
||||
colored = "2.1.0"
|
||||
|
@ -13,6 +13,7 @@ use super::{AbstractNode, Action, WithPosition};
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub enum Type {
|
||||
Any,
|
||||
Argument(Identifier),
|
||||
Boolean,
|
||||
Float,
|
||||
Function {
|
||||
@ -179,6 +180,7 @@ impl Display for Type {
|
||||
write!(f, ") : {return_type}")
|
||||
}
|
||||
Type::Structure { name, .. } => write!(f, "{name}"),
|
||||
Type::Argument(_) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ pub enum ValueNode {
|
||||
fields: Vec<(Identifier, WithPosition<Expression>)>,
|
||||
},
|
||||
ParsedFunction {
|
||||
type_arguments: Vec<WithPosition<Type>>,
|
||||
parameters: Vec<(Identifier, WithPosition<Type>)>,
|
||||
return_type: WithPosition<Type>,
|
||||
body: WithPosition<Block>,
|
||||
@ -112,6 +113,7 @@ impl AbstractNode for ValueNode {
|
||||
}
|
||||
|
||||
if let ValueNode::ParsedFunction {
|
||||
type_arguments,
|
||||
parameters,
|
||||
return_type,
|
||||
body,
|
||||
@ -121,6 +123,12 @@ impl AbstractNode for ValueNode {
|
||||
|
||||
function_context.inherit_types_from(context)?;
|
||||
|
||||
for r#type in type_arguments {
|
||||
if let Type::Argument(identifier) = &r#type.node {
|
||||
function_context.set_type(identifier.clone(), r#type.node.clone())?;
|
||||
}
|
||||
}
|
||||
|
||||
for (identifier, r#type) in parameters {
|
||||
function_context.set_type(identifier.clone(), r#type.node.clone())?;
|
||||
}
|
||||
@ -210,10 +218,11 @@ impl AbstractNode for ValueNode {
|
||||
ValueNode::Range(range) => Value::range(range),
|
||||
ValueNode::String(string) => Value::string(string),
|
||||
ValueNode::ParsedFunction {
|
||||
type_arguments,
|
||||
parameters,
|
||||
return_type,
|
||||
body,
|
||||
} => Value::function(parameters, return_type, body),
|
||||
} => Value::function(type_arguments, parameters, return_type, body),
|
||||
ValueNode::Structure {
|
||||
name,
|
||||
fields: expressions,
|
||||
@ -281,11 +290,13 @@ impl Ord for ValueNode {
|
||||
(String(_), _) => Ordering::Greater,
|
||||
(
|
||||
ParsedFunction {
|
||||
type_arguments: left_type_arguments,
|
||||
parameters: left_parameters,
|
||||
return_type: left_return,
|
||||
body: left_body,
|
||||
},
|
||||
ParsedFunction {
|
||||
type_arguments: right_type_arguments,
|
||||
parameters: right_parameters,
|
||||
return_type: right_return,
|
||||
body: right_body,
|
||||
@ -297,7 +308,13 @@ impl Ord for ValueNode {
|
||||
let return_cmp = left_return.cmp(right_return);
|
||||
|
||||
if return_cmp.is_eq() {
|
||||
let type_argument_cmp = left_type_arguments.cmp(right_type_arguments);
|
||||
|
||||
if type_argument_cmp.is_eq() {
|
||||
left_body.cmp(right_body)
|
||||
} else {
|
||||
type_argument_cmp
|
||||
}
|
||||
} else {
|
||||
return_cmp
|
||||
}
|
||||
|
@ -56,11 +56,7 @@ impl Context {
|
||||
}
|
||||
|
||||
pub fn contains(&self, identifier: &Identifier) -> Result<bool, RwLockPoisonError> {
|
||||
if self.inner.read()?.contains_key(identifier) {
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
Ok(self.inner.read()?.contains_key(identifier))
|
||||
}
|
||||
|
||||
pub fn get_type(&self, identifier: &Identifier) -> Result<Option<Type>, ValidationError> {
|
||||
@ -93,9 +89,9 @@ impl Context {
|
||||
}
|
||||
|
||||
pub fn set_value(&self, identifier: Identifier, value: Value) -> Result<(), RwLockPoisonError> {
|
||||
let mut inner = self.inner.write()?;
|
||||
|
||||
inner.insert(identifier, ValueData::Value(value));
|
||||
self.inner
|
||||
.write()?
|
||||
.insert(identifier, ValueData::Value(value));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ pub enum Keyword {
|
||||
Break,
|
||||
Else,
|
||||
Float,
|
||||
Fn,
|
||||
Int,
|
||||
If,
|
||||
List,
|
||||
@ -69,6 +70,7 @@ pub enum Keyword {
|
||||
Range,
|
||||
Struct,
|
||||
Str,
|
||||
Type,
|
||||
Loop,
|
||||
While,
|
||||
}
|
||||
@ -82,6 +84,7 @@ impl Display for Keyword {
|
||||
Keyword::Break => write!(f, "break"),
|
||||
Keyword::Else => write!(f, "else"),
|
||||
Keyword::Float => write!(f, "float"),
|
||||
Keyword::Fn => write!(f, "fn"),
|
||||
Keyword::Int => write!(f, "int"),
|
||||
Keyword::If => write!(f, "if"),
|
||||
Keyword::List => write!(f, "list"),
|
||||
@ -92,6 +95,7 @@ impl Display for Keyword {
|
||||
Keyword::Str => write!(f, "str"),
|
||||
Keyword::Loop => write!(f, "loop"),
|
||||
Keyword::While => write!(f, "while"),
|
||||
Keyword::Type => write!(f, "type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -143,13 +147,13 @@ impl Display for Operator {
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Control {
|
||||
Arrow,
|
||||
CurlyOpen,
|
||||
CurlyClose,
|
||||
SquareOpen,
|
||||
SquareClose,
|
||||
ParenOpen,
|
||||
ParenClose,
|
||||
Pipe,
|
||||
Comma,
|
||||
DoubleColon,
|
||||
Colon,
|
||||
@ -157,12 +161,13 @@ pub enum Control {
|
||||
Dot,
|
||||
DoubleDot,
|
||||
Semicolon,
|
||||
SkinnyArrow,
|
||||
FatArrow,
|
||||
}
|
||||
|
||||
impl Display for Control {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Control::Arrow => write!(f, "->"),
|
||||
Control::CurlyOpen => write!(f, "{{"),
|
||||
Control::CurlyClose => write!(f, "}}"),
|
||||
Control::Dollar => write!(f, "$"),
|
||||
@ -170,12 +175,15 @@ impl Display for Control {
|
||||
Control::SquareClose => write!(f, "]"),
|
||||
Control::ParenOpen => write!(f, "("),
|
||||
Control::ParenClose => write!(f, ")"),
|
||||
Control::Pipe => write!(f, "|"),
|
||||
Control::Comma => write!(f, ","),
|
||||
Control::DoubleColon => write!(f, "::"),
|
||||
Control::Colon => write!(f, ":"),
|
||||
Control::Dot => write!(f, "."),
|
||||
Control::Semicolon => write!(f, ";"),
|
||||
Control::DoubleDot => write!(f, ".."),
|
||||
Control::SkinnyArrow => write!(f, "->"),
|
||||
Control::FatArrow => write!(f, "=>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -265,13 +273,15 @@ pub fn lexer<'src>() -> impl Parser<
|
||||
.map(Token::Operator);
|
||||
|
||||
let control = choice((
|
||||
just("->").to(Control::Arrow),
|
||||
just("->").to(Control::SkinnyArrow),
|
||||
just("=>").to(Control::FatArrow),
|
||||
just("{").to(Control::CurlyOpen),
|
||||
just("}").to(Control::CurlyClose),
|
||||
just("[").to(Control::SquareOpen),
|
||||
just("]").to(Control::SquareClose),
|
||||
just("(").to(Control::ParenOpen),
|
||||
just(")").to(Control::ParenClose),
|
||||
just("|").to(Control::Pipe),
|
||||
just(",").to(Control::Comma),
|
||||
just(";").to(Control::Semicolon),
|
||||
just("::").to(Control::DoubleColon),
|
||||
@ -289,6 +299,7 @@ pub fn lexer<'src>() -> impl Parser<
|
||||
just("break").to(Keyword::Break),
|
||||
just("else").to(Keyword::Else),
|
||||
just("float").to(Keyword::Float),
|
||||
just("fn").to(Keyword::Fn),
|
||||
just("int").to(Keyword::Int),
|
||||
just("if").to(Keyword::If),
|
||||
just("list").to(Keyword::List),
|
||||
@ -297,6 +308,7 @@ pub fn lexer<'src>() -> impl Parser<
|
||||
just("range").to(Keyword::Range),
|
||||
just("struct").to(Keyword::Struct),
|
||||
just("str").to(Keyword::Str),
|
||||
just("type").to(Keyword::Type),
|
||||
just("loop").to(Keyword::Loop),
|
||||
just("while").to(Keyword::While),
|
||||
))
|
||||
|
@ -6,18 +6,256 @@ pub mod lexer;
|
||||
pub mod parser;
|
||||
pub mod value;
|
||||
|
||||
use std::{ops::Range, rc::Rc};
|
||||
|
||||
use abstract_tree::Type;
|
||||
use ariadne::{Color, Fmt, Label, Report, ReportKind};
|
||||
use context::Context;
|
||||
use error::Error;
|
||||
use error::{Error, RuntimeError, TypeConflict, ValidationError};
|
||||
use lexer::lex;
|
||||
use parser::parse;
|
||||
pub use value::Value;
|
||||
|
||||
pub fn interpret(source: &str) -> Result<Option<Value>, Vec<Error>> {
|
||||
pub fn interpret(source_id: Rc<String>, source: &str) -> Result<Option<Value>, InterpreterError> {
|
||||
let mut interpreter = Interpreter::new(Context::new());
|
||||
|
||||
interpreter.run(include_str!("../../std/io.ds"))?;
|
||||
interpreter.run(include_str!("../../std/thread.ds"))?;
|
||||
interpreter.run(source)
|
||||
interpreter.load_std()?;
|
||||
interpreter.run(source_id, source)
|
||||
}
|
||||
|
||||
pub fn interpret_without_std(
|
||||
source_id: Rc<String>,
|
||||
source: &str,
|
||||
) -> Result<Option<Value>, InterpreterError> {
|
||||
let mut interpreter = Interpreter::new(Context::new());
|
||||
|
||||
interpreter.run(source_id, source)
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct InterpreterError {
|
||||
source_id: Rc<String>,
|
||||
errors: Vec<Error>,
|
||||
}
|
||||
|
||||
impl InterpreterError {
|
||||
pub fn errors(&self) -> &Vec<Error> {
|
||||
&self.errors
|
||||
}
|
||||
}
|
||||
|
||||
impl InterpreterError {
|
||||
pub fn build_reports<'id>(self) -> Vec<Report<'id, (Rc<String>, Range<usize>)>> {
|
||||
let mut reports = Vec::new();
|
||||
|
||||
for error in self.errors {
|
||||
let (mut builder, validation_error, error_position) = match error {
|
||||
Error::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),
|
||||
self.source_id.clone(),
|
||||
span.1,
|
||||
)
|
||||
.with_message(description)
|
||||
.with_label(
|
||||
Label::new((self.source_id.clone(), span.0..span.1))
|
||||
.with_message(reason)
|
||||
.with_color(Color::Red),
|
||||
),
|
||||
None,
|
||||
span.into(),
|
||||
)
|
||||
}
|
||||
Error::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),
|
||||
self.source_id.clone(),
|
||||
span.1,
|
||||
)
|
||||
.with_message(description)
|
||||
.with_label(
|
||||
Label::new((self.source_id.clone(), span.0..span.1))
|
||||
.with_message(reason)
|
||||
.with_color(Color::Red),
|
||||
),
|
||||
None,
|
||||
span.into(),
|
||||
)
|
||||
}
|
||||
Error::Runtime { error, position } => (
|
||||
Report::build(
|
||||
ReportKind::Custom("Runtime Error", Color::Red),
|
||||
self.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,
|
||||
),
|
||||
Error::Validation { error, position } => (
|
||||
Report::build(
|
||||
ReportKind::Custom("Validation Error", Color::Magenta),
|
||||
self.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((self.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((self.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((
|
||||
self.source_id.clone(),
|
||||
expected_postion.0..expected_postion.1,
|
||||
))
|
||||
.with_message(format!(
|
||||
"Type {} established here.",
|
||||
expected.fg(type_color)
|
||||
)),
|
||||
Label::new((
|
||||
self.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((self.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((self.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((
|
||||
self.source_id.clone(),
|
||||
collection_position.0..collection_position.1,
|
||||
))
|
||||
.with_message(format!(
|
||||
"This has type {}.",
|
||||
collection_type.fg(type_color),
|
||||
)),
|
||||
Label::new((
|
||||
self.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!(),
|
||||
ValidationError::ExpectedIntegerFloatOrString { actual, position } => {
|
||||
builder = builder.with_message(format!(
|
||||
"Expected an {}, {} or {}.",
|
||||
Type::Integer.fg(type_color),
|
||||
Type::Float.fg(type_color),
|
||||
Type::String.fg(type_color)
|
||||
));
|
||||
|
||||
builder.add_labels([Label::new((
|
||||
self.source_id.clone(),
|
||||
position.0..position.1,
|
||||
))
|
||||
.with_message(format!("This has type {}.", actual.fg(type_color),))])
|
||||
}
|
||||
}
|
||||
}
|
||||
let report = builder.finish();
|
||||
|
||||
reports.push(report);
|
||||
}
|
||||
|
||||
reports
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Interpreter {
|
||||
@ -29,11 +267,36 @@ impl Interpreter {
|
||||
Interpreter { context }
|
||||
}
|
||||
|
||||
pub fn run(&mut self, source: &str) -> Result<Option<Value>, Vec<Error>> {
|
||||
let tokens = lex(source)?;
|
||||
let abstract_tree = parse(&tokens)?;
|
||||
let value_option = abstract_tree.run(&self.context)?;
|
||||
pub fn run(
|
||||
&mut self,
|
||||
source_id: Rc<String>,
|
||||
source: &str,
|
||||
) -> Result<Option<Value>, InterpreterError> {
|
||||
let tokens = lex(source).map_err(|errors| InterpreterError {
|
||||
source_id: source_id.clone(),
|
||||
errors,
|
||||
})?;
|
||||
let abstract_tree = parse(&tokens).map_err(|errors| InterpreterError {
|
||||
source_id: source_id.clone(),
|
||||
errors,
|
||||
})?;
|
||||
let value_option = abstract_tree
|
||||
.run(&self.context)
|
||||
.map_err(|errors| InterpreterError { source_id, errors })?;
|
||||
|
||||
Ok(value_option)
|
||||
}
|
||||
|
||||
pub fn load_std(&mut self) -> Result<(), InterpreterError> {
|
||||
self.run(
|
||||
Rc::new("std/io.ds".to_string()),
|
||||
include_str!("../../std/io.ds"),
|
||||
)?;
|
||||
self.run(
|
||||
Rc::new("std/io.ds".to_string()),
|
||||
include_str!("../../std/thread.ds"),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -66,15 +66,18 @@ pub fn parser<'src>() -> impl Parser<
|
||||
.map_with(|value, state| Expression::Value(value).with_position(state.span()));
|
||||
|
||||
let r#type = recursive(|r#type| {
|
||||
let function_type = r#type
|
||||
let function_type = just(Token::Keyword(Keyword::Fn))
|
||||
.ignore_then(
|
||||
r#type
|
||||
.clone()
|
||||
.separated_by(just(Token::Control(Control::Comma)))
|
||||
.collect()
|
||||
.delimited_by(
|
||||
just(Token::Control(Control::ParenOpen)),
|
||||
just(Token::Control(Control::ParenClose)),
|
||||
),
|
||||
)
|
||||
.then_ignore(just(Token::Control(Control::Arrow)))
|
||||
.then_ignore(just(Token::Control(Control::SkinnyArrow)))
|
||||
.then(r#type.clone())
|
||||
.map(|(parameter_types, return_type)| Type::Function {
|
||||
parameter_types,
|
||||
@ -110,20 +113,21 @@ pub fn parser<'src>() -> impl Parser<
|
||||
just(Token::Keyword(Keyword::Range)).to(Type::Range),
|
||||
just(Token::Keyword(Keyword::Str)).to(Type::String),
|
||||
just(Token::Keyword(Keyword::List)).to(Type::List),
|
||||
identifier.clone().try_map(move |identifier, span| {
|
||||
custom_types
|
||||
.0
|
||||
.borrow()
|
||||
.get(&identifier)
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
Rich::custom(span, format!("There is no type named {identifier}."))
|
||||
})
|
||||
identifier.clone().map(move |identifier| {
|
||||
if let Some(r#type) = custom_types.0.borrow().get(&identifier) {
|
||||
r#type.clone()
|
||||
} else {
|
||||
Type::Argument(identifier)
|
||||
}
|
||||
}),
|
||||
))
|
||||
})
|
||||
.map_with(|r#type, state| r#type.with_position(state.span()));
|
||||
|
||||
let type_argument = identifier
|
||||
.clone()
|
||||
.map_with(|identifier, state| Type::Argument(identifier).with_position(state.span()));
|
||||
|
||||
let type_specification = just(Token::Control(Control::Colon)).ignore_then(r#type.clone());
|
||||
|
||||
let structure_field_definition = identifier.clone().then(type_specification.clone());
|
||||
@ -213,7 +217,20 @@ pub fn parser<'src>() -> impl Parser<
|
||||
.with_position(state.span())
|
||||
});
|
||||
|
||||
let parsed_function = identifier
|
||||
let type_arguments = type_argument
|
||||
.clone()
|
||||
.separated_by(just(Token::Control(Control::Comma)))
|
||||
.at_least(1)
|
||||
.collect()
|
||||
.delimited_by(
|
||||
just(Token::Control(Control::ParenOpen)),
|
||||
just(Token::Control(Control::ParenClose)),
|
||||
);
|
||||
|
||||
let parsed_function = type_arguments
|
||||
.or_not()
|
||||
.then(
|
||||
identifier
|
||||
.clone()
|
||||
.then(type_specification.clone())
|
||||
.separated_by(just(Token::Control(Control::Comma)))
|
||||
@ -222,16 +239,20 @@ pub fn parser<'src>() -> impl Parser<
|
||||
just(Token::Control(Control::ParenOpen)),
|
||||
just(Token::Control(Control::ParenClose)),
|
||||
)
|
||||
.then(type_specification.clone())
|
||||
.then(block.clone())
|
||||
.map_with(|((parameters, return_type), body), state| {
|
||||
.then(r#type.clone())
|
||||
.then(block.clone()),
|
||||
)
|
||||
.map_with(
|
||||
|(type_arguments, ((parameters, return_type), body)), state| {
|
||||
Expression::Value(ValueNode::ParsedFunction {
|
||||
type_arguments: type_arguments.unwrap_or_else(|| Vec::with_capacity(0)),
|
||||
parameters,
|
||||
return_type,
|
||||
body: body.with_position(state.span()),
|
||||
})
|
||||
.with_position(state.span())
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
let built_in_function = {
|
||||
select! {
|
||||
@ -246,21 +267,45 @@ pub fn parser<'src>() -> impl Parser<
|
||||
.with_position(state.span())
|
||||
});
|
||||
|
||||
use Operator::*;
|
||||
|
||||
let structure_field = identifier
|
||||
.clone()
|
||||
.then_ignore(just(Token::Operator(Operator::Assign)))
|
||||
.then(positioned_expression.clone());
|
||||
|
||||
let structure_instance = identifier
|
||||
.clone()
|
||||
.then(
|
||||
structure_field
|
||||
.separated_by(just(Token::Control(Control::Comma)))
|
||||
.allow_trailing()
|
||||
.collect()
|
||||
.delimited_by(
|
||||
just(Token::Control(Control::CurlyOpen)),
|
||||
just(Token::Control(Control::CurlyClose)),
|
||||
),
|
||||
)
|
||||
.map_with(|(name, fields), state| {
|
||||
Expression::Value(ValueNode::Structure { name, fields })
|
||||
.with_position(state.span())
|
||||
});
|
||||
|
||||
let atom = choice((
|
||||
built_in_function.clone(),
|
||||
range.clone(),
|
||||
structure_instance.clone(),
|
||||
parsed_function.clone(),
|
||||
identifier_expression.clone(),
|
||||
basic_value.clone(),
|
||||
built_in_function.clone(),
|
||||
list.clone(),
|
||||
map.clone(),
|
||||
basic_value.clone(),
|
||||
identifier_expression.clone(),
|
||||
positioned_expression.clone().delimited_by(
|
||||
just(Token::Control(Control::ParenOpen)),
|
||||
just(Token::Control(Control::ParenClose)),
|
||||
),
|
||||
));
|
||||
|
||||
use Operator::*;
|
||||
|
||||
let logic_math_indexes_and_function_calls = atom.pratt((
|
||||
prefix(2, just(Token::Operator(Not)), |_, expression, span| {
|
||||
Expression::Logic(Box::new(Logic::Not(expression))).with_position(span)
|
||||
@ -395,32 +440,10 @@ pub fn parser<'src>() -> impl Parser<
|
||||
),
|
||||
));
|
||||
|
||||
let structure_field = identifier
|
||||
.clone()
|
||||
.then_ignore(just(Token::Operator(Operator::Assign)))
|
||||
.then(positioned_expression.clone());
|
||||
|
||||
let structure_instance = identifier
|
||||
.clone()
|
||||
.then(
|
||||
structure_field
|
||||
.separated_by(just(Token::Control(Control::Comma)))
|
||||
.allow_trailing()
|
||||
.collect()
|
||||
.delimited_by(
|
||||
just(Token::Control(Control::CurlyOpen)),
|
||||
just(Token::Control(Control::CurlyClose)),
|
||||
),
|
||||
)
|
||||
.map_with(|(name, fields), state| {
|
||||
Expression::Value(ValueNode::Structure { name, fields })
|
||||
.with_position(state.span())
|
||||
});
|
||||
|
||||
choice((
|
||||
structure_instance,
|
||||
range,
|
||||
logic_math_indexes_and_function_calls,
|
||||
range,
|
||||
structure_instance,
|
||||
parsed_function,
|
||||
built_in_function,
|
||||
list,
|
||||
@ -742,7 +765,7 @@ mod tests {
|
||||
#[test]
|
||||
fn function_type() {
|
||||
assert_eq!(
|
||||
parse(&lex("foobar : () -> any = some_function").unwrap()).unwrap()[0].node,
|
||||
parse(&lex("foobar : fn() -> any = some_function").unwrap()).unwrap()[0].node,
|
||||
Statement::Assignment(Assignment::new(
|
||||
Identifier::new("foobar").with_position((0, 6)),
|
||||
Some(
|
||||
@ -750,11 +773,11 @@ mod tests {
|
||||
parameter_types: vec![],
|
||||
return_type: Box::new(Type::Any)
|
||||
}
|
||||
.with_position((9, 18))
|
||||
.with_position((9, 20))
|
||||
),
|
||||
AssignmentOperator::Assign,
|
||||
Statement::Expression(Expression::Identifier(Identifier::new("some_function")))
|
||||
.with_position((21, 34))
|
||||
.with_position((23, 36))
|
||||
),)
|
||||
);
|
||||
}
|
||||
@ -762,9 +785,13 @@ mod tests {
|
||||
#[test]
|
||||
fn function_call() {
|
||||
assert_eq!(
|
||||
parse(&lex("output()").unwrap()).unwrap()[0].node,
|
||||
parse(&lex("io.write_line()").unwrap()).unwrap()[0].node,
|
||||
Statement::Expression(Expression::FunctionCall(FunctionCall::new(
|
||||
Expression::Identifier(Identifier::new("output")).with_position((0, 6)),
|
||||
Expression::MapIndex(Box::new(MapIndex::new(
|
||||
Expression::Identifier(Identifier::new("io")).with_position((0, 2)),
|
||||
Expression::Identifier(Identifier::new("write_line")).with_position((3, 13))
|
||||
)))
|
||||
.with_position((0, 13)),
|
||||
Vec::with_capacity(0),
|
||||
)))
|
||||
)
|
||||
@ -781,15 +808,45 @@ mod tests {
|
||||
#[test]
|
||||
fn function() {
|
||||
assert_eq!(
|
||||
parse(&lex("(x: int) : int { x }").unwrap()).unwrap()[0].node,
|
||||
parse(&lex("(x: int) int { x }").unwrap()).unwrap()[0].node,
|
||||
Statement::Expression(Expression::Value(ValueNode::ParsedFunction {
|
||||
type_arguments: Vec::with_capacity(0),
|
||||
parameters: vec![(Identifier::new("x"), Type::Integer.with_position((4, 7)))],
|
||||
return_type: Type::Integer.with_position((11, 14)),
|
||||
return_type: Type::Integer.with_position((9, 12)),
|
||||
body: Block::new(vec![Statement::Expression(Expression::Identifier(
|
||||
Identifier::new("x")
|
||||
),)
|
||||
.with_position((17, 18))])
|
||||
.with_position((0, 20))
|
||||
.with_position((15, 16))])
|
||||
.with_position((0, 18)),
|
||||
}),)
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_with_type_arguments() {
|
||||
assert_eq!(
|
||||
parse(&lex("(T, U)(x: T, y: U) T { x }").unwrap()).unwrap()[0].node,
|
||||
Statement::Expression(Expression::Value(ValueNode::ParsedFunction {
|
||||
type_arguments: vec![
|
||||
Type::Argument(Identifier::new("T")).with_position((1, 2)),
|
||||
Type::Argument(Identifier::new("U")).with_position((4, 5)),
|
||||
],
|
||||
parameters: vec![
|
||||
(
|
||||
Identifier::new("x"),
|
||||
Type::Argument(Identifier::new("T")).with_position((10, 11))
|
||||
),
|
||||
(
|
||||
Identifier::new("y"),
|
||||
Type::Argument(Identifier::new("U")).with_position((16, 17))
|
||||
)
|
||||
],
|
||||
return_type: Type::Argument(Identifier::new("T")).with_position((19, 20)),
|
||||
body: Block::new(vec![Statement::Expression(Expression::Identifier(
|
||||
Identifier::new("x")
|
||||
),)
|
||||
.with_position((23, 24))])
|
||||
.with_position((0, 26)),
|
||||
}),)
|
||||
)
|
||||
}
|
||||
|
@ -56,12 +56,14 @@ impl Value {
|
||||
}
|
||||
|
||||
pub fn function(
|
||||
type_arguments: Vec<WithPosition<Type>>,
|
||||
parameters: Vec<(Identifier, WithPosition<Type>)>,
|
||||
return_type: WithPosition<Type>,
|
||||
body: WithPosition<Block>,
|
||||
) -> Self {
|
||||
Value(Arc::new(ValueInner::Function(Function::Parsed(
|
||||
ParsedFunction {
|
||||
type_arguments,
|
||||
parameters,
|
||||
return_type,
|
||||
body,
|
||||
@ -137,10 +139,25 @@ impl Display for Value {
|
||||
ValueInner::Range(_) => todo!(),
|
||||
ValueInner::String(string) => write!(f, "{string}"),
|
||||
ValueInner::Function(Function::Parsed(ParsedFunction {
|
||||
type_arguments,
|
||||
parameters,
|
||||
return_type,
|
||||
body,
|
||||
})) => {
|
||||
if !type_arguments.is_empty() {
|
||||
write!(f, "(")?;
|
||||
|
||||
for (index, r#type) in type_arguments.into_iter().enumerate() {
|
||||
if index == type_arguments.len() - 1 {
|
||||
write!(f, "{}", r#type.node)?;
|
||||
} else {
|
||||
write!(f, "{} ", r#type.node)?;
|
||||
}
|
||||
}
|
||||
|
||||
write!(f, ")")?;
|
||||
}
|
||||
|
||||
write!(f, "(")?;
|
||||
|
||||
for (identifier, r#type) in parameters {
|
||||
@ -326,6 +343,7 @@ impl Function {
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct ParsedFunction {
|
||||
type_arguments: Vec<WithPosition<Type>>,
|
||||
parameters: Vec<(Identifier, WithPosition<Type>)>,
|
||||
return_type: WithPosition<Type>,
|
||||
body: WithPosition<Block>,
|
||||
|
@ -1,19 +1,27 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn logic() {
|
||||
assert_eq!(interpret("1 == 1").unwrap(), Some(Value::boolean(true)));
|
||||
assert_eq!(
|
||||
interpret("('42' == '42') && (42 != 0)").unwrap(),
|
||||
interpret(Rc::new("test".to_string()), "1 == 1").unwrap(),
|
||||
Some(Value::boolean(true))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "('42' == '42') && (42 != 0)").unwrap(),
|
||||
Some(Value::boolean(true))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn math() {
|
||||
assert_eq!(interpret("1 + 1").unwrap(), Some(Value::integer(2)));
|
||||
assert_eq!(
|
||||
interpret("2 * (21 + 19 + 1 * 2) / 2").unwrap(),
|
||||
interpret(Rc::new("test".to_string()), "1 + 1").unwrap(),
|
||||
Some(Value::integer(2))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "2 * (21 + 19 + 1 * 2) / 2").unwrap(),
|
||||
Some(Value::integer(42))
|
||||
);
|
||||
}
|
||||
@ -21,16 +29,19 @@ fn math() {
|
||||
#[test]
|
||||
fn list_index() {
|
||||
assert_eq!(
|
||||
interpret("foo = [1, 2, 3]; foo[2]").unwrap(),
|
||||
interpret(Rc::new("test".to_string()), "foo = [1, 2, 3]; foo[2]").unwrap(),
|
||||
Some(Value::integer(3))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_index() {
|
||||
assert_eq!(interpret("{ x = 3 }.x").unwrap(), Some(Value::integer(3)));
|
||||
assert_eq!(
|
||||
interpret("foo = { x = 3 }; foo.x").unwrap(),
|
||||
interpret(Rc::new("test".to_string()), "{ x = 3 }.x").unwrap(),
|
||||
Some(Value::integer(3))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "foo = { x = 3 }; foo.x").unwrap(),
|
||||
Some(Value::integer(3))
|
||||
);
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use dust_lang::{
|
||||
abstract_tree::Identifier,
|
||||
error::{Error, ValidationError},
|
||||
@ -8,8 +10,9 @@ use dust_lang::{
|
||||
fn function_call() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"
|
||||
foobar = (message : str) : str { message }
|
||||
foobar = (message : str) str { message }
|
||||
foobar('Hiya')
|
||||
",
|
||||
),
|
||||
@ -21,8 +24,9 @@ fn function_call() {
|
||||
fn call_empty_function() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"
|
||||
foobar = (message : str) : none {}
|
||||
foobar = (message : str) none {}
|
||||
foobar('Hiya')
|
||||
",
|
||||
),
|
||||
@ -34,11 +38,12 @@ fn call_empty_function() {
|
||||
fn callback() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"
|
||||
foobar = (cb : () -> str) : str {
|
||||
foobar = (cb: fn() -> str) str {
|
||||
cb()
|
||||
}
|
||||
foobar(() : str { 'Hiya' })
|
||||
foobar(() str { 'Hiya' })
|
||||
",
|
||||
),
|
||||
Ok(Some(Value::string("Hiya".to_string())))
|
||||
@ -47,30 +52,37 @@ fn callback() {
|
||||
|
||||
#[test]
|
||||
fn built_in_function_call() {
|
||||
assert_eq!(interpret("io.write_line('Hiya')"), Ok(None));
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "io.write_line('Hiya')"),
|
||||
Ok(None)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_context_does_not_capture_values() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"
|
||||
x = 1
|
||||
|
||||
foo = () : any { x }
|
||||
foo = () any { x }
|
||||
"
|
||||
),
|
||||
Err(vec![Error::Validation {
|
||||
)
|
||||
.unwrap_err()
|
||||
.errors(),
|
||||
&vec![Error::Validation {
|
||||
error: ValidationError::VariableNotFound(Identifier::new("x")),
|
||||
position: (32, 52).into()
|
||||
}])
|
||||
position: (32, 50).into()
|
||||
}]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"
|
||||
x = 1
|
||||
foo = (x : int) : int { x }
|
||||
foo = (x: int) int { x }
|
||||
foo(2)
|
||||
"
|
||||
),
|
||||
@ -82,9 +94,10 @@ fn function_context_does_not_capture_values() {
|
||||
fn function_context_captures_functions() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"
|
||||
bar = () : int { 2 }
|
||||
foo = () : int { bar() }
|
||||
bar = () int { 2 }
|
||||
foo = () int { bar() }
|
||||
foo()
|
||||
"
|
||||
),
|
||||
@ -96,8 +109,9 @@ fn function_context_captures_functions() {
|
||||
fn recursion() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"
|
||||
fib = (i : int) : int {
|
||||
fib = (i: int) int {
|
||||
if i <= 1 {
|
||||
1
|
||||
} else {
|
||||
|
@ -1,9 +1,12 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use dust_lang::*;
|
||||
|
||||
#[test]
|
||||
fn async_block() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"
|
||||
x = 41
|
||||
async {
|
||||
@ -21,6 +24,7 @@ fn async_block() {
|
||||
fn loops_and_breaks() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"
|
||||
i = 0
|
||||
loop {
|
||||
@ -37,6 +41,7 @@ fn loops_and_breaks() {
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"
|
||||
foobar = {
|
||||
while true {
|
||||
@ -55,7 +60,7 @@ fn loops_and_breaks() {
|
||||
#[test]
|
||||
fn r#if() {
|
||||
assert_eq!(
|
||||
interpret("if true { 'foobar' }"),
|
||||
interpret(Rc::new("test".to_string()), "if true { 'foobar' }"),
|
||||
Ok(Some(Value::string("foobar".to_string())))
|
||||
)
|
||||
}
|
||||
@ -63,7 +68,10 @@ fn r#if() {
|
||||
#[test]
|
||||
fn if_else() {
|
||||
assert_eq!(
|
||||
interpret("if false { 'foo' } else { 'bar' }"),
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"if false { 'foo' } else { 'bar' }"
|
||||
),
|
||||
Ok(Some(Value::string("bar".to_string())))
|
||||
)
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use dust_lang::{
|
||||
abstract_tree::{Identifier, Type},
|
||||
error::{Error, TypeConflict, ValidationError},
|
||||
@ -7,6 +9,7 @@ use dust_lang::{
|
||||
fn simple_structure() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"
|
||||
struct Foo {
|
||||
bar : int,
|
||||
@ -33,6 +36,7 @@ fn simple_structure() {
|
||||
fn field_type_error() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"
|
||||
struct Foo {
|
||||
bar : int,
|
||||
@ -42,8 +46,10 @@ fn field_type_error() {
|
||||
bar = 'hiya',
|
||||
}
|
||||
"
|
||||
),
|
||||
Err(vec![Error::Validation {
|
||||
)
|
||||
.unwrap_err()
|
||||
.errors(),
|
||||
&vec![Error::Validation {
|
||||
error: ValidationError::TypeCheck {
|
||||
conflict: TypeConflict {
|
||||
actual: Type::String,
|
||||
@ -53,7 +59,7 @@ fn field_type_error() {
|
||||
expected_position: (56, 59).into()
|
||||
},
|
||||
position: (96, 153).into()
|
||||
}])
|
||||
}]
|
||||
)
|
||||
}
|
||||
|
||||
@ -61,6 +67,7 @@ fn field_type_error() {
|
||||
fn nested_structure() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"
|
||||
struct Bar {
|
||||
baz : int
|
||||
@ -93,15 +100,18 @@ fn nested_structure() {
|
||||
fn undefined_struct() {
|
||||
assert_eq!(
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"
|
||||
Foo {
|
||||
bar = 42
|
||||
}
|
||||
"
|
||||
),
|
||||
Err(vec![Error::Validation {
|
||||
)
|
||||
.unwrap_err()
|
||||
.errors(),
|
||||
&vec![Error::Validation {
|
||||
error: error::ValidationError::VariableNotFound(Identifier::new("Foo")),
|
||||
position: (17, 69).into()
|
||||
}])
|
||||
}]
|
||||
)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::{collections::BTreeMap, rc::Rc};
|
||||
|
||||
use dust_lang::{
|
||||
abstract_tree::{Identifier, Type},
|
||||
@ -8,25 +8,37 @@ use dust_lang::{
|
||||
|
||||
#[test]
|
||||
fn none() {
|
||||
assert_eq!(interpret("x = 9"), Ok(None));
|
||||
assert_eq!(interpret("x = 1 + 1"), Ok(None));
|
||||
assert_eq!(interpret(Rc::new("test".to_string()), "x = 9"), Ok(None));
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "x = 1 + 1"),
|
||||
Ok(None)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn integer() {
|
||||
assert_eq!(interpret("1"), Ok(Some(Value::integer(1))));
|
||||
assert_eq!(interpret("123"), Ok(Some(Value::integer(123))));
|
||||
assert_eq!(interpret("-666"), Ok(Some(Value::integer(-666))));
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "1"),
|
||||
Ok(Some(Value::integer(1)))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "123"),
|
||||
Ok(Some(Value::integer(123)))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "-666"),
|
||||
Ok(Some(Value::integer(-666)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn integer_saturation() {
|
||||
assert_eq!(
|
||||
interpret("9223372036854775807 + 1"),
|
||||
interpret(Rc::new("test".to_string()), "9223372036854775807 + 1"),
|
||||
Ok(Some(Value::integer(i64::MAX)))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret("-9223372036854775808 - 1"),
|
||||
interpret(Rc::new("test".to_string()), "-9223372036854775808 - 1"),
|
||||
Ok(Some(Value::integer(i64::MIN)))
|
||||
);
|
||||
}
|
||||
@ -34,11 +46,11 @@ fn integer_saturation() {
|
||||
#[test]
|
||||
fn float() {
|
||||
assert_eq!(
|
||||
interpret("1.7976931348623157e308"),
|
||||
interpret(Rc::new("test".to_string()), "1.7976931348623157e308"),
|
||||
Ok(Some(Value::float(f64::MAX)))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret("-1.7976931348623157e308"),
|
||||
interpret(Rc::new("test".to_string()), "-1.7976931348623157e308"),
|
||||
Ok(Some(Value::float(f64::MIN)))
|
||||
);
|
||||
}
|
||||
@ -46,11 +58,11 @@ fn float() {
|
||||
#[test]
|
||||
fn float_saturation() {
|
||||
assert_eq!(
|
||||
interpret("1.7976931348623157e308 + 1"),
|
||||
interpret(Rc::new("test".to_string()), "1.7976931348623157e308 + 1"),
|
||||
Ok(Some(Value::float(f64::MAX)))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret("-1.7976931348623157e308 - 1"),
|
||||
interpret(Rc::new("test".to_string()), "-1.7976931348623157e308 - 1"),
|
||||
Ok(Some(Value::float(f64::MIN)))
|
||||
);
|
||||
}
|
||||
@ -58,27 +70,27 @@ fn float_saturation() {
|
||||
#[test]
|
||||
fn string() {
|
||||
assert_eq!(
|
||||
interpret("\"one\""),
|
||||
interpret(Rc::new("test".to_string()), "\"one\""),
|
||||
Ok(Some(Value::string("one".to_string())))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret("'one'"),
|
||||
interpret(Rc::new("test".to_string()), "'one'"),
|
||||
Ok(Some(Value::string("one".to_string())))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret("`one`"),
|
||||
interpret(Rc::new("test".to_string()), "`one`"),
|
||||
Ok(Some(Value::string("one".to_string())))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret("`'one'`"),
|
||||
interpret(Rc::new("test".to_string()), "`'one'`"),
|
||||
Ok(Some(Value::string("'one'".to_string())))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret("'`one`'"),
|
||||
interpret(Rc::new("test".to_string()), "'`one`'"),
|
||||
Ok(Some(Value::string("`one`".to_string())))
|
||||
);
|
||||
assert_eq!(
|
||||
interpret("\"'one'\""),
|
||||
interpret(Rc::new("test".to_string()), "\"'one'\""),
|
||||
Ok(Some(Value::string("'one'".to_string())))
|
||||
);
|
||||
}
|
||||
@ -86,7 +98,7 @@ fn string() {
|
||||
#[test]
|
||||
fn list() {
|
||||
assert_eq!(
|
||||
interpret("[1, 2, 'foobar']"),
|
||||
interpret(Rc::new("test".to_string()), "[1, 2, 'foobar']"),
|
||||
Ok(Some(Value::list(vec![
|
||||
Value::integer(1),
|
||||
Value::integer(2),
|
||||
@ -97,7 +109,10 @@ fn list() {
|
||||
|
||||
#[test]
|
||||
fn empty_list() {
|
||||
assert_eq!(interpret("[]"), Ok(Some(Value::list(Vec::new()))));
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "[]"),
|
||||
Ok(Some(Value::list(Vec::new())))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -108,14 +123,17 @@ fn map() {
|
||||
map.insert(Identifier::new("foo"), Value::string("bar".to_string()));
|
||||
|
||||
assert_eq!(
|
||||
interpret("{ x = 1, foo = 'bar' }"),
|
||||
interpret(Rc::new("test".to_string()), "{ x = 1, foo = 'bar' }"),
|
||||
Ok(Some(Value::map(map)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_map() {
|
||||
assert_eq!(interpret("{}"), Ok(Some(Value::map(BTreeMap::new()))));
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "{}"),
|
||||
Ok(Some(Value::map(BTreeMap::new())))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -126,7 +144,10 @@ fn map_types() {
|
||||
map.insert(Identifier::new("foo"), Value::string("bar".to_string()));
|
||||
|
||||
assert_eq!(
|
||||
interpret("{ x : int = 1, foo : str = 'bar' }"),
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"{ x : int = 1, foo : str = 'bar' }"
|
||||
),
|
||||
Ok(Some(Value::map(map)))
|
||||
);
|
||||
}
|
||||
@ -134,8 +155,10 @@ fn map_types() {
|
||||
#[test]
|
||||
fn map_type_errors() {
|
||||
assert_eq!(
|
||||
interpret("{ foo : bool = 'bar' }"),
|
||||
Err(vec![Error::Validation {
|
||||
interpret(Rc::new("test".to_string()), "{ foo : bool = 'bar' }")
|
||||
.unwrap_err()
|
||||
.errors(),
|
||||
&vec![Error::Validation {
|
||||
error: ValidationError::TypeCheck {
|
||||
conflict: TypeConflict {
|
||||
actual: Type::String,
|
||||
@ -145,11 +168,14 @@ fn map_type_errors() {
|
||||
expected_position: (8, 12).into(),
|
||||
},
|
||||
position: (0, 22).into()
|
||||
}])
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn range() {
|
||||
assert_eq!(interpret("0..100"), Ok(Some(Value::range(0..100))));
|
||||
assert_eq!(
|
||||
interpret(Rc::new("test".to_string()), "0..100"),
|
||||
Ok(Some(Value::range(0..100)))
|
||||
);
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use dust_lang::{
|
||||
abstract_tree::{AbstractNode, Block, Expression, Identifier, Statement, Type},
|
||||
error::{Error, TypeConflict, ValidationError},
|
||||
@ -7,7 +9,7 @@ use dust_lang::{
|
||||
#[test]
|
||||
fn set_and_get_variable() {
|
||||
assert_eq!(
|
||||
interpret("foobar = true; foobar"),
|
||||
interpret(Rc::new("test".to_string()), "foobar = true; foobar"),
|
||||
Ok(Some(Value::boolean(true)))
|
||||
);
|
||||
}
|
||||
@ -15,7 +17,7 @@ fn set_and_get_variable() {
|
||||
#[test]
|
||||
fn set_variable_with_type() {
|
||||
assert_eq!(
|
||||
interpret("foobar: bool = true; foobar"),
|
||||
interpret(Rc::new("test".to_string()), "foobar: bool = true; foobar"),
|
||||
Ok(Some(Value::boolean(true)))
|
||||
);
|
||||
}
|
||||
@ -23,8 +25,10 @@ fn set_variable_with_type() {
|
||||
#[test]
|
||||
fn set_variable_with_type_error() {
|
||||
assert_eq!(
|
||||
interpret("foobar: str = true"),
|
||||
Err(vec![Error::Validation {
|
||||
interpret(Rc::new("test".to_string()), "foobar: str = true")
|
||||
.unwrap_err()
|
||||
.errors(),
|
||||
&vec![Error::Validation {
|
||||
error: ValidationError::TypeCheck {
|
||||
conflict: TypeConflict {
|
||||
actual: Type::Boolean,
|
||||
@ -34,22 +38,26 @@ fn set_variable_with_type_error() {
|
||||
expected_position: (8, 11).into()
|
||||
},
|
||||
position: (0, 18).into()
|
||||
}])
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_variable() {
|
||||
assert_eq!(
|
||||
interpret("foobar = (x: int): int { x }; foobar"),
|
||||
interpret(
|
||||
Rc::new("test".to_string()),
|
||||
"foobar = (x: int) int { x }; foobar"
|
||||
),
|
||||
Ok(Some(Value::function(
|
||||
Vec::with_capacity(0),
|
||||
vec![(Identifier::new("x"), Type::Integer.with_position((13, 16)))],
|
||||
Type::Integer.with_position((19, 22)),
|
||||
Type::Integer.with_position((18, 21)),
|
||||
Block::new(vec![Statement::Expression(Expression::Identifier(
|
||||
Identifier::new("x")
|
||||
))
|
||||
.with_position((25, 26))])
|
||||
.with_position((9, 28))
|
||||
.with_position((24, 25))])
|
||||
.with_position((9, 27))
|
||||
)))
|
||||
);
|
||||
}
|
||||
|
@ -1,4 +1,10 @@
|
||||
use std::{borrow::Cow, io::stderr, path::PathBuf, process::Command, rc::Rc};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
io::{self, stderr},
|
||||
path::PathBuf,
|
||||
process::Command,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use ariadne::sources;
|
||||
use dust_lang::{
|
||||
@ -12,9 +18,7 @@ use reedline::{
|
||||
SqliteBackedHistory, Suggestion,
|
||||
};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
pub fn run_shell(context: Context) -> Result<(), Error> {
|
||||
pub fn run_shell(context: Context) -> Result<(), io::Error> {
|
||||
let mut interpreter = Interpreter::new(context.clone());
|
||||
let mut keybindings = default_emacs_keybindings();
|
||||
|
||||
@ -76,18 +80,16 @@ pub fn run_shell(context: Context) -> Result<(), Error> {
|
||||
continue;
|
||||
}
|
||||
|
||||
let run_result = interpreter.run(&buffer);
|
||||
let run_result = interpreter.run(Rc::new("input".to_string()), &buffer);
|
||||
|
||||
match run_result {
|
||||
Ok(Some(value)) => {
|
||||
println!("{value}")
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(errors) => {
|
||||
Err(error) => {
|
||||
let source_id = Rc::new("input".to_string());
|
||||
let reports = Error::Dust { errors }
|
||||
.build_reports(source_id.clone())
|
||||
.unwrap();
|
||||
let reports = error.build_reports();
|
||||
|
||||
for report in reports {
|
||||
report
|
||||
|
@ -1,263 +0,0 @@
|
||||
use ariadne::{Color, Fmt, Label, Report, ReportKind};
|
||||
use dust_lang::{
|
||||
abstract_tree::Type,
|
||||
error::{Error as DustError, RuntimeError, TypeConflict, ValidationError},
|
||||
};
|
||||
use std::{
|
||||
fmt::{self, Display, Formatter},
|
||||
io,
|
||||
ops::Range,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Dust {
|
||||
errors: Vec<dust_lang::error::Error>,
|
||||
},
|
||||
Io(io::Error),
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(error: io::Error) -> Self {
|
||||
Error::Io(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn build_reports<'id>(
|
||||
self,
|
||||
source_id: Rc<String>,
|
||||
) -> 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!(),
|
||||
ValidationError::ExpectedIntegerFloatOrString { actual, position } => {
|
||||
builder = builder.with_message(format!(
|
||||
"Expected an {}, {} or {}.",
|
||||
Type::Integer.fg(type_color),
|
||||
Type::Float.fg(type_color),
|
||||
Type::String.fg(type_color)
|
||||
));
|
||||
|
||||
builder.add_labels([Label::new((
|
||||
source_id.clone(),
|
||||
position.0..position.1,
|
||||
))
|
||||
.with_message(format!("This has type {}.", actual.fg(type_color),))])
|
||||
}
|
||||
}
|
||||
}
|
||||
let report = builder.finish();
|
||||
|
||||
reports.push(report);
|
||||
}
|
||||
|
||||
return Ok(reports);
|
||||
} else {
|
||||
return Ok(Vec::with_capacity(0));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Error::Dust { errors } => {
|
||||
for error in errors {
|
||||
writeln!(f, "{error:?}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Error::Io(io_error) => {
|
||||
write!(f, "{io_error}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,10 @@
|
||||
//! Command line interface for the dust programming language.
|
||||
mod cli;
|
||||
mod error;
|
||||
|
||||
use ariadne::sources;
|
||||
use clap::Parser;
|
||||
use cli::run_shell;
|
||||
use colored::Colorize;
|
||||
use error::Error;
|
||||
|
||||
use std::{
|
||||
fs::read_to_string,
|
||||
@ -14,7 +12,7 @@ use std::{
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use dust_lang::{context::Context, interpret};
|
||||
use dust_lang::{context::Context, interpret, interpret_without_std};
|
||||
|
||||
/// Command-line arguments to be parsed.
|
||||
#[derive(Parser, Debug)]
|
||||
@ -24,6 +22,9 @@ struct Args {
|
||||
#[arg(short, long)]
|
||||
command: Option<String>,
|
||||
|
||||
#[arg(long)]
|
||||
no_std: bool,
|
||||
|
||||
/// Location of the file to run.
|
||||
path: Option<String>,
|
||||
}
|
||||
@ -54,7 +55,11 @@ fn main() {
|
||||
return;
|
||||
};
|
||||
|
||||
let eval_result = interpret(&source);
|
||||
let eval_result = if args.no_std {
|
||||
interpret_without_std(source_id.clone(), &source)
|
||||
} else {
|
||||
interpret(source_id.clone(), &source)
|
||||
};
|
||||
|
||||
match eval_result {
|
||||
Ok(value) => {
|
||||
@ -62,27 +67,10 @@ fn main() {
|
||||
println!("{value}")
|
||||
}
|
||||
}
|
||||
Err(errors) => {
|
||||
let reports = Error::Dust { errors }
|
||||
.build_reports(source_id.clone())
|
||||
.unwrap();
|
||||
|
||||
for report in reports {
|
||||
Err(error) => {
|
||||
for report in error.build_reports() {
|
||||
report
|
||||
.write_for_stdout(
|
||||
sources([
|
||||
(source_id.clone(), source.as_str()),
|
||||
(
|
||||
Rc::new("std/io.ds".to_string()),
|
||||
include_str!("../../std/io.ds"),
|
||||
),
|
||||
(
|
||||
Rc::new("std/thread.ds".to_string()),
|
||||
include_str!("../../std/thread.ds"),
|
||||
),
|
||||
]),
|
||||
stderr(),
|
||||
)
|
||||
.write_for_stdout(sources([(source_id.clone(), source.as_str())]), stderr())
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
io = {
|
||||
read_line = () : str {
|
||||
read_line = () str {
|
||||
__READ_LINE__()
|
||||
}
|
||||
|
||||
write_line = (output: str) : none {
|
||||
write_line = (T)(output: T) none {
|
||||
__WRITE_LINE__(output)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
thread = {
|
||||
sleep = (milliseconds: int) : none {
|
||||
sleep = (milliseconds: int) none {
|
||||
__SLEEP__(milliseconds)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user