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