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