1use crate::ast::*;
7use serde_json::{json, Value as JsonValue};
8use thiserror::Error;
9
10#[derive(Debug, Error, Clone)]
12pub enum CompileError {
13 #[error("Unsupported expression: {0}")]
14 Unsupported(String),
15
16 #[error("Invalid lambda: {0}")]
17 InvalidLambda(String),
18
19 #[error("Parse error: {0}")]
20 ParseError(String),
21}
22
23impl From<crate::parser::ParseError> for CompileError {
24 fn from(e: crate::parser::ParseError) -> Self {
25 CompileError::ParseError(e.to_string())
26 }
27}
28
29#[derive(Debug, Clone, Default)]
31pub struct CompileOptions {
32 pub strict_equality_default: bool,
34 pub emit_safe_division: bool,
36}
37
38#[allow(dead_code)]
40pub struct Compiler {
41 options: CompileOptions,
42}
43
44impl Compiler {
45 pub fn new(options: CompileOptions) -> Self {
47 Self { options }
48 }
49
50 pub fn default_options() -> Self {
52 Self::new(CompileOptions::default())
53 }
54
55 pub fn compile(&self, expr: &Expr) -> Result<JsonValue, CompileError> {
57 match expr {
58 Expr::Literal(lit) => self.compile_literal(lit),
59 Expr::Var(var) => self.compile_var(var),
60 Expr::Binary(binary) => self.compile_binary(binary),
61 Expr::Unary(unary) => self.compile_unary(unary),
62 Expr::Postfix(postfix) => self.compile_postfix(postfix),
63 Expr::If(if_expr) => self.compile_if(if_expr),
64 Expr::Let(let_expr) => self.compile_let(let_expr),
65 Expr::Call(call) => self.compile_call(call),
66 Expr::MethodCall(method_call) => self.compile_method_call(method_call),
67 Expr::Property(prop) => self.compile_property(prop),
68 Expr::Index(idx) => self.compile_index(idx),
69 Expr::Array(items) => self.compile_array(items),
70 Expr::Lambda(lambda) => self.compile_lambda(lambda),
71 Expr::Query(query) => self.compile_query(query),
72 Expr::Template(parts) => self.compile_template(parts),
73 Expr::NullCoalesce(a, b) => self.compile_null_coalesce(a, b),
74 }
75 }
76
77 fn compile_literal(&self, lit: &Literal) -> Result<JsonValue, CompileError> {
78 Ok(match lit {
79 Literal::Null => JsonValue::Null,
80 Literal::Bool(b) => JsonValue::Bool(*b),
81 Literal::Integer(n) => json!(*n),
82 Literal::Float(n) => json!(*n),
83 Literal::String(s) => JsonValue::String(s.clone()),
84 })
85 }
86
87 fn compile_var(&self, var: &VarExpr) -> Result<JsonValue, CompileError> {
88 let path = if var.name.starts_with('/') {
90 &var.name[1..] } else {
92 &var.name
93 };
94
95 let normalized_path = path.replace('/', ".");
97
98 Ok(json!({ "var": normalized_path }))
99 }
100
101 fn compile_binary(&self, binary: &BinaryExpr) -> Result<JsonValue, CompileError> {
102 match binary.op {
104 BinaryOp::And => return self.compile_logical_chain(&binary.left, &binary.right, "and"),
105 BinaryOp::Or => return self.compile_logical_chain(&binary.left, &binary.right, "or"),
106 _ => {}
107 }
108
109 let left = self.compile(&binary.left)?;
110 let right = self.compile(&binary.right)?;
111
112 let op = match binary.op {
113 BinaryOp::Add => "+",
114 BinaryOp::Sub => "-",
115 BinaryOp::Mul => "*",
116 BinaryOp::Div => "/",
117 BinaryOp::Mod => "%",
118 BinaryOp::Pow => {
119 return Ok(json!({ "pow": [left, right] }));
121 }
122 BinaryOp::SafeDivZero => {
123 return Ok(json!({
125 "if": [
126 { "==": [right.clone(), 0] },
127 0,
128 { "/": [left, right] }
129 ]
130 }));
131 }
132 BinaryOp::SafeDivNull => {
133 return Ok(json!({
135 "if": [
136 { "==": [right.clone(), 0] },
137 null,
138 { "/": [left, right] }
139 ]
140 }));
141 }
142 BinaryOp::Eq => "==",
143 BinaryOp::StrictEq => "===",
144 BinaryOp::NotEq => "!=",
145 BinaryOp::StrictNotEq => "!==",
146 BinaryOp::Lt => "<",
147 BinaryOp::Gt => ">",
148 BinaryOp::LtEq => "<=",
149 BinaryOp::GtEq => ">=",
150 BinaryOp::And | BinaryOp::Or => unreachable!(), BinaryOp::In => "in",
152 };
153
154 Ok(json!({ op: [left, right] }))
155 }
156
157 fn compile_logical_chain(&self, left: &Expr, right: &Expr, op: &str) -> Result<JsonValue, CompileError> {
160 let target_op = if op == "and" { BinaryOp::And } else { BinaryOp::Or };
161
162 let mut operands = Vec::new();
163 self.collect_logical_operands(left, target_op, &mut operands)?;
164 self.collect_logical_operands(right, target_op, &mut operands)?;
165
166 Ok(json!({ op: operands }))
167 }
168
169 fn collect_logical_operands(&self, expr: &Expr, target_op: BinaryOp, operands: &mut Vec<JsonValue>) -> Result<(), CompileError> {
171 if let Expr::Binary(binary) = expr {
172 if binary.op == target_op {
173 self.collect_logical_operands(&binary.left, target_op, operands)?;
175 self.collect_logical_operands(&binary.right, target_op, operands)?;
176 return Ok(());
177 }
178 }
179 operands.push(self.compile(expr)?);
181 Ok(())
182 }
183
184 fn compile_unary(&self, unary: &UnaryExpr) -> Result<JsonValue, CompileError> {
185 let expr = self.compile(&unary.expr)?;
186
187 match unary.op {
188 UnaryOp::Not => Ok(json!({ "!": expr })),
189 UnaryOp::Neg => Ok(json!({ "-": [expr] })),
190 UnaryOp::Plus => Ok(expr), }
192 }
193
194 fn compile_postfix(&self, postfix: &PostfixExpr) -> Result<JsonValue, CompileError> {
195 let expr = self.compile(&postfix.expr)?;
196
197 match postfix.op {
198 PostfixOp::Truthy => Ok(json!({ "!!": expr })),
199 }
200 }
201
202 fn compile_if(&self, if_expr: &IfExpr) -> Result<JsonValue, CompileError> {
203 let mut args = Vec::new();
204
205 args.push(self.compile(&if_expr.condition)?);
207 args.push(self.compile(&if_expr.then_branch)?);
208
209 for (cond, then) in &if_expr.else_ifs {
211 args.push(self.compile(cond)?);
212 args.push(self.compile(then)?);
213 }
214
215 if let Some(ref else_branch) = if_expr.else_branch {
217 args.push(self.compile(else_branch)?);
218 }
219
220 Ok(json!({ "if": args }))
221 }
222
223 fn compile_let(&self, let_expr: &LetExpr) -> Result<JsonValue, CompileError> {
224 let compiled_value = self.compile(&let_expr.value)?;
227 self.compile_with_substitution(&let_expr.body, &let_expr.name, &compiled_value)
228 }
229
230 fn compile_with_substitution(&self, expr: &Expr, var_name: &str, replacement: &JsonValue) -> Result<JsonValue, CompileError> {
232 match expr {
233 Expr::Var(v) if v.name == var_name => Ok(replacement.clone()),
234 Expr::Var(v) => self.compile_var(v),
235 Expr::Literal(lit) => self.compile_literal(lit),
236 Expr::Binary(b) => {
237 match b.op {
239 BinaryOp::And => {
240 let mut operands = Vec::new();
241 self.collect_logical_operands_with_sub(&b.left, BinaryOp::And, &mut operands, var_name, replacement)?;
242 self.collect_logical_operands_with_sub(&b.right, BinaryOp::And, &mut operands, var_name, replacement)?;
243 return Ok(json!({ "and": operands }));
244 }
245 BinaryOp::Or => {
246 let mut operands = Vec::new();
247 self.collect_logical_operands_with_sub(&b.left, BinaryOp::Or, &mut operands, var_name, replacement)?;
248 self.collect_logical_operands_with_sub(&b.right, BinaryOp::Or, &mut operands, var_name, replacement)?;
249 return Ok(json!({ "or": operands }));
250 }
251 _ => {}
252 }
253
254 let left = self.compile_with_substitution(&b.left, var_name, replacement)?;
255 let right = self.compile_with_substitution(&b.right, var_name, replacement)?;
256 let op = match b.op {
257 BinaryOp::Add => "+",
258 BinaryOp::Sub => "-",
259 BinaryOp::Mul => "*",
260 BinaryOp::Div => "/",
261 BinaryOp::Mod => "%",
262 BinaryOp::Eq => "==",
263 BinaryOp::StrictEq => "===",
264 BinaryOp::NotEq => "!=",
265 BinaryOp::StrictNotEq => "!==",
266 BinaryOp::Lt => "<",
267 BinaryOp::Gt => ">",
268 BinaryOp::LtEq => "<=",
269 BinaryOp::GtEq => ">=",
270 BinaryOp::In => "in",
271 BinaryOp::Pow => return Ok(json!({ "pow": [left, right] })),
272 BinaryOp::SafeDivZero => {
273 return Ok(json!({
274 "if": [{ "==": [right.clone(), 0] }, 0, { "/": [left, right] }]
275 }));
276 }
277 BinaryOp::SafeDivNull => {
278 return Ok(json!({
279 "if": [{ "==": [right.clone(), 0] }, null, { "/": [left, right] }]
280 }));
281 }
282 BinaryOp::And | BinaryOp::Or => unreachable!(),
283 };
284 Ok(json!({ op: [left, right] }))
285 }
286 Expr::Unary(u) => {
287 let inner = self.compile_with_substitution(&u.expr, var_name, replacement)?;
288 match u.op {
289 UnaryOp::Not => Ok(json!({ "!": inner })),
290 UnaryOp::Neg => Ok(json!({ "-": [inner] })),
291 UnaryOp::Plus => Ok(inner),
292 }
293 }
294 Expr::Call(c) => {
295 let args: Vec<JsonValue> = c.args.iter()
296 .map(|a| self.compile_with_substitution(a, var_name, replacement))
297 .collect::<Result<_, _>>()?;
298 self.compile_call_with_args(&c.function, args)
300 }
301 Expr::If(if_expr) => {
302 let mut args = Vec::new();
303 args.push(self.compile_with_substitution(&if_expr.condition, var_name, replacement)?);
304 args.push(self.compile_with_substitution(&if_expr.then_branch, var_name, replacement)?);
305 for (cond, then) in &if_expr.else_ifs {
306 args.push(self.compile_with_substitution(cond, var_name, replacement)?);
307 args.push(self.compile_with_substitution(then, var_name, replacement)?);
308 }
309 if let Some(ref else_branch) = if_expr.else_branch {
310 args.push(self.compile_with_substitution(else_branch, var_name, replacement)?);
311 }
312 Ok(json!({ "if": args }))
313 }
314 Expr::Array(items) => {
315 let compiled: Vec<JsonValue> = items.iter()
316 .map(|i| self.compile_with_substitution(i, var_name, replacement))
317 .collect::<Result<_, _>>()?;
318 Ok(JsonValue::Array(compiled))
319 }
320 _ => self.compile(expr),
322 }
323 }
324
325 fn collect_logical_operands_with_sub(
327 &self,
328 expr: &Expr,
329 target_op: BinaryOp,
330 operands: &mut Vec<JsonValue>,
331 var_name: &str,
332 replacement: &JsonValue,
333 ) -> Result<(), CompileError> {
334 if let Expr::Binary(binary) = expr {
335 if binary.op == target_op {
336 self.collect_logical_operands_with_sub(&binary.left, target_op, operands, var_name, replacement)?;
337 self.collect_logical_operands_with_sub(&binary.right, target_op, operands, var_name, replacement)?;
338 return Ok(());
339 }
340 }
341 operands.push(self.compile_with_substitution(expr, var_name, replacement)?);
342 Ok(())
343 }
344
345 fn compile_call_with_args(&self, function: &str, args: Vec<JsonValue>) -> Result<JsonValue, CompileError> {
347 match function {
348 "min" => Ok(json!({ "min": args })),
349 "max" => Ok(json!({ "max": args })),
350 "sum" => Ok(json!({ "+": args })),
351 "abs" => {
352 if args.len() != 1 {
353 return Err(CompileError::Unsupported("abs requires 1 argument".into()));
354 }
355 let x = &args[0];
356 Ok(json!({ "if": [{ "<": [x, 0] }, { "-": [0, x] }, x] }))
357 }
358 "clamp" => {
359 if args.len() != 3 {
360 return Err(CompileError::Unsupported("clamp requires 3 arguments (min, max, value)".into()));
361 }
362 Ok(json!({ "max": [args[0].clone(), { "min": [args[1].clone(), args[2].clone()] }] }))
363 }
364 "floor" => Ok(json!({ "floor": args })),
365 "ceil" => Ok(json!({ "ceil": args })),
366 "round" => Ok(json!({ "round": args })),
367 "sqrt" => Ok(json!({ "sqrt": args })),
368 "pow" => Ok(json!({ "pow": args })),
369 "len" | "length" | "count" => Ok(json!({ "count": args })),
370 "substr" | "substring" => Ok(json!({ "substr": args })),
371 "cat" | "concat" => Ok(json!({ "cat": args })),
372 "upper" => Ok(json!({ "upper": args })),
373 "lower" => Ok(json!({ "lower": args })),
374 "merge" => Ok(json!({ "merge": args })),
375 other => Ok(json!({ other: args })),
376 }
377 }
378
379 fn compile_call(&self, call: &CallExpr) -> Result<JsonValue, CompileError> {
380 let args: Vec<JsonValue> = call.args.iter()
381 .map(|a| self.compile(a))
382 .collect::<Result<_, _>>()?;
383
384 match call.function.as_str() {
386 "min" => Ok(json!({ "min": args })),
388 "max" => Ok(json!({ "max": args })),
389 "sum" => {
390 Ok(json!({ "+": args }))
392 }
393
394 "abs" => {
396 if args.len() != 1 {
397 return Err(CompileError::Unsupported("abs requires 1 argument".into()));
398 }
399 let x = &args[0];
401 Ok(json!({
402 "if": [
403 { "<": [x, 0] },
404 { "-": [0, x] },
405 x
406 ]
407 }))
408 }
409 "round" => {
410 Ok(json!({ "round": args }))
412 }
413 "floor" => Ok(json!({ "floor": args })),
414 "ceil" => Ok(json!({ "ceil": args })),
415 "pow" => {
416 if args.len() != 2 {
417 return Err(CompileError::Unsupported("pow requires 2 arguments".into()));
418 }
419 Ok(json!({ "pow": args }))
420 }
421 "sqrt" => {
422 if args.len() != 1 {
423 return Err(CompileError::Unsupported("sqrt requires 1 argument".into()));
424 }
425 Ok(json!({ "sqrt": args }))
426 }
427
428 "clamp" => {
430 if args.len() != 3 {
431 return Err(CompileError::Unsupported("clamp requires 3 arguments (min, max, value)".into()));
432 }
433 let min_val = &args[0];
434 let max_val = &args[1];
435 let value = &args[2];
436 Ok(json!({
438 "max": [
439 min_val,
440 { "min": [max_val, value] }
441 ]
442 }))
443 }
444
445 "safe_div" => {
447 if args.len() < 2 || args.len() > 3 {
448 return Err(CompileError::Unsupported("safe_div requires 2-3 arguments".into()));
449 }
450 let a = &args[0];
451 let b = &args[1];
452 let default_val = if args.len() > 2 {
453 args[2].clone()
454 } else {
455 json!(0)
456 };
457 Ok(json!({
458 "if": [
459 { "==": [b, 0] },
460 default_val,
461 { "/": [a, b] }
462 ]
463 }))
464 }
465
466 "cat" => Ok(json!({ "cat": args })),
468 "substr" => Ok(json!({ "substr": args })),
469 "len" | "length" => {
470 if args.len() != 1 {
471 return Err(CompileError::Unsupported("len requires 1 argument".into()));
472 }
473 Ok(json!({ "len": args }))
475 }
476
477 "merge" => Ok(json!({ "merge": args })),
479 "count" => {
480 if args.len() != 1 {
481 return Err(CompileError::Unsupported("count requires 1 argument".into()));
482 }
483 Ok(json!({ "count": args }))
484 }
485
486 "missing" => Ok(json!({ "missing": args })),
488 "missing_some" => {
489 if args.len() != 2 {
490 return Err(CompileError::Unsupported("missing_some requires 2 arguments".into()));
491 }
492 Ok(json!({ "missing_some": args }))
493 }
494
495 "log" => {
497 if args.len() != 1 {
498 return Err(CompileError::Unsupported("log requires 1 argument".into()));
499 }
500 Ok(json!({ "log": args[0] }))
501 }
502
503 "contains" => {
505 if args.len() != 2 {
506 return Err(CompileError::Unsupported("contains requires 2 arguments".into()));
507 }
508 Ok(json!({ "in": [&args[1], &args[0]] }))
510 }
511
512 other => Ok(json!({ other: args })),
514 }
515 }
516
517 fn compile_method_call(&self, method: &MethodCallExpr) -> Result<JsonValue, CompileError> {
518 let object = self.compile(&method.object)?;
519 let args: Vec<JsonValue> = method.args.iter()
520 .map(|a| self.compile(a))
521 .collect::<Result<_, _>>()?;
522
523 match method.method.as_str() {
524 "map" => {
526 if args.len() != 1 {
527 return Err(CompileError::Unsupported("map requires 1 argument".into()));
528 }
529 Ok(json!({ "map": [object, args[0]] }))
530 }
531 "filter" => {
532 if args.len() != 1 {
533 return Err(CompileError::Unsupported("filter requires 1 argument".into()));
534 }
535 Ok(json!({ "filter": [object, args[0]] }))
536 }
537 "reduce" => {
538 if args.len() != 2 {
539 return Err(CompileError::Unsupported("reduce requires 2 arguments (initial, reducer)".into()));
540 }
541 Ok(json!({ "reduce": [object, args[1], args[0]] }))
542 }
543 "all" => {
544 if args.len() != 1 {
545 return Err(CompileError::Unsupported("all requires 1 argument".into()));
546 }
547 Ok(json!({ "all": [object, args[0]] }))
548 }
549 "some" => {
550 if args.len() != 1 {
551 return Err(CompileError::Unsupported("some requires 1 argument".into()));
552 }
553 Ok(json!({ "some": [object, args[0]] }))
554 }
555 "none" => {
556 if args.len() != 1 {
557 return Err(CompileError::Unsupported("none requires 1 argument".into()));
558 }
559 Ok(json!({ "none": [object, args[0]] }))
560 }
561 "contains" => {
562 if args.len() != 1 {
563 return Err(CompileError::Unsupported("contains requires 1 argument".into()));
564 }
565 Ok(json!({ "in": [args[0], object] }))
567 }
568
569 "substr" => {
571 let mut substr_args = vec![object];
572 substr_args.extend(args);
573 Ok(json!({ "substr": substr_args }))
574 }
575 "length" | "len" => {
576 Ok(json!({ "len": [object] }))
577 }
578
579 other => {
581 let mut all_args = vec![object];
582 all_args.extend(args);
583 Ok(json!({ other: all_args }))
584 }
585 }
586 }
587
588 fn compile_property(&self, prop: &PropertyExpr) -> Result<JsonValue, CompileError> {
589 let path = self.build_property_path(&prop.object, &prop.property)?;
592 Ok(json!({ "var": path }))
593 }
594
595 fn build_property_path(&self, object: &Expr, property: &str) -> Result<String, CompileError> {
596 match object {
597 Expr::Var(v) => {
598 let base = if v.name.starts_with('/') {
599 v.name[1..].replace('/', ".")
600 } else {
601 v.name.clone()
602 };
603 Ok(format!("{}.{}", base, property))
604 }
605 Expr::Property(p) => {
606 let base = self.build_property_path(&p.object, &p.property)?;
607 Ok(format!("{}.{}", base, property))
608 }
609 _ => {
610 Err(CompileError::Unsupported(
613 "Complex property access not yet supported".into()
614 ))
615 }
616 }
617 }
618
619 fn compile_index(&self, idx: &IndexExpr) -> Result<JsonValue, CompileError> {
620 match (&idx.object, &idx.index) {
624 (Expr::Var(v), Expr::Literal(Literal::Integer(n))) => {
625 let path = if v.name.starts_with('/') {
626 v.name[1..].replace('/', ".")
627 } else {
628 v.name.clone()
629 };
630 Ok(json!({ "var": format!("{}.{}", path, n) }))
631 }
632 _ => {
633 let arr = self.compile(&idx.object)?;
635 let index = self.compile(&idx.index)?;
636 Ok(json!({ "index": [arr, index] }))
637 }
638 }
639 }
640
641 fn compile_array(&self, items: &[Expr]) -> Result<JsonValue, CompileError> {
642 let compiled: Vec<JsonValue> = items.iter()
643 .map(|i| self.compile(i))
644 .collect::<Result<_, _>>()?;
645 Ok(JsonValue::Array(compiled))
646 }
647
648 fn compile_lambda(&self, lambda: &LambdaExpr) -> Result<JsonValue, CompileError> {
649 if lambda.params.len() == 1 {
657 let body = self.transform_lambda_body(&lambda.body, &lambda.params[0], "")?;
659 Ok(body)
660 } else if lambda.params.len() == 2 {
661 let body = self.transform_reduce_lambda(&lambda.body, &lambda.params[0], &lambda.params[1])?;
664 Ok(body)
665 } else {
666 Err(CompileError::InvalidLambda(
667 "Lambdas must have 1 or 2 parameters".into()
668 ))
669 }
670 }
671
672 fn transform_lambda_body(&self, expr: &Expr, param: &str, replacement: &str) -> Result<JsonValue, CompileError> {
673 match expr {
674 Expr::Var(v) if v.name == param => {
675 Ok(json!({ "var": replacement }))
676 }
677 Expr::Var(v) => {
678 self.compile_var(v)
680 }
681 Expr::Literal(lit) => self.compile_literal(lit),
682 Expr::Binary(b) => {
683 let left = self.transform_lambda_body(&b.left, param, replacement)?;
684 let right = self.transform_lambda_body(&b.right, param, replacement)?;
685 let op = match b.op {
686 BinaryOp::Add => "+",
687 BinaryOp::Sub => "-",
688 BinaryOp::Mul => "*",
689 BinaryOp::Div => "/",
690 BinaryOp::Mod => "%",
691 BinaryOp::Eq => "==",
692 BinaryOp::StrictEq => "===",
693 BinaryOp::NotEq => "!=",
694 BinaryOp::StrictNotEq => "!==",
695 BinaryOp::Lt => "<",
696 BinaryOp::Gt => ">",
697 BinaryOp::LtEq => "<=",
698 BinaryOp::GtEq => ">=",
699 BinaryOp::And => "and",
700 BinaryOp::Or => "or",
701 BinaryOp::In => "in",
702 BinaryOp::Pow => "pow",
703 BinaryOp::SafeDivZero | BinaryOp::SafeDivNull => {
704 return self.compile_binary(&BinaryExpr {
705 left: b.left.clone(),
706 op: b.op,
707 right: b.right.clone(),
708 span: b.span,
709 });
710 }
711 };
712 Ok(json!({ op: [left, right] }))
713 }
714 Expr::Unary(u) => {
715 let inner = self.transform_lambda_body(&u.expr, param, replacement)?;
716 match u.op {
717 UnaryOp::Not => Ok(json!({ "!": inner })),
718 UnaryOp::Neg => Ok(json!({ "-": [inner] })),
719 UnaryOp::Plus => Ok(inner),
720 }
721 }
722 Expr::Call(c) => {
723 let args: Vec<JsonValue> = c.args.iter()
724 .map(|a| self.transform_lambda_body(a, param, replacement))
725 .collect::<Result<_, _>>()?;
726 match c.function.as_str() {
727 "min" => Ok(json!({ "min": args })),
728 "max" => Ok(json!({ "max": args })),
729 other => Ok(json!({ other: args })),
730 }
731 }
732 _ => {
733 self.compile(expr)
736 }
737 }
738 }
739
740 fn transform_reduce_lambda(&self, expr: &Expr, acc_param: &str, cur_param: &str) -> Result<JsonValue, CompileError> {
741 match expr {
742 Expr::Var(v) if v.name == acc_param => {
743 Ok(json!({ "var": "accumulator" }))
744 }
745 Expr::Var(v) if v.name == cur_param => {
746 Ok(json!({ "var": "current" }))
747 }
748 Expr::Var(v) => {
749 self.compile_var(v)
750 }
751 Expr::Literal(lit) => self.compile_literal(lit),
752 Expr::Binary(b) => {
753 let left = self.transform_reduce_lambda(&b.left, acc_param, cur_param)?;
754 let right = self.transform_reduce_lambda(&b.right, acc_param, cur_param)?;
755 let op = match b.op {
756 BinaryOp::Add => "+",
757 BinaryOp::Sub => "-",
758 BinaryOp::Mul => "*",
759 BinaryOp::Div => "/",
760 BinaryOp::Mod => "%",
761 BinaryOp::Eq => "==",
762 BinaryOp::StrictEq => "===",
763 BinaryOp::NotEq => "!=",
764 BinaryOp::StrictNotEq => "!==",
765 BinaryOp::Lt => "<",
766 BinaryOp::Gt => ">",
767 BinaryOp::LtEq => "<=",
768 BinaryOp::GtEq => ">=",
769 BinaryOp::And => "and",
770 BinaryOp::Or => "or",
771 BinaryOp::In => "in",
772 BinaryOp::Pow => "pow",
773 _ => return self.compile(expr),
774 };
775 Ok(json!({ op: [left, right] }))
776 }
777 _ => self.compile(expr),
778 }
779 }
780
781 fn compile_query(&self, query: &QueryExpr) -> Result<JsonValue, CompileError> {
782 let source = self.compile(&query.source)?;
786 let projection = self.compile(&query.projection)?;
787
788 if let Some(ref filter) = query.filter {
789 let filter_compiled = self.compile(filter)?;
790 Ok(json!({
792 "map": [
793 { "filter": [source, filter_compiled] },
794 projection
795 ]
796 }))
797 } else {
798 Ok(json!({ "map": [source, projection] }))
800 }
801 }
802
803 fn compile_template(&self, parts: &[TemplateExpr]) -> Result<JsonValue, CompileError> {
804 let compiled_parts: Vec<JsonValue> = parts.iter()
808 .map(|part| match part {
809 TemplateExpr::Literal(s) => Ok(JsonValue::String(s.clone())),
810 TemplateExpr::Expr(e) => self.compile(e),
811 })
812 .collect::<Result<_, _>>()?;
813
814 Ok(json!({ "cat": compiled_parts }))
815 }
816
817 fn compile_null_coalesce(&self, a: &Expr, b: &Expr) -> Result<JsonValue, CompileError> {
818 let a_compiled = self.compile(a)?;
820 let b_compiled = self.compile(b)?;
821
822 Ok(json!({
823 "if": [
824 { "!=": [a_compiled.clone(), null] },
825 a_compiled,
826 b_compiled
827 ]
828 }))
829 }
830}
831
832#[cfg(test)]
833mod tests {
834 use super::*;
835 use crate::{Lexer, Parser};
836
837 fn compile(source: &str) -> Result<JsonValue, CompileError> {
838 let lexer = Lexer::new(source);
839 let mut parser = Parser::new(lexer);
840 let ast = parser.parse()?;
841 let compiler = Compiler::default_options();
842 compiler.compile(&ast)
843 }
844
845 #[test]
846 fn test_simple_comparison() {
847 let result = compile("x < 10").unwrap();
848 assert_eq!(result, json!({"<": [{"var": "x"}, 10]}));
849 }
850
851 #[test]
852 fn test_and_expression() {
853 let result = compile("a and b").unwrap();
854 assert_eq!(result, json!({"and": [{"var": "a"}, {"var": "b"}]}));
855 }
856
857 #[test]
858 fn test_and_chain_flattening() {
859 let result = compile("a and b and c and d").unwrap();
861 assert_eq!(
862 result,
863 json!({"and": [{"var": "a"}, {"var": "b"}, {"var": "c"}, {"var": "d"}]})
864 );
865 }
866
867 #[test]
868 fn test_or_chain_flattening() {
869 let result = compile("a or b or c").unwrap();
870 assert_eq!(
871 result,
872 json!({"or": [{"var": "a"}, {"var": "b"}, {"var": "c"}]})
873 );
874 }
875
876 #[test]
877 fn test_mixed_and_or_no_flatten() {
878 let result = compile("a and b or c").unwrap();
880 assert_eq!(
882 result,
883 json!({"or": [{"and": [{"var": "a"}, {"var": "b"}]}, {"var": "c"}]})
884 );
885 }
886
887 #[test]
888 fn test_brackets_precedence() {
889 let result = compile("a and (b or c)").unwrap();
891 assert_eq!(
892 result,
893 json!({"and": [{"var": "a"}, {"or": [{"var": "b"}, {"var": "c"}]}]})
894 );
895 }
896
897 #[test]
898 fn test_let_expression() {
899 let result = compile("let x = 5 in x * 2").unwrap();
901 assert_eq!(result, json!({"*": [5, 2]}));
902 }
903
904 #[test]
905 fn test_let_expression_with_var() {
906 let result = compile("let factor = /multiplier in factor * 10").unwrap();
908 assert_eq!(result, json!({"*": [{"var": "multiplier"}, 10]}));
909 }
910
911 #[test]
912 fn test_complex_expression() {
913 let result = compile("alert_acknowledged and time_since_alert_secs < 120").unwrap();
914 assert_eq!(
915 result,
916 json!({
917 "and": [
918 {"var": "alert_acknowledged"},
919 {"<": [{"var": "time_since_alert_secs"}, 120]}
920 ]
921 })
922 );
923 }
924
925 #[test]
926 fn test_clamp() {
927 let result = compile("clamp(0, 100, raw_score)").unwrap();
928 assert_eq!(
929 result,
930 json!({
931 "max": [
932 0,
933 {"min": [100, {"var": "raw_score"}]}
934 ]
935 })
936 );
937 }
938
939 #[test]
940 fn test_if_then_else() {
941 let result = compile("if x > 0 then 1 else 0").unwrap();
942 assert_eq!(
943 result,
944 json!({
945 "if": [
946 {">": [{"var": "x"}, 0]},
947 1,
948 0
949 ]
950 })
951 );
952 }
953
954 #[test]
955 fn test_path_variable() {
956 let result = compile("/users/count").unwrap();
957 assert_eq!(result, json!({"var": "users.count"}));
958 }
959
960 #[test]
961 fn test_safe_division() {
962 let result = compile("a /? b").unwrap();
963 assert_eq!(
964 result,
965 json!({
966 "if": [
967 {"==": [{"var": "b"}, 0]},
968 0,
969 {"/": [{"var": "a"}, {"var": "b"}]}
970 ]
971 })
972 );
973 }
974
975 #[test]
976 fn test_safe_div_function() {
977 let result = compile("safe_div(a, b, 0)").unwrap();
978 assert_eq!(
979 result,
980 json!({
981 "if": [
982 {"==": [{"var": "b"}, 0]},
983 0,
984 {"/": [{"var": "a"}, {"var": "b"}]}
985 ]
986 })
987 );
988 }
989
990 #[test]
991 fn test_in_operator() {
992 let result = compile("x in [1, 2, 3]").unwrap();
993 assert_eq!(
994 result,
995 json!({"in": [{"var": "x"}, [1, 2, 3]]})
996 );
997 }
998
999 #[test]
1000 fn test_method_call() {
1001 let result = compile("items.filter(x => x > 0)").unwrap();
1002 assert_eq!(
1003 result,
1004 json!({
1005 "filter": [
1006 {"var": "items"},
1007 {">": [{"var": ""}, 0]}
1008 ]
1009 })
1010 );
1011 }
1012
1013 #[test]
1014 fn test_contains_method() {
1015 let result = compile("[1, 2, 3].contains(x)").unwrap();
1016 assert_eq!(
1017 result,
1018 json!({"in": [{"var": "x"}, [1, 2, 3]]})
1019 );
1020 }
1021
1022 #[test]
1023 fn test_truthy_postfix() {
1024 let result = compile("x?").unwrap();
1025 assert_eq!(result, json!({"!!": {"var": "x"}}));
1026 }
1027
1028 #[test]
1029 fn test_equality_synonyms() {
1030 let sources = ["a === b", "a is b", "a eq b", "a equals b"];
1032 for source in sources {
1033 let result = compile(source).unwrap();
1034 assert_eq!(
1035 result,
1036 json!({"===": [{"var": "a"}, {"var": "b"}]}),
1037 "Failed for: {}", source
1038 );
1039 }
1040 }
1041
1042 #[test]
1043 fn test_nested_property() {
1044 let result = compile("user.address.city").unwrap();
1045 assert_eq!(result, json!({"var": "user.address.city"}));
1046 }
1047
1048 #[test]
1049 fn test_null_coalesce() {
1050 let result = compile("a ?? b").unwrap();
1051 assert_eq!(
1052 result,
1053 json!({
1054 "if": [
1055 {"!=": [{"var": "a"}, null]},
1056 {"var": "a"},
1057 {"var": "b"}
1058 ]
1059 })
1060 );
1061 }
1062}