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