2024-01-17 15:21:00 +00:00
|
|
|
use std::{collections::BTreeMap, env::args, sync::OnceLock};
|
2024-01-01 10:20:11 +00:00
|
|
|
|
2024-01-25 00:41:47 +00:00
|
|
|
use enum_iterator::{all, Sequence};
|
2024-01-01 10:20:11 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
2024-01-01 13:52:25 +00:00
|
|
|
use crate::{
|
2024-01-30 19:11:18 +00:00
|
|
|
built_in_functions::{fs::fs_functions, json::json_functions, str::string_functions, Callable},
|
2024-01-31 18:51:48 +00:00
|
|
|
error::{RuntimeError, SyntaxError, ValidationError},
|
2024-02-11 00:31:47 +00:00
|
|
|
AbstractTree, BuiltInFunction, Context, Format, Function, List, Map, SyntaxNode, Type, Value,
|
2024-01-01 13:52:25 +00:00
|
|
|
};
|
2024-01-01 10:20:11 +00:00
|
|
|
|
|
|
|
static ARGS: OnceLock<Value> = OnceLock::new();
|
2024-01-01 12:46:47 +00:00
|
|
|
static FS: OnceLock<Value> = OnceLock::new();
|
|
|
|
static JSON: OnceLock<Value> = OnceLock::new();
|
2024-01-01 10:20:11 +00:00
|
|
|
static RANDOM: OnceLock<Value> = OnceLock::new();
|
2024-01-01 13:52:25 +00:00
|
|
|
static STRING: OnceLock<Value> = OnceLock::new();
|
2024-01-01 10:20:11 +00:00
|
|
|
|
2024-01-30 23:13:30 +00:00
|
|
|
/// Returns the entire built-in value API.
|
2024-01-25 00:41:47 +00:00
|
|
|
pub fn built_in_values() -> impl Iterator<Item = BuiltInValue> {
|
|
|
|
all()
|
|
|
|
}
|
|
|
|
|
2024-01-30 23:13:30 +00:00
|
|
|
/// A variable with a hard-coded key that is globally available.
|
2024-01-25 00:41:47 +00:00
|
|
|
#[derive(Sequence, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
2024-01-01 10:20:11 +00:00
|
|
|
pub enum BuiltInValue {
|
2024-01-30 23:13:30 +00:00
|
|
|
/// The arguments used to launch the current program.
|
2024-01-01 10:20:11 +00:00
|
|
|
Args,
|
2024-01-30 23:13:30 +00:00
|
|
|
|
|
|
|
/// Create an error if two values are not equal.
|
2024-01-01 10:20:11 +00:00
|
|
|
AssertEqual,
|
2024-01-30 23:13:30 +00:00
|
|
|
|
|
|
|
/// File system tools.
|
2024-01-01 12:46:47 +00:00
|
|
|
Fs,
|
2024-01-30 23:13:30 +00:00
|
|
|
|
|
|
|
/// JSON format tools.
|
2024-01-01 12:46:47 +00:00
|
|
|
Json,
|
2024-01-30 23:13:30 +00:00
|
|
|
|
|
|
|
/// Get the length of a collection.
|
2024-01-01 10:20:11 +00:00
|
|
|
Length,
|
2024-01-30 23:13:30 +00:00
|
|
|
|
|
|
|
/// Print a value to stdout.
|
2024-01-01 10:20:11 +00:00
|
|
|
Output,
|
2024-01-30 23:13:30 +00:00
|
|
|
|
|
|
|
/// Random value generators.
|
2024-01-01 10:20:11 +00:00
|
|
|
Random,
|
2024-01-30 23:13:30 +00:00
|
|
|
|
|
|
|
/// String utilities.
|
2024-01-30 18:57:30 +00:00
|
|
|
Str,
|
2024-01-01 10:20:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl BuiltInValue {
|
2024-01-30 23:13:30 +00:00
|
|
|
/// Returns the hard-coded key used to identify the value.
|
2024-01-28 17:04:33 +00:00
|
|
|
pub fn name(&self) -> &'static str {
|
2024-01-06 13:11:09 +00:00
|
|
|
match self {
|
|
|
|
BuiltInValue::Args => "args",
|
|
|
|
BuiltInValue::AssertEqual => "assert_equal",
|
|
|
|
BuiltInValue::Fs => "fs",
|
|
|
|
BuiltInValue::Json => "json",
|
2024-01-30 23:13:30 +00:00
|
|
|
BuiltInValue::Length => BuiltInFunction::Length.name(),
|
2024-01-06 13:11:09 +00:00
|
|
|
BuiltInValue::Output => "output",
|
|
|
|
BuiltInValue::Random => "random",
|
2024-01-30 18:57:30 +00:00
|
|
|
BuiltInValue::Str => "str",
|
2024-01-06 13:11:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-30 23:13:30 +00:00
|
|
|
/// Returns a brief description of the value's features.
|
|
|
|
///
|
|
|
|
/// This is used by the shell when suggesting completions.
|
2024-01-28 17:04:33 +00:00
|
|
|
pub fn description(&self) -> &'static str {
|
|
|
|
match self {
|
|
|
|
BuiltInValue::Args => "The command line arguments sent to this program.",
|
|
|
|
BuiltInValue::AssertEqual => "Error if the two values are not equal.",
|
|
|
|
BuiltInValue::Fs => "File and directory tools.",
|
|
|
|
BuiltInValue::Json => "JSON formatting tools.",
|
|
|
|
BuiltInValue::Length => BuiltInFunction::Length.description(),
|
|
|
|
BuiltInValue::Output => "output",
|
|
|
|
BuiltInValue::Random => "random",
|
2024-01-30 18:57:30 +00:00
|
|
|
BuiltInValue::Str => "string",
|
2024-01-28 17:04:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-30 23:13:30 +00:00
|
|
|
/// Returns the value's type.
|
|
|
|
///
|
|
|
|
/// This is checked with a unit test to ensure it matches the value.
|
2024-01-25 00:41:47 +00:00
|
|
|
pub fn r#type(&self) -> Type {
|
2024-01-01 10:20:11 +00:00
|
|
|
match self {
|
2024-01-01 14:39:59 +00:00
|
|
|
BuiltInValue::Args => Type::list(Type::String),
|
2024-01-01 10:20:11 +00:00
|
|
|
BuiltInValue::AssertEqual => BuiltInFunction::AssertEqual.r#type(),
|
2024-01-06 08:47:54 +00:00
|
|
|
BuiltInValue::Fs => Type::Map(None),
|
|
|
|
BuiltInValue::Json => Type::Map(None),
|
2024-01-01 10:20:11 +00:00
|
|
|
BuiltInValue::Length => BuiltInFunction::Length.r#type(),
|
|
|
|
BuiltInValue::Output => BuiltInFunction::Output.r#type(),
|
2024-01-06 08:47:54 +00:00
|
|
|
BuiltInValue::Random => Type::Map(None),
|
2024-01-30 18:57:30 +00:00
|
|
|
BuiltInValue::Str => Type::Map(None),
|
2024-01-01 10:20:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-30 23:13:30 +00:00
|
|
|
/// Returns the value by creating it or, if it has already been accessed, retrieving it from its
|
|
|
|
/// [OnceLock][].
|
2024-01-25 00:41:47 +00:00
|
|
|
pub fn get(&self) -> &Value {
|
2024-01-01 10:20:11 +00:00
|
|
|
match self {
|
|
|
|
BuiltInValue::Args => ARGS.get_or_init(|| {
|
2024-01-01 17:39:03 +00:00
|
|
|
let args = args().map(|arg| Value::string(arg.to_string())).collect();
|
2024-01-01 10:20:11 +00:00
|
|
|
|
|
|
|
Value::List(List::with_items(args))
|
|
|
|
}),
|
|
|
|
BuiltInValue::AssertEqual => {
|
|
|
|
&Value::Function(Function::BuiltIn(BuiltInFunction::AssertEqual))
|
|
|
|
}
|
2024-01-01 12:46:47 +00:00
|
|
|
BuiltInValue::Fs => FS.get_or_init(|| {
|
2024-01-30 04:18:09 +00:00
|
|
|
let mut fs_context = BTreeMap::new();
|
2024-01-01 12:46:47 +00:00
|
|
|
|
2024-01-30 04:18:09 +00:00
|
|
|
for fs_function in fs_functions() {
|
|
|
|
let key = fs_function.name().to_string();
|
|
|
|
let value =
|
|
|
|
Value::Function(Function::BuiltIn(BuiltInFunction::Fs(fs_function)));
|
2024-01-01 12:46:47 +00:00
|
|
|
|
2024-02-11 01:50:49 +00:00
|
|
|
fs_context.insert(key, value);
|
2024-01-30 04:18:09 +00:00
|
|
|
}
|
|
|
|
|
2024-02-11 01:50:49 +00:00
|
|
|
Value::Map(Map::with_values(fs_context))
|
2024-01-01 12:46:47 +00:00
|
|
|
}),
|
|
|
|
BuiltInValue::Json => JSON.get_or_init(|| {
|
2024-01-26 22:14:57 +00:00
|
|
|
let mut json_context = BTreeMap::new();
|
2024-01-01 12:46:47 +00:00
|
|
|
|
2024-01-26 22:14:57 +00:00
|
|
|
for json_function in json_functions() {
|
|
|
|
let key = json_function.name().to_string();
|
|
|
|
let value =
|
|
|
|
Value::Function(Function::BuiltIn(BuiltInFunction::Json(json_function)));
|
|
|
|
|
2024-02-11 01:50:49 +00:00
|
|
|
json_context.insert(key, value);
|
2024-01-26 22:14:57 +00:00
|
|
|
}
|
2024-01-01 12:46:47 +00:00
|
|
|
|
2024-02-11 01:50:49 +00:00
|
|
|
Value::Map(Map::with_values(json_context))
|
2024-01-01 12:46:47 +00:00
|
|
|
}),
|
2024-01-01 10:20:11 +00:00
|
|
|
BuiltInValue::Length => &Value::Function(Function::BuiltIn(BuiltInFunction::Length)),
|
|
|
|
BuiltInValue::Output => &Value::Function(Function::BuiltIn(BuiltInFunction::Output)),
|
|
|
|
BuiltInValue::Random => RANDOM.get_or_init(|| {
|
2024-01-17 15:21:00 +00:00
|
|
|
let mut random_context = BTreeMap::new();
|
|
|
|
|
|
|
|
for built_in_function in [
|
|
|
|
BuiltInFunction::RandomBoolean,
|
|
|
|
BuiltInFunction::RandomFloat,
|
|
|
|
BuiltInFunction::RandomFrom,
|
|
|
|
BuiltInFunction::RandomInteger,
|
|
|
|
] {
|
|
|
|
let key = built_in_function.name().to_string();
|
2024-01-25 09:19:45 +00:00
|
|
|
let value = Value::Function(Function::BuiltIn(built_in_function));
|
2024-01-17 15:21:00 +00:00
|
|
|
|
2024-02-11 01:50:49 +00:00
|
|
|
random_context.insert(key, value);
|
2024-01-01 10:20:11 +00:00
|
|
|
}
|
|
|
|
|
2024-02-11 01:50:49 +00:00
|
|
|
Value::Map(Map::with_values(random_context))
|
2024-01-01 10:20:11 +00:00
|
|
|
}),
|
2024-01-30 18:57:30 +00:00
|
|
|
BuiltInValue::Str => STRING.get_or_init(|| {
|
2024-01-17 15:21:00 +00:00
|
|
|
let mut string_context = BTreeMap::new();
|
2024-01-01 13:52:25 +00:00
|
|
|
|
2024-01-17 15:21:00 +00:00
|
|
|
for string_function in string_functions() {
|
|
|
|
let key = string_function.name().to_string();
|
|
|
|
let value = Value::Function(Function::BuiltIn(BuiltInFunction::String(
|
|
|
|
string_function,
|
|
|
|
)));
|
2024-01-01 13:52:25 +00:00
|
|
|
|
2024-02-11 01:50:49 +00:00
|
|
|
string_context.insert(key, value);
|
2024-01-01 13:52:25 +00:00
|
|
|
}
|
|
|
|
|
2024-02-11 01:50:49 +00:00
|
|
|
Value::Map(Map::with_values(string_context))
|
2024-01-01 13:52:25 +00:00
|
|
|
}),
|
2024-01-01 10:20:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AbstractTree for BuiltInValue {
|
2024-02-11 00:31:47 +00:00
|
|
|
fn from_syntax(
|
|
|
|
node: SyntaxNode,
|
|
|
|
_source: &str,
|
|
|
|
_context: &Context,
|
|
|
|
) -> Result<Self, SyntaxError> {
|
2024-01-01 10:20:11 +00:00
|
|
|
let built_in_value = match node.kind() {
|
|
|
|
"args" => BuiltInValue::Args,
|
|
|
|
"assert_equal" => BuiltInValue::AssertEqual,
|
2024-01-01 12:46:47 +00:00
|
|
|
"fs" => BuiltInValue::Fs,
|
|
|
|
"json" => BuiltInValue::Json,
|
2024-01-01 10:20:11 +00:00
|
|
|
"length" => BuiltInValue::Length,
|
|
|
|
"output" => BuiltInValue::Output,
|
|
|
|
"random" => BuiltInValue::Random,
|
2024-01-30 18:57:30 +00:00
|
|
|
"str" => BuiltInValue::Str,
|
2024-01-01 10:20:11 +00:00
|
|
|
_ => todo!(),
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(built_in_value)
|
|
|
|
}
|
|
|
|
|
2024-02-11 00:31:47 +00:00
|
|
|
fn expected_type(&self, _context: &Context) -> Result<Type, ValidationError> {
|
2024-01-31 18:51:48 +00:00
|
|
|
Ok(self.r#type())
|
2024-01-01 10:20:11 +00:00
|
|
|
}
|
|
|
|
|
2024-02-11 00:31:47 +00:00
|
|
|
fn validate(&self, _source: &str, _context: &Context) -> Result<(), ValidationError> {
|
2024-01-31 18:51:48 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2024-02-11 00:31:47 +00:00
|
|
|
fn run(&self, _source: &str, _context: &Context) -> Result<Value, RuntimeError> {
|
2024-01-31 18:51:48 +00:00
|
|
|
Ok(self.get().clone())
|
2024-01-01 10:20:11 +00:00
|
|
|
}
|
|
|
|
}
|
2024-01-06 10:00:36 +00:00
|
|
|
|
2024-01-06 13:11:09 +00:00
|
|
|
impl Format for BuiltInValue {
|
2024-01-06 13:53:31 +00:00
|
|
|
fn format(&self, output: &mut String, _indent_level: u8) {
|
2024-01-06 13:11:09 +00:00
|
|
|
output.push_str(&self.get().to_string());
|
2024-01-06 10:00:36 +00:00
|
|
|
}
|
|
|
|
}
|
2024-01-30 23:13:30 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::built_in_values;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn check_built_in_types() {
|
|
|
|
for built_in_value in built_in_values() {
|
|
|
|
let expected = built_in_value.r#type();
|
|
|
|
let actual = built_in_value.get().r#type();
|
|
|
|
|
|
|
|
assert_eq!(expected, actual);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|