1use super::conversion_explanation::build_conversion_steps;
7use super::explanation::{ExplanationNode, ValueSource};
8use super::operations::{ComputationKind, OperationKind, OperationResult, VetoType};
9use crate::computation::{
10 arithmetic_operation, comparison_operation, convert_unit, UnitResolutionContext,
11};
12use crate::planning::semantics::{
13 negated_comparison, DataPath, Expression, ExpressionKind, LiteralValue,
14 MathematicalComputation, SemanticConversionTarget, Source, ValueKind,
15};
16use crate::planning::ExecutableRule;
17use std::collections::{HashMap, HashSet};
18use std::sync::Arc;
19
20fn get_explanation_node_required(
24 context: &crate::evaluation::EvaluationContext,
25 expr: &Expression,
26 operand_name: &str,
27) -> ExplanationNode {
28 let loc = expr
29 .source_location
30 .as_ref()
31 .expect("BUG: expression missing source in evaluation");
32 context
33 .get_explanation_node(expr)
34 .cloned()
35 .unwrap_or_else(|| {
36 unreachable!(
37 "BUG: {} was evaluated but has no explanation node ({}:{}:{})",
38 operand_name, loc.source_type, loc.span.line, loc.span.col
39 )
40 })
41}
42
43fn expr_ptr(expr: &Expression) -> usize {
44 expr as *const Expression as usize
45}
46
47pub(crate) fn finish_unit_conversion(
49 value: &LiteralValue,
50 target: &SemanticConversionTarget,
51 operand_explanation: ExplanationNode,
52 data_ref: Option<&DataPath>,
53 source_location: Option<Source>,
54 context: &mut crate::evaluation::EvaluationContext,
55) -> (OperationResult, ExplanationNode) {
56 let conversion_result = convert_unit(
57 value,
58 target,
59 UnitResolutionContext::WithIndex(&context.unit_index),
60 );
61
62 match &conversion_result {
63 OperationResult::Value(converted) => {
64 let converted = converted.as_ref();
65 let kind = ComputationKind::UnitConversion {
66 target: target.clone(),
67 };
68 let conversion_steps = build_conversion_steps(
69 value,
70 target,
71 converted,
72 data_ref,
73 UnitResolutionContext::WithIndex(&context.unit_index),
74 );
75 assert!(
76 !conversion_steps.is_empty(),
77 "BUG: unit conversion succeeded but explanation steps are empty"
78 );
79 context.push_operation(OperationKind::Computation {
80 kind: kind.clone(),
81 inputs: vec![value.clone()],
82 result: converted.clone(),
83 });
84 let explanation_node = ExplanationNode::Computation {
85 kind,
86 conversion_steps,
87 original_expression: String::new(),
88 expression: String::new(),
89 result: converted.clone(),
90 source_location: source_location.clone(),
91 operands: vec![operand_explanation],
92 };
93 (conversion_result, explanation_node)
94 }
95 OperationResult::Veto(_) => (conversion_result, operand_explanation),
96 }
97}
98
99fn data_ref_for_conversion_operand<'a>(
100 value_expr: &'a Expression,
101 operand_explanation: &'a ExplanationNode,
102) -> Option<&'a DataPath> {
103 if let ExpressionKind::DataPath(data_path) = &value_expr.kind {
104 return Some(data_path);
105 }
106 match operand_explanation {
107 ExplanationNode::Value {
108 source: ValueSource::Data { data_ref },
109 ..
110 } => Some(data_ref),
111 _ => None,
112 }
113}
114
115fn get_operand_result(
117 results: &HashMap<usize, OperationResult>,
118 expr: &Expression,
119 operand_name: &str,
120) -> OperationResult {
121 let loc = expr
122 .source_location
123 .as_ref()
124 .expect("BUG: expression missing source in evaluation");
125 results.get(&expr_ptr(expr)).cloned().unwrap_or_else(|| {
126 unreachable!(
127 "BUG: {} operand was marked ready but result is missing ({}:{}:{})",
128 operand_name, loc.source_type, loc.span.line, loc.span.col
129 )
130 })
131}
132
133fn unwrap_value_after_veto_check<'a>(
137 result: &'a OperationResult,
138 operand_name: &str,
139 source_location: &Option<crate::planning::semantics::Source>,
140) -> &'a LiteralValue {
141 result.value().unwrap_or_else(|| {
142 let loc = source_location
143 .as_ref()
144 .expect("BUG: expression missing source in evaluation");
145 unreachable!(
146 "BUG: {} passed Veto check but has no value ({}:{}:{})",
147 operand_name, loc.source_type, loc.span.line, loc.span.col
148 )
149 })
150}
151
152fn propagate_veto_explanation(
154 context: &mut crate::evaluation::EvaluationContext,
155 current: &Expression,
156 vetoed_operand: &Expression,
157 veto_result: OperationResult,
158 operand_name: &str,
159) -> OperationResult {
160 let node = get_explanation_node_required(context, vetoed_operand, operand_name);
161 context.set_explanation_node(current, node);
162 veto_result
163}
164
165fn evaluate_branch_dual(
167 branch: &crate::planning::execution_plan::Branch,
168 context: &mut crate::evaluation::EvaluationContext,
169 explanation_operand_name: &str,
170) -> (OperationResult, ExplanationNode) {
171 let authoritative = evaluate_expression(&branch.normalized_result, context);
172 let _ = evaluate_expression(&branch.result, context);
173 let explanation_node =
174 get_explanation_node_required(context, &branch.result, explanation_operand_name);
175 (authoritative, explanation_node)
176}
177
178pub(crate) fn evaluate_rule(
182 exec_rule: &ExecutableRule,
183 context: &mut crate::evaluation::EvaluationContext,
184) -> (OperationResult, crate::evaluation::explanation::Explanation) {
185 use crate::evaluation::explanation::{Branch, NonMatchedBranch};
186
187 if exec_rule.branches.len() == 1 {
189 return evaluate_rule_without_unless(exec_rule, context);
190 }
191
192 let mut non_matched_branches: Vec<NonMatchedBranch> = Vec::new();
194
195 for branch_index in (1..exec_rule.branches.len()).rev() {
197 let branch = &exec_rule.branches[branch_index];
198 if let Some(condition) = branch
199 .normalized_condition
200 .as_ref()
201 .or(branch.condition.as_ref())
202 {
203 let condition_result = evaluate_expression(condition, context);
204 let condition_explanation =
205 get_explanation_node_required(context, condition, "condition");
206
207 let matched = match condition_result {
208 OperationResult::Veto(ref reason) => {
209 let unless_clause_index = branch_index - 1;
211 context.push_operation(OperationKind::RuleBranchEvaluated {
212 index: Some(unless_clause_index),
213 matched: true,
214 result_value: Some(OperationResult::Veto(reason.clone())),
215 });
216
217 let matched_branch = Branch {
219 condition: Some(Box::new(condition_explanation)),
220 result: Box::new(ExplanationNode::Veto {
221 message: Some(reason.to_string()),
222 source_location: branch.result.source_location.clone(),
223 }),
224 clause_index: Some(unless_clause_index),
225 source_location: Some(branch.source.clone()),
226 };
227
228 let branches_node = ExplanationNode::Branches {
229 matched: Box::new(matched_branch),
230 non_matched: non_matched_branches,
231 source_location: Some(exec_rule.source.clone()),
232 };
233
234 let explanation = crate::evaluation::explanation::Explanation {
235 rule_path: exec_rule.path.clone(),
236 source: Some(exec_rule.source.clone()),
237 result: OperationResult::Veto(reason.clone()),
238 tree: Arc::new(branches_node),
239 };
240 return (OperationResult::Veto(reason.clone()), explanation);
241 }
242 OperationResult::Value(lit) => match &lit.value {
243 ValueKind::Boolean(b) => *b,
244 _ => {
245 let veto = OperationResult::Veto(VetoType::computation(
246 "Unless condition must evaluate to boolean",
247 ));
248 let explanation = crate::evaluation::explanation::Explanation {
249 rule_path: exec_rule.path.clone(),
250 source: Some(exec_rule.source.clone()),
251 result: veto.clone(),
252 tree: Arc::new(ExplanationNode::Veto {
253 message: Some(
254 "Unless condition must evaluate to boolean".to_string(),
255 ),
256 source_location: Some(exec_rule.source.clone()),
257 }),
258 };
259 return (veto, explanation);
260 }
261 },
262 };
263
264 let unless_clause_index = branch_index - 1;
265
266 if matched {
267 let (result, result_explanation) = evaluate_branch_dual(branch, context, "result");
268
269 context.push_operation(OperationKind::RuleBranchEvaluated {
270 index: Some(unless_clause_index),
271 matched: true,
272 result_value: Some(result.clone()),
273 });
274
275 let matched_branch = Branch {
277 condition: Some(Box::new(condition_explanation)),
278 result: Box::new(result_explanation),
279 clause_index: Some(unless_clause_index),
280 source_location: Some(branch.source.clone()),
281 };
282
283 let branches_node = ExplanationNode::Branches {
284 matched: Box::new(matched_branch),
285 non_matched: non_matched_branches,
286 source_location: Some(exec_rule.source.clone()),
287 };
288
289 let explanation = crate::evaluation::explanation::Explanation {
290 rule_path: exec_rule.path.clone(),
291 source: Some(exec_rule.source.clone()),
292 result: result.clone(),
293 tree: Arc::new(branches_node),
294 };
295 return (result, explanation);
296 }
297 context.push_operation(OperationKind::RuleBranchEvaluated {
299 index: Some(unless_clause_index),
300 matched: false,
301 result_value: None,
302 });
303
304 non_matched_branches.push(NonMatchedBranch {
305 condition: Box::new(condition_explanation),
306 result: None,
307 clause_index: Some(unless_clause_index),
308 source_location: Some(branch.source.clone()),
309 });
310 }
311 }
312
313 let default_branch = &exec_rule.branches[0];
314 let (default_result, default_result_explanation) =
315 evaluate_branch_dual(default_branch, context, "default result");
316
317 context.push_operation(OperationKind::RuleBranchEvaluated {
318 index: None,
319 matched: true,
320 result_value: Some(default_result.clone()),
321 });
322
323 let matched_branch = Branch {
325 condition: None,
326 result: Box::new(default_result_explanation),
327 clause_index: None,
328 source_location: Some(default_branch.source.clone()),
329 };
330
331 let branches_node = ExplanationNode::Branches {
332 matched: Box::new(matched_branch),
333 non_matched: non_matched_branches,
334 source_location: Some(exec_rule.source.clone()),
335 };
336
337 let explanation = crate::evaluation::explanation::Explanation {
338 rule_path: exec_rule.path.clone(),
339 source: Some(exec_rule.source.clone()),
340 result: default_result.clone(),
341 tree: Arc::new(branches_node),
342 };
343
344 (default_result, explanation)
345}
346
347fn evaluate_rule_without_unless(
349 exec_rule: &ExecutableRule,
350 context: &mut crate::evaluation::EvaluationContext,
351) -> (OperationResult, crate::evaluation::explanation::Explanation) {
352 let default_branch = &exec_rule.branches[0];
353 let (default_result, root_explanation_node) =
354 evaluate_branch_dual(default_branch, context, "default result");
355
356 context.push_operation(OperationKind::RuleBranchEvaluated {
357 index: None,
358 matched: true,
359 result_value: Some(default_result.clone()),
360 });
361
362 let explanation = crate::evaluation::explanation::Explanation {
363 rule_path: exec_rule.path.clone(),
364 source: Some(exec_rule.source.clone()),
365 result: default_result.clone(),
366 tree: Arc::new(root_explanation_node),
367 };
368
369 (default_result, explanation)
370}
371
372fn collect_postorder(root: &Expression) -> Vec<&Expression> {
379 enum Visit<'a> {
380 Enter(&'a Expression),
381 Exit(&'a Expression),
382 }
383
384 let mut stack: Vec<Visit<'_>> = vec![Visit::Enter(root)];
385 let mut seen: HashSet<usize> = HashSet::new();
386 let mut nodes: Vec<&Expression> = Vec::new();
387
388 while let Some(visit) = stack.pop() {
389 match visit {
390 Visit::Enter(e) => {
391 if !seen.insert(expr_ptr(e)) {
392 continue;
393 }
394 stack.push(Visit::Exit(e));
395 match &e.kind {
396 ExpressionKind::Arithmetic(left, _, right)
397 | ExpressionKind::Comparison(left, _, right)
398 | ExpressionKind::LogicalAnd(left, right)
399 | ExpressionKind::LogicalOr(left, right)
400 | ExpressionKind::RangeLiteral(left, right)
401 | ExpressionKind::RangeContainment(left, right) => {
402 stack.push(Visit::Enter(right));
403 stack.push(Visit::Enter(left));
404 }
405 ExpressionKind::LogicalNegation(operand, _)
406 | ExpressionKind::UnitConversion(operand, _)
407 | ExpressionKind::MathematicalComputation(_, operand)
408 | ExpressionKind::ResultIsVeto(operand)
409 | ExpressionKind::DateCalendar(_, _, operand)
410 | ExpressionKind::PastFutureRange(_, operand) => {
411 stack.push(Visit::Enter(operand));
412 }
413 ExpressionKind::DateRelative(_, date_expr) => {
414 stack.push(Visit::Enter(date_expr));
415 }
416 _ => {}
417 }
418 }
419 Visit::Exit(e) => {
420 nodes.push(e);
421 }
422 }
423 }
424
425 nodes
426}
427
428fn evaluate_expression(
429 expr: &Expression,
430 context: &mut crate::evaluation::EvaluationContext,
431) -> OperationResult {
432 let nodes = collect_postorder(expr);
433 let mut results: HashMap<usize, OperationResult> = HashMap::with_capacity(nodes.len());
434
435 for node in &nodes {
436 let result = evaluate_single_expression(node, &results, context);
437 results.insert(expr_ptr(node), result);
438 }
439
440 results.remove(&expr_ptr(expr)).unwrap_or_else(|| {
441 let loc = expr
442 .source_location
443 .as_ref()
444 .expect("BUG: expression missing source in evaluation");
445 unreachable!(
446 "BUG: expression was processed but has no result ({}:{}:{})",
447 loc.source_type, loc.span.start, loc.span.end
448 )
449 })
450}
451
452fn evaluate_single_expression(
456 current: &Expression,
457 results: &HashMap<usize, OperationResult>,
458 context: &mut crate::evaluation::EvaluationContext,
459) -> OperationResult {
460 match ¤t.kind {
461 ExpressionKind::Literal(lit) => {
462 let value = lit.as_ref().clone();
463 let explanation_node = ExplanationNode::Value {
464 value: value.clone(),
465 source: ValueSource::Literal,
466 source_location: current.source_location.clone(),
467 };
468 context.set_explanation_node(current, explanation_node);
469 OperationResult::Value(Box::new(value))
470 }
471
472 ExpressionKind::DataPath(data_path) => {
473 let data_path_clone = data_path.clone();
483 let mut value = context.get_data(data_path).cloned();
484 if value.is_none() {
485 if let Some(resolved) = context.lazy_rule_reference_resolve(data_path) {
486 match resolved {
487 Ok(v) => value = Some(v),
488 Err(veto) => {
489 let explanation_node = ExplanationNode::Veto {
490 message: Some(veto.to_string()),
491 source_location: current.source_location.clone(),
492 };
493 context.set_explanation_node(current, explanation_node);
494 return OperationResult::Veto(veto);
495 }
496 }
497 } else if let Some(veto) = context.get_reference_veto(data_path) {
498 let veto = veto.clone();
499 let explanation_node = ExplanationNode::Veto {
500 message: Some(veto.to_string()),
501 source_location: current.source_location.clone(),
502 };
503 context.set_explanation_node(current, explanation_node);
504 return OperationResult::Veto(veto);
505 }
506 }
507 match value {
508 Some(v) => {
509 context.push_operation(OperationKind::DataUsed {
510 data_ref: data_path_clone.clone(),
511 value: v.clone(),
512 });
513 let explanation_node = ExplanationNode::Value {
514 value: v.clone(),
515 source: ValueSource::Data {
516 data_ref: data_path_clone,
517 },
518 source_location: current.source_location.clone(),
519 };
520 context.set_explanation_node(current, explanation_node);
521 OperationResult::Value(Box::new(v))
522 }
523 None => {
524 let reason = VetoType::MissingData {
525 data: data_path_clone.clone(),
526 };
527 let explanation_node = ExplanationNode::Veto {
528 message: Some(reason.to_string()),
529 source_location: current.source_location.clone(),
530 };
531 context.set_explanation_node(current, explanation_node);
532 OperationResult::Veto(reason)
533 }
534 }
535 }
536
537 ExpressionKind::RulePath(rule_path) => {
538 let rule_path_clone = rule_path.clone();
542 let loc = current
543 .source_location
544 .as_ref()
545 .expect("BUG: expression missing source in evaluation");
546 let r = context.rule_results.get(rule_path).cloned().unwrap_or_else(|| {
547 unreachable!(
548 "BUG: Rule '{}' not found in results during topological-order evaluation ({}:{}:{})",
549 rule_path.rule, loc.source_type, loc.span.line, loc.span.col
550 )
551 });
552
553 context.push_operation(OperationKind::RuleUsed {
554 rule_path: rule_path_clone.clone(),
555 result: r.clone(),
556 });
557
558 let expansion = match context.get_rule_explanation(rule_path) {
560 Some(existing_explanation) => Arc::clone(&existing_explanation.tree),
561 None => Arc::new(ExplanationNode::Value {
562 value: match &r {
563 OperationResult::Value(v) => v.as_ref().clone(),
564 OperationResult::Veto(_) => LiteralValue::from_bool(false),
565 },
566 source: ValueSource::Computed,
567 source_location: current.source_location.clone(),
568 }),
569 };
570
571 let explanation_node = ExplanationNode::RuleReference {
572 rule_path: rule_path_clone,
573 result: r.clone(),
574 source_location: current.source_location.clone(),
575 expansion,
576 };
577 context.set_explanation_node(current, explanation_node);
578 r
579 }
580
581 ExpressionKind::Arithmetic(left, op, right) => {
582 let left_result = get_operand_result(results, left, "left");
583 let right_result = get_operand_result(results, right, "right");
584
585 if let OperationResult::Veto(_) = left_result {
586 return propagate_veto_explanation(
587 context,
588 current,
589 left,
590 left_result,
591 "left operand",
592 );
593 }
594 if let OperationResult::Veto(_) = right_result {
595 return propagate_veto_explanation(
596 context,
597 current,
598 right,
599 right_result,
600 "right operand",
601 );
602 }
603
604 let left_val = unwrap_value_after_veto_check(
605 &left_result,
606 "left operand",
607 ¤t.source_location,
608 );
609 let right_val = unwrap_value_after_veto_check(
610 &right_result,
611 "right operand",
612 ¤t.source_location,
613 );
614
615 let result = arithmetic_operation(left_val, op, right_val);
616
617 let left_explanation = get_explanation_node_required(context, left, "left operand");
618 let right_explanation = get_explanation_node_required(context, right, "right operand");
619
620 if let OperationResult::Value(ref val) = result {
621 let original_expr = format!("{} {} {}", left_val, op, right_val);
622 let substituted_expr = format!("{} {} {}", left_val, op, right_val);
623 context.push_operation(OperationKind::Computation {
624 kind: ComputationKind::Arithmetic(op.clone()),
625 inputs: vec![left_val.clone(), right_val.clone()],
626 result: val.as_ref().clone(),
627 });
628 let explanation_node = ExplanationNode::Computation {
629 kind: ComputationKind::Arithmetic(op.clone()),
630 conversion_steps: vec![],
631 original_expression: original_expr,
632 expression: substituted_expr,
633 result: val.as_ref().clone(),
634 source_location: current.source_location.clone(),
635 operands: vec![left_explanation, right_explanation],
636 };
637 context.set_explanation_node(current, explanation_node);
638 } else if let OperationResult::Veto(_) = result {
639 context.set_explanation_node(current, left_explanation);
640 }
641 result
642 }
643
644 ExpressionKind::Comparison(left, op, right) => {
645 let left_result = get_operand_result(results, left, "left");
646 let right_result = get_operand_result(results, right, "right");
647
648 if let OperationResult::Veto(_) = left_result {
649 return propagate_veto_explanation(
650 context,
651 current,
652 left,
653 left_result,
654 "left operand",
655 );
656 }
657 if let OperationResult::Veto(_) = right_result {
658 return propagate_veto_explanation(
659 context,
660 current,
661 right,
662 right_result,
663 "right operand",
664 );
665 }
666
667 let left_val = unwrap_value_after_veto_check(
668 &left_result,
669 "left operand",
670 ¤t.source_location,
671 );
672 let right_val = unwrap_value_after_veto_check(
673 &right_result,
674 "right operand",
675 ¤t.source_location,
676 );
677
678 let result = comparison_operation(
679 left_val,
680 op,
681 right_val,
682 UnitResolutionContext::WithIndex(&context.unit_index),
683 );
684
685 let left_explanation = get_explanation_node_required(context, left, "left operand");
686 let right_explanation = get_explanation_node_required(context, right, "right operand");
687
688 if let OperationResult::Value(ref val) = result {
689 let is_false = matches!(val.as_ref().value, ValueKind::Boolean(false));
690 let (display_op, original_expr, substituted_expr, display_result) = if is_false {
691 let negated_op = negated_comparison(op.clone());
692 let orig = format!("{} {} {}", left_val, negated_op, right_val);
693 let sub = format!("{} {} {}", left_val, negated_op, right_val);
694 (negated_op, orig, sub, LiteralValue::from_bool(true))
695 } else {
696 let original_expr = format!("{} {} {}", left_val, op, right_val);
697 let substituted_expr = format!("{} {} {}", left_val, op, right_val);
698 (
699 op.clone(),
700 original_expr,
701 substituted_expr,
702 val.as_ref().clone(),
703 )
704 };
705 context.push_operation(OperationKind::Computation {
706 kind: ComputationKind::Comparison(op.clone()),
707 inputs: vec![left_val.clone(), right_val.clone()],
708 result: val.as_ref().clone(),
709 });
710 let explanation_node = ExplanationNode::Computation {
711 kind: ComputationKind::Comparison(display_op),
712 conversion_steps: vec![],
713 original_expression: original_expr,
714 expression: substituted_expr,
715 result: display_result,
716 source_location: current.source_location.clone(),
717 operands: vec![left_explanation, right_explanation],
718 };
719 context.set_explanation_node(current, explanation_node);
720 } else if let OperationResult::Veto(_) = result {
721 context.set_explanation_node(current, left_explanation);
722 }
723 result
724 }
725
726 ExpressionKind::LogicalAnd(left, right) => {
727 let left_result = get_operand_result(results, left, "left");
728 if let OperationResult::Veto(_) = left_result {
729 return propagate_veto_explanation(
730 context,
731 current,
732 left,
733 left_result,
734 "left operand",
735 );
736 }
737
738 let left_val = unwrap_value_after_veto_check(
739 &left_result,
740 "left operand",
741 ¤t.source_location,
742 );
743 let left_bool = match &left_val.value {
744 ValueKind::Boolean(b) => b,
745 _ => unreachable!(
746 "BUG: logical AND with non-boolean operand; planning should have rejected this"
747 ),
748 };
749
750 if !*left_bool {
751 let left_explanation = get_explanation_node_required(context, left, "left operand");
752 context.set_explanation_node(current, left_explanation);
753 OperationResult::Value(Box::new(LiteralValue::from_bool(false)))
754 } else {
755 let right_result = get_operand_result(results, right, "right");
756 let right_explanation =
757 get_explanation_node_required(context, right, "right operand");
758 context.set_explanation_node(current, right_explanation);
759 right_result
760 }
761 }
762
763 ExpressionKind::LogicalOr(left, right) => {
764 let left_result = get_operand_result(results, left, "left");
765 if matches!(&left_result, OperationResult::Veto(_)) {
766 let right_result = get_operand_result(results, right, "right");
767 let right_explanation =
768 get_explanation_node_required(context, right, "right operand");
769 context.set_explanation_node(current, right_explanation);
770 return right_result;
771 }
772
773 let left_val = unwrap_value_after_veto_check(
774 &left_result,
775 "left operand",
776 ¤t.source_location,
777 );
778 if matches!(&left_val.value, ValueKind::Boolean(false)) {
779 let right_result = get_operand_result(results, right, "right");
780 let right_explanation =
781 get_explanation_node_required(context, right, "right operand");
782 context.set_explanation_node(current, right_explanation);
783 return right_result;
784 }
785
786 let left_explanation = get_explanation_node_required(context, left, "left operand");
787 context.set_explanation_node(current, left_explanation);
788 left_result
789 }
790
791 ExpressionKind::LogicalNegation(operand, _) => {
792 let result = get_operand_result(results, operand, "operand");
793 if let OperationResult::Veto(_) = result {
794 return propagate_veto_explanation(context, current, operand, result, "operand");
795 }
796
797 let value = unwrap_value_after_veto_check(&result, "operand", ¤t.source_location);
798 let operand_explanation = get_explanation_node_required(context, operand, "operand");
799 match &value.value {
800 ValueKind::Boolean(b) => {
801 let result_bool = !*b;
802 context.set_explanation_node(current, operand_explanation);
803 OperationResult::Value(Box::new(LiteralValue::from_bool(result_bool)))
804 }
805 _ => unreachable!(
806 "BUG: logical NOT with non-boolean operand; planning should have rejected this"
807 ),
808 }
809 }
810
811 ExpressionKind::UnitConversion(value_expr, target) => {
812 let result = get_operand_result(results, value_expr, "operand");
813 if let OperationResult::Veto(_) = result {
814 return propagate_veto_explanation(context, current, value_expr, result, "operand");
815 }
816
817 let value = unwrap_value_after_veto_check(&result, "operand", ¤t.source_location);
818 let operand_explanation = get_explanation_node_required(context, value_expr, "operand");
819 let data_ref = data_ref_for_conversion_operand(value_expr, &operand_explanation);
820 let (conversion_result, explanation_node) = finish_unit_conversion(
821 value,
822 target,
823 operand_explanation.clone(),
824 data_ref,
825 current.source_location.clone(),
826 context,
827 );
828 context.set_explanation_node(current, explanation_node);
829 conversion_result
830 }
831
832 ExpressionKind::MathematicalComputation(op, operand) => {
833 let result = get_operand_result(results, operand, "operand");
834 if let OperationResult::Veto(_) = result {
835 return propagate_veto_explanation(context, current, operand, result, "operand");
836 }
837
838 let value = unwrap_value_after_veto_check(&result, "operand", ¤t.source_location);
839 let operand_explanation = get_explanation_node_required(context, operand, "operand");
840 let math_result = evaluate_mathematical_operator(op, value, context);
841 context.set_explanation_node(current, operand_explanation);
842 math_result
843 }
844
845 ExpressionKind::ResultIsVeto(operand) => {
846 let operand_result = get_operand_result(results, operand, "operand");
847 let is_vetoed = operand_result.vetoed();
848 let result = OperationResult::Value(Box::new(LiteralValue::from_bool(is_vetoed)));
849
850 let operand_explanation = get_explanation_node_required(context, operand, "operand");
851 let operand_display = match &operand_result {
852 OperationResult::Value(value) => value.to_string(),
853 OperationResult::Veto(veto) => veto.to_string(),
854 };
855 let (original_expression, expression, display_result) = if is_vetoed {
856 (
857 format!("{operand_display} is veto"),
858 format!("{operand_display} is veto"),
859 LiteralValue::from_bool(true),
860 )
861 } else {
862 (
863 format!("{operand_display} is veto"),
864 format!("{operand_display} is not veto"),
865 LiteralValue::from_bool(true),
866 )
867 };
868 let computation_result = result
869 .value()
870 .expect("BUG: ResultIsVeto always produces Value")
871 .clone();
872 context.push_operation(OperationKind::Computation {
873 kind: ComputationKind::ResultIsVeto,
874 inputs: vec![],
875 result: computation_result,
876 });
877 let explanation_node = ExplanationNode::Computation {
878 kind: ComputationKind::ResultIsVeto,
879 conversion_steps: vec![],
880 original_expression,
881 expression,
882 result: display_result,
883 source_location: current.source_location.clone(),
884 operands: vec![operand_explanation],
885 };
886 context.set_explanation_node(current, explanation_node);
887 result
888 }
889
890 ExpressionKind::Veto(veto_expr) => {
891 let explanation_node = ExplanationNode::Veto {
892 message: veto_expr.message.clone(),
893 source_location: current.source_location.clone(),
894 };
895 context.set_explanation_node(current, explanation_node);
896 OperationResult::Veto(VetoType::UserDefined {
897 message: veto_expr.message.clone(),
898 })
899 }
900
901 ExpressionKind::Now => {
902 let value = context.now().clone();
903 let explanation_node = ExplanationNode::Value {
904 value: value.clone(),
905 source: ValueSource::Computed,
906 source_location: current.source_location.clone(),
907 };
908 context.set_explanation_node(current, explanation_node);
909 OperationResult::Value(Box::new(value))
910 }
911
912 ExpressionKind::DateRelative(kind, date_expr) => {
913 let date_result = get_operand_result(results, date_expr, "date operand");
914 if let OperationResult::Veto(_) = date_result {
915 return propagate_veto_explanation(
916 context,
917 current,
918 date_expr,
919 date_result,
920 "date operand",
921 );
922 }
923
924 let date_val = unwrap_value_after_veto_check(
925 &date_result,
926 "date operand",
927 ¤t.source_location,
928 );
929
930 let date_semantic = match &date_val.value {
931 ValueKind::Date(dt) => dt,
932 _ => unreachable!(
933 "BUG: date sugar with non-date operand; planning should have rejected this"
934 ),
935 };
936
937 let now_val = context.now();
938 let now_semantic = match &now_val.value {
939 ValueKind::Date(dt) => dt,
940 _ => unreachable!("BUG: context.now() must be a Date value"),
941 };
942
943 let result = crate::computation::datetime::compute_date_relative(
944 kind,
945 date_semantic,
946 now_semantic,
947 );
948
949 let date_explanation =
950 get_explanation_node_required(context, date_expr, "date operand");
951 context.set_explanation_node(current, date_explanation);
952 result
953 }
954
955 ExpressionKind::DateCalendar(kind, unit, date_expr) => {
956 let date_result = get_operand_result(results, date_expr, "date operand");
957 if let OperationResult::Veto(_) = date_result {
958 return propagate_veto_explanation(
959 context,
960 current,
961 date_expr,
962 date_result,
963 "date operand",
964 );
965 }
966
967 let date_val = unwrap_value_after_veto_check(
968 &date_result,
969 "date operand",
970 ¤t.source_location,
971 );
972
973 let date_semantic = match &date_val.value {
974 ValueKind::Date(dt) => dt,
975 _ => unreachable!(
976 "BUG: calendar sugar with non-date operand; planning should have rejected this"
977 ),
978 };
979
980 let now_val = context.now();
981 let now_semantic = match &now_val.value {
982 ValueKind::Date(dt) => dt,
983 _ => unreachable!("BUG: context.now() must be a Date value"),
984 };
985
986 let result = crate::computation::datetime::compute_date_calendar(
987 kind,
988 unit,
989 date_semantic,
990 now_semantic,
991 );
992
993 let date_explanation =
994 get_explanation_node_required(context, date_expr, "date operand");
995 context.set_explanation_node(current, date_explanation);
996 result
997 }
998
999 ExpressionKind::RangeLiteral(left, right) => {
1000 let left_result = get_operand_result(results, left, "left endpoint");
1001 let right_result = get_operand_result(results, right, "right endpoint");
1002
1003 if let OperationResult::Veto(_) = left_result {
1004 return propagate_veto_explanation(
1005 context,
1006 current,
1007 left,
1008 left_result,
1009 "left endpoint",
1010 );
1011 }
1012 if let OperationResult::Veto(_) = right_result {
1013 return propagate_veto_explanation(
1014 context,
1015 current,
1016 right,
1017 right_result,
1018 "right endpoint",
1019 );
1020 }
1021
1022 let left_value = unwrap_value_after_veto_check(
1023 &left_result,
1024 "left endpoint",
1025 ¤t.source_location,
1026 );
1027 let right_value = unwrap_value_after_veto_check(
1028 &right_result,
1029 "right endpoint",
1030 ¤t.source_location,
1031 );
1032
1033 let range_value = LiteralValue::range(left_value.clone(), right_value.clone());
1034 let explanation_node = ExplanationNode::Value {
1035 value: range_value.clone(),
1036 source: ValueSource::Computed,
1037 source_location: current.source_location.clone(),
1038 };
1039 context.set_explanation_node(current, explanation_node);
1040 OperationResult::Value(Box::new(range_value))
1041 }
1042
1043 ExpressionKind::PastFutureRange(kind, offset_expr) => {
1044 let offset_result = get_operand_result(results, offset_expr, "offset operand");
1045 if let OperationResult::Veto(_) = offset_result {
1046 return propagate_veto_explanation(
1047 context,
1048 current,
1049 offset_expr,
1050 offset_result,
1051 "offset operand",
1052 );
1053 }
1054
1055 let offset_value = unwrap_value_after_veto_check(
1056 &offset_result,
1057 "offset operand",
1058 ¤t.source_location,
1059 );
1060
1061 let now_value = context.now();
1062 let now_semantic = match &now_value.value {
1063 ValueKind::Date(date_time) => date_time,
1064 _ => unreachable!("BUG: context.now() must be a Date value"),
1065 };
1066
1067 let result = crate::computation::datetime::evaluate_past_future_range(
1068 kind,
1069 offset_value,
1070 now_semantic,
1071 );
1072
1073 match &result {
1074 OperationResult::Value(range_value) => {
1075 let explanation_node = ExplanationNode::Value {
1076 value: range_value.as_ref().clone(),
1077 source: ValueSource::Computed,
1078 source_location: current.source_location.clone(),
1079 };
1080 context.set_explanation_node(current, explanation_node);
1081 }
1082 OperationResult::Veto(_) => {
1083 let offset_explanation =
1084 get_explanation_node_required(context, offset_expr, "offset operand");
1085 context.set_explanation_node(current, offset_explanation);
1086 }
1087 }
1088 result
1089 }
1090
1091 ExpressionKind::RangeContainment(value_expr, range_expr) => {
1092 let value_result = get_operand_result(results, value_expr, "value operand");
1093 let range_result = get_operand_result(results, range_expr, "range operand");
1094
1095 if let OperationResult::Veto(_) = value_result {
1096 return propagate_veto_explanation(
1097 context,
1098 current,
1099 value_expr,
1100 value_result,
1101 "value operand",
1102 );
1103 }
1104 if let OperationResult::Veto(_) = range_result {
1105 return propagate_veto_explanation(
1106 context,
1107 current,
1108 range_expr,
1109 range_result,
1110 "range operand",
1111 );
1112 }
1113
1114 let value_literal = unwrap_value_after_veto_check(
1115 &value_result,
1116 "value operand",
1117 ¤t.source_location,
1118 );
1119 let range_literal = unwrap_value_after_veto_check(
1120 &range_result,
1121 "range operand",
1122 ¤t.source_location,
1123 );
1124
1125 let contained = match &range_literal.value {
1126 ValueKind::Range(range_left, range_right) => crate::computation::range::check_containment(
1127 value_literal,
1128 range_left.as_ref(),
1129 range_right.as_ref(),
1130 ),
1131 _ => unreachable!(
1132 "BUG: range containment reached runtime with non-range operand; planning should have rejected this"
1133 ),
1134 };
1135
1136 let result_value = LiteralValue::from_bool(contained);
1137 let explanation_node = ExplanationNode::Value {
1138 value: result_value.clone(),
1139 source: ValueSource::Computed,
1140 source_location: current.source_location.clone(),
1141 };
1142 context.set_explanation_node(current, explanation_node);
1143 OperationResult::Value(Box::new(result_value))
1144 }
1145 }
1146}
1147
1148fn evaluate_mathematical_operator(
1149 op: &MathematicalComputation,
1150 value: &LiteralValue,
1151 context: &mut crate::evaluation::EvaluationContext,
1152) -> OperationResult {
1153 use crate::computation::decimal_math::{decimal_acos, decimal_asin, decimal_atan};
1154 use rust_decimal::MathematicalOps;
1155
1156 match &value.value {
1157 ValueKind::Number(stored_rational) => {
1158 use crate::computation::rational::{commit_rational_to_decimal, decimal_to_rational};
1159 let stored_decimal = match commit_rational_to_decimal(stored_rational) {
1160 Ok(decimal) => decimal,
1161 Err(_) => {
1162 return OperationResult::Veto(VetoType::computation(
1163 "Mathematical operation requires a decimal-representable input",
1164 ));
1165 }
1166 };
1167 let decimal_result: Option<rust_decimal::Decimal> = match op {
1168 MathematicalComputation::Abs => Some(stored_decimal.abs()),
1169 MathematicalComputation::Floor => Some(stored_decimal.floor()),
1170 MathematicalComputation::Ceil => Some(stored_decimal.ceil()),
1171 MathematicalComputation::Round => Some(stored_decimal.round()),
1172 MathematicalComputation::Sqrt => stored_decimal.sqrt(),
1173 MathematicalComputation::Sin => stored_decimal.checked_sin(),
1174 MathematicalComputation::Cos => stored_decimal.checked_cos(),
1175 MathematicalComputation::Tan => stored_decimal.checked_tan(),
1176 MathematicalComputation::Log => stored_decimal.checked_ln(),
1177 MathematicalComputation::Exp => stored_decimal.checked_exp(),
1178 MathematicalComputation::Asin => decimal_asin(stored_decimal),
1179 MathematicalComputation::Acos => decimal_acos(stored_decimal),
1180 MathematicalComputation::Atan => decimal_atan(stored_decimal),
1181 };
1182
1183 let committed_decimal = match decimal_result {
1184 Some(committed) => committed,
1185 None => {
1186 return OperationResult::Veto(VetoType::computation(
1187 "Mathematical operation result is undefined for this input",
1188 ));
1189 }
1190 };
1191
1192 let result_rational = decimal_to_rational(committed_decimal)
1193 .expect("BUG: transcendental result must lift back to stored rational");
1194 let result_value =
1195 LiteralValue::number_with_type(result_rational, value.lemma_type.clone());
1196 context.push_operation(OperationKind::Computation {
1197 kind: ComputationKind::Mathematical(op.clone()),
1198 inputs: vec![value.clone()],
1199 result: result_value.clone(),
1200 });
1201 OperationResult::Value(Box::new(result_value))
1202 }
1203 _ => unreachable!(
1204 "BUG: mathematical operator with non-number operand; planning should have rejected this"
1205 ),
1206 }
1207}