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