Skip to main content

zincite_query/program/
builder.rs

1//! Owned typed values lower directly to the same prepared report program.
2use super::*;
3use zirql::{ValueExpression as E, ValueExpressionKind as K};
4
5/// A typed value whose native leaves are shared expression wrappers around Query.
6/// The wrapper permits flat native pipelines to be released iteratively.
7pub type ProgramValue = E<zirql::Expression<ProgramNative, zirql::NativeSetOperator, ()>>;
8/// A typed pure function with located pattern parameters and unresolved host types.
9pub type ProgramFunction = zirql::UnresolvedPatternFunctionDeclaration<
10    zirql::Expression<ProgramNative, zirql::NativeSetOperator, ()>,
11>;
12
13/// Typed native stages use the same selection and collection execution as text.
14#[derive(Debug)]
15pub enum ProgramNative {
16    Query(Query),
17    Ordering(zirql::NativeOrdering<zirql::Expression<Self, zirql::NativeSetOperator, ()>>),
18    /// Construct an insertion-ordered dictionary from singleton-relative selectors.
19    Mapping(zirql::NativeMap<zirql::Expression<Self, zirql::NativeSetOperator, ()>>),
20    /// Stably sort dictionary values with the native typed scalar key contract.
21    MapSorting,
22    /// Keep original subjects whose pure native relation is nonempty.
23    Exists(Box<zirql::Expression<Self, zirql::NativeSetOperator, ()>>),
24    /// Replace the native selection until its ordered identities are unchanged.
25    FixedPoint(Box<zirql::Expression<Self, zirql::NativeSetOperator, ()>>),
26}
27
28impl ProgramNative {
29    fn take_selectors(
30        &mut self,
31        pending: &mut Vec<zirql::Expression<Self, zirql::NativeSetOperator, ()>>,
32    ) {
33        match self {
34            Self::Ordering(ordering) => pending.extend(ordering.selector.take()),
35            Self::Mapping(mapping) => {
36                pending.push(std::mem::replace(
37                    &mut mapping.key,
38                    Located::new(ExpressionKind::Input, 0..0),
39                ));
40                pending.push(std::mem::replace(
41                    &mut mapping.value,
42                    Located::new(ExpressionKind::Input, 0..0),
43                ));
44            }
45            Self::Exists(body) | Self::FixedPoint(body) => pending.push(*std::mem::replace(
46                body,
47                Box::new(Located::new(ExpressionKind::Input, 0..0)),
48            )),
49            Self::Query(_) | Self::MapSorting => {}
50        }
51    }
52}
53
54impl Drop for ProgramNative {
55    fn drop(&mut self) {
56        let mut pending = Vec::new();
57        self.take_selectors(&mut pending);
58        while let Some(expression) = pending.pop() {
59            match expression.value {
60                ExpressionKind::Input | ExpressionKind::Binding(_) => {}
61                ExpressionKind::Group(value) | ExpressionKind::Field { value, .. } => {
62                    pending.push(*value)
63                }
64                ExpressionKind::Record(fields) => {
65                    pending.extend(fields.into_iter().map(|(_, value)| value))
66                }
67                ExpressionKind::Set { left, right, .. } => {
68                    pending.push(*left);
69                    pending.push(*right);
70                }
71                ExpressionKind::Sequence { input, steps } => {
72                    pending.push(*input);
73                    for mut step in steps {
74                        if let ExpressionStep::Stage(stage) = &mut step.value {
75                            stage.take_selectors(&mut pending);
76                        }
77                    }
78                }
79            }
80        }
81    }
82}
83
84struct Inputs {
85    functions: Vec<ProgramFunction>,
86    bindings: Vec<(zirql::Pattern, ProgramValue)>,
87    report: Option<ProgramValue>,
88}
89impl Drop for Inputs {
90    fn drop(&mut self) {
91        for mut function in std::mem::take(&mut self.functions) {
92            for parameter in &mut function.parameters {
93                dispose_signature(std::mem::replace(
94                    &mut parameter.value.declaration.value,
95                    zirql::SignatureType::Integer,
96                ));
97            }
98            dispose_signature(std::mem::replace(
99                &mut function.result.value,
100                zirql::SignatureType::Integer,
101            ));
102            zirql::dispose_unresolved_pattern_functions(vec![function]);
103        }
104        for (pattern, value) in std::mem::take(&mut self.bindings) {
105            zirql::dispose_pattern(pattern);
106            zirql::dispose_native_value(value);
107        }
108        if let Some(value) = self.report.take() {
109            zirql::dispose_native_value(value);
110        }
111    }
112}
113impl Program {
114    /// Construct a pure report without parsing generated source. Native leaves
115    /// use shared expressions with `ProgramNative` stages and native set
116    /// operators. Query leaves own domain syntax; ordering selectors retain
117    /// their singleton input and the caller's query range.
118    ///
119    /// All input trees transfer to this call, including on failure. Preparation
120    /// against the current input/model happens during ordinary evaluation.
121    ///
122    /// # Errors
123    /// Unsupported constructs, duplicate names and exhausted construction bounds
124    /// retain caller-supplied ranges. Evaluation also checks scope, purity,
125    /// pattern compatibility and native types before executing statements.
126    pub fn from_values(
127        functions: Vec<ProgramFunction>,
128        bindings: Vec<(zirql::Pattern, ProgramValue)>,
129        final_report: ProgramValue,
130        limits: ProgramLimits,
131    ) -> Result<Self, QueryError> {
132        let mut inputs = Inputs {
133            functions,
134            bindings,
135            report: Some(final_report),
136        };
137        let mut budget = Budget::for_program(limits.core());
138        let range = inputs.report.as_ref().unwrap().range.clone();
139        let mut names = std::collections::BTreeSet::new();
140        for function in &inputs.functions {
141            functions::check_signature(&function.result, &mut budget, limits.nesting)?;
142            for parameter in &function.parameters {
143                functions::check_signature(
144                    &parameter.value.declaration,
145                    &mut budget,
146                    limits.nesting,
147                )?;
148            }
149            for query in functions::check_value(&function.value, &mut budget, limits.nesting)? {
150                check_native(query, &mut budget, limits.nesting)?;
151            }
152        }
153        let mut binding_names = Vec::new();
154        for (pattern, value) in &inputs.bindings {
155            let selected =
156                zirql::pattern_names(pattern, &mut budget).map_err(pipeline::core_error)?;
157            for name in &selected {
158                if !names.insert(name.value.clone()) {
159                    return Err(QueryError::query(
160                        name.range.clone(),
161                        "binding is already defined",
162                    ));
163                }
164            }
165            binding_names.push(selected.into_iter().cloned().collect());
166            for query in functions::check_value(value, &mut budget, limits.nesting)? {
167                check_native(query, &mut budget, limits.nesting)?;
168            }
169        }
170        for query in
171            functions::check_value(inputs.report.as_ref().unwrap(), &mut budget, limits.nesting)?
172        {
173            check_native(query, &mut budget, limits.nesting)?;
174        }
175        let mut queries = Vec::new();
176        let mut lower = |query, range: Range<usize>| {
177            let index = queries.len();
178            queries.push(ProgramQuery {
179                query,
180                arguments: Rc::new(Vec::new()),
181                copy_units: range.len() + 1,
182            });
183            value_stage(Stage::Query(index, Rc::new(Vec::new())), range)
184        };
185        let mut functions = std::mem::take(&mut inputs.functions)
186            .into_iter()
187            .map(|function| zirql::UnresolvedPatternFunctionDeclaration {
188                name: function.name,
189                parameters: function.parameters,
190                result: function.result,
191                value: map_queries(function.value, &mut |query| map_native(query, &mut lower)),
192            })
193            .collect::<Vec<_>>();
194        let mut statements = Vec::new();
195        for ((pattern, value), names) in std::mem::take(&mut inputs.bindings)
196            .into_iter()
197            .zip(binding_names)
198        {
199            let range = value.range.clone();
200            statements.push(Located::new(
201                zirql::Statement::BindFields {
202                    names,
203                    expression: value_stage(
204                        Stage::PatternBinding(
205                            pattern,
206                            map_queries(value, &mut |query| map_native(query, &mut lower)),
207                        ),
208                        range.clone(),
209                    ),
210                },
211                range,
212            ));
213        }
214        statements.push(Located::new(
215            zirql::Statement::Evaluate {
216                expression: value_stage(
217                    Stage::FunctionValue(map_queries(
218                        inputs.report.take().unwrap(),
219                        &mut |query| map_native(query, &mut lower),
220                    )),
221                    range.clone(),
222                ),
223                implicit_output: Some(Emitter::Json),
224            },
225            range.clone(),
226        ));
227        for function in &mut functions {
228            functions::lower_primitives(
229                &mut function.value,
230                &BTreeMap::new(),
231                &queries,
232                &Default::default(),
233            );
234        }
235        for statement in &mut statements {
236            let expression = match &mut statement.value {
237                zirql::Statement::Bind { expression, .. }
238                | zirql::Statement::BindFields { expression, .. }
239                | zirql::Statement::Evaluate { expression, .. } => expression,
240            };
241            if let ExpressionKind::Sequence { steps, .. } = &mut expression.value {
242                for step in steps {
243                    if let ExpressionStep::Stage(
244                        Stage::FunctionValue(value) | Stage::PatternBinding(_, value),
245                    ) = &mut step.value
246                    {
247                        functions::lower_primitives(
248                            value,
249                            &BTreeMap::new(),
250                            &queries,
251                            &Default::default(),
252                        );
253                    }
254                }
255            }
256        }
257        let report_model_demand = queries
258            .iter()
259            .any(|query| query.query.requires_model_facts())
260            || functions.iter().any(|function| {
261                functions::signature_needs_model(&function.result.value)
262                    || function.parameters.iter().any(|parameter| {
263                        functions::signature_needs_model(&parameter.value.declaration.value)
264                    })
265            });
266        Ok(Self {
267            parameters: Vec::new(),
268            program: zirql::Program::new(statements, range.clone()),
269            preflight_work: limits.work - budget.remaining_work(),
270            preflight_results: limits.results - budget.remaining_results().unwrap(),
271            preflight_range: Some(range),
272            queries,
273            functions,
274            function_limits: zirql::FunctionLimits { active_depth: 64 },
275            report_model_demand,
276            editing: false,
277            prepared: true,
278            prepared_values: false,
279        })
280    }
281}
282
283fn map_native(
284    query: zirql::Expression<ProgramNative, zirql::NativeSetOperator, ()>,
285    lower: &mut impl FnMut(Query, Range<usize>) -> Expression,
286) -> Expression {
287    let range = query.range;
288    Located::new(
289        match query.value {
290            ExpressionKind::Input => ExpressionKind::Input,
291            ExpressionKind::Binding(name) => ExpressionKind::Binding(name),
292            ExpressionKind::Group(value) => {
293                ExpressionKind::Group(Box::new(map_native(*value, lower)))
294            }
295            ExpressionKind::Field { value, field } => ExpressionKind::Field {
296                value: Box::new(map_native(*value, lower)),
297                field,
298            },
299            ExpressionKind::Record(fields) => ExpressionKind::Record(
300                fields
301                    .into_iter()
302                    .map(|(name, value)| (name, map_native(value, lower)))
303                    .collect(),
304            ),
305            ExpressionKind::Set {
306                operator,
307                left,
308                right,
309            } => ExpressionKind::Set {
310                operator,
311                left: Box::new(map_native(*left, lower)),
312                right: Box::new(map_native(*right, lower)),
313            },
314            ExpressionKind::Sequence { input, steps } => {
315                let mut input = map_native(*input, lower);
316                for step in steps {
317                    match step.value {
318                        ExpressionStep::Stage(mut stage) => {
319                            let mut leaf = match &mut stage {
320                                ProgramNative::Query(query) => lower(
321                                    std::mem::replace(
322                                        query,
323                                        Query {
324                                            stages: Vec::new(),
325                                            range: 0..0,
326                                            nesting_limit: 1,
327                                        },
328                                    ),
329                                    step.range.clone(),
330                                ),
331                                ProgramNative::Mapping(mapping) => value_stage(
332                                    Stage::Mapping(native::MappingStage::new(zirql::NativeMap {
333                                        key: map_native(
334                                            std::mem::replace(
335                                                &mut mapping.key,
336                                                Located::new(ExpressionKind::Input, 0..0),
337                                            ),
338                                            lower,
339                                        ),
340                                        value: map_native(
341                                            std::mem::replace(
342                                                &mut mapping.value,
343                                                Located::new(ExpressionKind::Input, 0..0),
344                                            ),
345                                            lower,
346                                        ),
347                                    })),
348                                    step.range.clone(),
349                                ),
350                                ProgramNative::Exists(body) | ProgramNative::FixedPoint(body) => {
351                                    let body = map_native(
352                                        *std::mem::replace(
353                                            body,
354                                            Box::new(Located::new(ExpressionKind::Input, 0..0)),
355                                        ),
356                                        lower,
357                                    );
358                                    let body = native::BodyStage::new(body);
359                                    let stage = if matches!(stage, ProgramNative::Exists(_)) {
360                                        Stage::WhereExists(body)
361                                    } else {
362                                        Stage::FixedPoint(body)
363                                    };
364                                    value_stage(stage, step.range.clone())
365                                }
366                                ProgramNative::MapSorting => {
367                                    value_stage(Stage::MapSorting, step.range.clone())
368                                }
369                                ProgramNative::Ordering(ordering) => value_stage(
370                                    Stage::Ordering(native::OrderingStage::new(
371                                        zirql::NativeOrdering {
372                                            order: ordering.order,
373                                            selector: ordering
374                                                .selector
375                                                .take()
376                                                .map(|selector| map_native(selector, lower)),
377                                        },
378                                    )),
379                                    step.range.clone(),
380                                ),
381                            };
382                            let ExpressionKind::Sequence { steps, .. } = &mut leaf.value else {
383                                unreachable!()
384                            };
385                            input = Located::new(
386                                ExpressionKind::Sequence {
387                                    input: Box::new(input),
388                                    steps: std::mem::take(steps),
389                                },
390                                step.range,
391                            );
392                        }
393                        ExpressionStep::Emit { .. } => {
394                            unreachable!("typed emission rejected before lowering")
395                        }
396                    }
397                }
398                return input;
399            }
400        },
401        range,
402    )
403}
404
405pub(crate) fn map_queries<Q, R>(value: E<Q>, lower: &mut impl FnMut(Q) -> R) -> E<R> {
406    let range = value.range;
407    let value = match value.value {
408        K::CurrentInput => K::CurrentInput,
409        K::Literal(value) => K::Literal(value),
410        K::Name(name) => K::Name(name),
411        K::NativeQuery(query) => K::NativeQuery(Box::new(lower(*query))),
412        K::NativeValue(query) => K::NativeValue(Box::new(lower(*query))),
413        K::Call {
414            function,
415            arguments,
416        } => K::Call {
417            function,
418            arguments: arguments
419                .into_iter()
420                .map(|value| map_queries(value, lower))
421                .collect(),
422        },
423        K::Array(values) => K::Array(values.into_iter().map(|v| map_queries(v, lower)).collect()),
424        K::Tuple(values) => K::Tuple(values.into_iter().map(|v| map_queries(v, lower)).collect()),
425        K::Set(values) => K::Set(values.into_iter().map(|v| map_queries(v, lower)).collect()),
426        K::Dictionary(entries) => K::Dictionary(
427            entries
428                .into_iter()
429                .map(|(key, value)| (map_queries(key, lower), map_queries(value, lower)))
430                .collect(),
431        ),
432        K::DictionaryComprehension { clauses, key, head } => K::DictionaryComprehension {
433            clauses: map_clauses(clauses, lower),
434            key: Box::new(map_queries(*key, lower)),
435            head: Box::new(map_queries(*head, lower)),
436        },
437        K::Record(fields) => K::Record(
438            fields
439                .into_iter()
440                .map(|(n, v)| (n, map_queries(v, lower)))
441                .collect(),
442        ),
443        K::Block { bindings, value } => K::Block {
444            bindings: bindings
445                .into_iter()
446                .map(|(p, v)| (p, map_queries(v, lower)))
447                .collect(),
448            value: Box::new(map_queries(*value, lower)),
449        },
450        K::ValueMatch { subject, arms } => K::ValueMatch {
451            subject: Box::new(map_queries(*subject, lower)),
452            arms: arms
453                .into_iter()
454                .map(|arm| zirql::ValueMatchArm {
455                    pattern: arm.pattern,
456                    guard: arm.guard.map(|v| map_queries(v, lower)),
457                    value: map_queries(arm.value, lower),
458                })
459                .collect(),
460        },
461        K::Conditional {
462            branches,
463            otherwise,
464        } => K::Conditional {
465            branches: branches
466                .into_iter()
467                .map(|(a, b)| (map_queries(a, lower), map_queries(b, lower)))
468                .collect(),
469            otherwise: Box::new(map_queries(*otherwise, lower)),
470        },
471        K::KeyedArray {
472            axes,
473            declared_axis_sources,
474            entries,
475        } => K::KeyedArray {
476            axes,
477            declared_axis_sources: declared_axis_sources
478                .map(|values| values.into_iter().map(|v| map_queries(v, lower)).collect()),
479            entries: entries
480                .into_iter()
481                .map(|(a, b)| (map_queries(a, lower), map_queries(b, lower)))
482                .collect(),
483        },
484        K::Comprehension {
485            constructor,
486            clauses,
487            head,
488        } => K::Comprehension {
489            constructor,
490            clauses: map_clauses(clauses, lower),
491            head: Box::new(map_queries(*head, lower)),
492        },
493        K::KeyedComprehension {
494            clauses,
495            key,
496            head,
497            declared_axis_sources,
498            empty_axis_sources,
499        } => K::KeyedComprehension {
500            declared_axis_sources: declared_axis_sources
501                .map(|values| values.into_iter().map(|v| map_queries(v, lower)).collect()),
502            clauses: map_clauses(clauses, lower),
503            key: Box::new(map_queries(*key, lower)),
504            head: Box::new(map_queries(*head, lower)),
505            empty_axis_sources: empty_axis_sources
506                .map(|values| values.into_iter().map(|v| map_queries(v, lower)).collect()),
507        },
508        K::Unique(value) => K::Unique(Box::new(map_queries(*value, lower))),
509        K::Tally(value) => K::Tally(Box::new(map_queries(*value, lower))),
510        K::Occurs(value) => K::Occurs(Box::new(map_queries(*value, lower))),
511        K::Absent(value) => K::Absent(Box::new(map_queries(*value, lower))),
512        K::One(value) => K::One(Box::new(map_queries(*value, lower))),
513        K::IndexSet(value) => K::IndexSet(Box::new(map_queries(*value, lower))),
514        K::Entries(value) => K::Entries(Box::new(map_queries(*value, lower))),
515        K::Unary { operator, operand } => K::Unary {
516            operator,
517            operand: Box::new(map_queries(*operand, lower)),
518        },
519        K::Binary {
520            operator,
521            left,
522            right,
523        } => K::Binary {
524            operator,
525            left: Box::new(map_queries(*left, lower)),
526            right: Box::new(map_queries(*right, lower)),
527        },
528        K::Field { value, field } => K::Field {
529            value: Box::new(map_queries(*value, lower)),
530            field,
531        },
532        K::TupleAccess { value, position } => K::TupleAccess {
533            value: Box::new(map_queries(*value, lower)),
534            position,
535        },
536        K::ArrayAccess { array, index } => K::ArrayAccess {
537            array: Box::new(map_queries(*array, lower)),
538            index: Box::new(map_queries(*index, lower)),
539        },
540        K::Lookup { value, key } => K::Lookup {
541            value: Box::new(map_queries(*value, lower)),
542            key: Box::new(map_queries(*key, lower)),
543        },
544        K::HasKey { value, key } => K::HasKey {
545            value: Box::new(map_queries(*value, lower)),
546            key: Box::new(map_queries(*key, lower)),
547        },
548        K::Default { value, fallback } => K::Default {
549            value: Box::new(map_queries(*value, lower)),
550            fallback: Box::new(map_queries(*fallback, lower)),
551        },
552        K::ZipExact { left, right } => K::ZipExact {
553            left: Box::new(map_queries(*left, lower)),
554            right: Box::new(map_queries(*right, lower)),
555        },
556        K::Reduce { operator, array } => K::Reduce {
557            operator,
558            array: Box::new(map_queries(*array, lower)),
559        },
560        K::PredicateSubject => K::PredicateSubject,
561        K::SubjectScope { subject, argument } => K::SubjectScope {
562            subject: Box::new(map_queries(*subject, lower)),
563            argument: zirql::PredicateArgument {
564                argument: argument.argument,
565                body: Box::new(map_queries(*argument.body, lower)),
566            },
567        },
568        K::PredicateScope { subject, predicate } => K::PredicateScope {
569            subject: Box::new(map_queries(*subject, lower)),
570            predicate: zirql::PredicateArgument {
571                argument: predicate.argument,
572                body: Box::new(map_queries(*predicate.body, lower)),
573            },
574        },
575        K::Filter { array, predicate } => K::Filter {
576            array: Box::new(map_queries(*array, lower)),
577            predicate: zirql::PredicateArgument {
578                argument: predicate.argument,
579                body: Box::new(map_queries(*predicate.body, lower)),
580            },
581        },
582        K::Sort {
583            array,
584            selector,
585            descending,
586        } => K::Sort {
587            array: Box::new(map_queries(*array, lower)),
588            selector,
589            descending,
590        },
591        K::Extremum {
592            operator,
593            array,
594            field,
595        } => K::Extremum {
596            operator,
597            array: Box::new(map_queries(*array, lower)),
598            field,
599        },
600        K::GroupBy { rows, field } => K::GroupBy {
601            rows: Box::new(map_queries(*rows, lower)),
602            field,
603        },
604        K::Require {
605            condition,
606            value,
607            message,
608        } => K::Require {
609            condition: Box::new(map_queries(*condition, lower)),
610            value: Box::new(map_queries(*value, lower)),
611            message,
612        },
613        K::Range {
614            lower: start,
615            upper,
616        } => K::Range {
617            lower: Box::new(map_queries(*start, lower)),
618            upper: Box::new(map_queries(*upper, lower)),
619        },
620        K::Queue { items, element } => K::Queue {
621            items: Box::new(map_queries(*items, lower)),
622            element,
623        },
624        K::PopFront(value) => K::PopFront(Box::new(map_queries(*value, lower))),
625        K::Break(value) => K::Break(Box::new(map_queries(*value, lower))),
626        K::Continue(values) => {
627            K::Continue(values.into_iter().map(|v| map_queries(v, lower)).collect())
628        }
629        K::PushBackAll { queue, items } => K::PushBackAll {
630            queue: Box::new(map_queries(*queue, lower)),
631            items: Box::new(map_queries(*items, lower)),
632        },
633        K::IdentitySet { element } => K::IdentitySet { element },
634        K::Insert { set, item } => K::Insert {
635            set: Box::new(map_queries(*set, lower)),
636            item: Box::new(map_queries(*item, lower)),
637        },
638        K::Fold {
639            items,
640            initial,
641            step,
642        } => K::Fold {
643            items: Box::new(map_queries(*items, lower)),
644            initial: Box::new(map_queries(*initial, lower)),
645            step,
646        },
647        K::Scan {
648            items,
649            initial,
650            step,
651        } => K::Scan {
652            items: Box::new(map_queries(*items, lower)),
653            initial: Box::new(map_queries(*initial, lower)),
654            step,
655        },
656        K::Loop { states, body } => K::Loop {
657            states: states
658                .into_iter()
659                .map(|(n, v)| (n, map_queries(v, lower)))
660                .collect(),
661            body: Box::new(map_queries(*body, lower)),
662        },
663    };
664    Located::new(value, range)
665}
666fn map_clauses<Q, R>(
667    clauses: Vec<Located<zirql::ValueGenerator<Q>>>,
668    lower: &mut impl FnMut(Q) -> R,
669) -> Vec<Located<zirql::ValueGenerator<R>>> {
670    clauses
671        .into_iter()
672        .map(|clause| {
673            Located::new(
674                match clause.value {
675                    zirql::ValueGenerator::In {
676                        binders,
677                        source,
678                        filter,
679                    } => zirql::ValueGenerator::In {
680                        binders,
681                        source: map_queries(source, lower),
682                        filter: filter.map(|v| map_queries(v, lower)),
683                    },
684                    zirql::ValueGenerator::Bind {
685                        binder,
686                        value,
687                        filter,
688                    } => zirql::ValueGenerator::Bind {
689                        binder,
690                        value: map_queries(value, lower),
691                        filter: filter.map(|v| map_queries(v, lower)),
692                    },
693                },
694                clause.range,
695            )
696        })
697        .collect()
698}
699
700fn check_native(
701    query: &zirql::Expression<ProgramNative, zirql::NativeSetOperator, ()>,
702    budget: &mut Budget,
703    nesting: usize,
704) -> Result<(), QueryError> {
705    let mut pending = vec![(query, 0)];
706    while let Some((query, depth)) = pending.pop() {
707        if depth >= nesting.min(64) {
708            return Err(QueryError::query(
709                query.range.clone(),
710                "native expression nesting limit exceeded",
711            ));
712        }
713        budget
714            .charge(1, zirql::Location::Query(query.range.clone()))
715            .map_err(pipeline::core_error)?;
716        match &query.value {
717            ExpressionKind::Input | ExpressionKind::Binding(_) => {}
718            ExpressionKind::Group(value) | ExpressionKind::Field { value, .. } => {
719                pending.push((value, depth + 1))
720            }
721            ExpressionKind::Record(fields) => {
722                let location = zirql::Location::Query(query.range.clone());
723                budget
724                    .charge(fields.len(), location.clone())
725                    .map_err(pipeline::core_error)?;
726                budget
727                    .charge_results(fields.len(), location)
728                    .map_err(pipeline::core_error)?;
729                pending.extend(fields.iter().map(|(_, v)| (v, depth + 1)))
730            }
731            ExpressionKind::Set { left, right, .. } => {
732                pending.push((left, depth + 1));
733                pending.push((right, depth + 1));
734            }
735            ExpressionKind::Sequence { input, steps } => {
736                pending.push((input, depth + 1));
737                for step in steps {
738                    budget
739                        .charge_results(1, zirql::Location::Query(step.range.clone()))
740                        .map_err(pipeline::core_error)?;
741                    match &step.value {
742                        ExpressionStep::Stage(ProgramNative::Query(query))
743                            if !query.is_editing()
744                                && !query.stages.iter().any(|stage| {
745                                    matches!(
746                                        stage.kind,
747                                        language::StageKind::Emit
748                                            | language::StageKind::Json
749                                            | language::StageKind::EmitDocument
750                                    )
751                                }) =>
752                        {
753                            budget
754                                .charge(
755                                    query.range.len() + query.stages.len(),
756                                    zirql::Location::Query(step.range.clone()),
757                                )
758                                .map_err(pipeline::core_error)?;
759                            budget
760                                .charge_results(
761                                    query.stages.len(),
762                                    zirql::Location::Query(step.range.clone()),
763                                )
764                                .map_err(pipeline::core_error)?;
765                        }
766                        ExpressionStep::Stage(ProgramNative::Mapping(mapping)) => {
767                            pending.push((&mapping.key, depth + 1));
768                            pending.push((&mapping.value, depth + 1));
769                        }
770                        ExpressionStep::Stage(
771                            ProgramNative::Exists(body) | ProgramNative::FixedPoint(body),
772                        ) => pending.push((body, depth + 1)),
773                        ExpressionStep::Stage(ProgramNative::MapSorting) => {}
774                        ExpressionStep::Stage(ProgramNative::Ordering(ordering)) => {
775                            if let Some(selector) = &ordering.selector {
776                                pending.push((selector, depth + 1));
777                            }
778                        }
779                        ExpressionStep::Stage(_) | ExpressionStep::Emit { .. } => {
780                            return Err(QueryError::query(
781                                step.range.clone(),
782                                "typed report values must be pure",
783                            ));
784                        }
785                    }
786                }
787            }
788        }
789    }
790    Ok(())
791}
792
793fn dispose_signature(signature: zirql::SignatureType) {
794    use zirql::SignatureType as S;
795    let mut pending = vec![signature];
796    while let Some(signature) = pending.pop() {
797        match signature {
798            S::Optional(value)
799            | S::Sequence(value)
800            | S::Set(value)
801            | S::Keyed(value)
802            | S::Queue(value)
803            | S::IdentitySet(value) => pending.push(value.value),
804            S::Tuple(values) => pending.extend(values.into_iter().map(|value| value.value)),
805            S::Record(fields) => pending.extend(fields.into_iter().map(|(_, value)| value.value)),
806            S::Boolean | S::Integer | S::Float | S::String | S::Named(_) => {}
807        }
808    }
809}