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