mod bin;
mod helper;
mod lazy_module;
mod pprint;
use crate::RuntimeErrorKind;
use crate::{Environment, Expression, RuntimeError, eval::State, libs::lazy_module::LazyModule};
pub use bin::colors::{handle_color, handle_style};
pub use bin::filesize_lib::from_size_str;
pub use bin::math_lib::handle_math;
pub use bin::time_lib::parse as time_parse;
pub use bin::time_lib::parse_datetime_arg as from_time_str;
pub use bin::top::regist_info;
pub use pprint::pretty_printer;
use std::{
borrow::Cow,
cell::RefCell,
collections::{BTreeMap, HashMap},
rc::Rc,
sync::LazyLock,
};
pub struct BuiltinInfo {
pub descr: &'static str,
pub hint: &'static str,
}
pub type BuiltinFunc =
fn(Vec<Expression>, &mut Environment, contex: &Expression) -> Result<Expression, RuntimeError>;
pub type SelfExpandFunc = fn(
&[Expression],
&mut Environment,
&mut State,
contex: &Expression,
) -> Result<Expression, RuntimeError>;
thread_local! {
pub static LIBS_INFO: LazyLock<BTreeMap<&'static str, BTreeMap<&'static str,BuiltinInfo>>> =LazyLock::new(regist_all_info);
static SE_LIB: RefCell<HashMap<&'static str, SelfExpandFunc>> = RefCell::new({
bin::se_lib::regist_se()
});
static TOP_LIB: RefCell<HashMap<&'static str, Rc<BuiltinFunc>>> = RefCell::new({
bin::top::regist_all()
});
static BOOL_LIB: RefCell<HashMap<&'static str, Rc<BuiltinFunc>>> = RefCell::new({
bin::boolean_lib::regist_all()
});
static STRING_LIB: LazyModule = bin::string_lib::regist_lazy();
static LIST_LIB: LazyModule = bin::list_lib::regist_lazy();
static BSET_LIB: LazyModule = bin::bset_lib::regist_lazy();
static MAP_LIB: LazyModule = bin::map_lib::regist_lazy();
static HMAP_LIB: LazyModule = bin::hmap_lib::regist_lazy();
static TABLE_LIB: LazyModule = bin::table_lib::regist_lazy();
static TIME_LIB: LazyModule = bin::time_lib::regist_lazy();
static REGEX_LIB: LazyModule = bin::regex_lib::regist_lazy();
static MATH_LIB: LazyModule = bin::math_lib::regist_lazy();
static RAND_LIB: LazyModule = bin::rand_lib::regist_lazy();
static LOG_LIB: LazyModule = bin::log_lib::regist_lazy();
static FS_LIB: LazyModule = bin::fs_lib::regist_lazy();
static UI_LIB: LazyModule = bin::ui_lib::regist_lazy();
static INTO_LIB: LazyModule = bin::into_lib::regist_lazy();
static SYS_LIB: LazyModule = bin::sys_lib::regist_lazy();
static FILESIZE_LIB: LazyModule = bin::filesize_lib::regist_lazy();
static FROM_LIB: LazyModule = bin::from_lib::regist_lazy();
static ABOUT_LIB: LazyModule = bin::about_lib::regist_lazy();
static CONSOLE_LIB: LazyModule = bin::console_lib::regist_lazy();
static BYTES_LIB: LazyModule = bin::bytes_lib::regist_lazy();
}
fn regist_all_info() -> BTreeMap<&'static str, BTreeMap<&'static str, BuiltinInfo>> {
let mut libs_info = BTreeMap::new();
let mut top_info = bin::top::regist_info();
let se_info = bin::se_lib::regist_info();
top_info.extend(se_info);
libs_info.insert("", top_info); libs_info.insert("boolean", bin::boolean_lib::regist_info());
libs_info.insert("string", bin::string_lib::regist_info());
libs_info.insert("list", bin::list_lib::regist_info());
libs_info.insert("set", bin::bset_lib::regist_info());
libs_info.insert("map", bin::map_lib::regist_info());
libs_info.insert("hmap", bin::hmap_lib::regist_info());
libs_info.insert("table", bin::table_lib::regist_info());
libs_info.insert("time", bin::time_lib::regist_info());
libs_info.insert("regex", bin::regex_lib::regist_info());
libs_info.insert("math", bin::math_lib::regist_info());
libs_info.insert("rand", bin::rand_lib::regist_info());
libs_info.insert("fs", bin::fs_lib::regist_info());
libs_info.insert("filesize", bin::filesize_lib::regist_info());
libs_info.insert("from", bin::from_lib::regist_info());
libs_info.insert("into", bin::into_lib::regist_info());
libs_info.insert("sys", bin::sys_lib::regist_info());
libs_info.insert("ui", bin::ui_lib::regist_info());
libs_info.insert("console", bin::console_lib::regist_info());
libs_info.insert("log", bin::log_lib::regist_info());
libs_info.insert("about", bin::about_lib::regist_info());
libs_info.insert("bytes", bin::bytes_lib::regist_info());
libs_info.insert("MATH", bin::math_lib::regist_const_math());
libs_info.insert("COLOR", bin::colors::regist_const_color());
libs_info.insert("STYLE", bin::colors::regist_const_style());
libs_info
}
fn no_lib_err(lib: &'static str, f: &str, ctx: &Expression) -> RuntimeError {
RuntimeError {
kind: RuntimeErrorKind::NoLibDefined(f.into(), lib.into()),
context: ctx.clone(),
depth: 0,
}
}
pub fn get_builtin_optimized(
lib_name: &str,
fn_name: &str,
ctx: &Expression,
) -> Result<Option<Rc<BuiltinFunc>>, RuntimeError> {
match lib_name {
"" => Ok(TOP_LIB.with_borrow(|m| m.get(fn_name).cloned())),
"boolean" => BOOL_LIB
.with(|m| m.borrow().get(fn_name).cloned())
.ok_or(no_lib_err("boolean", fn_name, ctx))
.map(Some),
"string" => STRING_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("string", fn_name, ctx))
.map(Some),
"list" => LIST_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("list", fn_name, ctx))
.map(Some),
"set" => BSET_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("set", fn_name, ctx))
.map(Some),
"map" => MAP_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("map", fn_name, ctx))
.map(Some),
"hmap" => HMAP_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("hmap", fn_name, ctx))
.map(Some),
"table" => TABLE_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("table", fn_name, ctx))
.map(Some),
"time" => TIME_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("time", fn_name, ctx))
.map(Some),
"regex" => REGEX_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("regex", fn_name, ctx))
.map(Some),
"math" => MATH_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("math", fn_name, ctx))
.map(Some),
"rand" => RAND_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("rand", fn_name, ctx))
.map(Some),
"log" => LOG_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("log", fn_name, ctx))
.map(Some),
"fs" => FS_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("fs", fn_name, ctx))
.map(Some),
"ui" => UI_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("ui", fn_name, ctx))
.map(Some),
"into" => INTO_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("into", fn_name, ctx))
.map(Some),
"sys" => SYS_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("sys", fn_name, ctx))
.map(Some),
"filesize" => FILESIZE_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("filesize", fn_name, ctx))
.map(Some),
"from" => FROM_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("from", fn_name, ctx))
.map(Some),
"about" => ABOUT_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("about", fn_name, ctx))
.map(Some),
"console" => CONSOLE_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("console", fn_name, ctx))
.map(Some),
"bytes" => BYTES_LIB
.with(|m| m.get_function(fn_name))
.ok_or(no_lib_err("console", fn_name, ctx))
.map(Some),
_ => Ok(None),
}
}
#[inline]
pub fn is_lib(name: &str) -> bool {
LIBS_INFO.with(|h| h.contains_key(name))
}
#[inline]
pub fn is_top_or_se(name: &str) -> bool {
SE_LIB.with_borrow(|s| s.contains_key(name)) || TOP_LIB.with_borrow(|h| h.contains_key(name))
}
pub fn get_lib_completions(prefix: &str) -> Option<Vec<&str>> {
if prefix.is_empty() || !prefix.is_ascii() {
return None;
}
let se = SE_LIB.with(|h| {
h.borrow()
.iter()
.filter(|(k, _)| k.starts_with(prefix))
.map(|(k, _)| *k)
.collect::<Vec<_>>()
});
if !se.is_empty() {
return Some(se);
}
let top = TOP_LIB.with(|h| {
h.borrow()
.iter()
.filter(|(k, _)| k.starts_with(prefix))
.map(|(k, _)| *k)
.collect::<Vec<_>>()
});
if !top.is_empty() {
return Some(top);
}
let lib = LIBS_INFO.with(|h| {
h.iter()
.filter(|(k, _)| k.starts_with(prefix))
.map(|(k, _)| *k)
.collect::<Vec<_>>()
});
if !lib.is_empty() {
return Some(lib);
}
None
}
fn get_belong_lib_name(exp: &Expression) -> Option<Cow<'static, str>> {
match exp {
Expression::List(_) | Expression::Range(..) => Some("list".into()),
Expression::BSet(_) => Some("set".into()),
Expression::Map(_) => Some("map".into()),
Expression::HMap(_) => Some("hmap".into()),
Expression::Table(_) => Some("table".into()),
Expression::String(_) | Expression::StringTemplate(_) => Some("string".into()),
Expression::Integer(_) | Expression::Float(_) => Some("math".into()),
Expression::DateTime(_) => Some("time".into()),
Expression::Boolean(_) => Some("boolean".into()),
Expression::Regex(_) => Some("regex".into()),
Expression::FileSize(_) => Some("filesize".into()),
Expression::Bytes(_) => Some("bytes".into()),
_ => None,
}
}
pub fn get_builtin_via_expr(
expr: &Expression,
fn_name: &str,
ctx: &Expression,
) -> Result<Option<Rc<BuiltinFunc>>, RuntimeError> {
match expr {
Expression::Blank => get_builtin_optimized("", fn_name, ctx),
Expression::Symbol(x) => get_builtin_optimized(x.as_ref(), fn_name, ctx),
other => match get_belong_lib_name(other) {
None => Ok(None),
Some(x) => get_builtin_optimized(x.as_ref(), fn_name, ctx),
},
}
}
pub fn exec_self_expand_lib(
fn_name: &str,
args: &[Expression],
env: &mut Environment,
state: &mut State,
ctx: &Expression,
) -> Result<Option<Expression>, RuntimeError> {
SE_LIB.with_borrow(|s| {
if let Some(selib) = s.get(fn_name) {
let result = selib(args, env, state, ctx)?;
return Ok(Some(result));
}
Ok(None)
})
}