ganit_core/eval/functions/
mod.rs1pub mod financial;
2pub mod logical;
3pub mod math;
4pub mod operator;
5pub mod statistical;
6pub mod text;
7
8use std::collections::HashMap;
9use crate::eval::context::Context;
10use crate::parser::ast::Expr;
11use crate::types::{ErrorKind, Value};
12
13pub struct EvalCtx<'r> {
18 pub ctx: Context,
19 pub registry: &'r Registry,
20}
21
22impl<'r> EvalCtx<'r> {
23 pub fn new(ctx: Context, registry: &'r Registry) -> Self {
24 Self { ctx, registry }
25 }
26}
27
28pub type EagerFn = fn(&[Value]) -> Value;
33
34pub type LazyFn = fn(&[Expr], &mut EvalCtx<'_>) -> Value;
37
38pub enum FunctionKind {
39 Eager(EagerFn),
40 Lazy(LazyFn),
41}
42
43#[derive(Debug)]
48pub struct FunctionMeta {
49 pub category: &'static str,
50 pub signature: &'static str,
51 pub description: &'static str,
52}
53
54pub struct Registry {
58 functions: HashMap<String, FunctionKind>,
59 metadata: HashMap<String, FunctionMeta>,
60}
61
62impl Registry {
63 pub fn new() -> Self {
64 let mut r = Self { functions: HashMap::new(), metadata: HashMap::new() };
65 math::register_math(&mut r);
66 logical::register_logical(&mut r);
67 text::register_text(&mut r);
68 financial::register_financial(&mut r);
69 statistical::register_statistical(&mut r);
70 operator::register_operator(&mut r);
71 r
72 }
73
74 pub fn register_eager(&mut self, name: &str, f: EagerFn, meta: FunctionMeta) {
77 let key = name.to_uppercase();
78 self.functions.insert(key.clone(), FunctionKind::Eager(f));
79 self.metadata.insert(key, meta);
80 }
81
82 pub fn register_lazy(&mut self, name: &str, f: LazyFn, meta: FunctionMeta) {
85 let key = name.to_uppercase();
86 self.functions.insert(key.clone(), FunctionKind::Lazy(f));
87 self.metadata.insert(key, meta);
88 }
89
90 pub fn register_internal(&mut self, name: &str, f: EagerFn) {
93 self.functions.insert(name.to_uppercase(), FunctionKind::Eager(f));
94 }
95
96 pub fn register_internal_lazy(&mut self, name: &str, f: LazyFn) {
99 self.functions.insert(name.to_uppercase(), FunctionKind::Lazy(f));
100 }
101
102 pub fn get(&self, name: &str) -> Option<&FunctionKind> {
103 self.functions.get(&name.to_uppercase())
104 }
105
106 pub fn list_functions(&self) -> impl Iterator<Item = (&str, &FunctionMeta)> {
109 self.metadata.iter().map(|(k, v)| (k.as_str(), v))
110 }
111}
112
113impl Default for Registry {
114 fn default() -> Self {
115 Self::new()
116 }
117}
118
119pub fn check_arity(args: &[Value], min: usize, max: usize) -> Option<Value> {
123 if args.len() < min || args.len() > max {
124 Some(Value::Error(ErrorKind::NA))
125 } else {
126 None
127 }
128}
129
130pub fn check_arity_len(count: usize, min: usize, max: usize) -> Option<Value> {
133 if count < min || count > max {
134 Some(Value::Error(ErrorKind::NA))
135 } else {
136 None
137 }
138}
139
140#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn list_functions_matches_registry() {
148 let registry = Registry::new();
149 let listed: Vec<(&str, &FunctionMeta)> = registry.list_functions().collect();
150 assert!(!listed.is_empty(), "registry should expose at least one function");
151 for (name, _meta) in &listed {
153 assert!(
154 registry.get(name).is_some(),
155 "listed function {name} not found via get()"
156 );
157 }
158 assert_eq!(listed.len(), registry.metadata.len());
160 }
161}