qql_core/params/
formula.rs1use super::value::{bind_value, resolve_param, resolve_positional};
4use crate::ast::Value;
5use crate::ast::filter::FilterExpr;
6use crate::ast::formula::FormulaExpr;
7use crate::ast::looks_like_iso_datetime;
8use crate::error::{QqlError, Span};
9use alloc::format;
10
11pub fn bind_formula<F>(
13 formula: &mut FormulaExpr,
14 lookup: &F,
15 positional: &[Value],
16 bind_filter_fn: &impl Fn(&mut FilterExpr, &F, &[Value]) -> Result<(), QqlError>,
17) -> Result<(), QqlError>
18where
19 F: Fn(&str) -> Option<Value>,
20{
21 match formula {
22 FormulaExpr::Variable { name } => {
23 if let Some(param_name) = name.strip_prefix(':') {
24 let val = resolve_param(param_name, None, lookup)?;
25 *formula = value_to_formula_constant(val, None)?;
26 } else if let Some(idx_str) = name.strip_prefix('?')
27 && let Ok(idx) = idx_str.parse::<usize>()
28 {
29 let val = resolve_positional(idx, None, positional)?;
30 *formula = value_to_formula_constant(val, None)?;
31 }
32 }
33 FormulaExpr::Sum { left, right }
34 | FormulaExpr::Sub { left, right }
35 | FormulaExpr::Mul { left, right }
36 | FormulaExpr::Div { left, right, .. }
37 | FormulaExpr::Pow {
38 base: left,
39 exponent: right,
40 } => {
41 bind_formula(left, lookup, positional, bind_filter_fn)?;
42 bind_formula(right, lookup, positional, bind_filter_fn)?;
43 }
44 FormulaExpr::Neg { operand }
45 | FormulaExpr::Abs { x: operand }
46 | FormulaExpr::Sqrt { x: operand, .. }
47 | FormulaExpr::Log { x: operand, .. }
48 | FormulaExpr::Ln { x: operand, .. }
49 | FormulaExpr::Exp { x: operand }
50 | FormulaExpr::Acosh { x: operand, .. } => {
51 bind_formula(operand, lookup, positional, bind_filter_fn)?;
52 }
53 FormulaExpr::Max { args } | FormulaExpr::Min { args } => {
54 for arg in args {
55 bind_formula(arg, lookup, positional, bind_filter_fn)?;
56 }
57 }
58 FormulaExpr::Decay { x, target, .. } => {
59 bind_formula(x, lookup, positional, bind_filter_fn)?;
60 if let Some(t) = target {
61 bind_formula(t, lookup, positional, bind_filter_fn)?;
62 }
63 }
64 FormulaExpr::Case { cond, then_, else_ } => {
65 bind_filter_fn(cond, lookup, positional)?;
66 bind_formula(then_, lookup, positional, bind_filter_fn)?;
67 bind_formula(else_, lookup, positional, bind_filter_fn)?;
68 }
69 FormulaExpr::MatchCondition { values, .. } => {
70 for v in values {
71 bind_value(v, lookup, positional)?;
72 }
73 }
74 _ => {}
75 }
76 Ok(())
77}
78
79pub fn value_to_formula_constant(val: Value, span: Option<Span>) -> Result<FormulaExpr, QqlError> {
81 match val {
82 Value::Float(f) => Ok(FormulaExpr::Constant { value: f }),
83 Value::Int(i) => Ok(FormulaExpr::Constant { value: i as f64 }),
84 Value::Str(s) => {
85 if looks_like_iso_datetime(&s) {
86 Ok(FormulaExpr::Datetime { value: s })
87 } else if let Ok(f) = s.parse::<f64>() {
88 Ok(FormulaExpr::Constant { value: f })
89 } else {
90 Ok(FormulaExpr::Variable { name: s })
91 }
92 }
93 _ => Err(QqlError::validation(
94 "QQL-BIND-TYPE-MISMATCH",
95 format!("formula parameter cannot be bound to value: {:?}", val),
96 span,
97 )),
98 }
99}