1use super::explanation::{ExplanationNode, ValueSource};
7use super::operations::{ComputationKind, OperationKind, OperationResult, VetoType};
8use crate::computation::{arithmetic_operation, comparison_operation};
9use crate::planning::semantics::{
10 negated_comparison, Expression, ExpressionKind, LiteralValue, MathematicalComputation,
11 ValueKind,
12};
13use crate::planning::ExecutableRule;
14use std::collections::{HashMap, HashSet};
15use std::sync::Arc;
16
17fn get_explanation_node_required(
21 context: &crate::evaluation::EvaluationContext,
22 expr: &Expression,
23 operand_name: &str,
24) -> ExplanationNode {
25 let loc = expr
26 .source_location
27 .as_ref()
28 .expect("BUG: expression missing source in evaluation");
29 context
30 .get_explanation_node(expr)
31 .cloned()
32 .unwrap_or_else(|| {
33 unreachable!(
34 "BUG: {} was evaluated but has no explanation node ({}:{}:{})",
35 operand_name, loc.attribute, loc.span.line, loc.span.col
36 )
37 })
38}
39
40fn expr_ptr(expr: &Expression) -> usize {
41 expr as *const Expression as usize
42}
43
44fn get_operand_result(
46 results: &HashMap<usize, OperationResult>,
47 expr: &Expression,
48 operand_name: &str,
49) -> OperationResult {
50 let loc = expr
51 .source_location
52 .as_ref()
53 .expect("BUG: expression missing source in evaluation");
54 results.get(&expr_ptr(expr)).cloned().unwrap_or_else(|| {
55 unreachable!(
56 "BUG: {} operand was marked ready but result is missing ({}:{}:{})",
57 operand_name, loc.attribute, loc.span.line, loc.span.col
58 )
59 })
60}
61
62fn unwrap_value_after_veto_check<'a>(
66 result: &'a OperationResult,
67 operand_name: &str,
68 source_location: &Option<crate::planning::semantics::Source>,
69) -> &'a LiteralValue {
70 result.value().unwrap_or_else(|| {
71 let loc = source_location
72 .as_ref()
73 .expect("BUG: expression missing source in evaluation");
74 unreachable!(
75 "BUG: {} passed Veto check but has no value ({}:{}:{})",
76 operand_name, loc.attribute, loc.span.line, loc.span.col
77 )
78 })
79}
80
81fn propagate_veto_explanation(
83 context: &mut crate::evaluation::EvaluationContext,
84 current: &Expression,
85 vetoed_operand: &Expression,
86 veto_result: OperationResult,
87 operand_name: &str,
88) -> OperationResult {
89 let node = get_explanation_node_required(context, vetoed_operand, operand_name);
90 context.set_explanation_node(current, node);
91 veto_result
92}
93
94pub(crate) fn evaluate_rule(
98 exec_rule: &ExecutableRule,
99 context: &mut crate::evaluation::EvaluationContext,
100) -> (OperationResult, crate::evaluation::explanation::Explanation) {
101 use crate::evaluation::explanation::{Branch, NonMatchedBranch};
102
103 if exec_rule.branches.len() == 1 {
105 return evaluate_rule_without_unless(exec_rule, context);
106 }
107
108 let mut non_matched_branches: Vec<NonMatchedBranch> = Vec::new();
110
111 for branch_index in (1..exec_rule.branches.len()).rev() {
113 let branch = &exec_rule.branches[branch_index];
114 if let Some(ref condition) = branch.condition {
115 let condition_result = evaluate_expression(condition, context);
116 let condition_explanation =
117 get_explanation_node_required(context, condition, "condition");
118
119 let matched = match condition_result {
120 OperationResult::Veto(ref reason) => {
121 let unless_clause_index = branch_index - 1;
123 context.push_operation(OperationKind::RuleBranchEvaluated {
124 index: Some(unless_clause_index),
125 matched: true,
126 result_value: Some(OperationResult::Veto(reason.clone())),
127 });
128
129 let matched_branch = Branch {
131 condition: Some(Box::new(condition_explanation)),
132 result: Box::new(ExplanationNode::Veto {
133 message: Some(reason.to_string()),
134 source_location: branch.result.source_location.clone(),
135 }),
136 clause_index: Some(unless_clause_index),
137 source_location: Some(branch.source.clone()),
138 };
139
140 let branches_node = ExplanationNode::Branches {
141 matched: Box::new(matched_branch),
142 non_matched: non_matched_branches,
143 source_location: Some(exec_rule.source.clone()),
144 };
145
146 let explanation = crate::evaluation::explanation::Explanation {
147 rule_path: exec_rule.path.clone(),
148 source: Some(exec_rule.source.clone()),
149 result: OperationResult::Veto(reason.clone()),
150 tree: Arc::new(branches_node),
151 };
152 return (OperationResult::Veto(reason.clone()), explanation);
153 }
154 OperationResult::Value(lit) => match &lit.value {
155 ValueKind::Boolean(b) => *b,
156 _ => {
157 let veto = OperationResult::Veto(VetoType::computation(
158 "Unless condition must evaluate to boolean",
159 ));
160 let explanation = crate::evaluation::explanation::Explanation {
161 rule_path: exec_rule.path.clone(),
162 source: Some(exec_rule.source.clone()),
163 result: veto.clone(),
164 tree: Arc::new(ExplanationNode::Veto {
165 message: Some(
166 "Unless condition must evaluate to boolean".to_string(),
167 ),
168 source_location: Some(exec_rule.source.clone()),
169 }),
170 };
171 return (veto, explanation);
172 }
173 },
174 };
175
176 let unless_clause_index = branch_index - 1;
177
178 if matched {
179 let result = evaluate_expression(&branch.result, context);
181
182 context.push_operation(OperationKind::RuleBranchEvaluated {
183 index: Some(unless_clause_index),
184 matched: true,
185 result_value: Some(result.clone()),
186 });
187
188 let result_explanation =
189 get_explanation_node_required(context, &branch.result, "result");
190
191 let matched_branch = Branch {
193 condition: Some(Box::new(condition_explanation)),
194 result: Box::new(result_explanation),
195 clause_index: Some(unless_clause_index),
196 source_location: Some(branch.source.clone()),
197 };
198
199 let branches_node = ExplanationNode::Branches {
200 matched: Box::new(matched_branch),
201 non_matched: non_matched_branches,
202 source_location: Some(exec_rule.source.clone()),
203 };
204
205 let explanation = crate::evaluation::explanation::Explanation {
206 rule_path: exec_rule.path.clone(),
207 source: Some(exec_rule.source.clone()),
208 result: result.clone(),
209 tree: Arc::new(branches_node),
210 };
211 return (result, explanation);
212 }
213 context.push_operation(OperationKind::RuleBranchEvaluated {
215 index: Some(unless_clause_index),
216 matched: false,
217 result_value: None,
218 });
219
220 non_matched_branches.push(NonMatchedBranch {
221 condition: Box::new(condition_explanation),
222 result: None,
223 clause_index: Some(unless_clause_index),
224 source_location: Some(branch.source.clone()),
225 });
226 }
227 }
228
229 let default_branch = &exec_rule.branches[0];
231 let default_result = evaluate_expression(&default_branch.result, context);
232
233 context.push_operation(OperationKind::RuleBranchEvaluated {
234 index: None,
235 matched: true,
236 result_value: Some(default_result.clone()),
237 });
238
239 let default_result_explanation =
240 get_explanation_node_required(context, &default_branch.result, "default result");
241
242 let matched_branch = Branch {
244 condition: None,
245 result: Box::new(default_result_explanation),
246 clause_index: None,
247 source_location: Some(default_branch.source.clone()),
248 };
249
250 let branches_node = ExplanationNode::Branches {
251 matched: Box::new(matched_branch),
252 non_matched: non_matched_branches,
253 source_location: Some(exec_rule.source.clone()),
254 };
255
256 let explanation = crate::evaluation::explanation::Explanation {
257 rule_path: exec_rule.path.clone(),
258 source: Some(exec_rule.source.clone()),
259 result: default_result.clone(),
260 tree: Arc::new(branches_node),
261 };
262
263 (default_result, explanation)
264}
265
266fn evaluate_rule_without_unless(
268 exec_rule: &ExecutableRule,
269 context: &mut crate::evaluation::EvaluationContext,
270) -> (OperationResult, crate::evaluation::explanation::Explanation) {
271 let default_branch = &exec_rule.branches[0];
272 let default_result = evaluate_expression(&default_branch.result, context);
273
274 context.push_operation(OperationKind::RuleBranchEvaluated {
275 index: None,
276 matched: true,
277 result_value: Some(default_result.clone()),
278 });
279
280 let root_explanation_node =
281 get_explanation_node_required(context, &default_branch.result, "default result");
282
283 let explanation = crate::evaluation::explanation::Explanation {
284 rule_path: exec_rule.path.clone(),
285 source: Some(exec_rule.source.clone()),
286 result: default_result.clone(),
287 tree: Arc::new(root_explanation_node),
288 };
289
290 (default_result, explanation)
291}
292
293fn collect_postorder(root: &Expression) -> Vec<&Expression> {
300 enum Visit<'a> {
301 Enter(&'a Expression),
302 Exit(&'a Expression),
303 }
304
305 let mut stack: Vec<Visit<'_>> = vec![Visit::Enter(root)];
306 let mut seen: HashSet<usize> = HashSet::new();
307 let mut nodes: Vec<&Expression> = Vec::new();
308
309 while let Some(visit) = stack.pop() {
310 match visit {
311 Visit::Enter(e) => {
312 if !seen.insert(expr_ptr(e)) {
313 continue;
314 }
315 stack.push(Visit::Exit(e));
316 match &e.kind {
317 ExpressionKind::Arithmetic(left, _, right)
318 | ExpressionKind::Comparison(left, _, right)
319 | ExpressionKind::LogicalAnd(left, right) => {
320 stack.push(Visit::Enter(right));
321 stack.push(Visit::Enter(left));
322 }
323 ExpressionKind::LogicalNegation(operand, _)
324 | ExpressionKind::UnitConversion(operand, _)
325 | ExpressionKind::MathematicalComputation(_, operand)
326 | ExpressionKind::DateCalendar(_, _, operand) => {
327 stack.push(Visit::Enter(operand));
328 }
329 ExpressionKind::DateRelative(_, date_expr, tolerance_expr) => {
330 if let Some(tol) = tolerance_expr {
331 stack.push(Visit::Enter(tol));
332 }
333 stack.push(Visit::Enter(date_expr));
334 }
335 _ => {}
336 }
337 }
338 Visit::Exit(e) => {
339 nodes.push(e);
340 }
341 }
342 }
343
344 nodes
345}
346
347fn evaluate_expression(
348 expr: &Expression,
349 context: &mut crate::evaluation::EvaluationContext,
350) -> OperationResult {
351 let nodes = collect_postorder(expr);
352 let mut results: HashMap<usize, OperationResult> = HashMap::with_capacity(nodes.len());
353
354 for node in &nodes {
355 let result = evaluate_single_expression(node, &results, context);
356 results.insert(expr_ptr(node), result);
357 }
358
359 results.remove(&expr_ptr(expr)).unwrap_or_else(|| {
360 let loc = expr
361 .source_location
362 .as_ref()
363 .expect("BUG: expression missing source in evaluation");
364 unreachable!(
365 "BUG: expression was processed but has no result ({}:{}:{})",
366 loc.attribute, loc.span.start, loc.span.end
367 )
368 })
369}
370
371fn evaluate_single_expression(
375 current: &Expression,
376 results: &HashMap<usize, OperationResult>,
377 context: &mut crate::evaluation::EvaluationContext,
378) -> OperationResult {
379 match ¤t.kind {
380 ExpressionKind::Literal(lit) => {
381 let value = lit.as_ref().clone();
382 let explanation_node = ExplanationNode::Value {
383 value: value.clone(),
384 source: ValueSource::Literal,
385 source_location: current.source_location.clone(),
386 };
387 context.set_explanation_node(current, explanation_node);
388 OperationResult::Value(Box::new(value))
389 }
390
391 ExpressionKind::DataPath(data_path) => {
392 let data_path_clone = data_path.clone();
402 let mut value = context.get_data(data_path).cloned();
403 if value.is_none() {
404 if let Some(resolved) = context.lazy_rule_reference_resolve(data_path) {
405 match resolved {
406 Ok(v) => value = Some(v),
407 Err(veto) => {
408 let explanation_node = ExplanationNode::Veto {
409 message: Some(veto.to_string()),
410 source_location: current.source_location.clone(),
411 };
412 context.set_explanation_node(current, explanation_node);
413 return OperationResult::Veto(veto);
414 }
415 }
416 } else if let Some(veto) = context.get_reference_veto(data_path) {
417 let veto = veto.clone();
418 let explanation_node = ExplanationNode::Veto {
419 message: Some(veto.to_string()),
420 source_location: current.source_location.clone(),
421 };
422 context.set_explanation_node(current, explanation_node);
423 return OperationResult::Veto(veto);
424 }
425 }
426 match value {
427 Some(v) => {
428 context.push_operation(OperationKind::DataUsed {
429 data_ref: data_path_clone.clone(),
430 value: v.clone(),
431 });
432 let explanation_node = ExplanationNode::Value {
433 value: v.clone(),
434 source: ValueSource::Data {
435 data_ref: data_path_clone,
436 },
437 source_location: current.source_location.clone(),
438 };
439 context.set_explanation_node(current, explanation_node);
440 OperationResult::Value(Box::new(v))
441 }
442 None => {
443 let reason = VetoType::MissingData {
444 data: data_path_clone.clone(),
445 };
446 let explanation_node = ExplanationNode::Veto {
447 message: Some(reason.to_string()),
448 source_location: current.source_location.clone(),
449 };
450 context.set_explanation_node(current, explanation_node);
451 OperationResult::Veto(reason)
452 }
453 }
454 }
455
456 ExpressionKind::RulePath(rule_path) => {
457 let rule_path_clone = rule_path.clone();
461 let loc = current
462 .source_location
463 .as_ref()
464 .expect("BUG: expression missing source in evaluation");
465 let r = context.rule_results.get(rule_path).cloned().unwrap_or_else(|| {
466 unreachable!(
467 "BUG: Rule '{}' not found in results during topological-order evaluation ({}:{}:{})",
468 rule_path.rule, loc.attribute, loc.span.line, loc.span.col
469 )
470 });
471
472 context.push_operation(OperationKind::RuleUsed {
473 rule_path: rule_path_clone.clone(),
474 result: r.clone(),
475 });
476
477 let expansion = match context.get_rule_explanation(rule_path) {
479 Some(existing_explanation) => Arc::clone(&existing_explanation.tree),
480 None => Arc::new(ExplanationNode::Value {
481 value: match &r {
482 OperationResult::Value(v) => v.as_ref().clone(),
483 OperationResult::Veto(_) => LiteralValue::from_bool(false),
484 },
485 source: ValueSource::Computed,
486 source_location: current.source_location.clone(),
487 }),
488 };
489
490 let explanation_node = ExplanationNode::RuleReference {
491 rule_path: rule_path_clone,
492 result: r.clone(),
493 source_location: current.source_location.clone(),
494 expansion,
495 };
496 context.set_explanation_node(current, explanation_node);
497 r
498 }
499
500 ExpressionKind::Arithmetic(left, op, right) => {
501 let left_result = get_operand_result(results, left, "left");
502 let right_result = get_operand_result(results, right, "right");
503
504 if let OperationResult::Veto(_) = left_result {
505 return propagate_veto_explanation(
506 context,
507 current,
508 left,
509 left_result,
510 "left operand",
511 );
512 }
513 if let OperationResult::Veto(_) = right_result {
514 return propagate_veto_explanation(
515 context,
516 current,
517 right,
518 right_result,
519 "right operand",
520 );
521 }
522
523 let left_val = unwrap_value_after_veto_check(
524 &left_result,
525 "left operand",
526 ¤t.source_location,
527 );
528 let right_val = unwrap_value_after_veto_check(
529 &right_result,
530 "right operand",
531 ¤t.source_location,
532 );
533
534 let result = arithmetic_operation(left_val, op, right_val);
535
536 let left_explanation = get_explanation_node_required(context, left, "left operand");
537 let right_explanation = get_explanation_node_required(context, right, "right operand");
538
539 if let OperationResult::Value(ref val) = result {
540 let original_expr = format!("{} {} {}", left_val, op, right_val);
541 let substituted_expr = format!("{} {} {}", left_val, op, right_val);
542 context.push_operation(OperationKind::Computation {
543 kind: ComputationKind::Arithmetic(op.clone()),
544 inputs: vec![left_val.clone(), right_val.clone()],
545 result: val.as_ref().clone(),
546 });
547 let explanation_node = ExplanationNode::Computation {
548 kind: ComputationKind::Arithmetic(op.clone()),
549 original_expression: original_expr,
550 expression: substituted_expr,
551 result: val.as_ref().clone(),
552 source_location: current.source_location.clone(),
553 operands: vec![left_explanation, right_explanation],
554 };
555 context.set_explanation_node(current, explanation_node);
556 } else if let OperationResult::Veto(_) = result {
557 context.set_explanation_node(current, left_explanation);
558 }
559 result
560 }
561
562 ExpressionKind::Comparison(left, op, right) => {
563 let left_result = get_operand_result(results, left, "left");
564 let right_result = get_operand_result(results, right, "right");
565
566 if let OperationResult::Veto(_) = left_result {
567 return propagate_veto_explanation(
568 context,
569 current,
570 left,
571 left_result,
572 "left operand",
573 );
574 }
575 if let OperationResult::Veto(_) = right_result {
576 return propagate_veto_explanation(
577 context,
578 current,
579 right,
580 right_result,
581 "right operand",
582 );
583 }
584
585 let left_val = unwrap_value_after_veto_check(
586 &left_result,
587 "left operand",
588 ¤t.source_location,
589 );
590 let right_val = unwrap_value_after_veto_check(
591 &right_result,
592 "right operand",
593 ¤t.source_location,
594 );
595
596 let result = comparison_operation(left_val, op, right_val);
597
598 let left_explanation = get_explanation_node_required(context, left, "left operand");
599 let right_explanation = get_explanation_node_required(context, right, "right operand");
600
601 if let OperationResult::Value(ref val) = result {
602 let is_false = matches!(val.as_ref().value, ValueKind::Boolean(false));
603 let (display_op, original_expr, substituted_expr, display_result) = if is_false {
604 let negated_op = negated_comparison(op.clone());
605 let orig = format!("{} {} {}", left_val, negated_op, right_val);
606 let sub = format!("{} {} {}", left_val, negated_op, right_val);
607 (negated_op, orig, sub, LiteralValue::from_bool(true))
608 } else {
609 let original_expr = format!("{} {} {}", left_val, op, right_val);
610 let substituted_expr = format!("{} {} {}", left_val, op, right_val);
611 (
612 op.clone(),
613 original_expr,
614 substituted_expr,
615 val.as_ref().clone(),
616 )
617 };
618 context.push_operation(OperationKind::Computation {
619 kind: ComputationKind::Comparison(op.clone()),
620 inputs: vec![left_val.clone(), right_val.clone()],
621 result: val.as_ref().clone(),
622 });
623 let explanation_node = ExplanationNode::Computation {
624 kind: ComputationKind::Comparison(display_op),
625 original_expression: original_expr,
626 expression: substituted_expr,
627 result: display_result,
628 source_location: current.source_location.clone(),
629 operands: vec![left_explanation, right_explanation],
630 };
631 context.set_explanation_node(current, explanation_node);
632 } else if let OperationResult::Veto(_) = result {
633 context.set_explanation_node(current, left_explanation);
634 }
635 result
636 }
637
638 ExpressionKind::LogicalAnd(left, right) => {
639 let left_result = get_operand_result(results, left, "left");
640 if let OperationResult::Veto(_) = left_result {
641 return propagate_veto_explanation(
642 context,
643 current,
644 left,
645 left_result,
646 "left operand",
647 );
648 }
649
650 let left_val = unwrap_value_after_veto_check(
651 &left_result,
652 "left operand",
653 ¤t.source_location,
654 );
655 let left_bool = match &left_val.value {
656 ValueKind::Boolean(b) => b,
657 _ => unreachable!(
658 "BUG: logical AND with non-boolean operand; planning should have rejected this"
659 ),
660 };
661
662 if !*left_bool {
663 let left_explanation = get_explanation_node_required(context, left, "left operand");
664 context.set_explanation_node(current, left_explanation);
665 OperationResult::Value(Box::new(LiteralValue::from_bool(false)))
666 } else {
667 let right_result = get_operand_result(results, right, "right");
668 let right_explanation =
669 get_explanation_node_required(context, right, "right operand");
670 context.set_explanation_node(current, right_explanation);
671 right_result
672 }
673 }
674
675 ExpressionKind::LogicalNegation(operand, _) => {
676 let result = get_operand_result(results, operand, "operand");
677 if let OperationResult::Veto(_) = result {
678 return propagate_veto_explanation(context, current, operand, result, "operand");
679 }
680
681 let value = unwrap_value_after_veto_check(&result, "operand", ¤t.source_location);
682 let operand_explanation = get_explanation_node_required(context, operand, "operand");
683 match &value.value {
684 ValueKind::Boolean(b) => {
685 let result_bool = !*b;
686 context.set_explanation_node(current, operand_explanation);
687 OperationResult::Value(Box::new(LiteralValue::from_bool(result_bool)))
688 }
689 _ => unreachable!(
690 "BUG: logical NOT with non-boolean operand; planning should have rejected this"
691 ),
692 }
693 }
694
695 ExpressionKind::UnitConversion(value_expr, target) => {
696 let result = get_operand_result(results, value_expr, "operand");
697 if let OperationResult::Veto(_) = result {
698 return propagate_veto_explanation(context, current, value_expr, result, "operand");
699 }
700
701 let value = unwrap_value_after_veto_check(&result, "operand", ¤t.source_location);
702 let operand_explanation = get_explanation_node_required(context, value_expr, "operand");
703
704 let conversion_result = crate::computation::convert_unit(value, target);
705
706 context.set_explanation_node(current, operand_explanation);
707 conversion_result
708 }
709
710 ExpressionKind::MathematicalComputation(op, operand) => {
711 let result = get_operand_result(results, operand, "operand");
712 if let OperationResult::Veto(_) = result {
713 return propagate_veto_explanation(context, current, operand, result, "operand");
714 }
715
716 let value = unwrap_value_after_veto_check(&result, "operand", ¤t.source_location);
717 let operand_explanation = get_explanation_node_required(context, operand, "operand");
718 let math_result = evaluate_mathematical_operator(op, value, context);
719 context.set_explanation_node(current, operand_explanation);
720 math_result
721 }
722
723 ExpressionKind::Veto(veto_expr) => {
724 let explanation_node = ExplanationNode::Veto {
725 message: veto_expr.message.clone(),
726 source_location: current.source_location.clone(),
727 };
728 context.set_explanation_node(current, explanation_node);
729 OperationResult::Veto(VetoType::UserDefined {
730 message: veto_expr.message.clone(),
731 })
732 }
733
734 ExpressionKind::Now => {
735 let value = context.now().clone();
736 let explanation_node = ExplanationNode::Value {
737 value: value.clone(),
738 source: ValueSource::Computed,
739 source_location: current.source_location.clone(),
740 };
741 context.set_explanation_node(current, explanation_node);
742 OperationResult::Value(Box::new(value))
743 }
744
745 ExpressionKind::DateRelative(kind, date_expr, tolerance_expr) => {
746 let date_result = get_operand_result(results, date_expr, "date operand");
747 if let OperationResult::Veto(_) = date_result {
748 return propagate_veto_explanation(
749 context,
750 current,
751 date_expr,
752 date_result,
753 "date operand",
754 );
755 }
756
757 let date_val = unwrap_value_after_veto_check(
758 &date_result,
759 "date operand",
760 ¤t.source_location,
761 );
762
763 let date_semantic = match &date_val.value {
764 ValueKind::Date(dt) => dt,
765 _ => unreachable!(
766 "BUG: date sugar with non-date operand; planning should have rejected this"
767 ),
768 };
769
770 let now_val = context.now();
771 let now_semantic = match &now_val.value {
772 ValueKind::Date(dt) => dt,
773 _ => unreachable!("BUG: context.now() must be a Date value"),
774 };
775
776 let tolerance = match tolerance_expr {
777 Some(tol_expr) => {
778 let tol_result = get_operand_result(results, tol_expr, "tolerance operand");
779 if let OperationResult::Veto(_) = tol_result {
780 return propagate_veto_explanation(
781 context,
782 current,
783 tol_expr,
784 tol_result,
785 "tolerance operand",
786 );
787 }
788 let tol_val = unwrap_value_after_veto_check(
789 &tol_result,
790 "tolerance operand",
791 ¤t.source_location,
792 );
793 match &tol_val.value {
794 ValueKind::Duration(amount, unit) => Some((*amount, unit.clone())),
795 _ => unreachable!(
796 "BUG: date sugar tolerance with non-duration; planning should have rejected this"
797 ),
798 }
799 }
800 None => None,
801 };
802
803 let result = crate::computation::datetime::compute_date_relative(
804 kind,
805 date_semantic,
806 tolerance.as_ref().map(|(a, u)| (a, u)),
807 now_semantic,
808 );
809
810 let date_explanation =
811 get_explanation_node_required(context, date_expr, "date operand");
812 context.set_explanation_node(current, date_explanation);
813 result
814 }
815
816 ExpressionKind::DateCalendar(kind, unit, date_expr) => {
817 let date_result = get_operand_result(results, date_expr, "date operand");
818 if let OperationResult::Veto(_) = date_result {
819 return propagate_veto_explanation(
820 context,
821 current,
822 date_expr,
823 date_result,
824 "date operand",
825 );
826 }
827
828 let date_val = unwrap_value_after_veto_check(
829 &date_result,
830 "date operand",
831 ¤t.source_location,
832 );
833
834 let date_semantic = match &date_val.value {
835 ValueKind::Date(dt) => dt,
836 _ => unreachable!(
837 "BUG: calendar sugar with non-date operand; planning should have rejected this"
838 ),
839 };
840
841 let now_val = context.now();
842 let now_semantic = match &now_val.value {
843 ValueKind::Date(dt) => dt,
844 _ => unreachable!("BUG: context.now() must be a Date value"),
845 };
846
847 let result = crate::computation::datetime::compute_date_calendar(
848 kind,
849 unit,
850 date_semantic,
851 now_semantic,
852 );
853
854 let date_explanation =
855 get_explanation_node_required(context, date_expr, "date operand");
856 context.set_explanation_node(current, date_explanation);
857 result
858 }
859 }
860}
861
862fn evaluate_mathematical_operator(
863 op: &MathematicalComputation,
864 value: &LiteralValue,
865 context: &mut crate::evaluation::EvaluationContext,
866) -> OperationResult {
867 match &value.value {
868 ValueKind::Number(n) => {
869 use rust_decimal::prelude::ToPrimitive;
870 let float_val = match n.to_f64() {
871 Some(v) => v,
872 None => {
873 return OperationResult::Veto(VetoType::computation(
874 "Cannot convert to float for mathematical operation",
875 ));
876 }
877 };
878
879 let math_result = match op {
880 MathematicalComputation::Sqrt => float_val.sqrt(),
881 MathematicalComputation::Sin => float_val.sin(),
882 MathematicalComputation::Cos => float_val.cos(),
883 MathematicalComputation::Tan => float_val.tan(),
884 MathematicalComputation::Asin => float_val.asin(),
885 MathematicalComputation::Acos => float_val.acos(),
886 MathematicalComputation::Atan => float_val.atan(),
887 MathematicalComputation::Log => float_val.ln(),
888 MathematicalComputation::Exp => float_val.exp(),
889 MathematicalComputation::Abs => {
890 return OperationResult::Value(Box::new(LiteralValue::number_with_type(
891 n.abs(),
892 value.lemma_type.clone(),
893 )));
894 }
895 MathematicalComputation::Floor => {
896 return OperationResult::Value(Box::new(LiteralValue::number_with_type(
897 n.floor(),
898 value.lemma_type.clone(),
899 )));
900 }
901 MathematicalComputation::Ceil => {
902 return OperationResult::Value(Box::new(LiteralValue::number_with_type(
903 n.ceil(),
904 value.lemma_type.clone(),
905 )));
906 }
907 MathematicalComputation::Round => {
908 return OperationResult::Value(Box::new(LiteralValue::number_with_type(
909 n.round(),
910 value.lemma_type.clone(),
911 )));
912 }
913 };
914
915 let decimal_result = match rust_decimal::Decimal::from_f64_retain(math_result) {
916 Some(d) => d,
917 None => {
918 return OperationResult::Veto(VetoType::computation(
919 "Mathematical operation result cannot be represented",
920 ));
921 }
922 };
923
924 let result_value =
925 LiteralValue::number_with_type(decimal_result, value.lemma_type.clone());
926 context.push_operation(OperationKind::Computation {
927 kind: ComputationKind::Mathematical(op.clone()),
928 inputs: vec![value.clone()],
929 result: result_value.clone(),
930 });
931 OperationResult::Value(Box::new(result_value))
932 }
933 _ => unreachable!(
934 "BUG: mathematical operator with non-number operand; planning should have rejected this"
935 ),
936 }
937}