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