Create new built-in function API
This commit is contained in:
parent
5d68b7b156
commit
b46dfc5791
293
Cargo.lock
generated
293
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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),
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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 })
|
||||
}
|
||||
|
87
src/built_in_functions/fs.rs
Normal file
87
src/built_in_functions/fs.rs
Normal 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)
|
||||
}
|
||||
}
|
12
src/built_in_functions/mod.rs
Normal file
12
src/built_in_functions/mod.rs
Normal 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>;
|
||||
}
|
17
src/built_in_functions/output.rs
Normal file
17
src/built_in_functions/output.rs
Normal 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)
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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',
|
||||
),
|
||||
}
|
||||
});
|
||||
|
@ -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": [
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user