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