1
0

Create new built-in function API

This commit is contained in:
Jeff 2023-11-28 17:54:17 -05:00
parent 5d68b7b156
commit b46dfc5791
15 changed files with 11479 additions and 12952 deletions

293
Cargo.lock generated
View File

@ -83,124 +83,6 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "async-attributes"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5"
dependencies = [
"quote",
"syn 1.0.109",
]
[[package]]
name = "async-channel"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
dependencies = [
"concurrent-queue",
"event-listener",
"futures-core",
]
[[package]]
name = "async-executor"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0c4a4f319e45986f347ee47fef8bf5e81c9abc3f6f58dc2391439f30df65f0"
dependencies = [
"async-lock",
"async-task",
"concurrent-queue",
"fastrand 2.0.1",
"futures-lite",
"slab",
]
[[package]]
name = "async-global-executor"
version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776"
dependencies = [
"async-channel",
"async-executor",
"async-io",
"async-lock",
"blocking",
"futures-lite",
"once_cell",
]
[[package]]
name = "async-io"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af"
dependencies = [
"async-lock",
"autocfg",
"cfg-if",
"concurrent-queue",
"futures-lite",
"log",
"parking",
"polling",
"rustix 0.37.27",
"slab",
"socket2 0.4.9",
"waker-fn",
]
[[package]]
name = "async-lock"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
dependencies = [
"event-listener",
]
[[package]]
name = "async-std"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d"
dependencies = [
"async-attributes",
"async-channel",
"async-global-executor",
"async-io",
"async-lock",
"crossbeam-utils",
"futures-channel",
"futures-core",
"futures-io",
"futures-lite",
"gloo-timers",
"kv-log-macro",
"log",
"memchr",
"once_cell",
"pin-project-lite",
"pin-utils",
"slab",
"wasm-bindgen-futures",
]
[[package]]
name = "async-task"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1"
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
version = "1.1.0"
@ -240,22 +122,6 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
name = "blocking"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a"
dependencies = [
"async-channel",
"async-lock",
"async-task",
"fastrand 2.0.1",
"futures-io",
"futures-lite",
"piper",
"tracing",
]
[[package]]
name = "bumpalo"
version = "3.14.0"
@ -353,15 +219,6 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "concurrent-queue"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "core-foundation"
version = "0.9.3"
@ -459,10 +316,9 @@ dependencies = [
[[package]]
name = "dust-lang"
version = "0.3.5"
version = "0.3.7"
dependencies = [
"ansi_term",
"async-std",
"cc",
"clap",
"comfy-table",
@ -536,21 +392,6 @@ dependencies = [
"str-buf",
]
[[package]]
name = "event-listener"
version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "fastrand"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
"instant",
]
[[package]]
name = "fastrand"
version = "2.0.1"
@ -564,7 +405,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef033ed5e9bad94e55838ca0ca906db0e043f517adda0c8b79c7a8c66c93c1b5"
dependencies = [
"cfg-if",
"rustix 0.38.17",
"rustix",
"windows-sys",
]
@ -619,21 +460,6 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
[[package]]
name = "futures-lite"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
dependencies = [
"fastrand 1.9.0",
"futures-core",
"futures-io",
"memchr",
"parking",
"pin-project-lite",
"waker-fn",
]
[[package]]
name = "futures-sink"
version = "0.3.28"
@ -693,18 +519,6 @@ dependencies = [
"url",
]
[[package]]
name = "gloo-timers"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "h2"
version = "0.3.21"
@ -858,26 +672,6 @@ dependencies = [
"hashbrown 0.14.1",
]
[[package]]
name = "instant"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if",
]
[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi",
"libc",
"windows-sys",
]
[[package]]
name = "ipnet"
version = "2.8.0"
@ -908,15 +702,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "kv-log-macro"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
dependencies = [
"log",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -969,12 +754,6 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "linux-raw-sys"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
name = "linux-raw-sys"
version = "0.4.8"
@ -996,9 +775,6 @@ name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
dependencies = [
"value-bag",
]
[[package]]
name = "memchr"
@ -1149,12 +925,6 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "parking"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -1196,39 +966,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "piper"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4"
dependencies = [
"atomic-waker",
"fastrand 2.0.1",
"futures-io",
]
[[package]]
name = "pkg-config"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "polling"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
dependencies = [
"autocfg",
"bitflags 1.3.2",
"cfg-if",
"concurrent-queue",
"libc",
"log",
"pin-project-lite",
"windows-sys",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
@ -1395,20 +1138,6 @@ version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustix"
version = "0.37.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2"
dependencies = [
"bitflags 1.3.2",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys 0.3.8",
"windows-sys",
]
[[package]]
name = "rustix"
version = "0.38.17"
@ -1418,7 +1147,7 @@ dependencies = [
"bitflags 2.4.0",
"errno",
"libc",
"linux-raw-sys 0.4.8",
"linux-raw-sys",
"windows-sys",
]
@ -1705,9 +1434,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
dependencies = [
"cfg-if",
"fastrand 2.0.1",
"fastrand",
"redox_syscall",
"rustix 0.38.17",
"rustix",
"windows-sys",
]
@ -1892,24 +1621,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "value-bag"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a72e1902dde2bd6441347de2b70b7f5d59bf157c6c62f0c44572607a1d55bbe"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "waker-fn"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690"
[[package]]
name = "want"
version = "0.3.1"

View File

@ -17,7 +17,6 @@ opt-level = 3
[dependencies]
ansi_term = "0.12.1"
async-std = { version = "1.12.0", features = ["attributes"] }
clap = { version = "4.4.4", features = ["derive"] }
comfy-table = "7.0.1"
csv = "1.2.2"

View File

@ -1,650 +0,0 @@
use std::{
env::current_dir,
fs::{copy, metadata, read_dir, read_to_string, remove_file, write, File},
io::Write,
path::PathBuf,
process::Command,
};
use rand::{random, thread_rng, Rng};
use reqwest::blocking::get;
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, Error, Expression, List, Map, Result, Table, Type, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub enum BuiltInFunction {
// General
Assert(Vec<Expression>),
AssertEqual(Vec<Expression>),
Download(Expression),
Context,
Help(Option<Expression>),
Length(Expression),
Output(Vec<Expression>),
OutputError(Vec<Expression>),
Type(Expression),
Workdir,
// Filesystem
Append(Vec<Expression>),
Metadata(Expression),
Move(Vec<Expression>),
Read(Option<Expression>),
Remove(Expression),
Write(Vec<Expression>),
// Format conversion
FromJson(Expression),
ToJson(Expression),
ToString(Expression),
ToFloat(Expression),
// Command
Bash(Vec<Expression>),
Fish(Vec<Expression>),
Raw(Vec<Expression>),
Sh(Vec<Expression>),
Zsh(Vec<Expression>),
// Random
Random(Vec<Expression>),
RandomBoolean,
RandomInteger,
RandomFloat,
// Table
Columns(Expression),
Rows(Expression),
// List
Reverse(Expression, Option<(Expression, Expression)>),
}
impl AbstractTree for BuiltInFunction {
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
debug_assert_eq!("built_in_function", node.kind());
fn parse_expressions(source: &str, node: Node) -> Result<Vec<Expression>> {
let mut expressions = Vec::new();
for index in 1..node.child_count() {
let child_node = node.child(index).unwrap();
if child_node.kind() == "expression" {
let expression = Expression::from_syntax_node(source, child_node)?;
expressions.push(expression);
}
}
Ok(expressions)
}
let tool_node = node.child(0).unwrap();
let tool = match tool_node.kind() {
"assert" => {
let expressions = parse_expressions(source, node)?;
BuiltInFunction::Assert(expressions)
}
"assert_equal" => {
let expressions = parse_expressions(source, node)?;
BuiltInFunction::AssertEqual(expressions)
}
"context" => BuiltInFunction::Context,
"download" => {
let expression_node = node.child(1).unwrap();
let expression = Expression::from_syntax_node(source, expression_node)?;
BuiltInFunction::Download(expression)
}
"help" => {
let child_node = node.child(1).unwrap();
let expression = if child_node.is_named() {
Some(Expression::from_syntax_node(source, child_node)?)
} else {
None
};
BuiltInFunction::Help(expression)
}
"length" => {
let expression_node = node.child(1).unwrap();
let expression = Expression::from_syntax_node(source, expression_node)?;
BuiltInFunction::Length(expression)
}
"output" => {
let expressions = parse_expressions(source, node)?;
BuiltInFunction::Output(expressions)
}
"output_error" => {
let expressions = parse_expressions(source, node)?;
BuiltInFunction::OutputError(expressions)
}
"type" => {
let expression_node = node.child(1).unwrap();
let expression = Expression::from_syntax_node(source, expression_node)?;
BuiltInFunction::Type(expression)
}
"workdir" => BuiltInFunction::Workdir,
"append" => {
let expressions = parse_expressions(source, node)?;
Error::expect_tool_argument_amount("append", 2, expressions.len())?;
BuiltInFunction::Append(expressions)
}
"metadata" => {
let expression_node = node.child(1).unwrap();
let expression = Expression::from_syntax_node(source, expression_node)?;
BuiltInFunction::Metadata(expression)
}
"move" => {
let expressions = parse_expressions(source, node)?;
Error::expect_tool_argument_amount("move", 2, expressions.len())?;
BuiltInFunction::Move(expressions)
}
"read" => {
let expression = if let Some(node) = node.child(1) {
Some(Expression::from_syntax_node(source, node)?)
} else {
None
};
BuiltInFunction::Read(expression)
}
"remove" => {
let expression_node = node.child(1).unwrap();
let expression = Expression::from_syntax_node(source, expression_node)?;
BuiltInFunction::Remove(expression)
}
"write" => {
let expressions = parse_expressions(source, node)?;
Error::expect_tool_argument_amount("write", 2, expressions.len())?;
BuiltInFunction::Write(expressions)
}
"from_json" => {
let expression_node = node.child(1).unwrap();
let expression = Expression::from_syntax_node(source, expression_node)?;
BuiltInFunction::FromJson(expression)
}
"to_json" => {
let expression_node = node.child(1).unwrap();
let expression = Expression::from_syntax_node(source, expression_node)?;
BuiltInFunction::ToJson(expression)
}
"to_string" => {
let expression_node = node.child(1).unwrap();
let expression = Expression::from_syntax_node(source, expression_node)?;
BuiltInFunction::ToString(expression)
}
"to_float" => {
let expression_node = node.child(1).unwrap();
let expression = Expression::from_syntax_node(source, expression_node)?;
BuiltInFunction::ToFloat(expression)
}
"bash" => {
let expressions = parse_expressions(source, node)?;
BuiltInFunction::Bash(expressions)
}
"fish" => {
let expressions = parse_expressions(source, node)?;
BuiltInFunction::Fish(expressions)
}
"raw" => {
let expressions = parse_expressions(source, node)?;
BuiltInFunction::Raw(expressions)
}
"sh" => {
let expressions = parse_expressions(source, node)?;
BuiltInFunction::Sh(expressions)
}
"zsh" => {
let expressions = parse_expressions(source, node)?;
BuiltInFunction::Zsh(expressions)
}
"random" => {
let expressions = parse_expressions(source, node)?;
BuiltInFunction::Random(expressions)
}
"random_boolean" => BuiltInFunction::RandomBoolean,
"random_float" => BuiltInFunction::RandomFloat,
"random_integer" => BuiltInFunction::RandomInteger,
"columns" => {
let expression_node = node.child(2).unwrap();
let expression = Expression::from_syntax_node(source, expression_node)?;
BuiltInFunction::Columns(expression)
}
"rows" => {
let expression_node = node.child(2).unwrap();
let expression = Expression::from_syntax_node(source, expression_node)?;
BuiltInFunction::Rows(expression)
}
"reverse" => {
let list_node = node.child(2).unwrap();
let list_expression = Expression::from_syntax_node(source, list_node)?;
let slice_range_nodes =
if let (Some(start_node), Some(end_node)) = (node.child(3), node.child(4)) {
let start = Expression::from_syntax_node(source, start_node)?;
let end = Expression::from_syntax_node(source, end_node)?;
Some((start, end))
} else {
None
};
BuiltInFunction::Reverse(list_expression, slice_range_nodes)
}
_ => {
return Err(Error::UnexpectedSyntaxNode {
expected: "built-in function",
actual: tool_node.kind(),
location: tool_node.start_position(),
relevant_source: source[tool_node.byte_range()].to_string(),
})
}
};
Ok(tool)
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
match self {
BuiltInFunction::Assert(expressions) => {
for expression in expressions {
let value = expression.run(source, context)?;
if value.as_boolean()? {
continue;
} else {
return Err(Error::AssertFailed);
}
}
Ok(Value::Empty)
}
BuiltInFunction::AssertEqual(expressions) => {
let mut prev_value = None;
for expression in expressions {
let value = expression.run(source, context)?;
if let Some(prev_value) = &prev_value {
if &value == prev_value {
continue;
} else {
return Err(Error::AssertEqualFailed {
expected: prev_value.clone(),
actual: value,
});
}
}
prev_value = Some(value);
}
Ok(Value::Empty)
}
BuiltInFunction::Context => Ok(Value::Map(context.clone())),
BuiltInFunction::Download(expression) => {
let value = expression.run(source, context)?;
let url = value.as_string()?;
let data = get(url)?.text()?;
Ok(Value::String(data))
}
BuiltInFunction::Length(expression) => {
let value = expression.run(source, context)?;
let length = match value {
Value::List(list) => list.items().len(),
Value::Map(map) => map.variables()?.len(),
Value::Table(table) => table.len(),
Value::String(string) => string.chars().count(),
Value::Function(_) => todo!(),
Value::Float(_) => todo!(),
Value::Integer(_) => todo!(),
Value::Boolean(_) => todo!(),
Value::Empty => todo!(),
};
Ok(Value::Integer(length as i64))
}
BuiltInFunction::Help(_expression) => {
let mut help_table =
Table::new(vec!["tool".to_string(), "description".to_string()]);
help_table.insert(vec![
Value::String("help".to_string()),
Value::String("Get info on tools.".to_string()),
])?;
Ok(Value::Table(help_table))
}
BuiltInFunction::Output(expressions) => {
for expression in expressions {
let value = expression.run(source, context)?;
println!("{value}");
}
Ok(Value::Empty)
}
BuiltInFunction::OutputError(expressions) => {
for expression in expressions {
let value = expression.run(source, context)?;
eprintln!("{value}");
}
Ok(Value::Empty)
}
BuiltInFunction::Type(expression) => {
let run_expression = expression.run(source, context);
let value_type = if let Ok(value) = run_expression {
value.r#type()
} else if let Err(Error::VariableIdentifierNotFound(_)) = run_expression {
Type::Any
} else {
return run_expression;
};
Ok(Value::String(value_type.to_string()))
}
BuiltInFunction::Workdir => {
let workdir = current_dir()?.to_string_lossy().to_string();
Ok(Value::String(workdir))
}
BuiltInFunction::Append(expressions) => {
let path_value = expressions[0].run(source, context)?;
let path = path_value.as_string()?;
let data = expressions[1].run(source, context)?.to_string();
let mut file = File::options().append(true).open(path)?;
file.write(data.as_bytes())?;
Ok(Value::Empty)
}
BuiltInFunction::Metadata(expression) => {
let path_value = expression.run(source, context)?;
let path = path_value.as_string()?;
let metadata = metadata(path)?;
let file_type = if metadata.is_dir() {
"dir".to_string()
} else if metadata.is_file() {
"file".to_string()
} else if metadata.is_symlink() {
"link".to_string()
} else {
"unknown".to_string()
};
let size = metadata.len() as i64;
let created = metadata.created()?.elapsed()?.as_secs() as i64;
let modified = metadata.modified()?.elapsed()?.as_secs() as i64;
let accessed = metadata.accessed()?.elapsed()?.as_secs() as i64;
let metadata_output = Map::new();
{
let mut metadata_variables = metadata_output.variables_mut()?;
metadata_variables.insert("type".to_string(), Value::String(file_type));
metadata_variables.insert("size".to_string(), Value::Integer(size));
metadata_variables.insert("created".to_string(), Value::Integer(created));
metadata_variables.insert("modified".to_string(), Value::Integer(modified));
metadata_variables.insert("accessed".to_string(), Value::Integer(accessed));
}
Ok(Value::Map(metadata_output))
}
BuiltInFunction::Move(expressions) => {
let from_value = expressions[0].run(source, context)?;
let from = from_value.as_string()?;
let to_value = expressions[1].run(source, context)?;
let to = to_value.as_string()?;
copy(from, to)?;
remove_file(from)?;
Ok(Value::Empty)
}
BuiltInFunction::Read(expression) => {
let path = if let Some(expression) = expression {
let path_value = expression.run(source, context)?;
PathBuf::from(path_value.as_string()?)
} else {
PathBuf::from(".")
};
let content = if path.is_dir() {
let dir = read_dir(&path)?;
let mut contents = Vec::new();
for file in dir {
let file = file?;
let file_path = file.path().to_string_lossy().to_string();
contents.push(Value::String(file_path));
}
Value::List(List::with_items(contents))
} else {
Value::String(read_to_string(path)?)
};
Ok(content)
}
BuiltInFunction::Remove(expression) => {
let path_value = expression.run(source, context)?;
let path = PathBuf::from(path_value.as_string()?);
remove_file(path)?;
Ok(Value::Empty)
}
BuiltInFunction::Write(expressions) => {
let path_value = expressions[0].run(source, context)?;
let path = path_value.as_string()?;
let data_value = expressions[1].run(source, context)?;
let data = data_value.as_string()?;
write(path, data)?;
Ok(Value::Empty)
}
BuiltInFunction::FromJson(expression) => {
let json_value = expression.run(source, context)?;
let json = json_value.as_string()?;
let value = serde_json::from_str(json)?;
Ok(value)
}
BuiltInFunction::ToJson(expression) => {
let value = expression.run(source, context)?;
let json = serde_json::to_string(&value)?;
Ok(Value::String(json))
}
BuiltInFunction::ToString(expression) => {
let value = expression.run(source, context)?;
let string = value.to_string();
Ok(Value::String(string))
}
BuiltInFunction::ToFloat(expression) => {
let value = expression.run(source, context)?;
let float = match value {
Value::String(string) => string.parse()?,
Value::Float(float) => float,
Value::Integer(integer) => integer as f64,
_ => return Err(Error::ExpectedNumberOrString { actual: value }),
};
Ok(Value::Float(float))
}
BuiltInFunction::Bash(expressions) => {
let mut command = Command::new("bash");
for expression in expressions {
let value = expression.run(source, context)?;
let command_input = value.as_string()?;
command.arg(command_input);
}
let output = command.spawn()?.wait_with_output()?.stdout;
Ok(Value::String(String::from_utf8(output)?))
}
BuiltInFunction::Fish(expressions) => {
let mut command = Command::new("fish");
for expression in expressions {
let value = expression.run(source, context)?;
let command_input = value.as_string()?;
command.arg(command_input);
}
let output = command.spawn()?.wait_with_output()?.stdout;
Ok(Value::String(String::from_utf8(output)?))
}
BuiltInFunction::Raw(expressions) => {
let raw_command = expressions[0].run(source, context)?;
let mut command = Command::new(raw_command.as_string()?);
for expression in &expressions[1..] {
let value = expression.run(source, context)?;
let command_input = value.as_string()?;
command.arg(command_input);
}
let output = command.spawn()?.wait_with_output()?.stdout;
Ok(Value::String(String::from_utf8(output)?))
}
BuiltInFunction::Sh(expressions) => {
let mut command = Command::new("sh");
for expression in expressions {
let value = expression.run(source, context)?;
let command_input = value.as_string()?;
command.arg(command_input);
}
let output = command.spawn()?.wait_with_output()?.stdout;
Ok(Value::String(String::from_utf8(output)?))
}
BuiltInFunction::Zsh(expressions) => {
let mut command = Command::new("zsh");
for expression in expressions {
let value = expression.run(source, context)?;
let command_input = value.as_string()?;
command.arg(command_input);
}
let output = command.spawn()?.wait_with_output()?.stdout;
Ok(Value::String(String::from_utf8(output)?))
}
BuiltInFunction::Random(expressions) => {
if expressions.len() == 1 {
let value = expressions[0].run(source, context)?;
let list = value.as_list()?.items();
if list.len() == 1 {
return Ok(list.first().cloned().unwrap());
}
let range = 0..list.len();
let random_index = thread_rng().gen_range(range);
let random_value = list.get(random_index).ok_or(Error::ExpectedList {
actual: value.clone(),
})?;
return Ok(random_value.clone());
}
let range = 0..expressions.len();
let random_index = thread_rng().gen_range(range);
let random_expression = expressions.get(random_index).unwrap();
let value = random_expression.run(source, context)?;
Ok(value)
}
BuiltInFunction::RandomBoolean => Ok(Value::Boolean(random())),
BuiltInFunction::RandomFloat => Ok(Value::Float(random())),
BuiltInFunction::RandomInteger => Ok(Value::Integer(random())),
BuiltInFunction::Columns(expression) => {
let column_names = expression
.run(source, context)?
.as_table()?
.headers()
.iter()
.cloned()
.map(Value::String)
.collect();
Ok(Value::List(List::with_items(column_names)))
}
BuiltInFunction::Rows(expression) => {
let rows = expression
.run(source, context)?
.as_table()?
.rows()
.iter()
.cloned()
.map(|row| Value::List(List::with_items(row)))
.collect();
Ok(Value::List(List::with_items(rows)))
}
BuiltInFunction::Reverse(list_expression, slice_range) => {
let expression_run = list_expression.run(source, context)?;
let list = expression_run.as_list()?;
if let Some((start, end)) = slice_range {
let start = start.run(source, context)?.as_integer()? as usize;
let end = end.run(source, context)?.as_integer()? as usize;
list.items_mut()[start..end].reverse()
} else {
list.items_mut().reverse()
};
Ok(Value::List(list.clone()))
}
}
}
}

View File

@ -2,8 +2,7 @@ use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{
value_node::ValueNode, AbstractTree, BuiltInFunction, Error, Identifier, Index, Map, Result,
Value, Yield,
value_node::ValueNode, AbstractTree, Error, Identifier, Index, Map, Result, Value, Yield,
};
use super::{function_call::FunctionCall, logic::Logic, math::Math};
@ -16,7 +15,6 @@ pub enum Expression {
Math(Box<Math>),
Logic(Box<Logic>),
FunctionCall(Box<FunctionCall>),
Tool(Box<BuiltInFunction>),
Yield(Box<Yield>),
}
@ -39,7 +37,6 @@ impl AbstractTree for Expression {
"function_call" => {
Expression::FunctionCall(Box::new(FunctionCall::from_syntax_node(source, child)?))
}
"tool" => Expression::Tool(Box::new(BuiltInFunction::from_syntax_node(source, child)?)),
"yield" => Expression::Yield(Box::new(Yield::from_syntax_node(source, child)?)),
_ => {
return Err(Error::UnexpectedSyntaxNode {
@ -61,7 +58,6 @@ impl AbstractTree for Expression {
Expression::Math(math) => math.run(source, context),
Expression::Logic(logic) => logic.run(source, context),
Expression::FunctionCall(function_call) => function_call.run(source, context),
Expression::Tool(tool) => tool.run(source, context),
Expression::Index(index) => index.run(source, context),
Expression::Yield(r#yield) => r#yield.run(source, context),
}

View File

@ -1,24 +1,32 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, BuiltInFunction, Error, Map, Result, Value};
use crate::{AbstractTree, Error, Map, Result, Value, BUILT_IN_FUNCTIONS};
use super::expression::Expression;
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub enum FunctionCall {
BuiltIn(Box<BuiltInFunction>),
ContextDefined {
name: Expression,
arguments: Vec<Expression>,
},
pub struct FunctionCall {
function: Expression,
arguments: Vec<Expression>,
}
impl FunctionCall {
pub fn new(function: Expression, arguments: Vec<Expression>) -> Self {
Self {
function,
arguments,
}
}
}
impl AbstractTree for FunctionCall {
fn from_syntax_node(source: &str, node: Node) -> Result<Self> {
debug_assert_eq!("function_call", node.kind());
let function_node = node.child(1).unwrap();
let expression_node = node.child(1).unwrap();
let function = Expression::from_syntax_node(source, expression_node)?;
let mut arguments = Vec::new();
for index in 2..node.child_count() - 1 {
@ -31,39 +39,43 @@ impl AbstractTree for FunctionCall {
}
}
let function_call = if function_node.kind() == "built_in_function" {
let function = BuiltInFunction::from_syntax_node(source, function_node)?;
FunctionCall::BuiltIn(Box::new(function))
} else {
let name = Expression::from_syntax_node(source, function_node)?;
FunctionCall::ContextDefined { name, arguments }
};
Ok(function_call)
Ok(FunctionCall {
function,
arguments,
})
}
fn run(&self, source: &str, context: &mut Map) -> Result<Value> {
let (name, arguments) = match self {
FunctionCall::BuiltIn(function) => return function.run(source, context),
FunctionCall::ContextDefined { name, arguments } => (name, arguments),
};
let function = if let Expression::Identifier(identifier) = &self.function {
let key = identifier.inner();
let function = if let Expression::Identifier(identifier) = name {
if let Some(value) = context.variables()?.get(identifier.inner()) {
for built_in_function in BUILT_IN_FUNCTIONS {
if key == built_in_function.name() {
let mut arguments = Vec::with_capacity(self.arguments.len());
for expression in &self.arguments {
let value = expression.run(source, context)?;
arguments.push(value);
}
return built_in_function.run(&arguments);
}
}
if let Some(value) = context.variables()?.get(key) {
value.as_function().cloned()
} else {
return Err(Error::FunctionIdentifierNotFound(identifier.clone()));
}
} else {
let name_run = name.run(source, context)?;
let expression_run = self.function.run(source, context)?;
name_run.as_function().cloned()
expression_run.as_function().cloned()
}?;
let mut function_context = Map::clone_from(context)?;
let parameter_expression_pairs = function.parameters().iter().zip(arguments.iter());
let parameter_expression_pairs = function.parameters().iter().zip(self.arguments.iter());
for ((identifier, r#type), expression) in parameter_expression_pairs {
let key = identifier.clone().take_inner();

View File

@ -8,7 +8,6 @@
pub mod assignment;
pub mod block;
pub mod built_in_function;
pub mod expression;
pub mod filter;
pub mod find;
@ -33,10 +32,10 @@ pub mod r#while;
pub mod r#yield;
pub use {
assignment::*, block::*, built_in_function::*, expression::*, filter::*, find::*,
function_call::*, identifier::*, if_else::*, index::*, index_assignment::IndexAssignment,
insert::*, logic::*, math::*, r#for::*, r#match::*, r#type::*, r#use::*, r#while::*,
r#yield::*, remove::*, select::*, statement::*, transform::*, value_node::*,
assignment::*, block::*, expression::*, filter::*, find::*, function_call::*, identifier::*,
if_else::*, index::*, index_assignment::IndexAssignment, insert::*, logic::*, math::*,
r#for::*, r#match::*, r#type::*, r#use::*, r#while::*, r#yield::*, remove::*, select::*,
statement::*, transform::*, value_node::*,
};
use tree_sitter::Node;

View File

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize};
use tree_sitter::Node;
use crate::{AbstractTree, BuiltInFunction, Expression, FunctionCall, Result, Value};
use crate::{AbstractTree, Expression, FunctionCall, Result, Value};
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
pub struct Yield {
@ -14,6 +14,8 @@ impl AbstractTree for Yield {
let input = Expression::from_syntax_node(source, input_node)?;
let function_node = node.child(3).unwrap();
let function = Expression::from_syntax_node(source, function_node)?;
let mut arguments = Vec::new();
arguments.push(input);
@ -28,15 +30,7 @@ impl AbstractTree for Yield {
}
}
let call = if function_node.kind() == "built_in_function" {
let function = BuiltInFunction::from_syntax_node(source, function_node)?;
FunctionCall::BuiltIn(Box::new(function))
} else {
let name = Expression::from_syntax_node(source, function_node)?;
FunctionCall::ContextDefined { name, arguments }
};
let call = FunctionCall::new(function, arguments);
Ok(Yield { call })
}

View File

@ -0,0 +1,87 @@
use std::{
fs::{read_dir, read_to_string, write, File},
io::Write as IoWrite,
path::PathBuf,
};
use crate::{BuiltInFunction, List, Map, Result, Value};
pub struct Read;
impl BuiltInFunction for Read {
fn name(&self) -> &'static str {
"read"
}
fn run(&self, arguments: &[Value]) -> Result<Value> {
let path_string = arguments.first().unwrap_or(&Value::Empty).as_string()?;
let path = PathBuf::from(path_string);
if path.is_dir() {
let files = List::new();
for entry in read_dir(&path)? {
let entry = entry?;
let file_data = Map::new();
{
let mut file_data_variables = file_data.variables_mut()?;
let name = entry.file_name().to_string_lossy().to_string();
let metadata = entry.metadata()?;
let created = metadata.created()?.elapsed()?.as_secs() as i64;
let modified = metadata.modified()?.elapsed()?.as_secs() as i64;
file_data_variables.insert("name".to_string(), Value::String(name));
file_data_variables.insert("created".to_string(), Value::Integer(created));
file_data_variables.insert("modified".to_string(), Value::Integer(modified));
}
files.items_mut().push(Value::Map(file_data));
}
return Ok(Value::List(files));
}
let file_content = read_to_string(path)?;
Ok(Value::String(file_content))
}
}
pub struct Write;
impl BuiltInFunction for Write {
fn name(&self) -> &'static str {
"write"
}
fn run(&self, arguments: &[Value]) -> Result<Value> {
let file_content = arguments.first().unwrap_or(&Value::Empty).as_string()?;
let path = arguments.get(1).unwrap_or(&Value::Empty).as_string()?;
write(path, file_content)?;
Ok(Value::Empty)
}
}
pub struct Append;
impl BuiltInFunction for Append {
fn name(&self) -> &'static str {
"append"
}
fn run(&self, arguments: &[Value]) -> Result<Value> {
let file_content = arguments.first().unwrap_or(&Value::Empty).as_string()?;
let path = arguments.get(1).unwrap_or(&Value::Empty).as_string()?;
File::options()
.append(true)
.create(true)
.open(path)?
.write_all(file_content.as_bytes())?;
Ok(Value::Empty)
}
}

View File

@ -0,0 +1,12 @@
use crate::{Result, Value};
mod fs;
mod output;
pub const BUILT_IN_FUNCTIONS: [&dyn BuiltInFunction; 4] =
[&output::Output, &fs::Read, &fs::Write, &fs::Append];
pub trait BuiltInFunction {
fn name(&self) -> &'static str;
fn run(&self, arguments: &[Value]) -> Result<Value>;
}

View File

@ -0,0 +1,17 @@
use crate::{BuiltInFunction, Result, Value};
pub struct Output;
impl BuiltInFunction for Output {
fn name(&self) -> &'static str {
"output"
}
fn run(&self, arguments: &[Value]) -> Result<Value> {
for argument in arguments {
println!("{argument}");
}
Ok(Value::Empty)
}
}

View File

@ -6,12 +6,14 @@
pub use crate::{
abstract_tree::*,
built_in_functions::{BuiltInFunction, BUILT_IN_FUNCTIONS},
error::*,
evaluator::*,
value::{function::Function, list::List, map::Map, table::Table, Value},
};
mod abstract_tree;
pub mod built_in_functions;
mod error;
mod evaluator;
mod value;

View File

@ -286,79 +286,18 @@ module.exports = grammar({
function_call: $ => prec.right(1, seq(
'(',
choice(
$.built_in_function,
$._context_defined_function,
),
')',
)),
_context_defined_function: $ => prec.right(1, seq(
$.expression,
optional($._expression_list),
)),
built_in_function: $ => prec.right(seq(
$._built_in_function_name,
optional($._expression_list),
')',
)),
yield: $ => prec.left(seq(
$.expression,
'->',
'(',
choice(
$.built_in_function,
$._context_defined_function,
),
$.expression,
optional($._expression_list),
')',
)),
_built_in_function_name: $ => choice(
// General
'assert',
'assert_equal',
'context',
'download',
'help',
'length',
'output',
'output_error',
'type',
// Filesystem
'append',
'metadata',
'move',
'read',
'workdir',
'write',
// Format conversion
'from_json',
'to_json',
'to_string',
'to_float',
// Command
'bash',
'fish',
'raw',
'sh',
'zsh',
// Random
'random',
'random_boolean',
'random_float',
'random_integer',
// Tables
'columns',
'rows',
// Lists
'reverse',
),
}
});

View File

@ -256,10 +256,6 @@
{
"type": "SYMBOL",
"name": "map"
},
{
"type": "SYMBOL",
"name": "future"
}
]
},
@ -486,7 +482,7 @@
{
"type": "REPEAT",
"content": {
"type": "PREC_RIGHT",
"type": "PREC_LEFT",
"value": 0,
"content": {
"type": "SEQ",
@ -520,43 +516,41 @@
"map": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "map"
},
{
"type": "SYMBOL",
"name": "block"
}
]
},
"future": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "async"
},
{
"type": "SYMBOL",
"name": "block"
}
]
},
"await": {
"type": "SEQ",
"members": [
{
"type": "STRING",
"value": "await"
},
{
"type": "STRING",
"value": "{"
},
{
"type": "SYMBOL",
"name": "_expression_list"
"type": "REPEAT",
"content": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "identifier"
},
{
"type": "STRING",
"value": "="
},
{
"type": "SYMBOL",
"name": "statement"
},
{
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": ","
},
{
"type": "BLANK"
}
]
}
]
}
},
{
"type": "STRING",
@ -1236,32 +1230,6 @@
"type": "STRING",
"value": "("
},
{
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "built_in_function"
},
{
"type": "SYMBOL",
"name": "_context_defined_function"
}
]
},
{
"type": "STRING",
"value": ")"
}
]
}
},
"_context_defined_function": {
"type": "PREC_RIGHT",
"value": 1,
"content": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "expression"
@ -1277,31 +1245,10 @@
"type": "BLANK"
}
]
}
]
}
},
"built_in_function": {
"type": "PREC_RIGHT",
"value": 0,
"content": {
"type": "SEQ",
"members": [
{
"type": "SYMBOL",
"name": "_built_in_function_name"
},
{
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "_expression_list"
},
{
"type": "BLANK"
}
]
"type": "STRING",
"value": ")"
}
]
}
@ -1324,16 +1271,19 @@
"type": "STRING",
"value": "("
},
{
"type": "SYMBOL",
"name": "expression"
},
{
"type": "CHOICE",
"members": [
{
"type": "SYMBOL",
"name": "built_in_function"
"name": "_expression_list"
},
{
"type": "SYMBOL",
"name": "_context_defined_function"
"type": "BLANK"
}
]
},
@ -1343,135 +1293,6 @@
}
]
}
},
"_built_in_function_name": {
"type": "CHOICE",
"members": [
{
"type": "STRING",
"value": "assert"
},
{
"type": "STRING",
"value": "assert_equal"
},
{
"type": "STRING",
"value": "context"
},
{
"type": "STRING",
"value": "download"
},
{
"type": "STRING",
"value": "help"
},
{
"type": "STRING",
"value": "length"
},
{
"type": "STRING",
"value": "output"
},
{
"type": "STRING",
"value": "output_error"
},
{
"type": "STRING",
"value": "type"
},
{
"type": "STRING",
"value": "append"
},
{
"type": "STRING",
"value": "metadata"
},
{
"type": "STRING",
"value": "move"
},
{
"type": "STRING",
"value": "read"
},
{
"type": "STRING",
"value": "workdir"
},
{
"type": "STRING",
"value": "write"
},
{
"type": "STRING",
"value": "from_json"
},
{
"type": "STRING",
"value": "to_json"
},
{
"type": "STRING",
"value": "to_string"
},
{
"type": "STRING",
"value": "to_float"
},
{
"type": "STRING",
"value": "bash"
},
{
"type": "STRING",
"value": "fish"
},
{
"type": "STRING",
"value": "raw"
},
{
"type": "STRING",
"value": "sh"
},
{
"type": "STRING",
"value": "zsh"
},
{
"type": "STRING",
"value": "random"
},
{
"type": "STRING",
"value": "random_boolean"
},
{
"type": "STRING",
"value": "random_float"
},
{
"type": "STRING",
"value": "random_integer"
},
{
"type": "STRING",
"value": "columns"
},
{
"type": "STRING",
"value": "rows"
},
{
"type": "STRING",
"value": "reverse"
}
]
}
},
"extras": [

View File

@ -70,21 +70,6 @@
"named": true,
"fields": {}
},
{
"type": "built_in_function",
"named": true,
"fields": {},
"children": {
"multiple": true,
"required": false,
"types": [
{
"type": "expression",
"named": true
}
]
}
},
{
"type": "else",
"named": true,
@ -212,10 +197,6 @@
"multiple": true,
"required": true,
"types": [
{
"type": "built_in_function",
"named": true
},
{
"type": "expression",
"named": true
@ -678,10 +659,6 @@
"multiple": true,
"required": true,
"types": [
{
"type": "built_in_function",
"named": true
},
{
"type": "expression",
"named": true
@ -793,18 +770,6 @@
"type": "any",
"named": false
},
{
"type": "append",
"named": false
},
{
"type": "assert",
"named": false
},
{
"type": "assert_equal",
"named": false
},
{
"type": "async",
"named": false
@ -813,26 +778,10 @@
"type": "async for",
"named": false
},
{
"type": "bash",
"named": false
},
{
"type": "bool",
"named": false
},
{
"type": "columns",
"named": false
},
{
"type": "context",
"named": false
},
{
"type": "download",
"named": false
},
{
"type": "else",
"named": false
@ -845,10 +794,6 @@
"type": "false",
"named": false
},
{
"type": "fish",
"named": false
},
{
"type": "float",
"named": true
@ -865,14 +810,6 @@
"type": "from",
"named": false
},
{
"type": "from_json",
"named": false
},
{
"type": "help",
"named": false
},
{
"type": "identifier",
"named": true
@ -901,10 +838,6 @@
"type": "into",
"named": false
},
{
"type": "length",
"named": false
},
{
"type": "list",
"named": false
@ -917,66 +850,14 @@
"type": "match",
"named": false
},
{
"type": "metadata",
"named": false
},
{
"type": "move",
"named": false
},
{
"type": "output",
"named": false
},
{
"type": "output_error",
"named": false
},
{
"type": "random",
"named": false
},
{
"type": "random_boolean",
"named": false
},
{
"type": "random_float",
"named": false
},
{
"type": "random_integer",
"named": false
},
{
"type": "raw",
"named": false
},
{
"type": "read",
"named": false
},
{
"type": "return",
"named": false
},
{
"type": "reverse",
"named": false
},
{
"type": "rows",
"named": false
},
{
"type": "select",
"named": false
},
{
"type": "sh",
"named": false
},
{
"type": "str",
"named": false
@ -989,26 +870,10 @@
"type": "table",
"named": false
},
{
"type": "to_float",
"named": false
},
{
"type": "to_json",
"named": false
},
{
"type": "to_string",
"named": false
},
{
"type": "true",
"named": false
},
{
"type": "type",
"named": false
},
{
"type": "use",
"named": false
@ -1017,18 +882,6 @@
"type": "while",
"named": false
},
{
"type": "workdir",
"named": false
},
{
"type": "write",
"named": false
},
{
"type": "zsh",
"named": false
},
{
"type": "{",
"named": false

File diff suppressed because it is too large Load Diff