1use crate::parsing::arena::{ExprId, ExprKind, ExprRef};
7use crate::parsing::ast::{
8 ArithmeticComputation, CalendarPeriodUnit, ComparisonComputation, ConversionTypeKind,
9 DateCalendarKind, DateRelativeKind, LogicalConnective, MathematicalComputation, RangeBound,
10 RoundingMode, VetoExpression,
11};
12use crate::planning::execution_plan::ExecutionPlan;
13use crate::planning::instance_list::{row_end_chain, RowEndView};
14use crate::planning::semantics::{
15 DataDefinition, DataPath, Expression, ExpressionKind, ListOp, PathSegment, RowEnd, RulePath,
16 SemanticConversionTarget, SpecIdentity, TypedLiteral,
17};
18use indexmap::IndexMap;
19use serde::{Deserialize, Serialize};
20use std::collections::{BTreeMap, HashSet};
21use std::sync::Arc;
22
23pub(crate) struct ShowScope<'a> {
26 pub(crate) show_keys: &'a HashSet<RulePath>,
27 pub(crate) data: &'a IndexMap<DataPath, DataDefinition>,
28 pub(crate) templates: &'a BTreeMap<SpecIdentity, Arc<ExecutionPlan>>,
29}
30
31enum ImportMember {
34 Rule(RulePath),
35 Data(DataPath),
36}
37
38impl ShowScope<'_> {
39 fn import_member(
41 &self,
42 base: &ExpressionKind,
43 path: &[String],
44 name: &str,
45 ) -> Option<ImportMember> {
46 let ExpressionKind::DataPath(slot) = base else {
47 return None;
48 };
49 let Some(DataDefinition::Import { row, .. }) = self.data.get(slot) else {
50 return None;
51 };
52 let template = self.templates.get(&row.identity).unwrap_or_else(|| {
53 panic!(
54 "BUG: `uses` slot '{slot}' has no template of '{}'",
55 row.identity
56 )
57 });
58 let parts: Vec<&str> = path
59 .iter()
60 .map(String::as_str)
61 .chain(std::iter::once(name))
62 .collect();
63 let chain = row_end_chain(
64 &row.identity,
65 RowEndView::Plan(template),
66 self.templates,
67 &parts,
68 )
69 .unwrap_or_else(|| {
70 panic!(
71 "BUG: member '{}' of `uses` slot '{slot}' planned but unresolved",
72 parts.join(".")
73 )
74 });
75 let mut segments = vec![PathSegment::import(&slot.data, &row.identity)];
76 for (hop, next) in chain.iter().zip(chain.iter().skip(1)) {
77 let RowEnd::Data(alias) = &hop.end else {
78 panic!(
79 "BUG: a member chain hop is a `uses` slot, got {:?}",
80 hop.end
81 );
82 };
83 segments.push(PathSegment::import(&alias.data, &next.identity));
84 }
85 let last = chain.last().expect("BUG: a member chain has a last end");
86 Some(match &last.end {
87 RowEnd::Rule(rule) => ImportMember::Rule(RulePath::new(segments, rule.rule.clone())),
88 RowEnd::Data(data) => ImportMember::Data(DataPath {
89 segments,
90 data: data.data.clone(),
91 }),
92 })
93 }
94}
95
96#[derive(Debug, Clone, PartialEq, Eq)]
98pub struct ShowBranch {
99 pub condition: Option<ShowExpression>,
101 pub result: ShowExpression,
102}
103
104#[derive(Debug, Clone, PartialEq, Eq)]
106pub struct ShowRule {
107 pub lemma_type: crate::planning::semantics::LemmaType,
108 pub path: Vec<crate::planning::semantics::PathSegment>,
110 pub branches: Vec<ShowBranch>,
111 pub depends_on_rules: Vec<RulePath>,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq)]
118pub enum ShowConversionTarget {
119 Type(ConversionTypeKind),
120 Unit { unit_name: String },
121}
122
123#[derive(Debug, Clone, PartialEq, Eq)]
128pub enum ShowExpression {
129 Literal(Box<TypedLiteral>),
130 Data {
131 name: String,
132 },
133 Rule {
134 name: String,
135 },
136 And {
138 operands: Vec<ShowExpression>,
139 },
140 Or {
141 operands: Vec<ShowExpression>,
142 },
143 Not {
144 operand: Box<ShowExpression>,
145 },
146 Arithmetic {
148 first: Box<ShowExpression>,
149 steps: Vec<ShowArithmeticStep>,
150 },
151 Comparison {
152 op: ComparisonComputation,
153 left: Box<ShowExpression>,
154 right: Box<ShowExpression>,
155 },
156 UnitConversion {
158 operand: Box<ShowExpression>,
159 targets: Vec<ShowConversionTarget>,
160 },
161 Math {
162 op: MathematicalComputation,
163 operand: Box<ShowExpression>,
164 },
165 StepRounding {
167 mode: RoundingMode,
168 operand: Box<ShowExpression>,
169 step: Option<Box<ShowExpression>>,
170 },
171 Veto {
172 message: Option<String>,
173 },
174 Now,
175 DateRelative {
176 kind: DateRelativeKind,
177 operand: Box<ShowExpression>,
178 },
179 DateCalendar {
180 kind: DateCalendarKind,
181 unit: CalendarPeriodUnit,
182 operand: Box<ShowExpression>,
183 },
184 RangeLiteral {
185 from: Box<ShowExpression>,
186 to: Box<ShowExpression>,
187 },
188 PastFutureRange {
189 kind: DateRelativeKind,
190 operand: Box<ShowExpression>,
191 },
192 RangeContainment {
193 value: Box<ShowExpression>,
194 range: Box<ShowExpression>,
195 },
196 RangeBound {
197 bound: RangeBound,
198 operand: Box<ShowExpression>,
199 },
200 IsVeto {
201 operand: Box<ShowExpression>,
202 },
203 SpecMember {
205 base: String,
206 member: String,
207 },
208 Member {
210 base: Box<ShowExpression>,
211 member: String,
212 },
213 RowField {
215 field: String,
216 qualifier: Option<Vec<String>>,
218 },
219 RowValue,
221 RowFieldValue {
223 field: String,
224 value: Box<ShowExpression>,
225 },
226 ListCommand {
228 list: Box<ShowExpression>,
229 command: ShowListCommand,
230 direction: Option<SortDirection>,
231 args: Vec<ShowExpression>,
232 },
233}
234
235#[derive(Debug, Clone, PartialEq, Eq)]
237pub struct ShowArithmeticStep {
238 pub op: ArithmeticComputation,
239 pub operand: ShowExpression,
240}
241
242#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
244#[serde(rename_all = "snake_case")]
245pub enum ShowListCommand {
246 Find,
247 First,
248 Last,
249 Count,
250 Sum,
251 Avg,
252 Product,
253 Min,
254 Max,
255 Any,
256 All,
257 Where,
258 Sort,
259 Top,
260 With,
261 Key,
262}
263
264#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
265#[serde(rename_all = "snake_case")]
266pub enum SortDirection {
267 Asc,
268 Desc,
269}
270
271impl ShowExpression {
272 pub(crate) fn operands(&self) -> Vec<&ShowExpression> {
274 match self {
275 Self::Literal(_)
276 | Self::Data { .. }
277 | Self::Rule { .. }
278 | Self::Veto { .. }
279 | Self::Now
280 | Self::SpecMember { .. }
281 | Self::RowField { .. }
282 | Self::RowValue => Vec::new(),
283 Self::And { operands } | Self::Or { operands } => operands.iter().collect(),
284 Self::Not { operand }
285 | Self::UnitConversion { operand, .. }
286 | Self::Math { operand, .. }
287 | Self::DateRelative { operand, .. }
288 | Self::DateCalendar { operand, .. }
289 | Self::PastFutureRange { operand, .. }
290 | Self::RangeBound { operand, .. }
291 | Self::IsVeto { operand } => vec![operand.as_ref()],
292 Self::Arithmetic { first, steps } => {
293 let mut out = vec![first.as_ref()];
294 out.extend(steps.iter().map(|step| &step.operand));
295 out
296 }
297 Self::Comparison { left, right, .. }
298 | Self::RangeLiteral {
299 from: left,
300 to: right,
301 }
302 | Self::RangeContainment {
303 value: left,
304 range: right,
305 } => vec![left.as_ref(), right.as_ref()],
306 Self::StepRounding { operand, step, .. } => {
307 let mut out = vec![operand.as_ref()];
308 if let Some(step) = step {
309 out.push(step.as_ref());
310 }
311 out
312 }
313 Self::Member { base, .. } => vec![base.as_ref()],
314 Self::RowFieldValue { value, .. } => vec![value.as_ref()],
315 Self::ListCommand { list, args, .. } => {
316 let mut out = vec![list.as_ref()];
317 out.extend(args.iter());
318 out
319 }
320 }
321 }
322
323 pub(crate) fn fold<U>(&self, mut step: impl FnMut(&ShowExpression, Vec<U>) -> U) -> U {
326 let mut values: Vec<U> = Vec::new();
327 let mut stack: Vec<(&ShowExpression, bool)> = vec![(self, false)];
328 while let Some((node, expanded)) = stack.pop() {
329 let operands = node.operands();
330 if !expanded {
331 stack.push((node, true));
332 stack.extend(operands.iter().rev().map(|&operand| (operand, false)));
333 continue;
334 }
335 let split = values
336 .len()
337 .checked_sub(operands.len())
338 .expect("BUG: operand folded before its parent");
339 let args = values.split_off(split);
340 values.push(step(node, args));
341 }
342 values
343 .pop()
344 .expect("BUG: fold produced no value for its root")
345 }
346}
347
348pub(crate) fn take_operand<T, I: Iterator<Item = T>>(operands: &mut I) -> T {
350 operands
351 .next()
352 .expect("BUG: operand missing for its parent node")
353}
354
355fn project(
362 root: ExprRef<'_, ExpressionKind>,
363 scope: &ShowScope<'_>,
364 scratch: &mut ProjectScratch,
365) -> ShowExpression {
366 let ProjectScratch { values, stack } = scratch;
367 stack.push((root.id, false));
368 while let Some((id, expanded)) = stack.pop() {
369 let node = root.child(id);
370 if !expanded {
371 stack.push((id, true));
372 let start = stack.len();
373 for_each_projection_operand(node, scope, |operand| stack.push((operand, false)));
374 stack[start..].reverse();
375 continue;
376 }
377 let mut arity = 0;
378 for_each_projection_operand(node, scope, |_| arity += 1);
379 let split = values
380 .len()
381 .checked_sub(arity)
382 .expect("BUG: operand projected before its parent");
383 let shown = project_node(node, values.drain(split..), scope);
384 values.push(shown);
385 }
386 let shown = values
387 .pop()
388 .expect("BUG: projection produced no value for its root");
389 assert!(
390 values.is_empty(),
391 "BUG: projection left operands without a parent"
392 );
393 shown
394}
395
396#[derive(Default)]
398struct ProjectScratch {
399 values: Vec<ShowExpression>,
400 stack: Vec<(ExprId, bool)>,
401}
402
403fn for_each_projection_operand(
407 node: ExprRef<'_, ExpressionKind>,
408 scope: &ShowScope<'_>,
409 mut f: impl FnMut(ExprId),
410) {
411 match node.kind() {
412 ExpressionKind::Piecewise(_) => {}
413 ExpressionKind::SpecMember { base, path, name } => {
414 let base_kind = node.child(*base).kind();
415 if !matches!(base_kind, ExpressionKind::RulePath(_))
416 && scope.import_member(base_kind, path, name).is_none()
417 {
418 f(*base);
419 }
420 }
421 ExpressionKind::UnitConversion(_, _) => f(conversion_operand(node)),
422 other => other.for_each_child(f),
423 }
424}
425
426fn conversion_operand(node: ExprRef<'_, ExpressionKind>) -> ExprId {
428 let mut current = node;
429 while let ExpressionKind::UnitConversion(operand, _) = current.kind() {
430 current = current.child(*operand);
431 }
432 current.id
433}
434
435fn conversion_run(node: ExprRef<'_, ExpressionKind>) -> (ExprId, Vec<ShowConversionTarget>) {
437 let mut current = node;
438 let mut targets = Vec::new();
439 while let ExpressionKind::UnitConversion(operand, target) = current.kind() {
440 targets.push(conversion_target_from(target));
441 current = current.child(*operand);
442 }
443 targets.reverse();
444 (current.id, targets)
445}
446
447fn project_node(
449 node: ExprRef<'_, ExpressionKind>,
450 mut operands: std::vec::Drain<'_, ShowExpression>,
451 scope: &ShowScope<'_>,
452) -> ShowExpression {
453 let show_keys = scope.show_keys;
454 match node.kind() {
455 ExpressionKind::Literal(typed) => ShowExpression::Literal(Box::new(typed.as_ref().clone())),
456 ExpressionKind::DataPath(path) => ShowExpression::Data {
457 name: path.input_key(),
458 },
459 ExpressionKind::RulePath(path) => {
460 if !show_keys.contains(path) {
461 panic!(
462 "BUG: rule leaf '{}' not in Show.rules keys",
463 path.input_key()
464 );
465 }
466 ShowExpression::Rule {
467 name: path.input_key(),
468 }
469 }
470 ExpressionKind::Logical { connective, .. } => match connective {
471 LogicalConnective::And => ShowExpression::And {
472 operands: operands.collect(),
473 },
474 LogicalConnective::Or => ShowExpression::Or {
475 operands: operands.collect(),
476 },
477 },
478 ExpressionKind::LogicalNegation(_, _) => ShowExpression::Not {
479 operand: Box::new(take_operand(&mut operands)),
480 },
481 ExpressionKind::Arithmetic { steps, .. } => {
482 let first = Box::new(take_operand(&mut operands));
483 let steps = steps
484 .iter()
485 .map(|step| ShowArithmeticStep {
486 op: step.op.clone(),
487 operand: take_operand(&mut operands),
488 })
489 .collect();
490 ShowExpression::Arithmetic { first, steps }
491 }
492 ExpressionKind::Comparison(_, op, _) => {
493 let left = Box::new(take_operand(&mut operands));
494 let right = Box::new(take_operand(&mut operands));
495 ShowExpression::Comparison {
496 op: op.clone(),
497 left,
498 right,
499 }
500 }
501 ExpressionKind::UnitConversion(_, _) => {
502 let (_, targets) = conversion_run(node);
503 ShowExpression::UnitConversion {
504 operand: Box::new(take_operand(&mut operands)),
505 targets,
506 }
507 }
508 ExpressionKind::MathematicalComputation(op, _) => ShowExpression::Math {
509 op: op.clone(),
510 operand: Box::new(take_operand(&mut operands)),
511 },
512 ExpressionKind::StepRounding { mode, step, .. } => {
513 let operand = Box::new(take_operand(&mut operands));
514 ShowExpression::StepRounding {
515 mode: *mode,
516 operand,
517 step: step.map(|_| Box::new(take_operand(&mut operands))),
518 }
519 }
520 ExpressionKind::Veto(VetoExpression { message }) => ShowExpression::Veto {
521 message: message.clone(),
522 },
523 ExpressionKind::Now => ShowExpression::Now,
524 ExpressionKind::DateRelative(kind, _) => ShowExpression::DateRelative {
525 kind: *kind,
526 operand: Box::new(take_operand(&mut operands)),
527 },
528 ExpressionKind::DateCalendar(kind, unit, _) => ShowExpression::DateCalendar {
529 kind: *kind,
530 unit: *unit,
531 operand: Box::new(take_operand(&mut operands)),
532 },
533 ExpressionKind::RangeLiteral(_, _) => {
534 let from = Box::new(take_operand(&mut operands));
535 let to = Box::new(take_operand(&mut operands));
536 ShowExpression::RangeLiteral { from, to }
537 }
538 ExpressionKind::PastFutureRange(kind, _) => ShowExpression::PastFutureRange {
539 kind: *kind,
540 operand: Box::new(take_operand(&mut operands)),
541 },
542 ExpressionKind::RangeContainment(_, _) => {
543 let value = Box::new(take_operand(&mut operands));
544 let range = Box::new(take_operand(&mut operands));
545 ShowExpression::RangeContainment { value, range }
546 }
547 ExpressionKind::RangeBound(bound, _) => ShowExpression::RangeBound {
548 bound: *bound,
549 operand: Box::new(take_operand(&mut operands)),
550 },
551 ExpressionKind::ResultIsVeto(_) => ShowExpression::IsVeto {
552 operand: Box::new(take_operand(&mut operands)),
553 },
554 ExpressionKind::Piecewise(_) => {
555 panic!("BUG: piecewise inside a rule branch")
556 }
557 ExpressionKind::SpecMember { base, path, name } => {
558 match scope.import_member(node.child(*base).kind(), path, name) {
559 Some(ImportMember::Rule(rule)) => {
560 if !show_keys.contains(&rule) {
561 panic!(
562 "BUG: `uses` member rule '{}' not in Show.rules keys",
563 rule.input_key()
564 );
565 }
566 return ShowExpression::Rule {
567 name: rule.input_key(),
568 };
569 }
570 Some(ImportMember::Data(data)) => {
571 return ShowExpression::Data {
572 name: data.input_key(),
573 };
574 }
575 None => {}
576 }
577 let mut member = String::new();
578 for segment in path {
579 member.push_str(segment);
580 member.push('.');
581 }
582 member.push_str(name);
583 match node.child(*base).kind() {
584 ExpressionKind::RulePath(rule) => {
585 if !show_keys.contains(rule) {
586 panic!(
587 "BUG: spec member base '{}' not in Show.rules keys",
588 rule.input_key()
589 );
590 }
591 ShowExpression::SpecMember {
592 base: rule.input_key(),
593 member,
594 }
595 }
596 _ => ShowExpression::Member {
597 base: Box::new(take_operand(&mut operands)),
598 member,
599 },
600 }
601 }
602 ExpressionKind::RowField { end, qualifier, .. } => ShowExpression::RowField {
603 field: end.to_string(),
604 qualifier: qualifier.clone(),
605 },
606 ExpressionKind::RowValue { .. } => ShowExpression::RowValue,
607 ExpressionKind::ListCommand { op, .. } => {
608 let list = Box::new(take_operand(&mut operands));
609 let (command, direction, args) = show_list_op(op, &mut operands);
610 ShowExpression::ListCommand {
611 list,
612 command,
613 direction,
614 args,
615 }
616 }
617 }
618}
619
620fn show_list_op(
622 op: &ListOp,
623 operands: &mut std::vec::Drain<'_, ShowExpression>,
624) -> (ShowListCommand, Option<SortDirection>, Vec<ShowExpression>) {
625 use crate::planning::semantics::{ListPredKind, ListReduce};
626 let predicate = |kind: &ListPredKind| match kind {
627 ListPredKind::Any => ShowListCommand::Any,
628 ListPredKind::All => ShowListCommand::All,
629 ListPredKind::Where => ShowListCommand::Where,
630 };
631 match op {
632 ListOp::Find { keys } => (
633 ShowListCommand::Find,
634 None,
635 operands.by_ref().take(keys.len()).collect(),
636 ),
637 ListOp::First => (ShowListCommand::First, None, Vec::new()),
638 ListOp::Last => (ShowListCommand::Last, None, Vec::new()),
639 ListOp::Count => (ShowListCommand::Count, None, Vec::new()),
640 ListOp::Reduce { kind, argument } => {
641 let command = match kind {
642 ListReduce::Sum => ShowListCommand::Sum,
643 ListReduce::Avg => ShowListCommand::Avg,
644 ListReduce::Product => ShowListCommand::Product,
645 ListReduce::Min => ShowListCommand::Min,
646 ListReduce::Max => ShowListCommand::Max,
647 };
648 let args = if argument.is_some() {
649 vec![take_operand(operands)]
650 } else {
651 Vec::new()
652 };
653 (command, None, args)
654 }
655 ListOp::Predicate { kind, .. } => (predicate(kind), None, vec![take_operand(operands)]),
656 ListOp::Sort {
657 descending,
658 argument,
659 } => {
660 let direction = if *descending {
661 SortDirection::Desc
662 } else {
663 SortDirection::Asc
664 };
665 let args = if argument.is_some() {
666 vec![take_operand(operands)]
667 } else {
668 Vec::new()
669 };
670 (ShowListCommand::Sort, Some(direction), args)
671 }
672 ListOp::Top { .. } => (ShowListCommand::Top, None, vec![take_operand(operands)]),
673 ListOp::With { rows } => (
674 ShowListCommand::With,
675 None,
676 operands.by_ref().take(rows.len()).collect(),
677 ),
678 ListOp::WithRow { fields, .. } => (
679 ShowListCommand::With,
680 None,
681 fields
682 .iter()
683 .map(|(path, _)| ShowExpression::RowFieldValue {
684 field: path.input_key(),
685 value: Box::new(take_operand(operands)),
686 })
687 .collect(),
688 ),
689 ListOp::Key { fields } => (
690 ShowListCommand::Key,
691 None,
692 fields
693 .iter()
694 .map(|path| ShowExpression::Data {
695 name: path.input_key(),
696 })
697 .collect(),
698 ),
699 }
700}
701
702pub(crate) fn show_branches_from(
704 expression: &Expression,
705 branches: &[(Option<ExprId>, ExprId)],
706 scope: &ShowScope<'_>,
707) -> Vec<ShowBranch> {
708 let mut scratch = ProjectScratch::default();
709 branches
710 .iter()
711 .map(|(condition, result)| ShowBranch {
712 condition: condition
713 .map(|condition| project(expression.at(condition), scope, &mut scratch)),
714 result: project(expression.at(*result), scope, &mut scratch),
715 })
716 .collect()
717}
718
719pub fn project_depends_on_rules(
724 deps: &std::collections::BTreeSet<RulePath>,
725 catalog_keys: &HashSet<RulePath>,
726) -> Vec<RulePath> {
727 let mut out = Vec::new();
728 for path in deps {
729 if catalog_keys.contains(path) {
730 out.push(path.clone());
731 } else if path.segments.is_empty() {
732 panic!(
733 "BUG: local depends_on_rules entry '{}' missing from Show.rules keys",
734 path.input_key()
735 );
736 }
737 }
738 out
739}
740
741fn conversion_target_from(target: &SemanticConversionTarget) -> ShowConversionTarget {
742 match target {
743 SemanticConversionTarget::Type(kind) => ShowConversionTarget::Type(*kind),
744 SemanticConversionTarget::Unit { unit_name, .. } => ShowConversionTarget::Unit {
745 unit_name: unit_name.clone(),
746 },
747 }
748}