Skip to main content

zincite_query/
program.rs

1//! Native reports and atomic model-backed data batches using shared evaluation.
2use crate::{
3    Input, Limits, Query, QueryError, QueryResult, Selection, SemanticResult, SemanticStream, Work,
4    comprehension, data, inspection, language, pipeline, semantic,
5};
6use serde_json::{Value, json};
7use std::{collections::BTreeMap, ops::Range, rc::Rc, sync::Arc};
8use zincite_lint::{ModelContext, ModelFacts, SourceDiagnostic};
9use zirql::{
10    Bindings, Budget, EmissionPolicy, ExpressionKind, ExpressionStep, Located, NativeInput,
11    NativeValue, ProgramHost, ProjectionHost,
12};
13
14pub(crate) mod builder;
15pub use builder::{ProgramFunction, ProgramNative, ProgramValue};
16mod composition;
17mod functions;
18mod native;
19mod worklists;
20pub use worklists::{Computational, NativeSequence, NativeTuple};
21
22type Expression = zirql::Expression<Stage, zirql::NativeSetOperator, Emitter>;
23
24#[derive(Debug)]
25enum Stage {
26    Parameter(usize),
27    FunctionValue(zirql::ValueExpression<Expression>),
28    FunctionSource(zirql::ValueExpression<Expression>),
29    PatternBinding(zirql::Pattern, zirql::ValueExpression<Expression>),
30    RecordValue(Vec<(Located<String>, zirql::ValueExpression<Expression>)>),
31    Primitive(
32        functions::Primitive,
33        Vec<zirql::ValueExpression<Expression>>,
34    ),
35    Member(comprehension::MemberRequest),
36    Query(usize, Rc<Vec<QueryArgument>>),
37    Ordering(native::OrderingStage),
38    WhereExists(native::BodyStage),
39    FixedPoint(native::BodyStage),
40    Mapping(native::MappingStage),
41    MapSorting,
42    RetainPayload,
43    Projection(composition::ProjectionStage),
44    Match(composition::MatchStage),
45    Paths(composition::PathStage),
46    Filter(composition::FilterStage),
47    Data(Vec<data::Edit>),
48}
49
50impl Drop for Stage {
51    fn drop(&mut self) {
52        let empty = || {
53            Located::new(
54                zirql::ValueExpressionKind::Literal(zirql::ConstructedValue::Absent),
55                0..0,
56            )
57        };
58        match self {
59            Self::FunctionValue(value) | Self::FunctionSource(value) => {
60                zirql::dispose_native_value(std::mem::replace(value, empty()))
61            }
62            Self::PatternBinding(pattern, value) => {
63                zirql::dispose_pattern(std::mem::replace(
64                    pattern,
65                    Located::new(zirql::PatternKind::Wildcard, 0..0),
66                ));
67                zirql::dispose_native_value(std::mem::replace(value, empty()));
68            }
69            Self::RecordValue(fields) => {
70                for (_, value) in std::mem::take(fields) {
71                    zirql::dispose_native_value(value);
72                }
73            }
74            Self::Primitive(_, values) => {
75                for value in std::mem::take(values) {
76                    zirql::dispose_native_value(value);
77                }
78            }
79            Self::Parameter(_)
80            | Self::Member(_)
81            | Self::Query(..)
82            | Self::Ordering(_)
83            | Self::WhereExists(_)
84            | Self::FixedPoint(_)
85            | Self::Mapping(_)
86            | Self::MapSorting
87            | Self::RetainPayload
88            | Self::Projection(_)
89            | Self::Match(_)
90            | Self::Filter(_)
91            | Self::Paths(_)
92            | Self::Data(_) => {}
93        }
94    }
95}
96
97/// Program bounds retain the single-pipeline stream ceiling and add a request
98/// allowance for produced/copied native entries. Model tokens consume work.
99#[derive(Clone, Copy, Debug)]
100pub struct ProgramLimits {
101    pub work: usize,
102    pub results: usize,
103    pub nesting: usize,
104    pub stream_items: usize,
105}
106impl Default for ProgramLimits {
107    fn default() -> Self {
108        let limits = Limits::default();
109        Self {
110            work: limits.work,
111            results: 100_000,
112            nesting: limits.nesting,
113            stream_items: limits.collection,
114        }
115    }
116}
117impl ProgramLimits {
118    fn query(self) -> Limits {
119        Limits {
120            work: self.work,
121            collection: self.stream_items,
122            nesting: self.nesting,
123        }
124    }
125    fn core(self) -> zirql::ProgramLimits {
126        zirql::ProgramLimits {
127            work: self.work,
128            results: self.results,
129            nesting: self.nesting,
130            stream_items: self.stream_items,
131        }
132    }
133}
134
135#[derive(Clone, Copy, Debug, PartialEq, Eq)]
136enum Emitter {
137    Source,
138    Json,
139}
140#[derive(Clone, Debug, PartialEq)]
141enum Kind {
142    Nodes,
143    Node,
144    Computational(Rc<zirql::DeclaredType<Kind>>),
145    Tuple(Rc<Vec<Kind>>),
146    Sequence(Rc<zirql::DeclaredType<Kind>>),
147    Declaration,
148    Strings,
149    Literals,
150    Count,
151    Tally,
152    Record,
153    Dictionary(Rc<Vec<Kind>>),
154    Payload(Rc<Kind>),
155    Entities,
156    Declarations,
157    Types,
158    Instantiations,
159    Constructed,
160    Scalar(zirql::ParameterType),
161    Value(Rc<Vec<zirql::ValueType>>),
162    Capture(Rc<zirql::NativeCapture<Kind>>),
163    Captures(Rc<zirql::NativeCapture<Kind>>),
164}
165
166/// Native report leaves retain source selections and semantic identities.
167/// Constructed values have no input-source ranges; record labels preserve order.
168#[derive(Debug)]
169pub enum ReportValue<'a> {
170    Retained(Arc<Self>),
171    /// A best-effort native selection that skipped an uncertain observation.
172    /// Ordinary output remains available; required nested observations refuse it.
173    IncompleteSelection {
174        value: Box<Self>,
175        error: Arc<QueryError>,
176    },
177    Computational(Computational<'a>),
178    Tuple(NativeTuple<'a>),
179    Sequence(NativeSequence<'a>),
180    Item(Box<Self>),
181    /// Retain the native entity element contract even for an empty stream.
182    EntitySequence {
183        declarations: bool,
184        owner: Option<Rc<ModelFacts<'a>>>,
185        value: Box<Self>,
186    },
187    Query(SemanticStream<'a>),
188    Record(Vec<(String, ReportValue<'a>)>),
189    Capture(zirql::NativeCapture<ReportValue<'a>>),
190    Captures(CaptureMatches<'a>),
191    Constructed(zirql::ConstructedValue),
192    SharedConstructed(Arc<zirql::ConstructedValue>),
193}
194impl<'a> ReportValue<'a> {
195    pub fn fields(&self) -> Option<&[(String, ReportValue<'a>)]> {
196        match self {
197            Self::Retained(value) => value.fields(),
198            Self::Item(value)
199            | Self::EntitySequence { value, .. }
200            | Self::IncompleteSelection { value, .. } => value.fields(),
201            Self::Record(fields) => Some(fields),
202            Self::Capture(capture) => Some(&capture.fields),
203            Self::Computational(_)
204            | Self::Tuple(_)
205            | Self::Sequence(_)
206            | Self::Query(_)
207            | Self::Constructed(_)
208            | Self::SharedConstructed(_)
209            | Self::Captures(_) => None,
210        }
211    }
212    pub fn stream(&self) -> Option<&SemanticStream<'a>> {
213        match self {
214            Self::Retained(value) => value.stream(),
215            Self::Item(value)
216            | Self::EntitySequence { value, .. }
217            | Self::IncompleteSelection { value, .. } => value.stream(),
218            Self::Query(stream) => Some(stream),
219            Self::Computational(_)
220            | Self::Tuple(_)
221            | Self::Sequence(_)
222            | Self::Record(_)
223            | Self::Constructed(_)
224            | Self::SharedConstructed(_)
225            | Self::Capture(_)
226            | Self::Captures(_) => None,
227        }
228    }
229}
230
231/// Native match rows retain their declared fields even when no candidate matches.
232#[derive(Debug)]
233pub struct CaptureMatches<'a> {
234    rows: Vec<zirql::NativeCapture<ReportValue<'a>>>,
235    schema: Rc<zirql::NativeCapture<Kind>>,
236}
237impl<'a> CaptureMatches<'a> {
238    pub fn rows(&self) -> &[zirql::NativeCapture<ReportValue<'a>>] {
239        &self.rows
240    }
241}
242
243#[derive(Debug)]
244pub struct ProgramEmission<'a> {
245    value: ReportValue<'a>,
246    emitter: Emitter,
247    range: Range<usize>,
248}
249impl<'a> ProgramEmission<'a> {
250    pub fn value(&self) -> &ReportValue<'a> {
251        &self.value
252    }
253    pub fn is_json(&self) -> bool {
254        self.emitter == Emitter::Json
255    }
256}
257
258/// Successful evaluation returns all emission requests together. No callback
259/// runs during evaluation. Rendering spends the same request budget and callers
260/// should commit the returned bytes only after rendering succeeds.
261#[derive(Debug)]
262pub struct ProgramResult<'a> {
263    emissions: Vec<ProgramEmission<'a>>,
264    candidate: Option<Input>,
265    candidate_range: Range<usize>,
266    semantic: Option<SemanticResult<'a>>,
267    facts: Option<Rc<ModelFacts<'a>>>,
268    report_limitations: Vec<SourceDiagnostic>,
269    budget: Budget,
270}
271impl<'a> ProgramResult<'a> {
272    /// The complete reparsed document, present only for an editing program.
273    pub fn candidate(&self) -> Option<&Input> {
274        self.candidate.as_ref()
275    }
276    pub fn emissions(&self) -> &[ProgramEmission<'a>] {
277        &self.emissions
278    }
279    pub fn semantic(&self) -> Option<&SemanticResult<'a>> {
280        self.semantic.as_ref()
281    }
282    /// Retained owner for native nominal IDs, including pure `.dzn` reports.
283    pub fn model_facts(&self) -> Option<&ModelFacts<'a>> {
284        self.facts.as_deref()
285    }
286    pub fn limitations(&self) -> &[SourceDiagnostic] {
287        self.semantic
288            .as_ref()
289            .map_or(&self.report_limitations, SemanticResult::limitations)
290    }
291    pub fn is_complete(&self) -> bool {
292        self.limitations().is_empty()
293    }
294
295    /// Render all requested outputs into one buffer. Repeated rendering charges
296    /// the allowance again; a failure returns no partial bytes.
297    pub fn render(&mut self) -> Result<Vec<u8>, QueryError> {
298        if let Some(candidate) = &self.candidate {
299            self.budget
300                .charge(
301                    candidate.source_bytes().len(),
302                    zirql::Location::Query(self.candidate_range.clone()),
303                )
304                .map_err(pipeline::core_error)?;
305            return Ok(candidate.source_bytes().to_vec());
306        }
307        let mut bytes = Vec::new();
308        for emission in &self.emissions {
309            let mut output = match (&emission.emitter, &emission.value) {
310                (Emitter::Source, ReportValue::Query(SemanticStream::Inspection(result))) => {
311                    charge_render(
312                        &emission.value,
313                        self.semantic.as_ref(),
314                        &mut self.budget,
315                        &emission.range,
316                    )?;
317                    if let QueryResult::Tally(values) = result.as_ref() {
318                        let mut output = Vec::new();
319                        serde_json::to_writer(
320                            BudgetWriter {
321                                bytes: &mut output,
322                                budget: &mut self.budget,
323                                range: &emission.range,
324                            },
325                            &crate::inspection::tally_json(values),
326                        )
327                        .map_err(|error| {
328                            QueryError::query(emission.range.clone(), error.to_string())
329                        })?;
330                        self.budget
331                            .charge(1, zirql::Location::Query(emission.range.clone()))
332                            .map_err(pipeline::core_error)?;
333                        output.push(b'\n');
334                        output
335                    } else {
336                        result.render()
337                    }
338                }
339                (Emitter::Source, ReportValue::Query(stream)) => {
340                    charge_render(
341                        &emission.value,
342                        self.semantic.as_ref(),
343                        &mut self.budget,
344                        &emission.range,
345                    )?;
346                    self.semantic.as_ref().unwrap().render_stream(stream)
347                }
348                _ => {
349                    admit_json(
350                        &emission.value,
351                        self.semantic.as_ref(),
352                        &mut Work {
353                            budget: &mut self.budget,
354                        },
355                        &emission.range,
356                    )?;
357                    // The envelope owns completion metadata and limitation rows.
358                    self.budget
359                        .charge_results(
360                            8 + self.limitations().len() * 18,
361                            zirql::Location::Query(emission.range.clone()),
362                        )
363                        .map_err(pipeline::core_error)?;
364                    for limitation in self
365                        .semantic
366                        .as_ref()
367                        .map_or(&self.report_limitations[..], SemanticResult::limitations)
368                    {
369                        self.budget
370                            .charge(
371                                limitation.location.path.as_os_str().len()
372                                    + limitation.message.len()
373                                    + 96,
374                                zirql::Location::Query(emission.range.clone()),
375                            )
376                            .map_err(pipeline::core_error)?;
377                    }
378                    let value = report_json(
379                        &emission.value,
380                        self.semantic.as_ref(),
381                        &mut self.budget,
382                        &emission.range,
383                    )?;
384                    let envelope = json!({"complete": self.is_complete(), "limitations": self.limitations().iter().map(|d| json!({"file": d.location.path.to_string_lossy(), "range": {"start": d.location.range.start, "end": d.location.range.end}, "line": d.location.line, "column": d.location.column, "message": d.message})).collect::<Vec<_>>()});
385                    let mut envelope = envelope;
386                    envelope
387                        .as_object_mut()
388                        .unwrap()
389                        .insert("result".to_owned(), value);
390                    let mut output = Vec::new();
391                    serde_json::to_writer(
392                        BudgetWriter {
393                            bytes: &mut output,
394                            budget: &mut self.budget,
395                            range: &emission.range,
396                        },
397                        &envelope,
398                    )
399                    .map_err(|error| {
400                        QueryError::query(emission.range.clone(), error.to_string())
401                    })?;
402                    self.budget
403                        .charge(1, zirql::Location::Query(emission.range.clone()))
404                        .map_err(pipeline::core_error)?;
405                    output.push(b'\n');
406                    output
407                }
408            };
409            // JSON's writer charged encoded bytes before appending. Source
410            // output was admitted by charge_render before its native renderer.
411            bytes.append(&mut output);
412        }
413        Ok(bytes)
414    }
415}
416struct BudgetWriter<'a> {
417    bytes: &'a mut Vec<u8>,
418    budget: &'a mut Budget,
419    range: &'a Range<usize>,
420}
421impl std::io::Write for BudgetWriter<'_> {
422    fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
423        self.budget
424            .charge(bytes.len(), zirql::Location::Query(self.range.clone()))
425            .map_err(std::io::Error::other)?;
426        self.bytes.extend_from_slice(bytes);
427        Ok(bytes.len())
428    }
429    fn flush(&mut self) -> std::io::Result<()> {
430        Ok(())
431    }
432}
433
434/// A parsed report or data program. Query-source strings lower once. Edit RHS
435/// expressions are lowered again with target evidence under the request budget;
436/// their original program coordinates are retained. Generated text is not evaluated.
437#[derive(Debug)]
438struct QueryArgument {
439    owner: usize,
440    kind: language::ArgumentKind,
441    expression: zirql::ValueExpression<Expression>,
442    eager_members: Vec<comprehension::MemberRequest>,
443}
444#[derive(Debug)]
445struct ProgramQuery {
446    query: Query,
447    arguments: Rc<Vec<QueryArgument>>,
448    copy_units: usize,
449}
450
451#[derive(Debug)]
452pub struct Program {
453    parameters: Vec<zirql::ParameterDeclaration>,
454    program: zirql::Program<Stage, zirql::NativeSetOperator, Emitter>,
455    preflight_work: usize,
456    preflight_results: usize,
457    preflight_range: Option<Range<usize>>,
458    queries: Vec<ProgramQuery>,
459    functions: Vec<zirql::UnresolvedPatternFunctionDeclaration<Expression>>,
460    function_limits: zirql::FunctionLimits,
461    report_model_demand: bool,
462    editing: bool,
463    prepared_values: bool,
464    prepared: bool,
465}
466impl Program {
467    /// Parse immutable bindings, reports, or one terminal data edit batch.
468    /// All names, terminal emitters and supported result transitions are checked
469    /// before any statement executes.
470    pub fn parse(source: &str, limits: ProgramLimits) -> Result<Self, QueryError> {
471        if source.len() > 1_048_576 {
472            return Err(QueryError::query(
473                0..0,
474                "program byte limit exceeded (1048576 bytes)",
475            ));
476        }
477        let mut parser = Parser::new(source, limits);
478        parser.collect_functions()?;
479        parser.prepared = true;
480        let mut statements = Vec::new();
481        let mut parameters = Vec::new();
482        let mut edits = Vec::new();
483        let mut target_names = std::collections::BTreeSet::new();
484        let mut output_seen = false;
485        let mut invalid_binding = None;
486        let mut document_seen = false;
487        parser.cursor.space();
488        while parser.cursor.position < source.len() {
489            let start = parser.cursor.position;
490            if let Some(range) = parser
491                .function_ranges
492                .iter()
493                .find(|range| range.start == start)
494            {
495                parser.cursor.position = range.end;
496                parser.cursor.space();
497                continue;
498            }
499            if document_seen {
500                return Err(parser.cursor.error("no statement may follow emit"));
501            }
502            let mut reader = zirql::ArgumentReader::new(source, parser.cursor.position)
503                .map_err(pipeline::core_error)?;
504            if source[parser.cursor.position..].starts_with("param")
505                && let Some(zirql::CommonStatement::Param(declaration)) =
506                    zirql::read_statement_prefix(&mut reader, &mut parser.preflight)
507                        .map_err(pipeline::core_error)?
508            {
509                parser.begin_pure(declaration.name.range.clone())?;
510                parser.cursor.position = reader.position();
511                parser
512                    .cursor
513                    .punctuation(b';', "expected ';' after parameter declaration")?;
514                if parser.names.contains_key(&declaration.name.value)
515                    || matches!(
516                        declaration.name.value.as_str(),
517                        "let" | "report" | "json" | "emit" | "create" | "replace" | "emit_document"
518                    )
519                {
520                    return Err(QueryError::query(
521                        declaration.name.range,
522                        "parameter name is reserved or already defined",
523                    ));
524                }
525                parser
526                    .preflight
527                    .charge(
528                        declaration.name.value.len() + 1,
529                        zirql::Location::Query(declaration.name.range.clone()),
530                    )
531                    .map_err(pipeline::core_error)?;
532                parser
533                    .preflight
534                    .charge_results(1, zirql::Location::Query(declaration.name.range.clone()))
535                    .map_err(pipeline::core_error)?;
536                parser.names.insert(
537                    declaration.name.value.clone(),
538                    Kind::Scalar(declaration.ty.value),
539                );
540                parser
541                    .preflight
542                    .charge(
543                        declaration.name.value.len() + 1,
544                        zirql::Location::Query(declaration.name.range.clone()),
545                    )
546                    .and_then(|()| {
547                        parser.preflight.charge_results(
548                            2,
549                            zirql::Location::Query(declaration.name.range.clone()),
550                        )
551                    })
552                    .map_err(pipeline::core_error)?;
553                statements.push(Located::new(
554                    zirql::Statement::Bind {
555                        name: declaration.name.clone(),
556                        expression: value_stage(
557                            Stage::Parameter(parameters.len()),
558                            declaration.name.range.clone(),
559                        ),
560                    },
561                    declaration.name.range.clone(),
562                ));
563                parameters.push(declaration);
564                parser.cursor.space();
565                continue;
566            }
567            let create = parser.cursor.take_word("create");
568            let replace = !create && parser.cursor.take_word("replace");
569            if create || replace {
570                if output_seen {
571                    return Err(parser
572                        .cursor
573                        .error("an edit batch cannot include report or query emissions"));
574                }
575                let (name, range) = parser.identifier("expected an assignment target")?;
576                if !parser.minizinc_identifier(&range) {
577                    return Err(QueryError::query(
578                        range,
579                        "expected a MiniZinc identifier or quoted identifier",
580                    ));
581                }
582                parser.begin_pure(range.clone())?;
583                parser
584                    .preflight
585                    .charge(name.len() + 1, zirql::Location::Query(range.clone()))
586                    .map_err(pipeline::core_error)?;
587                parser
588                    .preflight
589                    .charge_results(1, zirql::Location::Query(range.clone()))
590                    .map_err(pipeline::core_error)?;
591                if !target_names.insert(name.to_owned()) {
592                    return Err(QueryError::query(range, "duplicate edit target"));
593                }
594                parser
595                    .cursor
596                    .punctuation(b'=', "expected '=' after edit target")?;
597                parser.cursor.space();
598                let expression_range = parser.cursor.position..parser.expression_end()?;
599                let (_, names) = parser.pure_value::<()>(expression_range.clone())?;
600                parser
601                    .preflight
602                    .charge(
603                        expression_range.len() + name.len() + 1,
604                        zirql::Location::Query(range.clone()),
605                    )
606                    .map_err(pipeline::core_error)?;
607                edits.push(data::Edit {
608                    create,
609                    name: Located::new(name.to_owned(), range),
610                    source: source[expression_range.clone()].to_owned(),
611                    range: expression_range,
612                    names,
613                });
614                parser.cursor.punctuation(b';', "expected ';' after edit")?;
615                parser.cursor.space();
616                continue;
617            }
618            if parser.cursor.take_word("emit") || parser.cursor.take_word("emit_document") {
619                if edits.is_empty() || output_seen {
620                    return Err(QueryError::query(
621                        start..parser.cursor.position,
622                        "emit requires an edit batch",
623                    ));
624                }
625                parser.cursor.punctuation(b';', "expected ';' after emit")?;
626                let range = start..parser.cursor.position;
627                statements.push(Located::new(
628                    zirql::Statement::Evaluate {
629                        expression: value_stage(
630                            Stage::Data(std::mem::take(&mut edits)),
631                            range.clone(),
632                        ),
633                        implicit_output: None,
634                    },
635                    range,
636                ));
637                document_seen = true;
638                parser.cursor.space();
639                continue;
640            }
641            if !edits.is_empty() {
642                return Err(parser.cursor.error("an edit batch must end with emit"));
643            }
644            let quoted_let = source[start..].starts_with("let")
645                && parser
646                    .lexed
647                    .tokens()
648                    .iter()
649                    .filter(|token| {
650                        token.range.start >= start
651                            && !matches!(
652                                token.kind,
653                                zincite_syntax::TokenKind::Whitespace
654                                    | zincite_syntax::TokenKind::LineComment
655                                    | zincite_syntax::TokenKind::BlockComment
656                            )
657                    })
658                    .nth(1)
659                    .is_some_and(|token| token.kind == zincite_syntax::TokenKind::QuotedIdentifier);
660            if quoted_let {
661                parser.cursor.take_word("let");
662            }
663            let mut reader =
664                zirql::ArgumentReader::new(source, start).map_err(pipeline::core_error)?;
665            let pattern = if quoted_let {
666                None
667            } else {
668                zirql::read_pattern_let(&mut reader, &mut parser.preflight, limits.nesting)
669                    .map_err(pipeline::core_error)?
670            };
671            let is_let = pattern.is_some() || quoted_let;
672            if let Some(pattern) = &pattern {
673                parser.cursor.position = reader.position();
674                for name in zirql::pattern_names(pattern, &mut parser.preflight)
675                    .map_err(pipeline::core_error)?
676                {
677                    if !parser.minizinc_identifier(&name.range) {
678                        invalid_binding = Some(name.range.clone());
679                    }
680                    if matches!(
681                        name.value.as_str(),
682                        "let" | "report" | "json" | "emit" | "create" | "replace" | "emit_document"
683                    ) {
684                        return Err(QueryError::query(
685                            name.range.clone(),
686                            "binding name is reserved by the program grammar",
687                        ));
688                    }
689                    if parser.names.contains_key(&name.value) {
690                        return Err(QueryError::query(
691                            name.range.clone(),
692                            "binding is already defined",
693                        ));
694                    }
695                }
696                if !matches!(pattern.value, zirql::PatternKind::Name(_)) {
697                    parser.prepared = true;
698                }
699            }
700            let binding = if quoted_let {
701                let (name, range) = parser.identifier("expected a binding name")?;
702                if !parser.minizinc_identifier(&range) {
703                    invalid_binding = Some(range.clone());
704                }
705                if matches!(
706                    name,
707                    "let" | "report" | "json" | "emit" | "create" | "replace" | "emit_document"
708                ) {
709                    return Err(QueryError::query(
710                        range,
711                        "binding name is reserved by the program grammar",
712                    ));
713                }
714                if parser.names.contains_key(name) {
715                    return Err(QueryError::query(range, "binding is already defined"));
716                }
717                parser
718                    .cursor
719                    .punctuation(b'=', "expected '=' after binding name")?;
720                Some(Located::new(name.to_owned(), range))
721            } else if let Some(Located {
722                value: zirql::PatternKind::Name(name),
723                ..
724            }) = &pattern
725            {
726                Some(name.clone())
727            } else {
728                None
729            };
730            let report = !is_let && binding.is_none() && parser.cursor.take_word("report");
731            parser.cursor.space();
732            let expression_start = parser.cursor.position;
733            let expression_end = match parser.expression_end() {
734                Ok(end) => end,
735                Err(_) if !is_let && binding.is_none() => source.len(),
736                Err(error) => return Err(error),
737            };
738            let query_start = parser.queries.len();
739            let (expression, kind, emits) =
740                parser.composition_expression(expression_start..expression_end, !is_let, is_let)?;
741            let editing_expression = parser.queries[query_start..]
742                .iter()
743                .any(|query| query.query.is_editing());
744            if is_let && editing_expression {
745                return Err(QueryError::query(
746                    expression.range.clone(),
747                    "a binding cannot perform an edit",
748                ));
749            }
750            if (binding.is_some() || is_let) && emits {
751                return Err(QueryError::query(
752                    expression.range.clone(),
753                    "a binding cannot emit",
754                ));
755            }
756            if !(!is_let && binding.is_none() && parser.cursor.position == source.len()) {
757                parser
758                    .cursor
759                    .punctuation(b';', "expected ';' after program statement")?;
760            }
761            output_seen |= binding.is_none() && !is_let;
762            let statement = if let Some(pattern) =
763                pattern.filter(|p| !matches!(p.value, zirql::PatternKind::Name(_)))
764            {
765                let names = zirql::pattern_names(&pattern, &mut parser.preflight)
766                    .map_err(pipeline::core_error)?
767                    .into_iter()
768                    .cloned()
769                    .collect::<Vec<_>>();
770                for name in &names {
771                    parser.names.insert(name.value.clone(), Kind::Constructed);
772                }
773                let range = expression.range.clone();
774                let value = functions::into_value(expression);
775                zirql::Statement::BindFields {
776                    names,
777                    expression: value_stage(Stage::PatternBinding(pattern, value), range),
778                }
779            } else if let Some(name) = binding {
780                parser.names.insert(name.value.clone(), kind);
781                zirql::Statement::Bind { name, expression }
782            } else {
783                zirql::Statement::Evaluate {
784                    expression,
785                    implicit_output: Some(
786                        if report
787                            || !editing_expression
788                                && matches!(
789                                    kind,
790                                    Kind::Record
791                                        | Kind::Dictionary(_)
792                                        | Kind::Constructed
793                                        | Kind::Value(_)
794                                        | Kind::Capture(_)
795                                        | Kind::Captures(_)
796                                )
797                        {
798                            Emitter::Json
799                        } else {
800                            Emitter::Source
801                        },
802                    ),
803                }
804            };
805            statements.push(Located::new(statement, start..parser.cursor.position));
806            parser.cursor.space();
807        }
808        if !edits.is_empty() {
809            return Err(parser.cursor.error("an edit batch must end with emit"));
810        }
811        let scalar_edit = parser.queries.iter().any(|query| {
812            query
813                .query
814                .stages
815                .iter()
816                .any(|stage| matches!(stage.kind, language::StageKind::FilterElements(_)))
817        });
818        let editing = document_seen || parser.queries.iter().any(|query| query.query.is_editing());
819        if scalar_edit
820            && (document_seen
821                || parser
822                    .queries
823                    .iter()
824                    .filter(|query| query.query.is_editing())
825                    .count()
826                    != 1
827                || statements
828                    .iter()
829                    .filter(|statement| {
830                        matches!(statement.value, zirql::Statement::Evaluate { .. })
831                    })
832                    .count()
833                    != 1
834                || statements.iter().any(|statement| {
835                    matches!(
836                        statement.value,
837                        zirql::Statement::Evaluate {
838                            implicit_output: Some(Emitter::Json),
839                            ..
840                        }
841                    )
842                }))
843        {
844            return Err(QueryError::query(
845                0..source.len(),
846                "a scalar edit program requires one edit pipeline and no other outputs",
847            ));
848        }
849        if document_seen || scalar_edit {
850            if !parser.functions.is_empty() {
851                return Err(QueryError::query(
852                    parser.functions[0].name.range.clone(),
853                    "query functions are unavailable in data programs",
854                ));
855            }
856            let mut budget =
857                std::mem::replace(&mut parser.preflight, Budget::for_program(limits.core()));
858            for statement in &statements {
859                let expression = match &statement.value {
860                    zirql::Statement::Bind { expression, .. }
861                    | zirql::Statement::BindFields { expression, .. }
862                    | zirql::Statement::Evaluate { expression, .. } => expression,
863                };
864                parser.admit_data_expression(expression, &mut budget, true)?;
865            }
866            parser.preflight = budget;
867            if let Some(range) = invalid_binding {
868                return Err(QueryError::query(
869                    range,
870                    "expected a MiniZinc identifier or quoted identifier",
871                ));
872            }
873        }
874        if statements.is_empty() {
875            return Err(QueryError::query(
876                0..source.len(),
877                "expected a program statement",
878            ));
879        }
880        if parser.prepared {
881            let preserve_native = parser.functions.is_empty()
882                && !statements.iter().any(|statement| {
883                    matches!(statement.value, zirql::Statement::BindFields { .. })
884                });
885            statements =
886                functions::wrap_program_entries(statements, preserve_native, &parser.queries);
887        }
888        Ok(Self {
889            parameters,
890            program: zirql::Program::new(statements, 0..source.len()),
891            queries: std::mem::take(&mut parser.queries),
892            functions: std::mem::take(&mut parser.functions),
893            function_limits: zirql::FunctionLimits { active_depth: 64 },
894            report_model_demand: parser.report_model_demand || document_seen,
895            editing,
896            prepared_values: parser.prepared_values,
897            prepared: parser.prepared,
898            preflight_work: limits
899                .work
900                .checked_sub(parser.preflight.remaining_work())
901                .unwrap(),
902            preflight_results: limits
903                .results
904                .checked_sub(parser.preflight.remaining_results().unwrap())
905                .unwrap(),
906            preflight_range: parser.preflight_range.take(),
907        })
908    }
909    pub fn is_editing(&self) -> bool {
910        self.editing
911    }
912    pub fn requires_model_facts(&self) -> bool {
913        self.report_model_demand
914            || self
915                .queries
916                .iter()
917                .any(|query| query.query.requires_model_facts())
918    }
919    pub fn evaluate<'a>(
920        &self,
921        input: &'a Input,
922        limits: ProgramLimits,
923    ) -> Result<ProgramResult<'a>, QueryError> {
924        self.evaluate_with_parameters(input, zirql::ParameterInputs::Typed(&[]), limits)
925    }
926    pub fn evaluate_with_model<'a>(
927        &self,
928        input: &'a Input,
929        model: Option<&'a ModelContext>,
930        limits: ProgramLimits,
931    ) -> Result<ProgramResult<'a>, QueryError> {
932        self.evaluate_with_model_and_parameters(
933            input,
934            model,
935            zirql::ParameterInputs::Typed(&[]),
936            limits,
937        )
938    }
939    pub fn evaluate_with_facts<'a>(
940        &self,
941        input: &'a Input,
942        facts: Rc<ModelFacts<'a>>,
943        limits: ProgramLimits,
944    ) -> Result<ProgramResult<'a>, QueryError> {
945        self.evaluate_with_facts_and_parameters(
946            input,
947            facts,
948            zirql::ParameterInputs::Typed(&[]),
949            limits,
950        )
951    }
952    /// Set the independent active function depth; compatible tail calls reuse it.
953    pub fn with_function_limits(mut self, limits: zirql::FunctionLimits) -> Self {
954        self.function_limits = limits;
955        self
956    }
957
958    /// Required scalar declarations retained by this program.
959    pub fn parameters(&self) -> &[zirql::ParameterDeclaration] {
960        &self.parameters
961    }
962
963    /// Admit the complete caller set before native/model execution.
964    pub fn evaluate_with_parameters<'a>(
965        &self,
966        input: &'a Input,
967        parameters: zirql::ParameterInputs<'_>,
968        limits: ProgramLimits,
969    ) -> Result<ProgramResult<'a>, QueryError> {
970        self.evaluate_with_model_and_parameters(input, None, parameters, limits)
971    }
972    pub fn evaluate_with_model_and_parameters<'a>(
973        &self,
974        input: &'a Input,
975        model: Option<&'a ModelContext>,
976        parameters: zirql::ParameterInputs<'_>,
977        limits: ProgramLimits,
978    ) -> Result<ProgramResult<'a>, QueryError> {
979        let facts = model.map(|model| Rc::new(ModelFacts::new(model)));
980        self.run(input, facts, parameters, limits)
981    }
982    pub fn evaluate_with_facts_and_parameters<'a>(
983        &self,
984        input: &'a Input,
985        facts: Rc<ModelFacts<'a>>,
986        parameters: zirql::ParameterInputs<'_>,
987        limits: ProgramLimits,
988    ) -> Result<ProgramResult<'a>, QueryError> {
989        self.run(input, Some(facts), parameters, limits)
990    }
991    fn run<'a>(
992        &self,
993        input: &'a Input,
994        facts: Option<Rc<ModelFacts<'a>>>,
995        parameters: zirql::ParameterInputs<'_>,
996        limits: ProgramLimits,
997    ) -> Result<ProgramResult<'a>, QueryError> {
998        let mut budget = Budget::for_program(limits.core());
999        if let Some(range) = &self.preflight_range {
1000            budget
1001                .charge(self.preflight_work, zirql::Location::Query(range.clone()))
1002                .map_err(pipeline::core_error)?;
1003            budget
1004                .charge_results(
1005                    self.preflight_results,
1006                    zirql::Location::Query(range.clone()),
1007                )
1008                .map_err(pipeline::core_error)?;
1009        }
1010        let initial = zirql::admit_parameters(
1011            &self.parameters,
1012            parameters,
1013            &[],
1014            self.program.range(),
1015            &mut budget,
1016        )
1017        .map_err(pipeline::core_error)?;
1018        budget
1019            .charge_results(initial.len(), zirql::Location::Query(self.program.range()))
1020            .map_err(pipeline::core_error)?;
1021        let parameter_values = initial.into_iter().map(|(_, value)| Some(value)).collect();
1022        let bindings = Bindings::default();
1023        let empty = Query {
1024            stages: Vec::new(),
1025            range: self.program.range(),
1026            nesting_limit: limits.nesting,
1027        };
1028        let demand = self
1029            .queries
1030            .iter()
1031            .find(|query| query.query.requires_model_facts())
1032            .map(|query| &query.query)
1033            .unwrap_or(&empty);
1034        let (mut context, rows) =
1035            pipeline::prepare_context(demand, input, facts.clone(), limits.query(), &mut budget)?;
1036        let input_value = ReportValue::Query(context.restore_stream(rows));
1037        let pipelines = self
1038            .queries
1039            .iter()
1040            .map(|query| {
1041                if query.arguments.is_empty() && !query.query.stages.iter().any(|stage| matches!(&stage.kind, language::StageKind::FilterElements(comparison) if comparison.predicate.is_some())) {
1042                    pipeline::lower_pipeline(&query.query, &mut budget).map(Some)
1043                } else {
1044                    Ok(None)
1045                }
1046            })
1047            .collect::<Result<Vec<_>, _>>()?;
1048        let mut context = Context {
1049            functions: None,
1050            query: &mut context,
1051            input,
1052            facts,
1053            facts_admitted: false,
1054            parameters: parameter_values,
1055            emissions: Vec::new(),
1056            candidate: None,
1057            guard_unknown: None,
1058        };
1059        let mut written_values: Vec<&zirql::ValueExpression<Expression>> = self
1060            .functions
1061            .iter()
1062            .map(|function| &function.value)
1063            .collect();
1064        let mut written_native = self
1065            .program
1066            .statements()
1067            .iter()
1068            .map(|statement| match &statement.value {
1069                zirql::Statement::Bind { expression, .. }
1070                | zirql::Statement::BindFields { expression, .. }
1071                | zirql::Statement::Evaluate { expression, .. } => expression,
1072            })
1073            .collect();
1074        admit_written_members(
1075            &mut written_native,
1076            &mut written_values,
1077            &mut context,
1078            &mut budget,
1079            limits.nesting,
1080        )?;
1081        let signatures = functions::resolve_signatures(&self.functions, &mut context, &mut budget)?;
1082        let host = NativeHost {
1083            functions: self.prepared,
1084            signatures: &signatures,
1085            declarations: &self.functions,
1086            parameters: &self.parameters,
1087            queries: &self.queries,
1088            pipelines,
1089            nesting: limits.nesting,
1090            limits: limits.query(),
1091            editing: self.editing,
1092        };
1093
1094        let function_host = composition::CompositionHost(&host);
1095        let session = if !self.prepared && !self.prepared_values {
1096            None
1097        } else {
1098            Some(functions::Session::new(
1099                &self.functions,
1100                &signatures,
1101                &function_host,
1102                &self.program,
1103                self.function_limits,
1104                Kind::Nodes,
1105                &mut budget,
1106            )?)
1107        };
1108        context.functions = session.as_ref();
1109        zirql::evaluate_program(
1110            &self.program,
1111            &host,
1112            input_value,
1113            &mut context,
1114            bindings,
1115            &mut budget,
1116        )
1117        .map_err(pipeline::evaluation_error)?;
1118        let report_limitations = if context.facts_admitted {
1119            let limitations = context
1120                .facts
1121                .as_ref()
1122                .unwrap()
1123                .model()
1124                .limitations
1125                .as_slice();
1126            budget
1127                .charge_results(
1128                    limitations.len(),
1129                    zirql::Location::Query(self.program.range()),
1130                )
1131                .map_err(pipeline::core_error)?;
1132            for limitation in limitations {
1133                budget
1134                    .charge(
1135                        limitation.message.len() + limitation.location.path.as_os_str().len() + 1,
1136                        zirql::Location::Query(self.program.range()),
1137                    )
1138                    .map_err(pipeline::core_error)?;
1139            }
1140            limitations.to_vec()
1141        } else {
1142            Vec::new()
1143        };
1144        let candidate = context.candidate;
1145        let retained_facts = context.facts;
1146        let emissions = context.emissions;
1147        let semantic = if let Some(mut report) = context.query.report.take() {
1148            report.finish(
1149                std::mem::take(&mut context.query.callable_references),
1150                &mut Work {
1151                    budget: &mut budget,
1152                },
1153                demand
1154                    .stages
1155                    .iter()
1156                    .find(|stage| semantic::requests_facts(stage))
1157                    .unwrap(),
1158                &self.program.range(),
1159                limits.query(),
1160            )?;
1161            Some(report)
1162        } else {
1163            None
1164        };
1165        for emission in &emissions {
1166            validate_value(
1167                &emission.value,
1168                emission.emitter == Emitter::Json
1169                    || matches!(emission.value, ReportValue::Record(_)),
1170                &mut budget,
1171                &emission.range,
1172            )?;
1173        }
1174        Ok(ProgramResult {
1175            emissions,
1176            candidate,
1177            candidate_range: self.program.range(),
1178            facts: retained_facts,
1179            report_limitations: if semantic.is_some() {
1180                Vec::new()
1181            } else {
1182                report_limitations
1183            },
1184            semantic,
1185            budget,
1186        })
1187    }
1188}
1189
1190impl Drop for Program {
1191    fn drop(&mut self) {
1192        zirql::dispose_unresolved_pattern_functions(std::mem::take(&mut self.functions));
1193        let program = std::mem::replace(&mut self.program, zirql::Program::new(Vec::new(), 0..0));
1194        for statement in program.into_statements() {
1195            let expression = match statement.value {
1196                zirql::Statement::Bind { expression, .. }
1197                | zirql::Statement::BindFields { expression, .. }
1198                | zirql::Statement::Evaluate { expression, .. } => expression,
1199            };
1200            zirql::dispose_expression(expression);
1201        }
1202    }
1203}
1204
1205struct Parser<'a> {
1206    cursor: language::Parser<'a>,
1207    limits: ProgramLimits,
1208    names: BTreeMap<String, Kind>,
1209    queries: Vec<ProgramQuery>,
1210    functions: Vec<zirql::UnresolvedPatternFunctionDeclaration<Expression>>,
1211    function_ranges: Vec<Range<usize>>,
1212    function_names: std::collections::BTreeSet<String>,
1213    constructed_functions: std::collections::BTreeSet<String>,
1214    function_scope: Option<BTreeMap<String, Kind>>,
1215    reader_nesting: usize,
1216    report_model_demand: bool,
1217    prepared: bool,
1218    preflight: Budget,
1219    preflight_range: Option<Range<usize>>,
1220    lexed: zincite_syntax::LexedSource,
1221    composition_error: Option<QueryError>,
1222    prepared_values: bool,
1223}
1224impl<'a> Parser<'a> {
1225    fn new(source: &'a str, limits: ProgramLimits) -> Self {
1226        Self {
1227            cursor: language::Parser {
1228                source,
1229                position: 0,
1230                nesting_limit: limits.nesting.min(64),
1231                collection_limit: limits.stream_items,
1232                arguments: None,
1233                shared_budget: None,
1234            },
1235            limits,
1236            names: BTreeMap::new(),
1237            queries: Vec::new(),
1238            functions: Vec::new(),
1239            function_ranges: Vec::new(),
1240            function_names: std::collections::BTreeSet::new(),
1241            constructed_functions: std::collections::BTreeSet::new(),
1242            function_scope: None,
1243            reader_nesting: limits.nesting,
1244            report_model_demand: false,
1245            prepared: false,
1246            preflight: Budget::for_program(limits.core()),
1247            preflight_range: None,
1248            composition_error: None,
1249            prepared_values: false,
1250            lexed: zincite_syntax::lex(source),
1251        }
1252    }
1253}
1254impl Drop for Parser<'_> {
1255    fn drop(&mut self) {
1256        zirql::dispose_unresolved_pattern_functions(std::mem::take(&mut self.functions));
1257    }
1258}
1259struct ConstructedTypes;
1260impl<Q> zirql::ValueQueryHost<Q> for ConstructedTypes {
1261    type Error = zirql::QueryError;
1262    fn query_types(
1263        &mut self,
1264        _: &Q,
1265        _: &[(&str, Vec<zirql::ValueType>)],
1266        _: &mut Budget,
1267        range: &Range<usize>,
1268    ) -> Result<Vec<zirql::ValueType>, zirql::QueryError> {
1269        Err(zirql::QueryError::query(
1270            range.clone(),
1271            "native leaves are unavailable in constructed values",
1272        ))
1273    }
1274    fn evaluate_query(
1275        &mut self,
1276        _: &Q,
1277        _: &[(&str, zirql::ConstructedValue)],
1278        _: &mut Budget,
1279    ) -> Result<zirql::ConstructedValue, Self::Error> {
1280        unreachable!("CST metadata bridge")
1281    }
1282    fn read_binding(
1283        &mut self,
1284        _: &str,
1285        _: &Range<usize>,
1286        _: &mut Budget,
1287    ) -> Result<zirql::ConstructedValue, Self::Error> {
1288        unreachable!("CST metadata bridge")
1289    }
1290}
1291impl<'a> Parser<'a> {
1292    fn expression_end(&self) -> Result<usize, QueryError> {
1293        use zincite_syntax::TokenKind as T;
1294        let mut depth = 0usize;
1295        let tokens = self.lexed.tokens();
1296        let first = tokens.partition_point(|t| t.range.start < self.cursor.position);
1297        for token in &tokens[first..] {
1298            match token.kind {
1299                T::LeftParen | T::LeftBracket | T::LeftBrace | T::MatrixStart => depth += 1,
1300                T::RightParen | T::RightBracket | T::RightBrace | T::MatrixEnd => {
1301                    depth = depth.saturating_sub(1)
1302                }
1303                T::Semicolon if depth == 0 => return Ok(token.range.start),
1304                _ => {}
1305            }
1306        }
1307        Err(self.cursor.error("expected ';' after program statement"))
1308    }
1309    fn begin_pure(&mut self, range: Range<usize>) -> Result<(), QueryError> {
1310        if self.preflight_range.is_none() {
1311            let units = self
1312                .cursor
1313                .source
1314                .len()
1315                .checked_add(self.lexed.tokens().len())
1316                .ok_or_else(|| {
1317                    QueryError::query(range.clone(), "program allocation size overflow")
1318                })?;
1319            self.preflight
1320                .charge(units, zirql::Location::Query(range.clone()))
1321                .map_err(pipeline::core_error)?;
1322            self.preflight_range = Some(range);
1323        }
1324        Ok(())
1325    }
1326    fn pure_value<Q>(
1327        &mut self,
1328        range: Range<usize>,
1329    ) -> Result<(comprehension::LoweredValue<Q>, Vec<Located<String>>), QueryError> {
1330        self.begin_pure(range.clone())?;
1331        let expression = comprehension::read_data(
1332            &self.cursor.source[range.clone()],
1333            range.clone(),
1334            &mut self.preflight,
1335            self.reader_nesting,
1336        )?;
1337        let names = self.value_names(&expression, &range, None)?;
1338        self.cursor.position = range.end;
1339        Ok((expression, names))
1340    }
1341    fn scope_types(
1342        &mut self,
1343        range: &Range<usize>,
1344    ) -> Result<BTreeMap<String, zirql::ValueType>, QueryError> {
1345        let mut types = BTreeMap::new();
1346        for (name, kind) in &self.names {
1347            self.preflight
1348                .charge(name.len() + 3, zirql::Location::Query(range.clone()))
1349                .map_err(pipeline::core_error)?;
1350            let ty = match kind {
1351                Kind::Scalar(ty) => (*ty).into(),
1352                Kind::Value(types) if types.len() == 1 => zirql::copy_value_types(
1353                    types,
1354                    &mut self.preflight,
1355                    zirql::Location::Query(range.clone()),
1356                )
1357                .map_err(pipeline::core_error)?
1358                .pop()
1359                .unwrap(),
1360                Kind::Count => zirql::ValueType::Integer,
1361                Kind::Strings => zirql::ValueType::Array(zirql::ArrayType::Elements(vec![
1362                    zirql::ValueType::String,
1363                ])),
1364                Kind::Literals => zirql::ValueType::Array(zirql::ArrayType::Elements(vec![
1365                    zirql::ValueType::Unestablished,
1366                ])),
1367                _ => zirql::ValueType::Unestablished,
1368            };
1369            types.insert(name.clone(), ty);
1370        }
1371        Ok(types)
1372    }
1373    fn value_names<Q>(
1374        &mut self,
1375        expression: &comprehension::LoweredValue<Q>,
1376        range: &Range<usize>,
1377        expected: Option<language::ArgumentKind>,
1378    ) -> Result<Vec<Located<String>>, QueryError> {
1379        self.report_model_demand |= !expression.members.is_empty()
1380            || self
1381                .names
1382                .values()
1383                .any(|kind| matches!(kind, Kind::Literals));
1384        let mut types = self.scope_types(range)?;
1385        for member in &expression.members {
1386            self.preflight
1387                .charge(
1388                    member.slot.len() + 1,
1389                    zirql::Location::Query(member.range.clone()),
1390                )
1391                .map_err(pipeline::core_error)?;
1392            types.insert(member.slot.clone(), zirql::ValueType::Unestablished);
1393        }
1394        self.report_model_demand |= !expression.members.is_empty()
1395            || self
1396                .names
1397                .values()
1398                .any(|kind| matches!(kind, Kind::Literals));
1399        let types = types
1400            .into_iter()
1401            .map(|(name, ty)| (name, vec![ty]))
1402            .collect();
1403        let mut names = zirql::free_query_value_names(
1404            &expression.expression,
1405            &types,
1406            &mut ConstructedTypes,
1407            &mut self.preflight,
1408        )
1409        .map_err(pipeline::core_error)?;
1410        names.retain(|name| !name.value.starts_with('\0'));
1411        for name in &names {
1412            if !matches!(
1413                self.names[&name.value],
1414                Kind::Count
1415                    | Kind::Strings
1416                    | Kind::Literals
1417                    | Kind::Constructed
1418                    | Kind::Scalar(_)
1419                    | Kind::Value(_)
1420            ) {
1421                return Err(QueryError::query(
1422                    name.range.clone(),
1423                    "native result requires an explicit names, count or values projection",
1424                ));
1425            }
1426        }
1427        if let Some(expected) = expected
1428            && zirql::query_value_types(
1429                &expression.expression,
1430                &types,
1431                &mut ConstructedTypes,
1432                &mut self.preflight,
1433            )
1434            .map_err(pipeline::core_error)?
1435            .iter()
1436            .any(|ty| !expected.admits(ty))
1437        {
1438            return Err(QueryError::query(
1439                expression.expression.range.clone(),
1440                "primitive argument has an incompatible scalar type",
1441            ));
1442        }
1443        Ok(names)
1444    }
1445    fn minizinc_identifier(&self, range: &Range<usize>) -> bool {
1446        let i = self
1447            .lexed
1448            .tokens()
1449            .partition_point(|token| token.range.start < range.start);
1450        self.lexed.tokens().get(i).is_some_and(|token| {
1451            token.range == *range
1452                && matches!(
1453                    token.kind,
1454                    zincite_syntax::TokenKind::Identifier
1455                        | zincite_syntax::TokenKind::QuotedIdentifier
1456                )
1457        })
1458    }
1459    fn identifier(&mut self, message: &str) -> Result<(&'a str, Range<usize>), QueryError> {
1460        self.cursor.space();
1461        if self.cursor.current() != Some(b'\'') {
1462            return self.cursor.word(message);
1463        }
1464        let start = self.cursor.position;
1465        self.cursor.position += 1;
1466        let contents = self.cursor.position;
1467        while self
1468            .cursor
1469            .current()
1470            .is_some_and(|byte| !matches!(byte, b'\'' | b'\r' | b'\n' | 0))
1471        {
1472            self.cursor.position += 1;
1473        }
1474        if self.cursor.current() != Some(b'\'') || self.cursor.position == contents {
1475            return Err(self.cursor.error("expected a nonempty quoted identifier"));
1476        }
1477        let end = self.cursor.position;
1478        self.cursor.position += 1;
1479        Ok((
1480            &self.cursor.source[contents..end],
1481            start..self.cursor.position,
1482        ))
1483    }
1484}
1485
1486fn value_stage(stage: Stage, range: Range<usize>) -> Expression {
1487    Located::new(
1488        ExpressionKind::Sequence {
1489            input: Box::new(Located::new(ExpressionKind::Input, range.clone())),
1490            steps: vec![Located::new(ExpressionStep::Stage(stage), range.clone())],
1491        },
1492        range,
1493    )
1494}
1495
1496// Recognize native binding records before their parser
1497// performs name/type/purity validation. This never mutates the query table.
1498fn admit_argument_members(
1499    argument: &QueryArgument,
1500    context: &mut Context<'_, '_>,
1501    budget: &mut Budget,
1502) -> Result<(), zirql::EvaluationError<QueryError>> {
1503    for member in &argument.eager_members {
1504        let facts = context
1505            .report_facts(budget, &member.range)
1506            .map_err(zirql::EvaluationError::Host)?;
1507        comprehension::resolve_member(
1508            facts.as_deref(),
1509            Some(&member.enum_name.value),
1510            &member.member_name.value,
1511            budget,
1512            &member.range,
1513        )
1514        .map_err(zirql::EvaluationError::Host)?;
1515    }
1516    Ok(())
1517}
1518fn set_predicate_argument(
1519    predicate: &mut language::Predicate,
1520    owner: usize,
1521    kind: &mut Option<language::PredicateKind>,
1522    budget: &mut Budget,
1523    range: &Range<usize>,
1524) -> Result<bool, zirql::QueryError> {
1525    budget.charge(1, zirql::Location::Query(range.clone()))?;
1526    match &mut predicate.kind {
1527        language::PredicateKind::Kind(_)
1528        | language::PredicateKind::Name(_)
1529        | language::PredicateKind::Type(_)
1530        | language::PredicateKind::Instantiation(_)
1531        | language::PredicateKind::SourceKind(_) => {
1532            if predicate.range.start == owner {
1533                predicate.kind = kind.take().expect("argument is replaced once");
1534                return Ok(true);
1535            }
1536        }
1537    }
1538    Ok(false)
1539}
1540
1541fn validate_stages(query: &Query, mut kind: Kind) -> Result<Kind, QueryError> {
1542    kind = match kind {
1543        Kind::Node => Kind::Nodes,
1544        Kind::Declaration => Kind::Declarations,
1545        other => other,
1546    };
1547    use language::StageKind as S;
1548    for stage in &query.stages {
1549        let accepts = match &stage.kind {
1550            S::Json | S::Emit => true,
1551            S::Items => {
1552                let valid = matches!(kind, Kind::Nodes);
1553                kind = Kind::Nodes;
1554                valid
1555            }
1556            S::Count => {
1557                let valid = matches!(
1558                    kind,
1559                    Kind::Nodes
1560                        | Kind::Strings
1561                        | Kind::Literals
1562                        | Kind::Entities
1563                        | Kind::Declarations
1564                        | Kind::Types
1565                        | Kind::Instantiations
1566                );
1567                kind = Kind::Count;
1568                valid
1569            }
1570            S::Tally => {
1571                let valid = matches!(kind, Kind::Strings);
1572                kind = Kind::Tally;
1573                valid
1574            }
1575            S::Unique => matches!(
1576                kind,
1577                Kind::Nodes
1578                    | Kind::Strings
1579                    | Kind::Entities
1580                    | Kind::Declarations
1581                    | Kind::Types
1582                    | Kind::Instantiations
1583            ),
1584            S::Head(_) | S::Tail(_) | S::Reverse
1585                if matches!(kind, Kind::Dictionary(_) | Kind::Record | Kind::Tally) =>
1586            {
1587                true
1588            }
1589            S::Head(_) | S::Tail(_) | S::Reverse | S::Check { .. } => matches!(
1590                kind,
1591                Kind::Nodes
1592                    | Kind::Strings
1593                    | Kind::Literals
1594                    | Kind::Entities
1595                    | Kind::Declarations
1596                    | Kind::Types
1597                    | Kind::Instantiations
1598            ),
1599            S::Range(_) => matches!(kind, Kind::Nodes),
1600            S::Filter(_) | S::SharedFilter(_) => matches!(
1601                kind,
1602                Kind::Nodes
1603                    | Kind::Entities
1604                    | Kind::Declarations
1605                    | Kind::Types
1606                    | Kind::Instantiations
1607            ),
1608            S::Declarations => {
1609                let valid = matches!(
1610                    kind,
1611                    Kind::Nodes
1612                        | Kind::Entities
1613                        | Kind::Declarations
1614                        | Kind::Types
1615                        | Kind::Instantiations
1616                );
1617                kind = Kind::Declarations;
1618                valid
1619            }
1620            S::References | S::Uses | S::TransitiveReferences(_) => {
1621                let valid = matches!(
1622                    kind,
1623                    Kind::Nodes
1624                        | Kind::Entities
1625                        | Kind::Declarations
1626                        | Kind::Types
1627                        | Kind::Instantiations
1628                );
1629                kind = Kind::Entities;
1630                valid
1631            }
1632            S::ReferencedDeclarations => matches!(kind, Kind::Declarations),
1633            S::Types => {
1634                let valid = matches!(
1635                    kind,
1636                    Kind::Nodes
1637                        | Kind::Entities
1638                        | Kind::Declarations
1639                        | Kind::Types
1640                        | Kind::Instantiations
1641                );
1642                kind = Kind::Types;
1643                valid
1644            }
1645            S::Instantiations => {
1646                let valid = matches!(
1647                    kind,
1648                    Kind::Nodes
1649                        | Kind::Entities
1650                        | Kind::Declarations
1651                        | Kind::Types
1652                        | Kind::Instantiations
1653                );
1654                kind = Kind::Instantiations;
1655                valid
1656            }
1657            S::CallNames | S::AnnotationNames => {
1658                let valid = matches!(kind, Kind::Nodes);
1659                kind = Kind::Strings;
1660                valid
1661            }
1662            S::Names | S::Text => {
1663                let valid = matches!(
1664                    kind,
1665                    Kind::Nodes
1666                        | Kind::Entities
1667                        | Kind::Declarations
1668                        | Kind::Types
1669                        | Kind::Instantiations
1670                );
1671                kind = Kind::Strings;
1672                valid
1673            }
1674            S::Values => {
1675                let valid = matches!(kind, Kind::Nodes);
1676                kind = Kind::Literals;
1677                valid
1678            }
1679            S::Fields | S::Elements | S::Keys => matches!(kind, Kind::Nodes),
1680            S::Expressions | S::Children | S::Subtree => {
1681                let valid = matches!(kind, Kind::Nodes);
1682                kind = Kind::Nodes;
1683                valid
1684            }
1685            S::FilterElements(_)
1686            | S::SetValue(_)
1687            | S::Remove
1688            | S::ReduceEnum(_, _)
1689            | S::EmitDocument => false,
1690        };
1691        if !accepts {
1692            return Err(QueryError::query(
1693                stage.range.clone(),
1694                "stage is incompatible with the program result kind",
1695            ));
1696        }
1697    }
1698    Ok(kind)
1699}
1700
1701struct Context<'c, 'a> {
1702    functions: Option<&'c functions::Session<'c, 'a>>,
1703    query: &'c mut pipeline::Context<'a>,
1704    input: &'a Input,
1705    facts: Option<Rc<ModelFacts<'a>>>,
1706    facts_admitted: bool,
1707    parameters: Vec<Option<zirql::ConstructedValue>>,
1708    emissions: Vec<ProgramEmission<'a>>,
1709    candidate: Option<Input>,
1710    guard_unknown: Option<SourceDiagnostic>,
1711}
1712impl<'a> Context<'_, 'a> {
1713    fn report_facts(
1714        &mut self,
1715        budget: &mut Budget,
1716        range: &Range<usize>,
1717    ) -> Result<Option<Rc<ModelFacts<'a>>>, QueryError> {
1718        if let Some(facts) = &self.facts
1719            && !self.facts_admitted
1720        {
1721            if let Some(report) = &mut self.query.report {
1722                report.admit_fact_owner(&mut Work { budget }, range)?;
1723            } else {
1724                semantic::admit_model_facts(facts.model(), &mut Work { budget }, range)?;
1725            }
1726            self.facts_admitted = true;
1727        }
1728        Ok(self.facts.clone())
1729    }
1730}
1731type NativePipeline<'q> =
1732    zirql::Pipeline<(&'q language::Predicate, &'q language::Stage), &'q language::Stage>;
1733struct ReportReads<'r, 'c, 'a> {
1734    context: &'r mut Context<'c, 'a>,
1735    bindings: &'r Bindings<ReportValue<'a>>,
1736    members: &'r [comprehension::MemberRequest],
1737    projected: BTreeMap<String, std::sync::Arc<zirql::ConstructedValue>>,
1738    nesting: usize,
1739}
1740impl zirql::ValueQueryHost<()> for ReportReads<'_, '_, '_> {
1741    type Error = zirql::EvaluationError<QueryError>;
1742    fn query_types(
1743        &mut self,
1744        _: &(),
1745        _: &[(&str, Vec<zirql::ValueType>)],
1746        _: &mut Budget,
1747        range: &Range<usize>,
1748    ) -> Result<Vec<zirql::ValueType>, zirql::QueryError> {
1749        Err(zirql::QueryError::query(
1750            range.clone(),
1751            "native leaves are unavailable in constructed values",
1752        ))
1753    }
1754    fn evaluate_query(
1755        &mut self,
1756        _: &(),
1757        _: &[(&str, zirql::ConstructedValue)],
1758        _: &mut Budget,
1759    ) -> Result<zirql::ConstructedValue, Self::Error> {
1760        unreachable!("constructed Data values have no native leaves")
1761    }
1762    fn read_shared_binding(
1763        &mut self,
1764        name: &str,
1765        range: &Range<usize>,
1766        budget: &mut Budget,
1767    ) -> Result<Option<std::sync::Arc<zirql::ConstructedValue>>, Self::Error> {
1768        budget.charge(1, zirql::Location::Query(range.clone()))?;
1769        Ok(self.projected.get(name).map(std::sync::Arc::clone))
1770    }
1771    fn read_binding(
1772        &mut self,
1773        name: &str,
1774        range: &Range<usize>,
1775        budget: &mut Budget,
1776    ) -> Result<zirql::ConstructedValue, Self::Error> {
1777        if let Some(value) = self.projected.get(name) {
1778            return Ok(zirql::copy_constructed_value(
1779                value,
1780                budget,
1781                zirql::Location::Query(range.clone()),
1782            )?);
1783        }
1784        for member in self.members {
1785            budget.charge(
1786                1 + name.len().min(member.slot.len()),
1787                zirql::Location::Query(range.clone()),
1788            )?;
1789            if member.slot == name {
1790                let facts = self
1791                    .context
1792                    .report_facts(budget, range)
1793                    .map_err(zirql::EvaluationError::Host)?;
1794                return comprehension::resolve_member(
1795                    facts.as_deref(),
1796                    Some(&member.enum_name.value),
1797                    &member.member_name.value,
1798                    budget,
1799                    &member.range,
1800                )
1801                .map(zirql::ConstructedValue::Member)
1802                .map_err(zirql::EvaluationError::Host);
1803            }
1804        }
1805        match functions::unretained(self.bindings.get(name, range.clone())?) {
1806            ReportValue::Constructed(value) => Ok(zirql::copy_constructed_value(
1807                value,
1808                budget,
1809                zirql::Location::Query(range.clone()),
1810            )?),
1811            ReportValue::SharedConstructed(value) => Ok(zirql::copy_constructed_value(
1812                value,
1813                budget,
1814                zirql::Location::Query(range.clone()),
1815            )?),
1816            ReportValue::Query(stream) => {
1817                let facts = if matches!(stream, SemanticStream::Inspection(value) if matches!(value.as_ref(), QueryResult::Literals(_)))
1818                {
1819                    self.context
1820                        .report_facts(budget, range)
1821                        .map_err(zirql::EvaluationError::Host)?
1822                } else {
1823                    self.context.facts.clone()
1824                };
1825                comprehension::project(stream, facts.as_deref(), budget, range, self.nesting)
1826                    .map_err(zirql::EvaluationError::Host)
1827            }
1828            _ => Err(zirql::EvaluationError::Host(QueryError::query(
1829                range.clone(),
1830                "native records require explicit scalar projections",
1831            ))),
1832        }
1833    }
1834}
1835struct NativeHost<'q> {
1836    functions: bool,
1837    signatures: &'q [zirql::ResolvedPatternFunctionSignature<'q, Kind>],
1838    declarations: &'q [zirql::UnresolvedPatternFunctionDeclaration<Expression>],
1839    parameters: &'q [zirql::ParameterDeclaration],
1840    queries: &'q [ProgramQuery],
1841    pipelines: Vec<Option<NativePipeline<'q>>>,
1842    editing: bool,
1843    nesting: usize,
1844    limits: Limits,
1845}
1846impl<'a, 'q> ProgramHost<Context<'_, 'a>> for NativeHost<'q> {
1847    type Value = ReportValue<'a>;
1848    type Stage = Stage;
1849    type SetOperator = zirql::NativeSetOperator;
1850    type Emitter = Emitter;
1851    type Error = QueryError;
1852    fn copy_value(
1853        &self,
1854        value: &ReportValue<'a>,
1855        context: &mut Context<'_, 'a>,
1856        budget: &mut Budget,
1857        range: &Range<usize>,
1858    ) -> Result<ReportValue<'a>, zirql::EvaluationError<QueryError>> {
1859        if context.query.complete_observations {
1860            native::require_complete(value, budget, range)?;
1861        }
1862        if let ReportValue::SharedConstructed(owner) = value {
1863            budget.charge(1, zirql::Location::Query(range.clone()))?;
1864            return Ok(ReportValue::SharedConstructed(Arc::clone(owner)));
1865        }
1866        if let ReportValue::Retained(owner) = value {
1867            budget.charge(1, zirql::Location::Query(range.clone()))?;
1868            return Ok(ReportValue::Retained(Arc::clone(owner)));
1869        }
1870        copy_value(value, budget, range).map_err(zirql::EvaluationError::Host)
1871    }
1872    fn evaluate_stage(
1873        &self,
1874        stage: &Stage,
1875        value: ReportValue<'a>,
1876        context: &mut Context<'_, 'a>,
1877        bindings: &Bindings<ReportValue<'a>>,
1878        budget: &mut Budget,
1879        range: &Range<usize>,
1880    ) -> Result<ReportValue<'a>, zirql::EvaluationError<QueryError>> {
1881        let partial = native::selection_error(&value).cloned();
1882        if context.query.complete_observations {
1883            native::require_complete(&value, budget, range)?;
1884        }
1885        let result = (|| {
1886            let index = match stage {
1887                Stage::FunctionValue(expression) | Stage::FunctionSource(expression) => {
1888                    let session = context.functions.ok_or_else(|| {
1889                        zirql::EvaluationError::Host(QueryError::query(
1890                            range.clone(),
1891                            "function session is unavailable",
1892                        ))
1893                    })?;
1894                    return session
1895                        .run(expression, bindings, &value, context, budget)
1896                        .map_err(zirql::EvaluationError::Host);
1897                }
1898                Stage::PatternBinding(_, expression) => {
1899                    let session = context.functions.expect("prepared pattern program");
1900                    return session
1901                        .bind(expression, bindings, &value, context, budget)
1902                        .map_err(zirql::EvaluationError::Host);
1903                }
1904                Stage::RecordValue(_) | Stage::Primitive(_, _) => {
1905                    return Err(zirql::QueryError::query(
1906                        range.clone(),
1907                        "native function primitive requires its active argument runner",
1908                    )
1909                    .into());
1910                }
1911                Stage::Member(member) => {
1912                    let facts = context
1913                        .report_facts(budget, &member.range)
1914                        .map_err(zirql::EvaluationError::Host)?;
1915                    return comprehension::resolve_member(
1916                        facts.as_deref(),
1917                        Some(&member.enum_name.value),
1918                        &member.member_name.value,
1919                        budget,
1920                        &member.range,
1921                    )
1922                    .map(zirql::ConstructedValue::Member)
1923                    .map(ReportValue::Constructed)
1924                    .map_err(zirql::EvaluationError::Host);
1925                }
1926                Stage::Parameter(index) => {
1927                    return Ok(ReportValue::Constructed(
1928                        context.parameters[*index]
1929                            .take()
1930                            .expect("admitted parameter is bound once"),
1931                    ));
1932                }
1933                Stage::Query(index, _) => *index,
1934                Stage::RetainPayload => return Ok(value),
1935                Stage::Mapping(mapping) => {
1936                    return composition::CompositionHost(self)
1937                        .mapping(mapping, value, context, bindings, budget, range);
1938                }
1939                Stage::MapSorting => {
1940                    return composition::CompositionHost(self)
1941                        .sort_map(value, context, budget, range);
1942                }
1943                Stage::WhereExists(body) => {
1944                    return composition::CompositionHost(self)
1945                        .where_exists(body, value, context, bindings, budget, range);
1946                }
1947                Stage::FixedPoint(body) => {
1948                    return composition::CompositionHost(self)
1949                        .fixed_point(body, value, context, bindings, budget, range);
1950                }
1951                Stage::Ordering(ordering) => {
1952                    return composition::CompositionHost(self)
1953                        .order(ordering, value, context, bindings, budget, range);
1954                }
1955                Stage::Projection(projection) => {
1956                    let host = composition::CompositionHost(self);
1957                    return zirql::evaluate_projection(
1958                        projection.get(),
1959                        &host,
1960                        value,
1961                        context,
1962                        bindings,
1963                        budget,
1964                    )
1965                    .map(ReportValue::Constructed);
1966                }
1967                Stage::Filter(predicate) => {
1968                    return composition::CompositionHost(self).guard_filter(
1969                        predicate.get(),
1970                        value,
1971                        context,
1972                        bindings,
1973                        budget,
1974                        range,
1975                    );
1976                }
1977                Stage::Match(matching) => {
1978                    return composition::CompositionHost(self).matching(
1979                        matching.get(),
1980                        value,
1981                        context,
1982                        bindings,
1983                        budget,
1984                    );
1985                }
1986                Stage::Paths(paths) => {
1987                    return composition::CompositionHost(self).paths(
1988                        paths.get(),
1989                        value,
1990                        context,
1991                        bindings,
1992                        budget,
1993                    );
1994                }
1995                Stage::Data(edits) => {
1996                    let facts = context
1997                        .report_facts(budget, range)
1998                        .map_err(zirql::EvaluationError::Host)?;
1999                    let facts = facts.ok_or_else(|| {
2000                        zirql::EvaluationError::Host(QueryError::query(
2001                            range.clone(),
2002                            "data construction requires read-only model context (--model)",
2003                        ))
2004                    })?;
2005                    let candidate = data::prepare_data_batch(
2006                        context.input,
2007                        &facts,
2008                        edits,
2009                        budget,
2010                        self.limits,
2011                        |expression, names, budget, range| {
2012                            self.constructed(expression, names, context, bindings, budget, range)
2013                                .map_err(pipeline::evaluation_error)
2014                        },
2015                    )
2016                    .map_err(zirql::EvaluationError::Host)?;
2017                    context.candidate = Some(candidate);
2018                    return Ok(value);
2019                }
2020            };
2021            self.execute_query(index, value, context, bindings, budget, range)
2022        })();
2023        result.and_then(|value| native::incomplete(value, partial, budget, range))
2024    }
2025    fn combine(
2026        &self,
2027        operator: &zirql::NativeSetOperator,
2028        left: ReportValue<'a>,
2029        right: ReportValue<'a>,
2030        context: &mut Context<'_, 'a>,
2031        budget: &mut Budget,
2032        range: &Range<usize>,
2033    ) -> Result<ReportValue<'a>, zirql::EvaluationError<QueryError>> {
2034        composition::CompositionHost(self)
2035            .combine_set(*operator, left, right, context, budget, range)
2036    }
2037    fn construct_record(
2038        &self,
2039        fields: Vec<(String, ReportValue<'a>)>,
2040        _: &mut Context<'_, 'a>,
2041        _: &mut Budget,
2042        _: &Range<usize>,
2043    ) -> Result<ReportValue<'a>, zirql::EvaluationError<QueryError>> {
2044        Ok(ReportValue::Record(fields))
2045    }
2046    fn capture_record<'v>(
2047        &self,
2048        value: &'v ReportValue<'a>,
2049    ) -> Option<&'v zirql::NativeCapture<ReportValue<'a>>> {
2050        if let ReportValue::Capture(capture) = functions::unretained(value) {
2051            Some(capture)
2052        } else {
2053            None
2054        }
2055    }
2056    fn emit(
2057        &self,
2058        emitter: &Emitter,
2059        value: &ReportValue<'a>,
2060        context: &mut Context<'_, 'a>,
2061        budget: &mut Budget,
2062        range: &Range<usize>,
2063    ) -> Result<(), zirql::EvaluationError<QueryError>> {
2064        let value = copy_value(value, budget, range).map_err(zirql::EvaluationError::Host)?;
2065        budget.charge_results(1, zirql::Location::Query(range.clone()))?;
2066        context.emissions.push(ProgramEmission {
2067            value,
2068            emitter: *emitter,
2069            range: range.clone(),
2070        });
2071        Ok(())
2072    }
2073}
2074impl<'a> NativeHost<'_> {
2075    fn execute_query(
2076        &self,
2077        index: usize,
2078        value: ReportValue<'a>,
2079        context: &mut Context<'_, 'a>,
2080        bindings: &Bindings<ReportValue<'a>>,
2081        budget: &mut Budget,
2082        range: &Range<usize>,
2083    ) -> Result<ReportValue<'a>, zirql::EvaluationError<QueryError>> {
2084        let declarations = functions::query_declarations(&self.queries[index].query, &value);
2085        let resolved;
2086        let lowered;
2087        let pipeline = if let Some(pipeline) = &self.pipelines[index] {
2088            pipeline
2089        } else {
2090            resolved =
2091                self.resolve_query(&self.queries[index], context, bindings, budget, range)?;
2092            lowered = pipeline::lower_pipeline(&resolved, budget)
2093                .map_err(zirql::EvaluationError::Host)?;
2094            &lowered
2095        };
2096        let retained = if self.queries[index].query.is_editing() {
2097            Some(self.copy_value(&value, context, budget, range)?)
2098        } else {
2099            None
2100        };
2101        let result = self.execute_pipeline(pipeline, value, context, budget, range)?;
2102        if let ReportValue::Query(SemanticStream::Inspection(result)) = &result
2103            && let QueryResult::Document(bytes) = result.as_ref()
2104        {
2105            budget.charge(
2106                bytes.len().saturating_mul(2),
2107                zirql::Location::Query(range.clone()),
2108            )?;
2109            let candidate = Input::parse_named(
2110                bytes.clone(),
2111                zincite_syntax::FileMode::Data,
2112                context.input.file(),
2113            )
2114            .map_err(|error| {
2115                zirql::EvaluationError::Host(QueryError::query(
2116                    range.clone(),
2117                    format!(
2118                        "invalid data edit candidate: {}",
2119                        error.diagnostics[0].message
2120                    ),
2121                ))
2122            })?;
2123            context.candidate = Some(candidate);
2124            return Ok(retained.expect("document results come from edit stages"));
2125        }
2126        self.retain_entity_contract(result, declarations, context, budget, range)
2127    }
2128    fn retain_entity_contract(
2129        &self,
2130        value: ReportValue<'a>,
2131        declarations: bool,
2132        context: &mut Context<'_, 'a>,
2133        budget: &mut Budget,
2134        range: &Range<usize>,
2135    ) -> Result<ReportValue<'a>, zirql::EvaluationError<QueryError>> {
2136        if matches!(
2137            functions::unretained(&value),
2138            ReportValue::Query(SemanticStream::Entities(_))
2139        ) {
2140            budget.charge_results(1, zirql::Location::Query(range.clone()))?;
2141            Ok(ReportValue::EntitySequence {
2142                declarations,
2143                owner: context.facts.clone(),
2144                value: Box::new(value),
2145            })
2146        } else {
2147            Ok(value)
2148        }
2149    }
2150    fn execute_pipeline(
2151        &self,
2152        pipeline: &NativePipeline<'_>,
2153        value: ReportValue<'a>,
2154        context: &mut Context<'_, 'a>,
2155        budget: &mut Budget,
2156        range: &Range<usize>,
2157    ) -> Result<ReportValue<'a>, zirql::EvaluationError<QueryError>> {
2158        if pipeline.stages().is_empty() {
2159            return Ok(value);
2160        }
2161        let value = functions::owned(value, budget, range).map_err(zirql::EvaluationError::Host)?;
2162        let dictionary = matches!(&value, ReportValue::Record(_))
2163            || matches!(&value, ReportValue::Query(SemanticStream::Inspection(result)) if matches!(result.as_ref(), QueryResult::Tally(_)));
2164        if dictionary {
2165            return native::bound_map(value, pipeline, budget, range);
2166        }
2167        let ReportValue::Query(stream) = value else {
2168            return Err(zirql::EvaluationError::Host(QueryError::query(
2169                range.clone(),
2170                "query stages require a native query result",
2171            )));
2172        };
2173        let SemanticStream::Inspection(ref value) = stream else {
2174            return self.run_pipeline(pipeline, stream, context, budget, range);
2175        };
2176        if matches!(
2177            value.as_ref(),
2178            QueryResult::Count(_) | QueryResult::Tally(_)
2179        ) {
2180            if !pipeline.stages().is_empty() {
2181                return Err(zirql::EvaluationError::Host(QueryError::query(
2182                    range.clone(),
2183                    "query stages require a stream",
2184                )));
2185            }
2186            return Ok(ReportValue::Query(stream));
2187        }
2188        self.run_pipeline(pipeline, stream, context, budget, range)
2189    }
2190    fn resolve_query(
2191        &self,
2192        template: &ProgramQuery,
2193        context: &mut Context<'_, 'a>,
2194        bindings: &Bindings<ReportValue<'a>>,
2195        budget: &mut Budget,
2196        range: &Range<usize>,
2197    ) -> Result<Query, zirql::EvaluationError<QueryError>> {
2198        let mut query = self.resolve_query_by(
2199            template,
2200            &mut |argument, budget| {
2201                admit_argument_members(argument, context, budget)?;
2202                zirql::evaluate_native_value(
2203                    &argument.expression,
2204                    &composition::CompositionHost(self),
2205                    ReportValue::Record(Vec::new()),
2206                    context,
2207                    bindings,
2208                    budget,
2209                )
2210            },
2211            budget,
2212            range,
2213        )?;
2214        for stage in &mut query.stages {
2215            if let language::StageKind::FilterElements(comparison) = &mut stage.kind
2216                && let Some(predicate) = &comparison.predicate
2217            {
2218                let mut types = BTreeMap::new();
2219                for (name, _) in bindings.iter() {
2220                    types.insert(name.to_owned(), zirql::ValueType::Unestablished);
2221                }
2222                types.insert("\0scalar".into(), zirql::ValueType::ScalarKind);
2223                let names = zirql::free_value_names(predicate, &types, budget)?;
2224                let mut selected = Vec::new();
2225                for name in names {
2226                    if name.value == "\0scalar" {
2227                        continue;
2228                    }
2229                    let value = bindings.get(&name.value, name.range.clone())?;
2230                    let value = composition::CompositionHost(self).project_value(
2231                        self.copy_value(value, context, budget, &name.range)?,
2232                        context,
2233                        budget,
2234                        &name.range,
2235                    )?;
2236                    selected.push((name, value));
2237                }
2238                comparison.bindings = Rc::new(Bindings::new(selected)?);
2239            }
2240        }
2241        Ok(query)
2242    }
2243
2244    fn resolve_query_by(
2245        &self,
2246        template: &ProgramQuery,
2247        read: &mut impl FnMut(
2248            &QueryArgument,
2249            &mut Budget,
2250        )
2251            -> Result<zirql::ConstructedValue, zirql::EvaluationError<QueryError>>,
2252        budget: &mut Budget,
2253        range: &Range<usize>,
2254    ) -> Result<Query, zirql::EvaluationError<QueryError>> {
2255        let units = template.copy_units.checked_mul(2).ok_or_else(|| {
2256            zirql::QueryError::query(range.clone(), "query allocation size overflow")
2257        })?;
2258        budget.charge(units, zirql::Location::Query(range.clone()))?;
2259        budget.charge_results(units, zirql::Location::Query(range.clone()))?;
2260        let mut query = template.query.clone();
2261        for argument in template.arguments.iter() {
2262            let range = &argument.expression.range;
2263            let value = read(argument, budget)?;
2264            let value = value.into_data();
2265            let ty = match &value {
2266                zirql::ConstructedValue::Integer(_) => zirql::ValueType::Integer,
2267                zirql::ConstructedValue::String(_) => zirql::ValueType::String,
2268                _ => {
2269                    return Err(zirql::EvaluationError::Host(QueryError::query(
2270                        range.clone(),
2271                        "primitive argument has an incompatible scalar type",
2272                    )));
2273                }
2274            };
2275            if !argument.kind.admits(&ty) {
2276                return Err(zirql::EvaluationError::Host(QueryError::query(
2277                    range.clone(),
2278                    "primitive argument has an incompatible scalar type",
2279                )));
2280            }
2281            if let zirql::ConstructedValue::Integer(value) = value {
2282                let value = usize::try_from(value).map_err(|_| {
2283                    zirql::EvaluationError::Host(QueryError::query(
2284                        range.clone(),
2285                        "primitive argument needs a non-negative int fitting usize",
2286                    ))
2287                })?;
2288                let stage = query
2289                    .stages
2290                    .iter_mut()
2291                    .find(|stage| stage.range.start == argument.owner)
2292                    .expect("stage argument came from this template");
2293                match (&mut stage.kind, argument.kind) {
2294                    (language::StageKind::Head(count), language::ArgumentKind::Head)
2295                    | (language::StageKind::Tail(count), language::ArgumentKind::Tail)
2296                    | (
2297                        language::StageKind::TransitiveReferences(count),
2298                        language::ArgumentKind::TransitiveReferences,
2299                    ) => *count = value,
2300                    (
2301                        language::StageKind::Check { expected, .. },
2302                        language::ArgumentKind::CheckCount | language::ArgumentKind::CheckOperand,
2303                    ) => *expected = Some(value),
2304                    (language::StageKind::Range(bounds), language::ArgumentKind::RangeStart) => {
2305                        bounds.start = value
2306                    }
2307                    (language::StageKind::Range(bounds), language::ArgumentKind::RangeEnd) => {
2308                        bounds.end = value
2309                    }
2310                    _ => unreachable!("integer argument target came from this native stage"),
2311                }
2312            } else if let zirql::ConstructedValue::String(value) = value {
2313                if matches!(
2314                    argument.kind,
2315                    language::ArgumentKind::CheckMessage | language::ArgumentKind::CheckOperand
2316                ) {
2317                    let stage = query
2318                        .stages
2319                        .iter_mut()
2320                        .find(|stage| stage.range.start == argument.owner)
2321                        .expect("stage argument came from this template");
2322                    let language::StageKind::Check { expected, message } = &mut stage.kind else {
2323                        unreachable!()
2324                    };
2325                    if matches!(argument.kind, language::ArgumentKind::CheckOperand) {
2326                        *expected = None;
2327                    }
2328                    *message = Some(value);
2329                    continue;
2330                }
2331                let mut kind = Some(
2332                    argument
2333                        .kind
2334                        .predicate(value, range.clone())
2335                        .map_err(zirql::EvaluationError::Host)?,
2336                );
2337                for stage in &mut query.stages {
2338                    if let language::StageKind::Filter(predicate) = &mut stage.kind
2339                        && set_predicate_argument(
2340                            predicate,
2341                            argument.owner,
2342                            &mut kind,
2343                            budget,
2344                            range,
2345                        )?
2346                    {
2347                        break;
2348                    }
2349                }
2350                assert!(
2351                    kind.is_none(),
2352                    "predicate argument came from this native template"
2353                );
2354            }
2355        }
2356        for stage in &query.stages {
2357            if let language::StageKind::Range(bounds) = &stage.kind
2358                && bounds.start > bounds.end
2359            {
2360                return Err(zirql::EvaluationError::Host(QueryError::query(
2361                    stage.range.clone(),
2362                    "range start exceeds end",
2363                )));
2364            }
2365        }
2366        Ok(query)
2367    }
2368
2369    fn constructed(
2370        &self,
2371        expression: &comprehension::LoweredValue,
2372        names: &[Located<String>],
2373        context: &mut Context<'_, 'a>,
2374        bindings: &Bindings<ReportValue<'a>>,
2375        budget: &mut Budget,
2376        range: &Range<usize>,
2377    ) -> Result<zirql::ConstructedValue, zirql::EvaluationError<QueryError>> {
2378        if !self.editing {
2379            let mut types = BTreeMap::new();
2380            let mut eager = std::collections::BTreeSet::new();
2381            comprehension::eager_names(&expression.expression, &mut eager, budget)
2382                .map_err(zirql::EvaluationError::Host)?;
2383            let mut reads = ReportReads {
2384                context,
2385                bindings,
2386                members: &expression.members,
2387                projected: BTreeMap::new(),
2388                nesting: self.nesting,
2389            };
2390            for name in names {
2391                budget.charge(
2392                    name.value.len() + 1,
2393                    zirql::Location::Query(name.range.clone()),
2394                )?;
2395                let value = bindings.get(&name.value, name.range.clone())?;
2396                let ty = if eager.contains(&name.value) {
2397                    let value = zirql::ValueQueryHost::read_binding(
2398                        &mut reads,
2399                        &name.value,
2400                        &name.range,
2401                        budget,
2402                    )?;
2403                    let ty = zirql::constructed_value_type(
2404                        &value,
2405                        budget,
2406                        zirql::Location::Query(name.range.clone()),
2407                    )?;
2408                    budget.charge_results(1, zirql::Location::Query(name.range.clone()))?;
2409                    reads
2410                        .projected
2411                        .insert(name.value.clone(), std::sync::Arc::new(value));
2412                    ty
2413                } else {
2414                    match functions::unretained(value) {
2415                        ReportValue::Retained(_)
2416                        | ReportValue::Item(_)
2417                        | ReportValue::EntitySequence { .. } => {
2418                            unreachable!("unwrapped report")
2419                        }
2420                        ReportValue::Constructed(value) => {
2421                            comprehension::value_facts(value, budget, &name.range)?
2422                        }
2423                        ReportValue::Query(SemanticStream::Inspection(value)) => {
2424                            match value.as_ref() {
2425                                QueryResult::Count(_) => zirql::ValueType::Integer,
2426                                QueryResult::Strings(_) => {
2427                                    zirql::ValueType::Array(zirql::ArrayType::Elements(vec![
2428                                        zirql::ValueType::String,
2429                                    ]))
2430                                }
2431                                QueryResult::Literals(_) => {
2432                                    zirql::ValueType::Array(zirql::ArrayType::Elements(Vec::new()))
2433                                }
2434                                _ => zirql::ValueType::Unestablished,
2435                            }
2436                        }
2437                        _ => zirql::ValueType::Unestablished,
2438                    }
2439                };
2440                budget.charge_results(2, zirql::Location::Query(name.range.clone()))?;
2441                types.insert(name.value.clone(), vec![ty]);
2442            }
2443            for member in &expression.members {
2444                budget.charge(
2445                    member.slot.len() + 1,
2446                    zirql::Location::Query(member.range.clone()),
2447                )?;
2448                let ty = if eager.contains(&member.slot) {
2449                    let value = zirql::ValueQueryHost::read_binding(
2450                        &mut reads,
2451                        &member.slot,
2452                        &member.range,
2453                        budget,
2454                    )?;
2455                    let ty = zirql::constructed_value_type(
2456                        &value,
2457                        budget,
2458                        zirql::Location::Query(member.range.clone()),
2459                    )?;
2460                    budget.charge_results(1, zirql::Location::Query(member.range.clone()))?;
2461                    reads
2462                        .projected
2463                        .insert(member.slot.clone(), std::sync::Arc::new(value));
2464                    ty
2465                } else {
2466                    zirql::ValueType::Unestablished
2467                };
2468                budget.charge_results(2, zirql::Location::Query(member.range.clone()))?;
2469                types.insert(member.slot.clone(), vec![ty]);
2470            }
2471            return zirql::evaluate_query_value(&expression.expression, &types, &mut reads, budget);
2472        }
2473        let facts = if !expression.members.is_empty() || names.iter().any(|name| matches!(bindings.get(&name.value, name.range.clone()), Ok(ReportValue::Query(SemanticStream::Inspection(value))) if matches!(value.as_ref(), QueryResult::Literals(_)))) {
2474                    context.report_facts(budget, range).map_err(zirql::EvaluationError::Host)?
2475                } else { context.facts.clone() };
2476        let count = names
2477            .len()
2478            .checked_add(expression.members.len())
2479            .ok_or_else(|| zirql::QueryError::query(range.clone(), "projection size overflow"))?;
2480        budget.charge(count, zirql::Location::Query(range.clone()))?;
2481        budget.charge_results(count, zirql::Location::Query(range.clone()))?;
2482        let mut projected = Vec::with_capacity(count);
2483        for name in names {
2484            budget.charge(
2485                name.value.len() + 20,
2486                zirql::Location::Query(name.range.clone()),
2487            )?;
2488            let value = bindings.get(&name.value, name.range.clone())?;
2489            let value = match functions::unretained(value) {
2490                ReportValue::IncompleteSelection { .. }
2491                | ReportValue::Retained(_)
2492                | ReportValue::Item(_)
2493                | ReportValue::EntitySequence { .. } => unreachable!("unwrapped report"),
2494                ReportValue::Constructed(value) => zirql::copy_constructed_value(
2495                    value,
2496                    budget,
2497                    zirql::Location::Query(name.range.clone()),
2498                )?,
2499                ReportValue::SharedConstructed(value) => zirql::copy_constructed_value(
2500                    value,
2501                    budget,
2502                    zirql::Location::Query(name.range.clone()),
2503                )?,
2504                ReportValue::Query(stream) => comprehension::project(
2505                    stream,
2506                    facts.as_deref(),
2507                    budget,
2508                    &name.range,
2509                    self.nesting,
2510                )
2511                .map_err(zirql::EvaluationError::Host)?,
2512                ReportValue::Computational(_)
2513                | ReportValue::Tuple(_)
2514                | ReportValue::Sequence(_)
2515                | ReportValue::Record(_)
2516                | ReportValue::Capture(_)
2517                | ReportValue::Captures(_) => {
2518                    return Err(zirql::EvaluationError::Host(QueryError::query(
2519                        name.range.clone(),
2520                        "native records require explicit scalar projections",
2521                    )));
2522                }
2523            };
2524            projected.push((
2525                Located::new(name.value.to_owned(), name.range.clone()),
2526                value,
2527            ));
2528        }
2529        for member in &expression.members {
2530            budget.charge(
2531                member.slot.len() + 1,
2532                zirql::Location::Query(member.range.clone()),
2533            )?;
2534            let value = comprehension::resolve_member(
2535                facts.as_deref(),
2536                Some(&member.enum_name.value),
2537                &member.member_name.value,
2538                budget,
2539                &member.range,
2540            )
2541            .map_err(zirql::EvaluationError::Host)?;
2542            projected.push((
2543                Located::new(member.slot.clone(), member.range.clone()),
2544                zirql::ConstructedValue::Member(value),
2545            ));
2546        }
2547        let bindings = Bindings::new(projected)?;
2548        let value = zirql::evaluate_value_with_host_validation(
2549            &expression.expression,
2550            &bindings,
2551            budget,
2552            comprehension::validate_data_value,
2553        )?;
2554        comprehension::validate_data_shape(&value, budget, range)
2555            .map_err(zirql::EvaluationError::Host)?;
2556        Ok(value)
2557    }
2558
2559    fn run_pipeline(
2560        &self,
2561        pipeline: &NativePipeline<'_>,
2562        stream: SemanticStream<'a>,
2563        context: &mut Context<'_, 'a>,
2564        budget: &mut Budget,
2565        range: &Range<usize>,
2566    ) -> Result<ReportValue<'a>, zirql::EvaluationError<QueryError>> {
2567        let previous = context.query.incomplete_observation.take();
2568        let rows = context.query.take_rows(stream);
2569        let input = NativeInput::from_admitted(
2570            &mut *context.query,
2571            rows,
2572            budget,
2573            zirql::Location::Query(range.clone()),
2574        )?;
2575        let result = zirql::evaluate(pipeline, &pipeline::NativeHost, input, budget)
2576            .map(|result| result.value);
2577        let partial = std::mem::replace(&mut context.query.incomplete_observation, previous);
2578        let value = result?;
2579        let stream = match value {
2580            NativeValue::Stream(rows) => context
2581                .query
2582                .terminal
2583                .take()
2584                .unwrap_or_else(|| context.query.restore_stream(rows)),
2585            NativeValue::Count(value) => {
2586                SemanticStream::Inspection(Box::new(QueryResult::Count(value)))
2587            }
2588            NativeValue::Tally(value) => {
2589                SemanticStream::Inspection(Box::new(QueryResult::Tally(value)))
2590            }
2591        };
2592        let value = native::incomplete(ReportValue::Query(stream), partial, budget, range)?;
2593        if context.query.complete_observations {
2594            native::require_complete(&value, budget, range)?;
2595        }
2596        Ok(value)
2597    }
2598}
2599
2600fn copy_value<'a>(
2601    value: &ReportValue<'a>,
2602    budget: &mut Budget,
2603    range: &Range<usize>,
2604) -> Result<ReportValue<'a>, QueryError> {
2605    if let Some(value) = worklists::copy(value) {
2606        budget
2607            .charge(1, zirql::Location::Query(range.clone()))
2608            .map_err(pipeline::core_error)?;
2609        budget
2610            .charge_results(1, zirql::Location::Query(range.clone()))
2611            .map_err(pipeline::core_error)?;
2612        return Ok(value);
2613    }
2614    match value {
2615        ReportValue::Retained(value) => return copy_value(value, budget, range),
2616        ReportValue::IncompleteSelection { value, error } => {
2617            let value = copy_value(value, budget, range)?;
2618            return native::incomplete(value, Some(Arc::clone(error)), budget, range)
2619                .map_err(pipeline::evaluation_error);
2620        }
2621        ReportValue::EntitySequence {
2622            declarations,
2623            owner,
2624            value,
2625        } => {
2626            return Ok(ReportValue::EntitySequence {
2627                declarations: *declarations,
2628                owner: owner.clone(),
2629                value: Box::new(copy_value(value, budget, range)?),
2630            });
2631        }
2632        ReportValue::Item(value) => {
2633            return Ok(ReportValue::Item(Box::new(copy_value(
2634                value, budget, range,
2635            )?)));
2636        }
2637        ReportValue::Record(fields) => {
2638            budget
2639                .charge(fields.len() + 1, zirql::Location::Query(range.clone()))
2640                .map_err(pipeline::core_error)?;
2641            budget
2642                .charge_results(fields.len() + 1, zirql::Location::Query(range.clone()))
2643                .map_err(pipeline::core_error)?;
2644            let mut copied = Vec::with_capacity(fields.len());
2645            for (label, value) in fields {
2646                budget
2647                    .charge(label.len() + 1, zirql::Location::Query(range.clone()))
2648                    .map_err(pipeline::core_error)?;
2649                copied.push((label.clone(), copy_value(value, budget, range)?));
2650            }
2651            return Ok(ReportValue::Record(copied));
2652        }
2653        ReportValue::Capture(capture) => {
2654            return copy_capture(capture, budget, range).map(ReportValue::Capture);
2655        }
2656        ReportValue::Captures(captures) => {
2657            budget
2658                .charge(
2659                    captures.rows.len() + 1,
2660                    zirql::Location::Query(range.clone()),
2661                )
2662                .map_err(pipeline::core_error)?;
2663            budget
2664                .charge_results(
2665                    captures.rows.len() + 1,
2666                    zirql::Location::Query(range.clone()),
2667                )
2668                .map_err(pipeline::core_error)?;
2669            let schema = composition::copy_schema(&captures.schema, budget, range)
2670                .map_err(pipeline::core_error)?;
2671            let rows = captures
2672                .rows
2673                .iter()
2674                .map(|row| copy_capture(row, budget, range))
2675                .collect::<Result<Vec<_>, _>>()?;
2676            return Ok(ReportValue::Captures(CaptureMatches { rows, schema }));
2677        }
2678        _ => {}
2679    }
2680    if let ReportValue::SharedConstructed(value) = value {
2681        return zirql::copy_constructed_value(value, budget, zirql::Location::Query(range.clone()))
2682            .map(ReportValue::Constructed)
2683            .map_err(pipeline::core_error);
2684    }
2685    if let ReportValue::Constructed(value) = value {
2686        return zirql::copy_constructed_value(value, budget, zirql::Location::Query(range.clone()))
2687            .map(ReportValue::Constructed)
2688            .map_err(pipeline::core_error);
2689    }
2690    budget
2691        .charge_results(value_units(value), zirql::Location::Query(range.clone()))
2692        .map_err(pipeline::core_error)?;
2693    budget
2694        .charge(value_bytes(value), zirql::Location::Query(range.clone()))
2695        .map_err(pipeline::core_error)?;
2696    Ok(copy_admitted(value))
2697}
2698fn copy_capture<'a>(
2699    capture: &zirql::NativeCapture<ReportValue<'a>>,
2700    budget: &mut Budget,
2701    range: &Range<usize>,
2702) -> Result<zirql::NativeCapture<ReportValue<'a>>, QueryError> {
2703    budget
2704        .charge_results(
2705            capture.fields.len() + 1,
2706            zirql::Location::Query(range.clone()),
2707        )
2708        .map_err(pipeline::core_error)?;
2709    let mut fields = Vec::with_capacity(capture.fields.len());
2710    for (name, value) in &capture.fields {
2711        budget
2712            .charge(name.len() + 1, zirql::Location::Query(range.clone()))
2713            .map_err(pipeline::core_error)?;
2714        fields.push((name.clone(), copy_value(value, budget, range)?));
2715    }
2716    Ok(zirql::NativeCapture { fields })
2717}
2718
2719fn copy_admitted<'a>(value: &ReportValue<'a>) -> ReportValue<'a> {
2720    if let Some(value) = worklists::copy(value) {
2721        return value;
2722    }
2723    ReportValue::Query(match value {
2724        ReportValue::Retained(value) => return ReportValue::Retained(Arc::clone(value)),
2725        ReportValue::Item(value) => return ReportValue::Item(Box::new(copy_admitted(value))),
2726        ReportValue::IncompleteSelection { value, error } => {
2727            return ReportValue::IncompleteSelection {
2728                value: Box::new(copy_admitted(value)),
2729                error: Arc::clone(error),
2730            };
2731        }
2732        ReportValue::EntitySequence {
2733            declarations,
2734            owner,
2735            value,
2736        } => {
2737            return ReportValue::EntitySequence {
2738                declarations: *declarations,
2739                owner: owner.clone(),
2740                value: Box::new(copy_admitted(value)),
2741            };
2742        }
2743        ReportValue::Computational(_)
2744        | ReportValue::Tuple(_)
2745        | ReportValue::Sequence(_)
2746        | ReportValue::Constructed(_)
2747        | ReportValue::SharedConstructed(_)
2748        | ReportValue::Capture(_)
2749        | ReportValue::Captures(_) => {
2750            unreachable!("structured copies use admitted paths")
2751        }
2752        ReportValue::Record(fields) => {
2753            return ReportValue::Record(
2754                fields
2755                    .iter()
2756                    .map(|(label, value)| (label.clone(), copy_admitted(value)))
2757                    .collect(),
2758            );
2759        }
2760        ReportValue::Query(SemanticStream::Entities(rows)) => {
2761            SemanticStream::Entities(rows.clone())
2762        }
2763        ReportValue::Query(SemanticStream::Types(rows)) => SemanticStream::Types(rows.clone()),
2764        ReportValue::Query(SemanticStream::Instantiations(rows)) => {
2765            SemanticStream::Instantiations(rows.clone())
2766        }
2767        ReportValue::Query(SemanticStream::Inspection(value)) => {
2768            SemanticStream::Inspection(Box::new(match value.as_ref() {
2769                QueryResult::Selection(s) => QueryResult::Selection(Selection {
2770                    input: s.input,
2771                    items: s.items.clone(),
2772                    document: s.document,
2773                }),
2774                QueryResult::Nodes(s) => QueryResult::Nodes(crate::NodeSelection {
2775                    input: s.input,
2776                    nodes: s.nodes.clone(),
2777                }),
2778                QueryResult::Literals(s) => QueryResult::Literals(crate::LiteralSelection {
2779                    input: s.input,
2780                    values: s.values.clone(),
2781                }),
2782                QueryResult::Strings(s) => QueryResult::Strings(s.clone()),
2783                QueryResult::Count(s) => QueryResult::Count(*s),
2784                QueryResult::Tally(s) => QueryResult::Tally(s.clone()),
2785                _ => unreachable!("programs retain native read-only leaves"),
2786            }))
2787        }
2788    })
2789}
2790fn value_units(value: &ReportValue<'_>) -> usize {
2791    match value {
2792        ReportValue::Computational(_) | ReportValue::Tuple(_) | ReportValue::Sequence(_) => 1,
2793        ReportValue::Retained(value) => value_units(value),
2794        ReportValue::Item(value)
2795        | ReportValue::EntitySequence { value, .. }
2796        | ReportValue::IncompleteSelection { value, .. } => value_units(value),
2797        ReportValue::Constructed(_) | ReportValue::SharedConstructed(_) => {
2798            unreachable!("constructed admission uses the core")
2799        }
2800        ReportValue::Capture(capture) => {
2801            1 + capture
2802                .fields
2803                .iter()
2804                .map(|(_, value)| 1 + value_units(value))
2805                .sum::<usize>()
2806        }
2807        ReportValue::Captures(captures) => {
2808            1 + captures
2809                .rows
2810                .iter()
2811                .map(|capture| {
2812                    1 + capture
2813                        .fields
2814                        .iter()
2815                        .map(|(_, value)| 1 + value_units(value))
2816                        .sum::<usize>()
2817                })
2818                .sum::<usize>()
2819        }
2820        ReportValue::Record(fields) => {
2821            1 + fields
2822                .iter()
2823                .map(|(_, value)| 1 + value_units(value))
2824                .sum::<usize>()
2825        }
2826        ReportValue::Query(SemanticStream::Entities(rows)) => rows.len(),
2827        ReportValue::Query(SemanticStream::Types(rows)) => rows
2828            .iter()
2829            .map(|row| {
2830                1 + row
2831                    .ty
2832                    .as_ref()
2833                    .map_or(0, |ty| semantic::type_allocation(ty).0)
2834            })
2835            .sum(),
2836        ReportValue::Query(SemanticStream::Instantiations(rows)) => rows.len(),
2837        ReportValue::Query(SemanticStream::Inspection(value)) => match value.as_ref() {
2838            QueryResult::Selection(s) => s.items.len(),
2839            QueryResult::Nodes(s) => s.nodes.len(),
2840            QueryResult::Strings(s) => s.len(),
2841            QueryResult::Count(_) => 1,
2842            QueryResult::Tally(s) => 1 + 2 * s.len(),
2843            QueryResult::Literals(s) => 1 + s.values.iter().map(literal_units).sum::<usize>(),
2844            _ => unreachable!(),
2845        },
2846    }
2847}
2848fn literal_units(value: &crate::LiteralValue<'_>) -> usize {
2849    use crate::LiteralKind as K;
2850    1 + match &value.kind {
2851        K::Set(values) | K::Tuple(values) => values.iter().map(literal_units).sum(),
2852        K::Record(fields) => fields
2853            .iter()
2854            .map(|field| 1 + literal_units(&field.value))
2855            .sum(),
2856        K::Array { entries, .. } => entries
2857            .iter()
2858            .map(|entry| entry.key.as_ref().map_or(0, literal_units) + literal_units(&entry.value))
2859            .sum(),
2860        K::Matrix { column_keys, rows } => {
2861            column_keys.iter().map(literal_units).sum::<usize>()
2862                + rows
2863                    .iter()
2864                    .map(|row| {
2865                        row.key.as_ref().map_or(0, literal_units)
2866                            + row.cells.iter().map(literal_units).sum::<usize>()
2867                    })
2868                    .sum::<usize>()
2869        }
2870        _ => 0,
2871    }
2872}
2873fn value_bytes(value: &ReportValue<'_>) -> usize {
2874    match value {
2875        ReportValue::Retained(value) => value_bytes(value),
2876        ReportValue::Item(value)
2877        | ReportValue::EntitySequence { value, .. }
2878        | ReportValue::IncompleteSelection { value, .. } => value_bytes(value),
2879        ReportValue::Constructed(_) | ReportValue::SharedConstructed(_) => {
2880            unreachable!("constructed admission uses the core")
2881        }
2882        ReportValue::Capture(capture) => capture
2883            .fields
2884            .iter()
2885            .map(|(label, value)| label.len() + value_bytes(value))
2886            .sum(),
2887        ReportValue::Captures(captures) => captures
2888            .rows
2889            .iter()
2890            .map(|capture| {
2891                capture
2892                    .fields
2893                    .iter()
2894                    .map(|(label, value)| label.len() + value_bytes(value))
2895                    .sum::<usize>()
2896            })
2897            .sum(),
2898        ReportValue::Record(fields) => fields
2899            .iter()
2900            .map(|(label, value)| label.len() + value_bytes(value))
2901            .sum(),
2902        ReportValue::Query(SemanticStream::Inspection(value)) => match value.as_ref() {
2903            QueryResult::Strings(values) => values.iter().map(String::len).sum(),
2904            QueryResult::Tally(values) => values.iter().map(|(key, _)| key.len()).sum(),
2905            _ => 0,
2906        },
2907        ReportValue::Query(SemanticStream::Types(rows)) => rows
2908            .iter()
2909            .map(|row| {
2910                subject_bytes(&row.subject)
2911                    + row
2912                        .ty
2913                        .as_ref()
2914                        .map_or(0, |ty| semantic::type_allocation(ty).1)
2915            })
2916            .sum(),
2917        ReportValue::Query(SemanticStream::Instantiations(rows)) => rows
2918            .iter()
2919            .map(|row| subject_bytes(&row.subject) + row.reason.as_ref().map_or(0, String::len))
2920            .sum(),
2921        _ => 0,
2922    }
2923}
2924fn subject_bytes(subject: &crate::SemanticSubject<'_>) -> usize {
2925    match subject {
2926        crate::SemanticSubject::Node { location, .. } => location.path.as_os_str().len(),
2927        _ => 0,
2928    }
2929}
2930fn admit_json<'a>(
2931    value: &ReportValue<'a>,
2932    semantic: Option<&SemanticResult<'a>>,
2933    work: &mut Work,
2934    range: &Range<usize>,
2935) -> Result<(), QueryError> {
2936    match value {
2937        ReportValue::Computational(value) => {
2938            return Err(QueryError::query(
2939                value.range(),
2940                "queue and identity_set values cannot be rendered",
2941            ));
2942        }
2943        ReportValue::Tuple(value) => {
2944            work.produce(value.values().len() + 1, range)?;
2945            for value in value.values() {
2946                admit_json(value, semantic, work, range)?;
2947            }
2948        }
2949        ReportValue::Sequence(value) => {
2950            work.produce(value.values().len() + 1, range)?;
2951            for value in value.values() {
2952                admit_json(value, semantic, work, range)?;
2953            }
2954        }
2955        ReportValue::Retained(value) => return admit_json(value, semantic, work, range),
2956        ReportValue::Item(value)
2957        | ReportValue::EntitySequence { value, .. }
2958        | ReportValue::IncompleteSelection { value, .. } => {
2959            return admit_json(value, semantic, work, range);
2960        }
2961        ReportValue::Constructed(_) | ReportValue::SharedConstructed(_) => {}
2962        ReportValue::Capture(capture) => {
2963            work.produce(1 + capture.fields.len(), range)?;
2964            for (label, value) in &capture.fields {
2965                work.spend(label.len(), range)?;
2966                admit_json(value, semantic, work, range)?;
2967            }
2968        }
2969        ReportValue::Captures(captures) => {
2970            work.produce(1 + captures.rows.len(), range)?;
2971            for capture in &captures.rows {
2972                work.produce(1 + capture.fields.len(), range)?;
2973                for (label, value) in &capture.fields {
2974                    work.spend(label.len(), range)?;
2975                    admit_json(value, semantic, work, range)?;
2976                }
2977            }
2978        }
2979        ReportValue::Record(fields) => {
2980            work.produce(1 + fields.len(), range)?;
2981            for (label, value) in fields {
2982                work.spend(label.len(), range)?;
2983                admit_json(value, semantic, work, range)?;
2984            }
2985        }
2986        ReportValue::Query(SemanticStream::Inspection(value)) => {
2987            inspection::admit_json(value, work, range)?
2988        }
2989        ReportValue::Query(stream) => semantic.unwrap().admit_program_json(stream, work, range)?,
2990    }
2991    Ok(())
2992}
2993fn validate_value(
2994    value: &ReportValue<'_>,
2995    json: bool,
2996    budget: &mut Budget,
2997    range: &Range<usize>,
2998) -> Result<(), QueryError> {
2999    match value {
3000        // Computational values are valid retained library results. Rendering
3001        // rejects them recursively in admit_json, before any bytes escape.
3002        ReportValue::Tuple(value) => {
3003            for value in value.values() {
3004                validate_value(value, true, budget, range)?;
3005            }
3006        }
3007        ReportValue::Sequence(value) => {
3008            for value in value.values() {
3009                validate_value(value, true, budget, range)?;
3010            }
3011        }
3012        ReportValue::Retained(value) => return validate_value(value, json, budget, range),
3013        ReportValue::Item(value)
3014        | ReportValue::EntitySequence { value, .. }
3015        | ReportValue::IncompleteSelection { value, .. } => {
3016            return validate_value(value, json, budget, range);
3017        }
3018        ReportValue::Constructed(value) => {
3019            comprehension::validate_report_shape(value, budget, range)?
3020        }
3021        ReportValue::SharedConstructed(value) => {
3022            comprehension::validate_report_shape(value, budget, range)?
3023        }
3024        ReportValue::Capture(capture) => {
3025            for (_, value) in &capture.fields {
3026                validate_value(value, true, budget, range)?;
3027            }
3028        }
3029        ReportValue::Captures(captures) => {
3030            for capture in &captures.rows {
3031                for (_, value) in &capture.fields {
3032                    validate_value(value, true, budget, range)?;
3033                }
3034            }
3035        }
3036        ReportValue::Record(fields) => {
3037            for (_, value) in fields {
3038                validate_value(value, true, budget, range)?;
3039            }
3040        }
3041        ReportValue::Query(SemanticStream::Inspection(value)) => {
3042            if let QueryResult::Selection(selection) = value.as_ref()
3043                && !selection.document
3044            {
3045                crate::validate_fragment_directives(
3046                    selection.input,
3047                    &selection.items,
3048                    &mut Work { budget },
3049                    range,
3050                )?;
3051            }
3052            if json {
3053                inspection::validate_json(value, &mut Work { budget }, range)?;
3054            }
3055        }
3056        _ => {}
3057    }
3058    Ok(())
3059}
3060fn report_json<'a>(
3061    value: &ReportValue<'a>,
3062    semantic: Option<&SemanticResult<'a>>,
3063    budget: &mut Budget,
3064    range: &Range<usize>,
3065) -> Result<Value, QueryError> {
3066    match value {
3067        ReportValue::Computational(value) => Err(QueryError::query(
3068            value.range(),
3069            "queue and identity_set values cannot be rendered",
3070        )),
3071        ReportValue::Tuple(value) => value
3072            .values()
3073            .iter()
3074            .map(|v| report_json(v, semantic, budget, range))
3075            .collect::<Result<Vec<_>, _>>()
3076            .map(Value::Array),
3077        ReportValue::Sequence(value) => value
3078            .values()
3079            .iter()
3080            .map(|v| report_json(v, semantic, budget, range))
3081            .collect::<Result<Vec<_>, _>>()
3082            .map(Value::Array),
3083        ReportValue::Retained(value) => report_json(value, semantic, budget, range),
3084        ReportValue::Item(value)
3085        | ReportValue::EntitySequence { value, .. }
3086        | ReportValue::IncompleteSelection { value, .. } => {
3087            report_json(value, semantic, budget, range)
3088        }
3089        ReportValue::Constructed(value) => comprehension::json_value(value, budget, range),
3090        ReportValue::SharedConstructed(value) => comprehension::json_value(value, budget, range),
3091        ReportValue::Capture(capture) => capture
3092            .fields
3093            .iter()
3094            .map(|(label, value)| Ok((label.clone(), report_json(value, semantic, budget, range)?)))
3095            .collect::<Result<serde_json::Map<_, _>, _>>()
3096            .map(Value::Object),
3097        ReportValue::Captures(captures) => captures
3098            .rows
3099            .iter()
3100            .map(|capture| {
3101                capture
3102                    .fields
3103                    .iter()
3104                    .map(|(label, value)| {
3105                        Ok((label.clone(), report_json(value, semantic, budget, range)?))
3106                    })
3107                    .collect::<Result<serde_json::Map<_, _>, _>>()
3108                    .map(Value::Object)
3109            })
3110            .collect::<Result<Vec<_>, _>>()
3111            .map(Value::Array),
3112        ReportValue::Record(fields) => fields
3113            .iter()
3114            .map(|(label, value)| Ok((label.clone(), report_json(value, semantic, budget, range)?)))
3115            .collect::<Result<serde_json::Map<_, _>, _>>()
3116            .map(Value::Object),
3117        ReportValue::Query(stream) => {
3118            if let Some(semantic) = semantic {
3119                semantic.json_stream(stream)
3120            } else {
3121                let SemanticStream::Inspection(value) = stream else {
3122                    unreachable!()
3123                };
3124                inspection::json_value(value)
3125            }
3126        }
3127    }
3128}
3129fn charge_render<'a>(
3130    value: &ReportValue<'a>,
3131    semantic: Option<&SemanticResult<'a>>,
3132    budget: &mut Budget,
3133    range: &Range<usize>,
3134) -> Result<(), QueryError> {
3135    budget
3136        .charge_results(value_units(value), zirql::Location::Query(range.clone()))
3137        .map_err(pipeline::core_error)?;
3138    let bytes = render_bytes(value, semantic);
3139    budget
3140        .charge(bytes, zirql::Location::Query(range.clone()))
3141        .map_err(pipeline::core_error)
3142}
3143
3144fn render_bytes<'a>(value: &ReportValue<'a>, semantic: Option<&SemanticResult<'a>>) -> usize {
3145    match value {
3146        ReportValue::Computational(_) | ReportValue::Tuple(_) | ReportValue::Sequence(_) => {
3147            unreachable!("native containers use JSON")
3148        }
3149        ReportValue::Retained(value) => render_bytes(value, semantic),
3150        ReportValue::Item(value)
3151        | ReportValue::EntitySequence { value, .. }
3152        | ReportValue::IncompleteSelection { value, .. } => render_bytes(value, semantic),
3153        ReportValue::Constructed(_) | ReportValue::SharedConstructed(_) => {
3154            unreachable!("constructed reports use tagged JSON")
3155        }
3156        ReportValue::Capture(_) | ReportValue::Captures(_) => unreachable!("captures use JSON"),
3157        ReportValue::Record(fields) => fields
3158            .iter()
3159            .map(|(label, value)| label.len() + render_bytes(value, semantic))
3160            .sum(),
3161        ReportValue::Query(SemanticStream::Inspection(value)) => match value.as_ref() {
3162            QueryResult::Selection(s) => {
3163                if s.document {
3164                    s.input.source_bytes().len()
3165                } else {
3166                    s.items.iter().map(|item| item.source_range.len()).sum()
3167                }
3168            }
3169            QueryResult::Nodes(s) => s.nodes.iter().map(|node| node.range.len()).sum(),
3170            QueryResult::Literals(s) => s.values.iter().map(|value| value.spelling.len()).sum(),
3171            QueryResult::Strings(s) => s.iter().map(|value| value.len() + 1).sum(),
3172            QueryResult::Count(_) => 32,
3173            QueryResult::Tally(_) => 0,
3174            _ => unreachable!(),
3175        },
3176        ReportValue::Query(SemanticStream::Entities(rows)) => rows
3177            .iter()
3178            .map(|entity| semantic.unwrap().source_bytes(*entity).len())
3179            .sum(),
3180        ReportValue::Query(stream) => semantic.unwrap().source_output_bytes(stream),
3181    }
3182}
3183
3184/// The fixed Query API retains the same shared predicate and native adapter.
3185#[derive(Debug)]
3186pub(crate) struct QueryPredicate {
3187    filter: composition::FilterStage,
3188    queries: Vec<ProgramQuery>,
3189    work: usize,
3190    nesting: usize,
3191    report_model_demand: bool,
3192}
3193impl QueryPredicate {
3194    pub(crate) fn requires_model_facts(&self) -> bool {
3195        self.report_model_demand
3196            || self
3197                .queries
3198                .iter()
3199                .any(|query| query.query.requires_model_facts())
3200    }
3201}
3202pub(crate) fn read_query_predicate(
3203    source: &str,
3204    start: usize,
3205    limits: Limits,
3206    budget: &mut Budget,
3207) -> Result<(Rc<QueryPredicate>, usize), QueryError> {
3208    let limits = ProgramLimits {
3209        nesting: limits.nesting,
3210        work: limits.work,
3211        stream_items: limits.collection,
3212        ..ProgramLimits::default()
3213    };
3214    let mut parser = Parser::new(source, limits);
3215    parser.prepared = true;
3216    let mut reader = zirql::ArgumentReader::new(source, start).map_err(pipeline::core_error)?;
3217    let before = budget.remaining_work();
3218    let predicate = zirql::read_native_predicate_argument(
3219        &mut reader,
3220        &mut parser,
3221        &[],
3222        budget,
3223        limits.nesting,
3224    )
3225    .map_err(pipeline::core_error)?;
3226    Ok((
3227        Rc::new(QueryPredicate {
3228            filter: composition::FilterStage::new(predicate),
3229            queries: std::mem::take(&mut parser.queries),
3230            work: before - budget.remaining_work(),
3231            nesting: limits.nesting,
3232            report_model_demand: parser.report_model_demand,
3233        }),
3234        reader.position(),
3235    ))
3236}
3237pub(crate) fn filter_query_rows<'a>(
3238    predicate: &QueryPredicate,
3239    rows: Vec<pipeline::Row<'a>>,
3240    query: &mut pipeline::Context<'a>,
3241    input: &'a Input,
3242    budget: &mut Budget,
3243    limits: Limits,
3244    range: &Range<usize>,
3245) -> Result<Vec<pipeline::Row<'a>>, QueryError> {
3246    budget
3247        .charge(
3248            predicate.work,
3249            zirql::Location::Query(predicate.filter.get().range.clone()),
3250        )
3251        .map_err(pipeline::core_error)?;
3252    let value = Located::new(
3253        zirql::ValueExpressionKind::NativeValue(Box::new(Located::new(
3254            ExpressionKind::Sequence {
3255                input: Box::new(value_stage(
3256                    Stage::Filter(predicate.filter.clone()),
3257                    range.clone(),
3258                )),
3259                steps: vec![Located::new(
3260                    ExpressionStep::Stage(Stage::RetainPayload),
3261                    range.clone(),
3262                )],
3263            },
3264            range.clone(),
3265        ))),
3266        range.clone(),
3267    );
3268    let program = zirql::Program::new(
3269        vec![Located::new(
3270            zirql::Statement::Evaluate {
3271                expression: value_stage(Stage::FunctionValue(value), range.clone()),
3272                implicit_output: None,
3273            },
3274            range.clone(),
3275        )],
3276        range.clone(),
3277    );
3278    let pipelines = predicate
3279        .queries
3280        .iter()
3281        .map(|query| {
3282            if query.arguments.is_empty() {
3283                pipeline::lower_pipeline(&query.query, budget).map(Some)
3284            } else {
3285                Ok(None)
3286            }
3287        })
3288        .collect::<Result<Vec<_>, _>>()?;
3289    let stream = query.restore_stream(rows);
3290    let declarations = matches!(&stream, SemanticStream::Entities(rows) if rows.iter().all(|row| matches!(row, semantic::SemanticEntity::Declaration(_))));
3291    let entities = matches!(&stream, SemanticStream::Entities(_));
3292    let facts = query.report.as_ref().map(|report| Rc::clone(&report.facts));
3293    let input_value = if entities {
3294        ReportValue::EntitySequence {
3295            declarations,
3296            owner: facts.clone(),
3297            value: Box::new(ReportValue::Query(stream)),
3298        }
3299    } else {
3300        ReportValue::Query(stream)
3301    };
3302    let mut context = Context {
3303        functions: None,
3304        query,
3305        input,
3306        facts,
3307        facts_admitted: false,
3308        parameters: Vec::new(),
3309        emissions: Vec::new(),
3310        candidate: None,
3311        guard_unknown: None,
3312    };
3313    let mut written_native = program
3314        .statements()
3315        .iter()
3316        .map(|statement| match &statement.value {
3317            zirql::Statement::Evaluate { expression, .. } => expression,
3318            _ => unreachable!("fixed Query filter has one evaluation"),
3319        })
3320        .collect();
3321    admit_written_members(
3322        &mut written_native,
3323        &mut Vec::new(),
3324        &mut context,
3325        budget,
3326        predicate.nesting.min(limits.nesting),
3327    )?;
3328    let host = NativeHost {
3329        functions: true,
3330        signatures: &[],
3331        declarations: &[],
3332        parameters: &[],
3333        queries: &predicate.queries,
3334        pipelines,
3335        nesting: predicate.nesting.min(limits.nesting),
3336        limits,
3337        editing: false,
3338    };
3339    let host = composition::CompositionHost(&host);
3340    let input_kind = zirql::ProjectionHost::kind(&host, &input_value, budget, range)
3341        .map_err(pipeline::core_error)?;
3342    let session = functions::Session::new(
3343        &[],
3344        &[],
3345        &host,
3346        &program,
3347        zirql::FunctionLimits { active_depth: 64 },
3348        input_kind,
3349        budget,
3350    )?;
3351    context.functions = Some(&session);
3352    let zirql::Statement::Evaluate { expression, .. } = &program.statements()[0].value else {
3353        unreachable!()
3354    };
3355    let ExpressionKind::Sequence { steps, .. } = &expression.value else {
3356        unreachable!()
3357    };
3358    let ExpressionStep::Stage(Stage::FunctionValue(value)) = &steps[0].value else {
3359        unreachable!()
3360    };
3361    let bindings = Bindings::new(Vec::new()).map_err(pipeline::core_error)?;
3362    let result = session.run(value, &bindings, &input_value, &mut context, budget)?;
3363    let ReportValue::Query(stream) = functions::owned(result, budget, range)? else {
3364        return Err(QueryError::query(
3365            range.clone(),
3366            "filter requires a native selection",
3367        ));
3368    };
3369    Ok(context.query.take_rows(stream))
3370}
3371
3372fn admit_written_members<'v, 'a>(
3373    native: &mut Vec<&'v Expression>,
3374    values: &mut Vec<&'v zirql::ValueExpression<Expression>>,
3375    context: &mut Context<'_, 'a>,
3376    budget: &mut Budget,
3377    nesting: usize,
3378) -> Result<(), QueryError> {
3379    while !native.is_empty() || !values.is_empty() {
3380        while let Some(value) = values.pop() {
3381            native.extend(functions::eager_native_values(value, budget, nesting)?);
3382        }
3383        let Some(expression) = native.pop() else {
3384            break;
3385        };
3386        budget
3387            .charge(1, zirql::Location::Query(expression.range.clone()))
3388            .map_err(pipeline::core_error)?;
3389        match &expression.value {
3390            ExpressionKind::Input | ExpressionKind::Binding(_) => {}
3391            ExpressionKind::Group(value) | ExpressionKind::Field { value, .. } => {
3392                native.push(value)
3393            }
3394            ExpressionKind::Record(fields) => native.extend(fields.iter().map(|(_, value)| value)),
3395            ExpressionKind::Set { left, right, .. } => {
3396                native.extend([left.as_ref(), right.as_ref()])
3397            }
3398            ExpressionKind::Sequence { input, steps } => {
3399                native.push(input);
3400                for step in steps {
3401                    let ExpressionStep::Stage(stage) = &step.value else {
3402                        continue;
3403                    };
3404                    match stage {
3405                        Stage::Member(member) => {
3406                            let facts = context.report_facts(budget, &member.range)?;
3407                            comprehension::resolve_member(
3408                                facts.as_deref(),
3409                                Some(&member.enum_name.value),
3410                                &member.member_name.value,
3411                                budget,
3412                                &member.range,
3413                            )?;
3414                        }
3415                        Stage::FunctionValue(value)
3416                        | Stage::FunctionSource(value)
3417                        | Stage::PatternBinding(_, value) => values.push(value),
3418                        Stage::RecordValue(fields) => {
3419                            values.extend(fields.iter().map(|(_, value)| value))
3420                        }
3421                        Stage::Primitive(_, arguments) => values.extend(arguments),
3422                        Stage::Query(_, arguments) => {
3423                            values.extend(arguments.iter().map(|argument| &argument.expression))
3424                        }
3425                        Stage::WhereExists(body) | Stage::FixedPoint(body) => {
3426                            values.push(&body.expression)
3427                        }
3428                        Stage::Mapping(mapping) => values.extend([&mapping.key, &mapping.value]),
3429                        Stage::Ordering(ordering) => values.extend(ordering.selector.iter()),
3430                        Stage::Projection(projection) => {
3431                            values.extend(projection.get().fields.iter().map(|(_, value)| value))
3432                        }
3433                        Stage::Filter(predicate) => {
3434                            for guard in composition::guard_leaves(predicate.get()) {
3435                                values.extend(guard.values());
3436                            }
3437                        }
3438                        Stage::Paths(paths) => {
3439                            for guard in composition::guard_leaves(&paths.get().target) {
3440                                values.extend(guard.values());
3441                            }
3442                        }
3443                        Stage::Match(matching) => {
3444                            for clause in &matching.get().clauses {
3445                                match &clause.value {
3446                                    zirql::MatchClause::From { source, .. }
3447                                    | zirql::MatchClause::Bind { value: source, .. } => {
3448                                        native.push(source)
3449                                    }
3450                                    zirql::MatchClause::Where { predicate } => {
3451                                        for guard in composition::guard_leaves(predicate) {
3452                                            values.extend(guard.values());
3453                                        }
3454                                    }
3455                                }
3456                            }
3457                        }
3458                        Stage::Parameter(_)
3459                        | Stage::Data(_)
3460                        | Stage::MapSorting
3461                        | Stage::RetainPayload => {}
3462                    }
3463                }
3464            }
3465        }
3466    }
3467    Ok(())
3468}