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, temporal, ExprCall, Function,
6};
7use crate::parser::compile;
8use crate::{bail, ContextProvider, Result, Value};
9use indexmap::IndexMap;
10use once_cell::sync::Lazy;
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 temporal::add_temporal_functions(&mut p);
77 array::add_array_functions(&mut p);
78 bitwise::add_bitwise_functions(&mut p);
79 collection::add_collection_functions(&mut p);
80 convert::add_conversion_functions(&mut p);
81 json::add_json_functions(&mut p);
82 misc::add_misc_functions(&mut p);
83 number::add_number_functions(&mut p);
84 p
85 }
86
87 pub fn add_function<F>(&mut self, name: &str, f: F)
108 where
109 F: Fn(ExprCall) -> Result<Value> + 'a + Sync + Send,
110 {
111 self.functions.insert(name.to_string(), Box::new(f));
112 }
113
114 pub fn run(&self, program: &Program, ctx: &dyn ContextProvider) -> Result<Value> {
116 let mut ctx = ContextScope::new(ctx);
117 for (id, expr) in &program.lines {
118 ctx.insert(id, self.eval_expr(&ctx, expr)?);
119 }
120 self.eval_expr(&ctx, &program.expr)
121 }
122
123 pub(crate) fn run_with_binding(
124 &self,
125 program: &Program,
126 ctx: &dyn ContextProvider,
127 key: &str,
128 value: Value,
129 ) -> Result<Value> {
130 self.run_with_bindings(program, ctx, [(key, value)])
131 }
132
133 pub(crate) fn run_with_bindings<'b>(
134 &self,
135 program: &Program,
136 ctx: &dyn ContextProvider,
137 bindings: impl IntoIterator<Item = (&'b str, Value)>,
138 ) -> Result<Value> {
139 let mut scope = ContextScope::new(ctx);
140 for (key, value) in bindings {
141 scope.insert(key, value);
142 }
143 self.run(program, &scope)
144 }
145
146 pub fn eval(&self, code: &str, ctx: &dyn ContextProvider) -> Result<Value> {
157 let program = compile(code)?;
158 self.run(&program, ctx)
159 }
160
161 pub fn eval_expr(&self, ctx: &dyn ContextProvider, node: &Node) -> Result<Value> {
162 let value = match node {
163 Node::Value(value) => value.clone(),
164 Node::Ident(id) => {
165 if id == "$env" {
166 Value::Map(ctx.environment().0)
167 } else if let Some(value) = ctx.get(id) {
168 value.clone()
169 } else if let Some(item) = ctx
170 .get("#")
171 .and_then(|o| o.as_map())
172 .and_then(|m| m.get(id))
173 {
174 item.clone()
175 } else {
176 bail!("unknown variable: {id}")
177 }
178 }
179 Node::Func {
180 ident,
181 args,
182 predicate,
183 } => {
184 let args = args
185 .iter()
186 .map(|e| self.eval_expr(ctx, e))
187 .collect::<Result<_>>()?;
188 self.eval_func(ctx, ident, args, predicate.as_deref())?
189 }
190 Node::Operation {
191 left,
192 operator,
193 right,
194 compiled_regex,
195 } => self.eval_operator(ctx, operator, left, right, compiled_regex.as_ref())?,
196 Node::Unary { operator, node } => self.eval_unary_operator(ctx, operator, node)?,
197 Node::Postfix { operator, node } => self.eval_postfix_operator(ctx, operator, node)?,
198 Node::Array(a) => Value::Array(
199 a.iter()
200 .map(|e| self.eval_expr(ctx, e))
201 .collect::<Result<_>>()?,
202 ), Node::Range(start, end) => {
204 match (self.eval_expr(ctx, start)?, self.eval_expr(ctx, end)?) {
205 (Value::Integer(start), Value::Integer(end)) => {
206 Value::Array((start..=end).map(Value::Integer).collect())
207 }
208 (start, end) => bail!("invalid range: {start:?}..{end:?}"),
209 }
210 }
211 Node::Conditional { condition, consequent, alternative } => {
212 match self.eval_expr(ctx, condition)? {
213 Value::Bool(true) => self.run(consequent, ctx)?,
214 Value::Bool(false) => self.run(alternative, ctx)?,
215 value => bail!("Invalid condition for if: {value:?}"),
216 }
217 }
218 };
219 Ok(value)
220 }
221}
222
223pub(crate) static DEFAULT_ENVIRONMENT: Lazy<Environment> = Lazy::new(Environment::new);