Skip to main content

lemma/planning/
show_expression.rs

1//! Show rule graph: resolved branch expressions projected for [`crate::Engine::show`].
2//!
3//! Built once from [`crate::planning::graph::RuleNode`] branches during plan build.
4//! Not NormalForm and not evaluation narration.
5
6use crate::parsing::arena::{ExprId, ExprKind, ExprRef};
7use crate::parsing::ast::{
8    ArithmeticComputation, CalendarPeriodUnit, ComparisonComputation, DateCalendarKind,
9    DateRelativeKind, MathematicalComputation, PrimitiveKind, RangeBound, VetoExpression,
10};
11use crate::planning::semantics::{
12    Expression, ExpressionKind, ListOp, SemanticConversionTarget, TypedLiteral,
13};
14use serde::{Deserialize, Serialize};
15use std::collections::HashSet;
16
17/// One arm of a rule's flat last-match table on Show.
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19pub struct ShowBranch {
20    /// Absent on the default arm; present on each `unless`.
21    pub condition: Option<ShowExpression>,
22    pub result: ShowExpression,
23}
24
25/// Local rule on Show: result type, authored branches, stored topo deps.
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
27pub struct ShowRule {
28    pub lemma_type: crate::planning::semantics::LemmaType,
29    /// Import hops to this rule (`[]` = this spec). Same shape as [`ShowData::path`].
30    pub path: Vec<crate::planning::semantics::PathSegment>,
31    pub branches: Vec<ShowBranch>,
32    /// Show.rules keys this rule depends on (planning topo). Always present.
33    pub depends_on_rules: Vec<String>,
34}
35
36/// Conversion target on a Show `as` expression (no owning type — inspection only).
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38#[serde(rename_all = "snake_case")]
39pub enum ShowConversionTarget {
40    Type(PrimitiveKind),
41    Unit { unit_name: String },
42}
43
44/// Resolved expression tree for Show branches (`tag = "type"` on the API mirror).
45///
46/// Literals stay [`TypedLiteral`] (postcard-safe ℚ). [`RuleResultValue`] is built at the
47/// JSON/`lemma::api` boundary only.
48#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
49pub enum ShowExpression {
50    Literal(Box<TypedLiteral>),
51    Data {
52        name: String,
53    },
54    Rule {
55        name: String,
56    },
57    /// `a and b and c`: one left fold, operands in source order.
58    And {
59        operands: Vec<ShowExpression>,
60    },
61    Not {
62        operand: Box<ShowExpression>,
63    },
64    /// `a + b - c`: one left fold on one precedence level, operands in source order.
65    Arithmetic {
66        first: Box<ShowExpression>,
67        steps: Vec<ShowArithmeticStep>,
68    },
69    Comparison {
70        op: ComparisonComputation,
71        left: Box<ShowExpression>,
72        right: Box<ShowExpression>,
73    },
74    /// `x as a as b`: one node over the innermost operand, `targets` in application order.
75    UnitConversion {
76        operand: Box<ShowExpression>,
77        targets: Vec<ShowConversionTarget>,
78    },
79    Math {
80        op: MathematicalComputation,
81        operand: Box<ShowExpression>,
82    },
83    Veto {
84        message: Option<String>,
85    },
86    Now,
87    DateRelative {
88        kind: DateRelativeKind,
89        operand: Box<ShowExpression>,
90    },
91    DateCalendar {
92        kind: DateCalendarKind,
93        unit: CalendarPeriodUnit,
94        operand: Box<ShowExpression>,
95    },
96    RangeLiteral {
97        from: Box<ShowExpression>,
98        to: Box<ShowExpression>,
99    },
100    PastFutureRange {
101        kind: DateRelativeKind,
102        operand: Box<ShowExpression>,
103    },
104    RangeContainment {
105        value: Box<ShowExpression>,
106        range: Box<ShowExpression>,
107    },
108    RangeBound {
109        bound: RangeBound,
110        operand: Box<ShowExpression>,
111    },
112    IsVeto {
113        operand: Box<ShowExpression>,
114    },
115    /// `rule.field` on a spec-valued rule. `member` is `field` or `alias.field`.
116    SpecMember {
117        base: String,
118        member: String,
119    },
120    /// `(expression).field` on a spec value that is not a rule reference.
121    Member {
122        base: Box<ShowExpression>,
123        member: String,
124    },
125    /// Field of the current row inside an instance-list command argument.
126    RowField {
127        field: String,
128    },
129    /// The current row's value: the implicit left operand of a value-list condition.
130    RowValue,
131    /// `field: value` of a row appended by `-> with`.
132    RowFieldValue {
133        field: String,
134        value: Box<ShowExpression>,
135    },
136    /// `direction` is set exactly when `command` is [`ShowListCommand::Sort`].
137    ListCommand {
138        list: Box<ShowExpression>,
139        command: ShowListCommand,
140        direction: Option<SortDirection>,
141        args: Vec<ShowExpression>,
142    },
143}
144
145/// One operator step of a Show arithmetic chain.
146#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
147pub struct ShowArithmeticStep {
148    pub op: ArithmeticComputation,
149    pub operand: ShowExpression,
150}
151
152/// Postfix list command on Show.
153#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
154#[serde(rename_all = "snake_case")]
155pub enum ShowListCommand {
156    Find,
157    First,
158    Last,
159    Count,
160    Sum,
161    Avg,
162    Product,
163    Min,
164    Max,
165    Any,
166    All,
167    Where,
168    Sort,
169    Top,
170    With,
171}
172
173#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
174#[serde(rename_all = "snake_case")]
175pub enum SortDirection {
176    Asc,
177    Desc,
178}
179
180impl ShowExpression {
181    /// Direct sub-expressions in the order [`Self::fold`] hands their results back.
182    pub(crate) fn operands(&self) -> Vec<&ShowExpression> {
183        match self {
184            Self::Literal(_)
185            | Self::Data { .. }
186            | Self::Rule { .. }
187            | Self::Veto { .. }
188            | Self::Now
189            | Self::SpecMember { .. }
190            | Self::RowField { .. }
191            | Self::RowValue => Vec::new(),
192            Self::And { operands } => operands.iter().collect(),
193            Self::Not { operand }
194            | Self::UnitConversion { operand, .. }
195            | Self::Math { operand, .. }
196            | Self::DateRelative { operand, .. }
197            | Self::DateCalendar { operand, .. }
198            | Self::PastFutureRange { operand, .. }
199            | Self::RangeBound { operand, .. }
200            | Self::IsVeto { operand } => vec![operand.as_ref()],
201            Self::Arithmetic { first, steps } => {
202                let mut out = vec![first.as_ref()];
203                out.extend(steps.iter().map(|step| &step.operand));
204                out
205            }
206            Self::Comparison { left, right, .. }
207            | Self::RangeLiteral {
208                from: left,
209                to: right,
210            }
211            | Self::RangeContainment {
212                value: left,
213                range: right,
214            } => vec![left.as_ref(), right.as_ref()],
215            Self::Member { base, .. } => vec![base.as_ref()],
216            Self::RowFieldValue { value, .. } => vec![value.as_ref()],
217            Self::ListCommand { list, args, .. } => {
218                let mut out = vec![list.as_ref()];
219                out.extend(args.iter());
220                out
221            }
222        }
223    }
224
225    /// Bottom-up fold with an explicit stack. `step` receives each node with its operands'
226    /// results in [`Self::operands`] order.
227    pub(crate) fn fold<U>(&self, mut step: impl FnMut(&ShowExpression, Vec<U>) -> U) -> U {
228        let mut values: Vec<U> = Vec::new();
229        let mut stack: Vec<(&ShowExpression, bool)> = vec![(self, false)];
230        while let Some((node, expanded)) = stack.pop() {
231            let operands = node.operands();
232            if !expanded {
233                stack.push((node, true));
234                stack.extend(operands.iter().rev().map(|&operand| (operand, false)));
235                continue;
236            }
237            let split = values
238                .len()
239                .checked_sub(operands.len())
240                .expect("BUG: operand folded before its parent");
241            let args = values.split_off(split);
242            values.push(step(node, args));
243        }
244        values
245            .pop()
246            .expect("BUG: fold produced no value for its root")
247    }
248}
249
250/// Next operand from a parent's operand list, in [`ShowExpression::operands`] order.
251pub(crate) fn take_operand<T, I: Iterator<Item = T>>(operands: &mut I) -> T {
252    operands
253        .next()
254        .expect("BUG: operand missing for its parent node")
255}
256
257/// Project the subtree at `root` into the Show tree.
258///
259/// `show_keys` are `RulePath::input_key` values for reachable Show rules. A
260/// `rule` leaf whose key is missing from that set is a bug.
261///
262/// Bottom-up projection with an explicit stack. Each node takes its operands' projections
263/// from the value stack in [`for_each_projection_operand`] order.
264fn project(
265    root: ExprRef<'_, ExpressionKind>,
266    show_keys: &HashSet<String>,
267    scratch: &mut ProjectScratch,
268) -> ShowExpression {
269    let ProjectScratch { values, stack } = scratch;
270    stack.push((root.id, false));
271    while let Some((id, expanded)) = stack.pop() {
272        let node = root.child(id);
273        if !expanded {
274            stack.push((id, true));
275            let start = stack.len();
276            for_each_projection_operand(node, |operand| stack.push((operand, false)));
277            stack[start..].reverse();
278            continue;
279        }
280        let mut arity = 0;
281        for_each_projection_operand(node, |_| arity += 1);
282        let split = values
283            .len()
284            .checked_sub(arity)
285            .expect("BUG: operand projected before its parent");
286        let shown = project_node(node, values.drain(split..), show_keys);
287        values.push(shown);
288    }
289    let shown = values
290        .pop()
291        .expect("BUG: projection produced no value for its root");
292    assert!(
293        values.is_empty(),
294        "BUG: projection left operands without a parent"
295    );
296    shown
297}
298
299/// Stacks of [`project`], reused across calls. Empty between calls.
300#[derive(Default)]
301struct ProjectScratch {
302    values: Vec<ShowExpression>,
303    stack: Vec<(ExprId, bool)>,
304}
305
306/// Calls `f` on the sub-expressions a Show node is built from, in [`project_node`] consumption order.
307/// An `as` run contributes only its innermost operand; a rule-path member base, none.
308fn for_each_projection_operand(node: ExprRef<'_, ExpressionKind>, mut f: impl FnMut(ExprId)) {
309    match node.kind() {
310        ExpressionKind::Piecewise(_) => {}
311        ExpressionKind::SpecMember { base, .. } => {
312            if !matches!(node.child(*base).kind(), ExpressionKind::RulePath(_)) {
313                f(*base);
314            }
315        }
316        ExpressionKind::UnitConversion(_, _) => f(conversion_operand(node)),
317        other => other.for_each_child(f),
318    }
319}
320
321/// Innermost operand of the `as` run ending at `node`.
322fn conversion_operand(node: ExprRef<'_, ExpressionKind>) -> ExprId {
323    let mut current = node;
324    while let ExpressionKind::UnitConversion(operand, _) = current.kind() {
325        current = current.child(*operand);
326    }
327    current.id
328}
329
330/// Innermost operand and targets (application order) of the `as` run ending at `node`.
331fn conversion_run(node: ExprRef<'_, ExpressionKind>) -> (ExprId, Vec<ShowConversionTarget>) {
332    let mut current = node;
333    let mut targets = Vec::new();
334    while let ExpressionKind::UnitConversion(operand, target) = current.kind() {
335        targets.push(conversion_target_from(target));
336        current = current.child(*operand);
337    }
338    targets.reverse();
339    (current.id, targets)
340}
341
342/// Build one Show node from its already-projected operands.
343fn project_node(
344    node: ExprRef<'_, ExpressionKind>,
345    mut operands: std::vec::Drain<'_, ShowExpression>,
346    show_keys: &HashSet<String>,
347) -> ShowExpression {
348    match node.kind() {
349        ExpressionKind::Literal(typed) => ShowExpression::Literal(Box::new(typed.as_ref().clone())),
350        ExpressionKind::DataPath(path) => ShowExpression::Data {
351            name: path.input_key(),
352        },
353        ExpressionKind::RulePath(path) => {
354            let key = path.input_key();
355            if !show_keys.contains(&key) {
356                panic!("BUG: rule leaf '{key}' not in Show.rules keys");
357            }
358            ShowExpression::Rule { name: key }
359        }
360        ExpressionKind::LogicalAnd { .. } => ShowExpression::And {
361            operands: operands.collect(),
362        },
363        ExpressionKind::LogicalNegation(_, _) => ShowExpression::Not {
364            operand: Box::new(take_operand(&mut operands)),
365        },
366        ExpressionKind::Arithmetic { steps, .. } => {
367            let first = Box::new(take_operand(&mut operands));
368            let steps = steps
369                .iter()
370                .map(|step| ShowArithmeticStep {
371                    op: step.op.clone(),
372                    operand: take_operand(&mut operands),
373                })
374                .collect();
375            ShowExpression::Arithmetic { first, steps }
376        }
377        ExpressionKind::Comparison(_, op, _) => {
378            let left = Box::new(take_operand(&mut operands));
379            let right = Box::new(take_operand(&mut operands));
380            ShowExpression::Comparison {
381                op: op.clone(),
382                left,
383                right,
384            }
385        }
386        ExpressionKind::UnitConversion(_, _) => {
387            let (_, targets) = conversion_run(node);
388            ShowExpression::UnitConversion {
389                operand: Box::new(take_operand(&mut operands)),
390                targets,
391            }
392        }
393        ExpressionKind::MathematicalComputation(op, _) => ShowExpression::Math {
394            op: op.clone(),
395            operand: Box::new(take_operand(&mut operands)),
396        },
397        ExpressionKind::Veto(VetoExpression { message }) => ShowExpression::Veto {
398            message: message.clone(),
399        },
400        ExpressionKind::Now => ShowExpression::Now,
401        ExpressionKind::DateRelative(kind, _) => ShowExpression::DateRelative {
402            kind: *kind,
403            operand: Box::new(take_operand(&mut operands)),
404        },
405        ExpressionKind::DateCalendar(kind, unit, _) => ShowExpression::DateCalendar {
406            kind: *kind,
407            unit: *unit,
408            operand: Box::new(take_operand(&mut operands)),
409        },
410        ExpressionKind::RangeLiteral(_, _) => {
411            let from = Box::new(take_operand(&mut operands));
412            let to = Box::new(take_operand(&mut operands));
413            ShowExpression::RangeLiteral { from, to }
414        }
415        ExpressionKind::PastFutureRange(kind, _) => ShowExpression::PastFutureRange {
416            kind: *kind,
417            operand: Box::new(take_operand(&mut operands)),
418        },
419        ExpressionKind::RangeContainment(_, _) => {
420            let value = Box::new(take_operand(&mut operands));
421            let range = Box::new(take_operand(&mut operands));
422            ShowExpression::RangeContainment { value, range }
423        }
424        ExpressionKind::RangeBound(bound, _) => ShowExpression::RangeBound {
425            bound: *bound,
426            operand: Box::new(take_operand(&mut operands)),
427        },
428        ExpressionKind::ResultIsVeto(_) => ShowExpression::IsVeto {
429            operand: Box::new(take_operand(&mut operands)),
430        },
431        ExpressionKind::Piecewise(_) => {
432            panic!("BUG: piecewise inside a rule branch")
433        }
434        ExpressionKind::SpecMember {
435            base, path, name, ..
436        } => {
437            let mut member = String::new();
438            for segment in path {
439                member.push_str(segment);
440                member.push('.');
441            }
442            member.push_str(name);
443            match node.child(*base).kind() {
444                ExpressionKind::RulePath(rule) => {
445                    let key = rule.input_key();
446                    if !show_keys.contains(&key) {
447                        panic!("BUG: spec member base '{key}' not in Show.rules keys");
448                    }
449                    ShowExpression::SpecMember { base: key, member }
450                }
451                _ => ShowExpression::Member {
452                    base: Box::new(take_operand(&mut operands)),
453                    member,
454                },
455            }
456        }
457        ExpressionKind::RowField { end, .. } => ShowExpression::RowField {
458            field: end.to_string(),
459        },
460        ExpressionKind::ListCommand { op, .. } => {
461            let list = Box::new(take_operand(&mut operands));
462            let (command, direction, args) = show_list_op(op, &mut operands);
463            ShowExpression::ListCommand {
464                list,
465                command,
466                direction,
467                args,
468            }
469        }
470    }
471}
472
473/// Operand expression ids of a list op, in the order [`show_list_op`] consumes them.
474fn show_list_op(
475    op: &ListOp,
476    operands: &mut std::vec::Drain<'_, ShowExpression>,
477) -> (ShowListCommand, Option<SortDirection>, Vec<ShowExpression>) {
478    use crate::parsing::ast::RowCondition;
479    use crate::planning::semantics::{ListPredKind, ListReduce};
480    let predicate = |kind: &ListPredKind| match kind {
481        ListPredKind::Any => ShowListCommand::Any,
482        ListPredKind::All => ShowListCommand::All,
483        ListPredKind::Where => ShowListCommand::Where,
484    };
485    match op {
486        ListOp::Find { keys } => (
487            ShowListCommand::Find,
488            None,
489            operands.by_ref().take(keys.len()).collect(),
490        ),
491        ListOp::First => (ShowListCommand::First, None, Vec::new()),
492        ListOp::Last => (ShowListCommand::Last, None, Vec::new()),
493        ListOp::Count => (ShowListCommand::Count, None, Vec::new()),
494        ListOp::Reduce { kind, argument } => {
495            let command = match kind {
496                ListReduce::Sum => ShowListCommand::Sum,
497                ListReduce::Avg => ShowListCommand::Avg,
498                ListReduce::Product => ShowListCommand::Product,
499                ListReduce::Min => ShowListCommand::Min,
500                ListReduce::Max => ShowListCommand::Max,
501            };
502            let args = if argument.is_some() {
503                vec![take_operand(operands)]
504            } else {
505                Vec::new()
506            };
507            (command, None, args)
508        }
509        ListOp::Predicate { kind, op, .. } => {
510            let right = Box::new(take_operand(operands));
511            let condition = match op {
512                RowCondition::Compare(comparison) => ShowExpression::Comparison {
513                    op: comparison.clone(),
514                    left: Box::new(ShowExpression::RowValue),
515                    right,
516                },
517                RowCondition::In => ShowExpression::RangeContainment {
518                    value: Box::new(ShowExpression::RowValue),
519                    range: right,
520                },
521            };
522            (predicate(kind), None, vec![condition])
523        }
524        ListOp::InstancePredicate { kind, .. } => {
525            (predicate(kind), None, vec![take_operand(operands)])
526        }
527        ListOp::Sort {
528            descending,
529            argument,
530        } => {
531            let direction = if *descending {
532                SortDirection::Desc
533            } else {
534                SortDirection::Asc
535            };
536            let args = if argument.is_some() {
537                vec![take_operand(operands)]
538            } else {
539                Vec::new()
540            };
541            (ShowListCommand::Sort, Some(direction), args)
542        }
543        ListOp::Top { .. } => (ShowListCommand::Top, None, vec![take_operand(operands)]),
544        ListOp::With { rows } => (
545            ShowListCommand::With,
546            None,
547            operands.by_ref().take(rows.len()).collect(),
548        ),
549        ListOp::WithRow { fields } => (
550            ShowListCommand::With,
551            None,
552            fields
553                .iter()
554                .map(|(path, _)| ShowExpression::RowFieldValue {
555                    field: path.input_key(),
556                    value: Box::new(take_operand(operands)),
557                })
558                .collect(),
559        ),
560    }
561}
562
563/// Project `RuleNode.branches` into Show arms (default first, no synthetic true).
564pub fn show_branches_from(
565    expression: &Expression,
566    branches: &[(Option<ExprId>, ExprId)],
567    show_keys: &HashSet<String>,
568) -> Vec<ShowBranch> {
569    let mut scratch = ProjectScratch::default();
570    branches
571        .iter()
572        .map(|(condition, result)| ShowBranch {
573            condition: condition
574                .map(|condition| project(expression.at(condition), show_keys, &mut scratch)),
575            result: project(expression.at(*result), show_keys, &mut scratch),
576        })
577        .collect()
578}
579
580/// Project `RuleNode.depends_on_rules` to Show.rules keys (BTreeSet order).
581pub fn project_depends_on_rules(
582    deps: &std::collections::BTreeSet<crate::planning::semantics::RulePath>,
583    show_keys: &HashSet<String>,
584) -> Vec<String> {
585    let mut out = Vec::new();
586    for path in deps {
587        let key = path.input_key();
588        if !show_keys.contains(&key) {
589            panic!("BUG: depends_on_rules entry '{key}' missing from Show.rules keys");
590        }
591        out.push(key);
592    }
593    out
594}
595
596/// Prefix Data/Rule/SpecMember names when lifting a template Show tree under a `uses` alias path.
597pub(crate) fn prefix_show_expression(prefix: &str, expression: &ShowExpression) -> ShowExpression {
598    expression.fold(|node, operands| prefix_show_node(prefix, node, operands))
599}
600
601fn prefix_show_node(
602    prefix: &str,
603    node: &ShowExpression,
604    operands: Vec<ShowExpression>,
605) -> ShowExpression {
606    let qualify = |name: &str| format!("{prefix}.{name}");
607    let mut operands = operands.into_iter();
608    match node {
609        ShowExpression::Literal(literal) => ShowExpression::Literal(literal.clone()),
610        ShowExpression::Data { name } => ShowExpression::Data {
611            name: qualify(name),
612        },
613        ShowExpression::Rule { name } => ShowExpression::Rule {
614            name: qualify(name),
615        },
616        ShowExpression::And { .. } => ShowExpression::And {
617            operands: operands.collect(),
618        },
619        ShowExpression::Not { .. } => ShowExpression::Not {
620            operand: Box::new(take_operand(&mut operands)),
621        },
622        ShowExpression::Arithmetic { steps, .. } => {
623            let first = Box::new(take_operand(&mut operands));
624            let steps = steps
625                .iter()
626                .map(|step| ShowArithmeticStep {
627                    op: step.op.clone(),
628                    operand: take_operand(&mut operands),
629                })
630                .collect();
631            ShowExpression::Arithmetic { first, steps }
632        }
633        ShowExpression::Comparison { op, .. } => {
634            let left = Box::new(take_operand(&mut operands));
635            let right = Box::new(take_operand(&mut operands));
636            ShowExpression::Comparison {
637                op: op.clone(),
638                left,
639                right,
640            }
641        }
642        ShowExpression::UnitConversion { targets, .. } => ShowExpression::UnitConversion {
643            operand: Box::new(take_operand(&mut operands)),
644            targets: targets.clone(),
645        },
646        ShowExpression::Math { op, .. } => ShowExpression::Math {
647            op: op.clone(),
648            operand: Box::new(take_operand(&mut operands)),
649        },
650        ShowExpression::Veto { message } => ShowExpression::Veto {
651            message: message.clone(),
652        },
653        ShowExpression::Now => ShowExpression::Now,
654        ShowExpression::DateRelative { kind, .. } => ShowExpression::DateRelative {
655            kind: *kind,
656            operand: Box::new(take_operand(&mut operands)),
657        },
658        ShowExpression::DateCalendar { kind, unit, .. } => ShowExpression::DateCalendar {
659            kind: *kind,
660            unit: *unit,
661            operand: Box::new(take_operand(&mut operands)),
662        },
663        ShowExpression::RangeLiteral { .. } => {
664            let from = Box::new(take_operand(&mut operands));
665            let to = Box::new(take_operand(&mut operands));
666            ShowExpression::RangeLiteral { from, to }
667        }
668        ShowExpression::PastFutureRange { kind, .. } => ShowExpression::PastFutureRange {
669            kind: *kind,
670            operand: Box::new(take_operand(&mut operands)),
671        },
672        ShowExpression::RangeContainment { .. } => {
673            let value = Box::new(take_operand(&mut operands));
674            let range = Box::new(take_operand(&mut operands));
675            ShowExpression::RangeContainment { value, range }
676        }
677        ShowExpression::RangeBound { bound, .. } => ShowExpression::RangeBound {
678            bound: *bound,
679            operand: Box::new(take_operand(&mut operands)),
680        },
681        ShowExpression::IsVeto { .. } => ShowExpression::IsVeto {
682            operand: Box::new(take_operand(&mut operands)),
683        },
684        ShowExpression::SpecMember { base, member } => ShowExpression::SpecMember {
685            base: qualify(base),
686            member: member.clone(),
687        },
688        ShowExpression::Member { member, .. } => ShowExpression::Member {
689            base: Box::new(take_operand(&mut operands)),
690            member: member.clone(),
691        },
692        ShowExpression::RowField { field } => ShowExpression::RowField {
693            field: field.clone(),
694        },
695        ShowExpression::RowValue => ShowExpression::RowValue,
696        ShowExpression::RowFieldValue { field, .. } => ShowExpression::RowFieldValue {
697            field: field.clone(),
698            value: Box::new(take_operand(&mut operands)),
699        },
700        ShowExpression::ListCommand {
701            command, direction, ..
702        } => {
703            let list = Box::new(take_operand(&mut operands));
704            ShowExpression::ListCommand {
705                list,
706                command: *command,
707                direction: *direction,
708                args: operands.collect(),
709            }
710        }
711    }
712}
713
714fn conversion_target_from(target: &SemanticConversionTarget) -> ShowConversionTarget {
715    match target {
716        SemanticConversionTarget::Type(kind) => ShowConversionTarget::Type(*kind),
717        SemanticConversionTarget::Unit { unit_name, .. } => ShowConversionTarget::Unit {
718            unit_name: unit_name.clone(),
719        },
720    }
721}