1use crate::ast::node::Node;
2use crate::ast::program::Program;
3use crate::context::ContextScope;
4use crate::functions::{
5 array, bitwise, collection, convert, json, misc, number, string, ExprCall, Function,
6};
7use crate::parser::compile;
8use crate::{bail, ContextProvider, Result, Value};
9use indexmap::IndexMap;
10use std::sync::LazyLock;
11use std::fmt;
12use std::fmt::{Debug, Formatter};
13
14pub fn run(program: &Program, ctx: &dyn ContextProvider) -> Result<Value> {
16 DEFAULT_ENVIRONMENT.run(program, ctx)
17}
18
19pub fn eval(code: &str, ctx: &dyn ContextProvider) -> Result<Value> {
28 DEFAULT_ENVIRONMENT.eval(code, ctx)
29}
30
31pub struct Environment<'a> {
54 pub(crate) functions: IndexMap<String, Function<'a>>,
55}
56
57impl Debug for Environment<'_> {
58 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
59 f.debug_struct("ExprEnvironment").finish()
60 }
61}
62
63impl Default for Environment<'_> {
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69impl<'a> Environment<'a> {
70 pub fn new() -> Self {
72 let mut p = Self {
73 functions: IndexMap::new(),
74 };
75 string::add_string_functions(&mut p);
76 #[cfg(feature = "temporal")]
77 crate::functions::temporal::add_temporal_functions(&mut p);
78 #[cfg(not(feature = "temporal"))]
79 crate::functions::add_disabled_functions(
80 &mut p,
81 "temporal",
82 &["now", "date", "duration", "timezone"],
83 );
84 array::add_array_functions(&mut p);
85 bitwise::add_bitwise_functions(&mut p);
86 collection::add_collection_functions(&mut p);
87 convert::add_conversion_functions(&mut p);
88 json::add_json_functions(&mut p);
89 misc::add_misc_functions(&mut p);
90 number::add_number_functions(&mut p);
91 p
92 }
93
94 pub fn add_function<F>(&mut self, name: &str, f: F)
115 where
116 F: Fn(ExprCall) -> Result<Value> + 'a + Sync + Send,
117 {
118 self.functions.insert(name.to_string(), Box::new(f));
119 }
120
121 pub fn run(&self, program: &Program, ctx: &dyn ContextProvider) -> Result<Value> {
123 let mut ctx = ContextScope::new(ctx);
124 for (id, expr) in &program.lines {
125 ctx.insert(id, self.eval_expr(&ctx, expr)?);
126 }
127 self.eval_expr(&ctx, &program.expr)
128 }
129
130 pub(crate) fn run_with_binding(
131 &self,
132 program: &Program,
133 ctx: &dyn ContextProvider,
134 key: &str,
135 value: Value,
136 ) -> Result<Value> {
137 self.run_with_bindings(program, ctx, [(key, value)])
138 }
139
140 pub(crate) fn run_with_bindings<'b>(
141 &self,
142 program: &Program,
143 ctx: &dyn ContextProvider,
144 bindings: impl IntoIterator<Item = (&'b str, Value)>,
145 ) -> Result<Value> {
146 let mut scope = ContextScope::new(ctx);
147 for (key, value) in bindings {
148 scope.insert(key, value);
149 }
150 self.run(program, &scope)
151 }
152
153 pub fn eval(&self, code: &str, ctx: &dyn ContextProvider) -> Result<Value> {
164 let program = compile(code)?;
165 self.run(&program, ctx)
166 }
167
168 pub fn eval_expr(&self, ctx: &dyn ContextProvider, node: &Node) -> Result<Value> {
169 let value = match node {
170 Node::Value(value) => value.clone(),
171 Node::Ident(id) => {
172 if id == "$env" {
173 Value::Map(ctx.environment().0)
174 } else if let Some(value) = ctx.get(id) {
175 value.clone()
176 } else if let Some(item) = ctx
177 .get("#")
178 .and_then(|o| o.as_map())
179 .and_then(|m| m.get(id))
180 {
181 item.clone()
182 } else {
183 bail!("unknown variable: {id}")
184 }
185 }
186 Node::Func {
187 ident,
188 args,
189 predicate,
190 } => {
191 let args = args
192 .iter()
193 .map(|e| self.eval_expr(ctx, e))
194 .collect::<Result<_>>()?;
195 self.eval_func(ctx, ident, args, predicate.as_deref())?
196 }
197 #[cfg(feature = "regex")]
198 Node::Operation {
199 left,
200 operator,
201 right,
202 compiled_regex,
203 } => self.eval_operator(ctx, operator, left, right, compiled_regex.as_ref())?,
204 #[cfg(not(feature = "regex"))]
205 Node::Operation {
206 left,
207 operator,
208 right,
209 } => self.eval_operator(ctx, operator, left, right)?,
210 Node::Unary { operator, node } => self.eval_unary_operator(ctx, operator, node)?,
211 Node::Postfix { operator, node } => self.eval_postfix_operator(ctx, operator, node)?,
212 Node::Array(a) => Value::Array(
213 a.iter()
214 .map(|e| self.eval_expr(ctx, e))
215 .collect::<Result<_>>()?,
216 ), Node::Range(start, end) => {
218 match (self.eval_expr(ctx, start)?, self.eval_expr(ctx, end)?) {
219 (Value::Integer(start), Value::Integer(end)) => {
220 Value::Array((start..=end).map(Value::Integer).collect())
221 }
222 (start, end) => bail!("invalid range: {start:?}..{end:?}"),
223 }
224 }
225 Node::Conditional { condition, consequent, alternative } => {
226 match self.eval_expr(ctx, condition)? {
227 Value::Bool(true) => self.run(consequent, ctx)?,
228 Value::Bool(false) => self.run(alternative, ctx)?,
229 value => bail!("Invalid condition for if: {value:?}"),
230 }
231 }
232 };
233 Ok(value)
234 }
235}
236
237pub(crate) static DEFAULT_ENVIRONMENT: LazyLock<Environment> = LazyLock::new(Environment::new);