1use crate::ast::node::Node;
2use crate::ast::program::Program;
3use crate::functions::{array, json, string, ExprCall, Function};
4use crate::{bail, Context, Result, Value};
5use crate::parser::compile;
6use indexmap::IndexMap;
7use once_cell::sync::Lazy;
8use std::fmt;
9use std::fmt::{Debug, Formatter};
10
11pub fn run(program: Program, ctx: &Context) -> Result<Value> {
13 DEFAULT_ENVIRONMENT.run(program, ctx)
14}
15
16pub fn eval(code: &str, ctx: &Context) -> Result<Value> {
25 DEFAULT_ENVIRONMENT.eval(code, ctx)
26}
27
28#[derive(Default)]
51pub struct Environment<'a> {
52 pub(crate) functions: IndexMap<String, Function<'a>>,
53}
54
55impl Debug for Environment<'_> {
56 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
57 f.debug_struct("ExprEnvironment").finish()
58 }
59}
60
61impl<'a> Environment<'a> {
62 pub fn new() -> Self {
64 let mut p = Self {
65 functions: IndexMap::new(),
66 };
67 string::add_string_functions(&mut p);
68 array::add_array_functions(&mut p);
69 json::add_json_functions(&mut p);
70 p
71 }
72
73 pub fn add_function<F>(&mut self, name: &str, f: F)
94 where
95 F: Fn(ExprCall) -> Result<Value> + 'a + Sync + Send,
96 {
97 self.functions.insert(name.to_string(), Box::new(f));
98 }
99
100 pub fn run(&self, program: Program, ctx: &Context) -> Result<Value> {
102 let mut ctx = ctx.clone();
103 ctx.insert("$env".to_string(), Value::Map(ctx.0.clone()));
104 for (id, expr) in program.lines {
105 ctx.insert(id, self.eval_expr(&ctx, expr)?);
106 }
107 self.eval_expr(&ctx, program.expr)
108 }
109
110 pub fn eval(&self, code: &str, ctx: &Context) -> Result<Value> {
121 let program = compile(code)?;
122 self.run(program, ctx)
123 }
124
125 pub fn eval_expr(&self, ctx: &Context, node: Node) -> Result<Value> {
126 let value = match node {
127 Node::Value(value) => value,
128 Node::Ident(id) => {
129 if let Some(value) = ctx.get(&id) {
130 value.clone()
131 } else if let Some(item) = ctx
132 .get("#")
133 .and_then(|o| o.as_map())
134 .and_then(|m| m.get(&id))
135 {
136 item.clone()
137 } else {
138 bail!("unknown variable: {id}")
139 }
140 }
141 Node::Func {
142 ident,
143 args,
144 predicate,
145 } => {
146 let args = args
147 .into_iter()
148 .map(|e| self.eval_expr(ctx, e))
149 .collect::<Result<_>>()?;
150 self.eval_func(ctx, ident, args, predicate.map(|l| *l))?
151 },
152 Node::Operation {
153 left,
154 operator,
155 right,
156 } => self.eval_operator(ctx, operator, *left, *right)?,
157 Node::Unary { operator, node } => self.eval_unary_operator(ctx, operator, *node)?,
158 Node::Postfix { operator, node } => self.eval_postfix_operator(ctx, operator, *node)?,
159 Node::Array(a) => Value::Array(
160 a.into_iter()
161 .map(|e| self.eval_expr(ctx, e))
162 .collect::<Result<_>>()?,
163 ), Node::Range(start, end) => match (self.eval_expr(ctx, *start)?, self.eval_expr(ctx, *end)?) {
165 (Value::Number(start), Value::Number(end)) => {
166 Value::Array((start..=end).map(Value::Number).collect())
167 }
168 (start, end) => bail!("invalid range: {start:?}..{end:?}"),
169 }
170 };
171 Ok(value)
172 }
173}
174
175pub(crate) static DEFAULT_ENVIRONMENT: Lazy<Environment> = Lazy::new(|| {
176 Environment::new()
177});