1use std::collections::HashMap;
3use std::sync::Arc;
4
5use serde_json::{Number, Value};
6
7use crate::ast::{BindingKind, Command, Expression, Pipeline};
8use crate::error::Error;
9
10pub type Function = dyn Fn(&mut EvalContext, &[Value]) -> Result<Value, Error> + Send + Sync;
12
13#[derive(Clone, Default)]
15pub struct FunctionRegistry {
16 map: Arc<HashMap<String, Arc<Function>>>,
17}
18
19impl FunctionRegistry {
20 pub fn empty() -> Self {
22 Self {
23 map: Arc::new(HashMap::new()),
24 }
25 }
26
27 pub fn builder() -> FunctionRegistryBuilder {
29 FunctionRegistryBuilder::new()
30 }
31
32 pub fn from_builder(builder: FunctionRegistryBuilder) -> Self {
34 builder.build()
35 }
36
37 pub fn get(&self, name: &str) -> Option<Arc<Function>> {
39 self.map.get(name).cloned()
40 }
41
42 pub fn is_empty(&self) -> bool {
44 self.map.is_empty()
45 }
46}
47
48#[derive(Default)]
50pub struct FunctionRegistryBuilder {
51 map: HashMap<String, Arc<Function>>,
52}
53
54impl FunctionRegistryBuilder {
55 pub fn new() -> Self {
57 Self {
58 map: HashMap::new(),
59 }
60 }
61
62 pub fn register<F>(&mut self, name: impl Into<String>, func: F) -> &mut Self
64 where
65 F: Fn(&mut EvalContext, &[Value]) -> Result<Value, Error> + Send + Sync + 'static,
66 {
67 self.map.insert(name.into(), Arc::new(func));
68 self
69 }
70
71 pub fn extend(&mut self, other: &FunctionRegistry) -> &mut Self {
73 for (key, value) in other.map.iter() {
74 self.map.insert(key.clone(), value.clone());
75 }
76 self
77 }
78
79 pub fn build(self) -> FunctionRegistry {
81 FunctionRegistry {
82 map: Arc::new(self.map),
83 }
84 }
85}
86
87pub struct EvalContext {
89 stack: Vec<Value>,
90 root: Value,
91 variables: Vec<HashMap<String, Value>>,
92 functions: FunctionRegistry,
93}
94
95impl EvalContext {
96 pub fn new(data: Value, functions: FunctionRegistry) -> Self {
98 let mut variables = Vec::new();
99 let mut scope = HashMap::new();
100 scope.insert("$".to_string(), data.clone());
101 variables.push(scope);
102
103 Self {
104 stack: vec![data.clone()],
105 root: data,
106 variables,
107 functions,
108 }
109 }
110
111 pub fn function(&self, name: &str) -> Option<Arc<Function>> {
113 self.functions.get(name)
114 }
115
116 pub fn push_scope(&mut self, value: Value) {
118 self.stack.push(value);
119 self.variables.push(self.new_scope());
120 }
121
122 pub fn pop_scope(&mut self) {
124 if self.stack.len() > 1 {
125 self.stack.pop();
126 }
127 if self.variables.len() > 1 {
128 self.variables.pop();
129 }
130 }
131
132 fn new_scope(&self) -> HashMap<String, Value> {
133 let mut scope = HashMap::new();
134 scope.insert("$".to_string(), self.root.clone());
135 scope
136 }
137
138 pub fn eval_pipeline(&mut self, pipeline: &Pipeline) -> Result<Value, Error> {
140 let mut iter = pipeline.commands.iter();
141 let first = iter
142 .next()
143 .ok_or_else(|| Error::render("empty pipeline", None))?;
144 let mut value = self.eval_command(first, None)?;
145
146 for command in iter {
147 value = self.eval_command(command, Some(value))?;
148 }
149
150 Ok(value)
151 }
152
153 fn eval_command(&mut self, command: &Command, input: Option<Value>) -> Result<Value, Error> {
154 if let Expression::Identifier(name) = &command.target {
155 if let Some(func) = self.functions.get(name.as_str()) {
156 let mut args = Vec::new();
157 for expr in &command.args {
158 args.push(self.eval_expression(expr)?);
159 }
160 if let Some(prev) = input {
161 args.push(prev);
162 }
163 return func(self, &args);
164 } else if !command.args.is_empty() || input.is_some() {
165 return Err(Error::render(format!("unknown function \"{name}\""), None));
166 }
167 }
168
169 if !command.args.is_empty() {
170 return Err(Error::render(
171 "arguments supplied to non-function expression",
172 None,
173 ));
174 }
175
176 if input.is_some() {
177 return Err(Error::render(
178 "cannot pipe value into non-function expression",
179 None,
180 ));
181 }
182
183 self.eval_expression(&command.target)
184 }
185
186 fn eval_expression(&mut self, expr: &Expression) -> Result<Value, Error> {
187 match expr {
188 Expression::Identifier(name) => Ok(self.resolve_identifier(name)),
189 Expression::Field(parts) => self.resolve_field(parts),
190 Expression::Variable(name) => Ok(self.resolve_variable(name)),
191 Expression::PipelineExpr(pipeline) => {
192 if pipeline.declarations.is_some() {
193 return Err(Error::render(
194 "pipeline declarations not allowed in expression",
195 None,
196 ));
197 }
198 self.eval_pipeline(pipeline)
199 }
200 Expression::StringLiteral(value) => Ok(Value::String(value.clone())),
201 Expression::NumberLiteral(text) => parse_number(text)
202 .map(Value::Number)
203 .ok_or_else(|| Error::render(format!("invalid number literal {text}"), None)),
204 Expression::BoolLiteral(flag) => Ok(Value::Bool(*flag)),
205 Expression::Nil => Ok(Value::Null),
206 }
207 }
208
209 fn resolve_identifier(&self, name: &str) -> Value {
210 for value in self.stack.iter().rev() {
211 if let Value::Object(map) = value {
212 if let Some(found) = map.get(name) {
213 return found.clone();
214 }
215 }
216 }
217 Value::Null
218 }
219
220 fn resolve_field(&self, parts: &[String]) -> Result<Value, Error> {
221 if parts.is_empty() {
222 return self
223 .stack
224 .last()
225 .cloned()
226 .ok_or_else(|| Error::render("dot resolution failed", None));
227 }
228 if let Some(first) = parts.first() {
229 if first.starts_with('$') {
230 let mut value = self.resolve_variable(first);
231 for part in parts.iter().skip(1) {
232 value = Self::project_field_segment(value, part)?;
233 }
234 return Ok(value);
235 }
236 }
237
238 let mut value = self
239 .stack
240 .last()
241 .cloned()
242 .ok_or_else(|| Error::render("dot resolution failed", None))?;
243
244 for part in parts {
245 value = Self::project_field_segment(value, part)?;
246 }
247
248 Ok(value)
249 }
250
251 fn resolve_variable(&self, name: &str) -> Value {
252 if name == "$" {
253 return self.root.clone();
254 }
255
256 for scope in self.variables.iter().rev() {
257 if let Some(value) = scope.get(name) {
258 return value.clone();
259 }
260 }
261
262 Value::Null
263 }
264
265 fn set_variable(&mut self, name: &str, kind: BindingKind, value: Value) -> Result<(), Error> {
266 if name == "$" {
267 return Err(Error::render("cannot assign to root variable", None));
268 }
269
270 match kind {
271 BindingKind::Declare => {
272 self.variables
273 .last_mut()
274 .expect("scope stack is non-empty")
275 .insert(name.to_string(), value);
276 Ok(())
277 }
278 BindingKind::Assign => {
279 for scope in self.variables.iter_mut().rev() {
280 if scope.contains_key(name) {
281 scope.insert(name.to_string(), value);
282 return Ok(());
283 }
284 }
285 Err(Error::render(format!("variable {name} not defined"), None))
286 }
287 }
288 }
289
290 fn project_field_segment(value: Value, part: &str) -> Result<Value, Error> {
291 match value {
292 Value::Object(map) => Ok(map.get(part).cloned().unwrap_or(Value::Null)),
293 Value::Array(list) => {
294 let index = part.parse::<usize>().map_err(|_| {
295 Error::render(format!("array index must be integer, got {part}"), None)
296 })?;
297 Ok(list.get(index).cloned().unwrap_or(Value::Null))
298 }
299 _ => Err(Error::render(
300 format!("cannot access field {part} on non-container value"),
301 None,
302 )),
303 }
304 }
305
306 pub(crate) fn apply_bindings(
307 &mut self,
308 pipeline: &Pipeline,
309 value: &Value,
310 ) -> Result<(), Error> {
311 if let Some(decls) = &pipeline.declarations {
312 if decls.variables.is_empty() {
313 return Ok(());
314 }
315
316 if decls.variables.len() == 1 {
317 self.set_variable(&decls.variables[0], decls.kind, value.clone())?;
318 } else if let Value::Array(items) = value {
319 for (idx, name) in decls.variables.iter().enumerate() {
320 let assigned = items.get(idx).cloned().unwrap_or(Value::Null);
321 self.set_variable(name, decls.kind, assigned)?;
322 }
323 } else {
324 for name in &decls.variables {
325 self.set_variable(name, decls.kind, value.clone())?;
326 }
327 }
328 }
329 Ok(())
330 }
331
332 pub(crate) fn predeclare_bindings(&mut self, pipeline: &Pipeline) {
333 if let Some(decls) = &pipeline.declarations {
334 if decls.kind == BindingKind::Declare {
335 for name in &decls.variables {
336 self.variables
337 .last_mut()
338 .expect("scope stack is non-empty")
339 .entry(name.clone())
340 .or_insert(Value::Null);
341 }
342 }
343 }
344 }
345
346 pub(crate) fn assign_range_bindings(
347 &mut self,
348 pipeline: &Pipeline,
349 key: Option<Value>,
350 value: Value,
351 ) -> Result<(), Error> {
352 if let Some(decls) = &pipeline.declarations {
353 match decls.variables.len() {
354 0 => {}
355 1 => {
356 self.set_variable(&decls.variables[0], decls.kind, value)?;
357 }
358 _ => {
359 let key_value = key.unwrap_or(Value::Null);
360 self.set_variable(&decls.variables[0], decls.kind, key_value)?;
361 if let Some(second) = decls.variables.get(1) {
362 self.set_variable(second, decls.kind, value)?;
363 }
364 }
365 }
366 }
367 Ok(())
368 }
369}
370
371pub fn value_to_string(value: &Value) -> String {
372 match value {
373 Value::Null => String::new(),
374 Value::Bool(b) => b.to_string(),
375 Value::Number(n) => {
376 if let Some(i) = n.as_i64() {
377 i.to_string()
378 } else if let Some(u) = n.as_u64() {
379 u.to_string()
380 } else {
381 let mut s = n.to_string();
382 if s.contains('.') {
383 while s.ends_with('0') {
384 s.pop();
385 }
386 if s.ends_with('.') {
387 s.pop();
388 }
389 }
390 s
391 }
392 }
393 Value::String(s) => s.clone(),
394 Value::Array(_) | Value::Object(_) => serde_json::to_string(value).unwrap_or_default(),
395 }
396}
397
398pub fn parse_number(text: &str) -> Option<Number> {
399 if !text.contains(['.', 'e', 'E']) {
400 if let Ok(value) = text.parse::<i64>() {
401 return Some(Number::from(value));
402 }
403 if let Ok(value) = text.parse::<u64>() {
404 return Some(Number::from(value));
405 }
406 }
407
408 text.parse::<f64>().ok().and_then(Number::from_f64)
409}
410
411pub fn is_empty(value: &Value) -> bool {
412 match value {
413 Value::Null => true,
414 Value::Bool(b) => !*b,
415 Value::Number(n) => {
416 if let Some(i) = n.as_i64() {
417 i == 0
418 } else if let Some(u) = n.as_u64() {
419 u == 0
420 } else {
421 n.as_f64().map(|f| f == 0.0).unwrap_or(false)
422 }
423 }
424 Value::String(s) => s.is_empty(),
425 Value::Array(arr) => arr.iter().all(is_empty),
426 Value::Object(map) => map.is_empty(),
427 }
428}
429
430pub fn is_truthy(value: &Value) -> bool {
431 match value {
432 Value::Null => false,
433 Value::Bool(b) => *b,
434 Value::Number(n) => {
435 if let Some(i) = n.as_i64() {
436 i != 0
437 } else if let Some(u) = n.as_u64() {
438 u != 0
439 } else {
440 n.as_f64().map(|f| f != 0.0).unwrap_or(false)
441 }
442 }
443 Value::String(s) => !s.is_empty(),
444 Value::Array(arr) => !arr.is_empty(),
445 Value::Object(map) => !map.is_empty(),
446 }
447}
448
449pub fn coerce_number(value: &Value) -> Result<f64, Error> {
450 if let Some(i) = value.as_i64() {
451 Ok(i as f64)
452 } else if let Some(u) = value.as_u64() {
453 Ok(u as f64)
454 } else if let Some(f) = value.as_f64() {
455 Ok(f)
456 } else if let Some(s) = value.as_str() {
457 s.parse::<f64>()
458 .map_err(|_| Error::render("cannot convert string to number", None))
459 } else {
460 Err(Error::render("expected numeric value for comparison", None))
461 }
462}