1use std::cmp::Ordering;
2use std::collections::BTreeMap;
3use std::mem::size_of;
4
5pub(crate) mod execution;
6pub(crate) mod pattern;
7pub use pattern::{Pattern, PatternKind, copy_pattern, dispose_pattern, pattern_names};
8pub(crate) mod prepared;
9use prepared::{
10 Frame, Preparation, PreparedArgument, PreparedGenerator, PreparedKind, PreparedValue,
11 RuntimeValue,
12};
13
14use crate::runtime_types::{annotate, check_known_type, compatible_declarations, copy_declaration};
15use crate::{Bindings, Budget, DeclaredType, Located, Location, QueryError, QueryErrorCategory};
16
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
19pub struct NominalTypeId(pub u64);
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
23pub struct NominalMemberId(pub u64);
24
25#[derive(Debug)]
29pub struct NominalMember {
30 pub type_id: NominalTypeId,
31 pub member_id: NominalMemberId,
32 pub declaration_order: usize,
33 pub type_name: String,
34 pub member_name: String,
35}
36
37impl PartialEq for NominalMember {
38 fn eq(&self, other: &Self) -> bool {
39 self.type_id == other.type_id && self.member_id == other.member_id
40 }
41}
42impl Eq for NominalMember {}
43
44#[derive(Debug, PartialEq, Eq)]
46pub enum ValueKey {
47 Integer(i64),
48 Member(NominalMember),
49}
50
51#[derive(Clone, Copy, Debug, PartialEq, Eq)]
52pub enum ValueKeyType {
53 Integer,
54 Member(NominalTypeId),
55}
56
57#[derive(Debug, PartialEq, Eq)]
60pub struct KeyAxis {
61 pub key_type: ValueKeyType,
62 pub keys: Vec<ValueKey>,
63}
64
65#[derive(Debug, PartialEq)]
67pub struct KeyedEntry {
68 pub key: Vec<ValueKey>,
69 pub value: ConstructedValue,
70}
71
72#[derive(Debug, PartialEq, Eq)]
76pub struct KeyedArrayType {
77 pub axes: Option<Vec<ValueKeyType>>,
78 pub elements: Vec<ValueType>,
79}
80
81#[derive(Clone, Copy, Debug, PartialEq, Eq)]
84pub enum SetElementType {
85 Boolean,
86 Integer,
87 Float,
88 Member(NominalTypeId),
89}
90
91#[derive(Debug, PartialEq)]
100pub enum ConstructedValue {
101 Absent,
102 Declared {
104 value: Box<Self>,
105 declaration: DeclaredType<()>,
106 },
107 Boolean(bool),
108 Integer(i64),
109 Float(f64),
110 String(String),
111 Member(NominalMember),
112 KeyedArray {
113 axes: Vec<KeyAxis>,
114 entries: Vec<KeyedEntry>,
115 },
116 Array(Vec<Self>),
117 Tuple(Vec<Self>),
118 Record(Vec<(String, Self)>),
119 Set {
120 element_type: Option<SetElementType>,
121 elements: Vec<Self>,
122 },
123}
124
125pub(crate) fn dispose_constructed_value(value: ConstructedValue) {
127 let mut pending = vec![value];
128 while let Some(value) = pending.pop() {
129 match value {
130 ConstructedValue::Declared { value, declaration } => {
131 crate::functions::dispose_declaration(declaration);
132 pending.push(*value);
133 }
134 ConstructedValue::Array(values) | ConstructedValue::Tuple(values) => {
135 pending.extend(values);
136 }
137 ConstructedValue::Record(fields) => {
138 pending.extend(fields.into_iter().map(|(_, value)| value));
139 }
140 ConstructedValue::KeyedArray { entries, .. } => {
141 pending.extend(entries.into_iter().map(|entry| entry.value));
142 }
143 ConstructedValue::Set { elements, .. } => pending.extend(elements),
144 ConstructedValue::Absent
145 | ConstructedValue::Boolean(_)
146 | ConstructedValue::Integer(_)
147 | ConstructedValue::Float(_)
148 | ConstructedValue::String(_)
149 | ConstructedValue::Member(_) => {}
150 }
151 }
152}
153
154#[derive(Debug, PartialEq, Eq)]
157pub enum ArrayType {
158 Positions(Vec<ValueType>),
159 Elements(Vec<ValueType>),
160}
161
162#[derive(Debug, PartialEq, Eq)]
166pub enum ValueType {
167 Absent,
168 Declared {
170 actual: Box<Self>,
171 declaration: DeclaredType<()>,
172 },
173 ScalarKind,
176 Boolean,
177 Integer,
178 Float,
179 String,
180 Member(NominalTypeId),
181 KeyedArray(KeyedArrayType),
182 Array(ArrayType),
183 ArrayKind(Vec<Self>),
185 Tuple(Vec<Self>),
186 TupleKind,
188 Record(Vec<(String, Self)>),
189 RecordKind,
191 Set(Option<SetElementType>),
192 Unestablished,
193}
194
195#[derive(Clone, Copy, Debug, PartialEq, Eq)]
196pub enum ValueUnaryOperator {
197 Positive,
198 Negative,
199 Not,
200}
201
202#[derive(Clone, Copy, Debug, PartialEq, Eq)]
203pub enum ValueBinaryOperator {
204 Add,
205 Subtract,
206 Multiply,
207 Divide,
208 IntegerDivide,
209 Modulo,
210 Less,
211 LessEqual,
212 Greater,
213 GreaterEqual,
214 Equal,
215 NotEqual,
216 In,
217 And,
218 Or,
219 Concatenate,
220 Contains,
222 StartsWith,
224 EndsWith,
226}
227
228#[derive(Clone, Copy, Debug, PartialEq, Eq)]
229pub enum ValueConstructor {
230 Array,
231 Set,
232}
233
234#[derive(Clone, Copy, Debug, PartialEq, Eq)]
238pub enum ValueReducer {
239 Length,
240 Sum,
241 Forall,
242 Exists,
243}
244
245#[derive(Clone, Copy, Debug, PartialEq, Eq)]
247pub enum ValueExtremum {
248 Min,
249 Max,
250}
251
252#[derive(Debug, PartialEq, Eq)]
254pub enum SortSelector {
255 Value,
257 Field(String),
259}
260
261pub type ValueExpression<Q = ()> = Located<ValueExpressionKind<Q>>;
262
263pub(crate) const PREDICATE_SUBJECT: &str = "\0predicate";
265
266#[derive(Debug, PartialEq)]
271pub struct PredicateArgument<Q = ()> {
272 pub argument: Option<Pattern>,
275 pub body: Box<ValueExpression<Q>>,
276}
277impl<Q> PredicateArgument<Q> {
278 pub fn bind_value(self, subject: ValueExpression<Q>) -> ValueExpression<Q> {
282 let range = self.body.range.clone();
283 Located::new(
284 ValueExpressionKind::SubjectScope {
285 subject: Box::new(subject),
286 argument: self,
287 },
288 range,
289 )
290 }
291 pub fn bind(self, subject: ValueExpression<Q>) -> ValueExpression<Q> {
295 let range = self.body.range.clone();
296 Located::new(
297 ValueExpressionKind::PredicateScope {
298 subject: Box::new(subject),
299 predicate: self,
300 },
301 range,
302 )
303 }
304}
305
306#[derive(Debug, PartialEq)]
310pub enum ValueExpressionKind<Q = ()> {
311 Literal(ConstructedValue),
312 CurrentInput,
314 Queue {
316 items: Box<ValueExpression<Q>>,
317 element: Option<Located<crate::SignatureType>>,
318 },
319 PopFront(Box<ValueExpression<Q>>),
321 PushBackAll {
322 queue: Box<ValueExpression<Q>>,
323 items: Box<ValueExpression<Q>>,
324 },
325 IdentitySet {
327 element: Located<crate::SignatureType>,
328 },
329 Insert {
330 set: Box<ValueExpression<Q>>,
331 item: Box<ValueExpression<Q>>,
332 },
333 NativeQuery(Box<Q>),
335 NativeValue(Box<Q>),
338 Call {
340 function: Located<String>,
341 arguments: Vec<ValueExpression<Q>>,
342 },
343 Fold {
374 items: Box<ValueExpression<Q>>,
375 initial: Box<ValueExpression<Q>>,
376 step: Located<String>,
377 },
378 Scan {
380 items: Box<ValueExpression<Q>>,
381 initial: Box<ValueExpression<Q>>,
382 step: Located<String>,
383 },
384 One(Box<ValueExpression<Q>>),
386 Name(String),
387 PredicateSubject,
389 SubjectScope {
391 subject: Box<ValueExpression<Q>>,
392 argument: PredicateArgument<Q>,
393 },
394 PredicateScope {
396 subject: Box<ValueExpression<Q>>,
397 predicate: PredicateArgument<Q>,
398 },
399 Occurs(Box<ValueExpression<Q>>),
400 Absent(Box<ValueExpression<Q>>),
401 Lookup {
402 value: Box<ValueExpression<Q>>,
403 key: Box<ValueExpression<Q>>,
404 },
405 HasKey {
406 value: Box<ValueExpression<Q>>,
407 key: Box<ValueExpression<Q>>,
408 },
409 Default {
410 value: Box<ValueExpression<Q>>,
411 fallback: Box<ValueExpression<Q>>,
412 },
413 Array(Vec<ValueExpression<Q>>),
414 Tuple(Vec<ValueExpression<Q>>),
415 Record(Vec<(Located<String>, ValueExpression<Q>)>),
416 Dictionary(Vec<(ValueExpression<Q>, ValueExpression<Q>)>),
418 DictionaryComprehension {
419 clauses: Vec<Located<ValueGenerator<Q>>>,
420 key: Box<ValueExpression<Q>>,
421 head: Box<ValueExpression<Q>>,
422 },
423 Set(Vec<ValueExpression<Q>>),
424 KeyedArray {
427 axes: Option<Vec<KeyAxis>>,
428 declared_axis_sources: Option<Vec<ValueExpression<Q>>>,
430 entries: Vec<(ValueExpression<Q>, ValueExpression<Q>)>,
431 },
432 KeyedComprehension {
434 clauses: Vec<Located<ValueGenerator<Q>>>,
435 key: Box<ValueExpression<Q>>,
436 head: Box<ValueExpression<Q>>,
437 declared_axis_sources: Option<Vec<ValueExpression<Q>>>,
438 empty_axis_sources: Option<Vec<ValueExpression<Q>>>,
440 },
441 Unary {
442 operator: ValueUnaryOperator,
443 operand: Box<ValueExpression<Q>>,
444 },
445 Binary {
446 operator: ValueBinaryOperator,
447 left: Box<ValueExpression<Q>>,
448 right: Box<ValueExpression<Q>>,
449 },
450 Field {
451 value: Box<ValueExpression<Q>>,
452 field: Located<String>,
453 },
454 TupleAccess {
455 value: Box<ValueExpression<Q>>,
456 position: Located<usize>,
457 },
458 ArrayAccess {
459 array: Box<ValueExpression<Q>>,
460 index: Box<ValueExpression<Q>>,
461 },
462 IndexSet(Box<ValueExpression<Q>>),
463 Entries(Box<ValueExpression<Q>>),
466 ZipExact {
469 left: Box<ValueExpression<Q>>,
470 right: Box<ValueExpression<Q>>,
471 },
472 Reduce {
474 operator: ValueReducer,
475 array: Box<ValueExpression<Q>>,
476 },
477 Filter {
479 array: Box<ValueExpression<Q>>,
480 predicate: PredicateArgument<Q>,
481 },
482 Sort {
488 array: Box<ValueExpression<Q>>,
489 selector: Option<Located<SortSelector>>,
490 descending: bool,
491 },
492 Unique(Box<ValueExpression<Q>>),
495 Tally(Box<ValueExpression<Q>>),
497 Extremum {
500 operator: ValueExtremum,
501 array: Box<ValueExpression<Q>>,
502 field: Option<Located<String>>,
503 },
504 GroupBy {
510 rows: Box<ValueExpression<Q>>,
511 field: Located<String>,
512 },
513 Require {
516 condition: Box<ValueExpression<Q>>,
517 value: Box<ValueExpression<Q>>,
518 message: Located<String>,
519 },
520 Range {
521 lower: Box<ValueExpression<Q>>,
522 upper: Box<ValueExpression<Q>>,
523 },
524 Conditional {
526 branches: Vec<(ValueExpression<Q>, ValueExpression<Q>)>,
527 otherwise: Box<ValueExpression<Q>>,
528 },
529 Loop {
572 states: Vec<(Located<String>, ValueExpression<Q>)>,
573 body: Box<ValueExpression<Q>>,
574 },
575 Break(Box<ValueExpression<Q>>),
577 Continue(Vec<ValueExpression<Q>>),
579 Block {
581 bindings: Vec<(Pattern, ValueExpression<Q>)>,
582 value: Box<ValueExpression<Q>>,
583 },
584 ValueMatch {
586 subject: Box<ValueExpression<Q>>,
587 arms: Vec<ValueMatchArm<Q>>,
588 },
589 Comprehension {
590 constructor: ValueConstructor,
591 clauses: Vec<Located<ValueGenerator<Q>>>,
592 head: Box<ValueExpression<Q>>,
593 },
594}
595
596#[derive(Debug, PartialEq)]
598pub struct ValueMatchArm<Q = ()> {
599 pub pattern: Pattern,
600 pub guard: Option<ValueExpression<Q>>,
601 pub value: ValueExpression<Q>,
602}
603
604#[derive(Debug, PartialEq)]
607pub enum ValueGenerator<Q = ()> {
608 In {
609 binders: Vec<Pattern>,
610 source: ValueExpression<Q>,
611 filter: Option<ValueExpression<Q>>,
612 },
613 Bind {
614 binder: Pattern,
615 value: ValueExpression<Q>,
616 filter: Option<ValueExpression<Q>>,
617 },
618}
619
620#[derive(Debug)]
623pub struct ConstructedRead {
624 value: RuntimeValue,
625 location: Location,
626 declaration: Option<DeclaredType<()>>,
627}
628impl ConstructedRead {
629 pub(crate) fn from_runtime(
630 value: RuntimeValue,
631 location: Location,
632 declaration: Option<DeclaredType<()>>,
633 ) -> Self {
634 Self {
635 value,
636 location,
637 declaration,
638 }
639 }
640 pub fn value(&self) -> &ConstructedValue {
641 self.value.as_ref()
642 }
643 pub fn declared_type(&self) -> Option<&DeclaredType<()>> {
647 self.declaration
648 .as_ref()
649 .or_else(|| declaration(self.value.as_ref()))
650 }
651 pub fn into_owned(self, budget: &mut Budget) -> Result<ConstructedValue, QueryError> {
656 budget.within_evaluation(1, self.location.clone(), |budget| {
657 let value = self.value.materialize(budget, &self.location)?;
658 if let Some(declaration) = self.declaration {
659 annotate(value, declaration, budget, &self.location)
660 } else {
661 Ok(value)
662 }
663 })
664 }
665}
666
667pub trait ValueArguments<Q, E: From<QueryError>> {
670 fn evaluate(
671 &mut self,
672 expression: &ValueExpression<Q>,
673 queries: &mut dyn ValueQueryHost<Q, Error = E>,
674 budget: &mut Budget,
675 ) -> Result<ConstructedRead, E>;
676}
677
678pub trait ValueQueryHost<Q> {
682 type Error: From<QueryError>;
683
684 fn current_input_types(
687 &mut self,
688 range: &std::ops::Range<usize>,
689 _budget: &mut Budget,
690 ) -> Result<Vec<ValueType>, QueryError> {
691 Err(QueryError::query(
692 range.clone(),
693 "constructed current input is unavailable",
694 ))
695 }
696 fn read_current_input(
699 &mut self,
700 range: &std::ops::Range<usize>,
701 _budget: &mut Budget,
702 ) -> Result<ConstructedValue, Self::Error> {
703 Err(QueryError::query(range.clone(), "constructed current input is unavailable").into())
704 }
705
706 fn predicate_subject_types(
709 &mut self,
710 range: &std::ops::Range<usize>,
711 _budget: &mut Budget,
712 ) -> Result<Vec<ValueType>, QueryError> {
713 Err(QueryError::query(
714 range.clone(),
715 "unnamed argument is only available in a predicate",
716 ))
717 }
718 fn read_predicate_subject(
719 &mut self,
720 range: &std::ops::Range<usize>,
721 _budget: &mut Budget,
722 ) -> Result<ConstructedValue, Self::Error> {
723 Err(QueryError::query(
724 range.clone(),
725 "unnamed argument is only available in a predicate",
726 )
727 .into())
728 }
729
730 fn prepare_computation(
733 &mut self,
734 _expression: &ValueExpression<Q>,
735 _budget: &mut Budget,
736 ) -> Result<(), QueryError> {
737 Ok(())
738 }
739 fn worklist_id(
741 &mut self,
742 expression: &ValueExpression<Q>,
743 _budget: &mut Budget,
744 ) -> Result<usize, QueryError> {
745 Err(QueryError::query(
746 expression.range.clone(),
747 "computational values require a native function host",
748 ))
749 }
750 fn worklist_types(
753 &mut self,
754 expression: &ValueExpression<Q>,
755 _budget: &mut Budget,
756 ) -> Result<Vec<ValueType>, QueryError> {
757 Err(QueryError::query(
758 expression.range.clone(),
759 "computational values require a native function host",
760 ))
761 }
762
763 fn function(
766 &mut self,
767 name: &Located<String>,
768 _budget: &mut Budget,
769 ) -> Result<(usize, usize), QueryError> {
770 Err(QueryError::query(
771 name.range.clone(),
772 "named functions require a function request",
773 ))
774 }
775 fn function_types(
776 &mut self,
777 name: &Located<String>,
778 budget: &mut Budget,
779 ) -> Result<Vec<ValueType>, QueryError> {
780 self.function(name, budget)?;
781 Ok(vec![ValueType::Unestablished])
782 }
783 fn accumulator_function(
786 &mut self,
787 step: &Located<String>,
788 _budget: &mut Budget,
789 ) -> Result<(usize, Vec<ValueType>), QueryError> {
790 Err(QueryError::query(
791 step.range.clone(),
792 "named accumulators require a function request",
793 ))
794 }
795 fn native_value_types(
796 &mut self,
797 _query: &Q,
798 _locals: &[(&str, Vec<ValueType>)],
799 _budget: &mut Budget,
800 range: &std::ops::Range<usize>,
801 ) -> Result<Vec<ValueType>, QueryError> {
802 Err(QueryError::query(
803 range.clone(),
804 "native values require a function request",
805 ))
806 }
807 fn enter_scope(
809 &mut self,
810 _budget: &mut Budget,
811 _range: &std::ops::Range<usize>,
812 ) -> Result<(), QueryError> {
813 Ok(())
814 }
815 fn leave_scope(&mut self) {}
816 fn prepare_pattern(
818 &mut self,
819 _pattern: &Pattern,
820 _source: &ValueExpression<Q>,
821 _budget: &mut Budget,
822 ) -> Result<(), QueryError> {
823 Ok(())
824 }
825 fn prepare_local(
826 &mut self,
827 _name: &Located<String>,
828 _value: &ValueExpression<Q>,
829 _types: &[ValueType],
830 _budget: &mut Budget,
831 ) -> Result<(), QueryError> {
832 Ok(())
833 }
834 fn prepare_generator(
839 &mut self,
840 _binder: &Pattern,
841 _source: &ValueExpression<Q>,
842 _types: &[ValueType],
843 _element: bool,
844 _budget: &mut Budget,
845 ) -> Result<(), QueryError> {
846 Ok(())
847 }
848 fn prepare_predicate(
851 &mut self,
852 _argument: Option<&Pattern>,
853 _source: &ValueExpression<Q>,
854 _types: &[ValueType],
855 _element: bool,
856 _budget: &mut Budget,
857 ) -> Result<(), QueryError> {
858 Ok(())
859 }
860 fn query_types(
861 &mut self,
862 query: &Q,
863 locals: &[(&str, Vec<ValueType>)],
864 budget: &mut Budget,
865 range: &std::ops::Range<usize>,
866 ) -> Result<Vec<ValueType>, QueryError>;
867 fn evaluate_query(
868 &mut self,
869 query: &Q,
870 locals: &[(&str, ConstructedValue)],
871 budget: &mut Budget,
872 ) -> Result<ConstructedValue, Self::Error>;
873 fn read_binding(
874 &mut self,
875 name: &str,
876 range: &std::ops::Range<usize>,
877 budget: &mut Budget,
878 ) -> Result<ConstructedValue, Self::Error>;
879 fn evaluate_query_with_shared_locals(
882 &mut self,
883 query: &Q,
884 locals: &[(&str, std::sync::Arc<ConstructedValue>)],
885 budget: &mut Budget,
886 range: &std::ops::Range<usize>,
887 ) -> Result<ConstructedValue, Self::Error> {
888 let location = Location::Query(range.clone());
889 let mut owned = metadata_vec(locals.len(), budget, &location)?;
890 for (name, value) in locals {
891 owned.push((
892 *name,
893 copy_constructed_value(value, budget, location.clone())?,
894 ));
895 }
896 self.evaluate_query(query, &owned, budget)
897 }
898 fn query_arguments<'q>(
900 &self,
901 _query: &'q Q,
902 _budget: &mut Budget,
903 _range: &std::ops::Range<usize>,
904 ) -> Result<Vec<&'q ValueExpression<Q>>, QueryError> {
905 Ok(Vec::new())
906 }
907 fn evaluate_query_with_arguments(
909 &mut self,
910 query: &Q,
911 locals: &[(&str, std::sync::Arc<ConstructedValue>)],
912 _arguments: &mut dyn ValueArguments<Q, Self::Error>,
913 budget: &mut Budget,
914 range: &std::ops::Range<usize>,
915 ) -> Result<ConstructedValue, Self::Error> {
916 self.evaluate_query_with_shared_locals(query, locals, budget, range)
917 }
918 fn read_shared_binding(
922 &mut self,
923 _name: &str,
924 _range: &std::ops::Range<usize>,
925 _budget: &mut Budget,
926 ) -> Result<Option<std::sync::Arc<ConstructedValue>>, Self::Error> {
927 Ok(None)
928 }
929 fn validate_name(
930 &mut self,
931 _name: &str,
932 _range: &std::ops::Range<usize>,
933 _budget: &mut Budget,
934 ) -> Result<(), QueryError> {
935 Ok(())
936 }
937}
938
939struct NoQueries<'a> {
940 bindings: Option<&'a Bindings<ConstructedValue>>,
941}
942impl ValueQueryHost<()> for NoQueries<'_> {
943 type Error = QueryError;
944 fn query_types(
945 &mut self,
946 _: &(),
947 _: &[(&str, Vec<ValueType>)],
948 _: &mut Budget,
949 range: &std::ops::Range<usize>,
950 ) -> Result<Vec<ValueType>, QueryError> {
951 Err(QueryError::query(
952 range.clone(),
953 "native queries are unavailable in this value expression",
954 ))
955 }
956 fn evaluate_query(
957 &mut self,
958 _: &(),
959 _: &[(&str, ConstructedValue)],
960 _: &mut Budget,
961 ) -> Result<ConstructedValue, QueryError> {
962 Err(QueryError::query(
963 0..0,
964 "native queries are unavailable in this value expression",
965 ))
966 }
967 fn read_shared_binding(
968 &mut self,
969 name: &str,
970 range: &std::ops::Range<usize>,
971 budget: &mut Budget,
972 ) -> Result<Option<std::sync::Arc<ConstructedValue>>, QueryError> {
973 budget.charge(1, Location::Query(range.clone()))?;
974 Ok(Some(
975 self.bindings
976 .expect("runtime value bindings")
977 .shared(name, range.clone())?,
978 ))
979 }
980
981 fn read_binding(
982 &mut self,
983 name: &str,
984 range: &std::ops::Range<usize>,
985 budget: &mut Budget,
986 ) -> Result<ConstructedValue, QueryError> {
987 copy_constructed_value(
988 self.bindings
989 .expect("runtime value bindings")
990 .get(name, range.clone())?,
991 budget,
992 Location::Query(range.clone()),
993 )
994 }
995}
996
997pub fn value_expression_types(
1002 expression: &ValueExpression,
1003 binding_types: &BTreeMap<String, ValueType>,
1004 budget: &mut Budget,
1005) -> Result<Vec<ValueType>, QueryError> {
1006 Analysis::new(
1007 Globals::Types(binding_types),
1008 &mut NoQueries { bindings: None },
1009 )
1010 .walk(expression, budget)
1011}
1012
1013pub fn query_value_types<Q, H: ValueQueryHost<Q>>(
1018 expression: &ValueExpression<Q>,
1019 binding_types: &BTreeMap<String, Vec<ValueType>>,
1020 queries: &mut H,
1021 budget: &mut Budget,
1022) -> Result<Vec<ValueType>, QueryError> {
1023 Analysis::new(Globals::Facts(binding_types), queries).walk(expression, budget)
1024}
1025
1026pub fn free_query_value_names<Q, H: ValueQueryHost<Q>>(
1031 expression: &ValueExpression<Q>,
1032 binding_types: &BTreeMap<String, Vec<ValueType>>,
1033 queries: &mut H,
1034 budget: &mut Budget,
1035) -> Result<Vec<Located<String>>, QueryError> {
1036 let mut analysis = Analysis::new(Globals::Facts(binding_types), queries);
1037 analysis.walk(expression, budget)?;
1038 Ok(analysis.free)
1039}
1040
1041pub fn evaluate_query_value<Q, H: ValueQueryHost<Q>>(
1046 expression: &ValueExpression<Q>,
1047 binding_types: &BTreeMap<String, Vec<ValueType>>,
1048 queries: &mut H,
1049 budget: &mut Budget,
1050) -> Result<ConstructedValue, H::Error> {
1051 let prepared = prepare_query_value(expression, binding_types, queries, budget)?;
1052 let mut frame = Frame::new(
1053 prepared.names,
1054 budget,
1055 &Location::Query(expression.range.clone()),
1056 )?;
1057 run(
1058 &prepared.expression,
1059 queries,
1060 &mut frame,
1061 budget,
1062 &mut |_, _, _| Ok(()),
1063 )
1064}
1065
1066pub(crate) struct PreparedQueryValue<'a, Q> {
1067 expression: PreparedValue<'a, Q>,
1068 names: Vec<&'a str>,
1069 pub types: Vec<ValueType>,
1070}
1071pub(crate) fn prepare_query_value<'a, Q, H: ValueQueryHost<Q>>(
1072 expression: &'a ValueExpression<Q>,
1073 binding_types: &BTreeMap<String, Vec<ValueType>>,
1074 queries: &mut H,
1075 budget: &mut Budget,
1076) -> Result<PreparedQueryValue<'a, Q>, QueryError> {
1077 let types = query_value_types(expression, binding_types, queries, budget)?;
1078 let mut preparation = Preparation::new();
1079 let expression = preparation.resolve(expression, queries, budget)?;
1080 Ok(PreparedQueryValue {
1081 expression,
1082 names: preparation.names,
1083 types,
1084 })
1085}
1086impl<'a, Q> PreparedQueryValue<'a, Q> {
1087 pub fn frame(&self, budget: &mut Budget) -> Result<Frame<'a>, QueryError> {
1088 let mut names = metadata_vec(
1089 self.names.len(),
1090 budget,
1091 &Location::Query(self.expression.range.clone()),
1092 )?;
1093 names.extend_from_slice(&self.names);
1094 Frame::new(
1095 names,
1096 budget,
1097 &Location::Query(self.expression.range.clone()),
1098 )
1099 }
1100 pub fn evaluate<H: ValueQueryHost<Q>>(
1101 &'a self,
1102 queries: &mut H,
1103 frame: &mut Frame<'a>,
1104 budget: &mut Budget,
1105 ) -> Result<ConstructedValue, H::Error> {
1106 run(&self.expression, queries, frame, budget, &mut |_, _, _| {
1107 Ok(())
1108 })
1109 }
1110}
1111
1112pub fn take_one<T>(mut values: Vec<T>, range: std::ops::Range<usize>) -> Result<T, QueryError> {
1117 if values.len() != 1 {
1118 return Err(QueryError::query(range, "one requires exactly one value"));
1119 }
1120 Ok(values.pop().expect("one child"))
1121}
1122
1123pub fn count_value(
1128 count: usize,
1129 budget: &mut Budget,
1130 location: Location,
1131) -> Result<ConstructedValue, QueryError> {
1132 let count =
1133 i64::try_from(count).map_err(|_| error(&location, "count exceeds signed 64-bit range"))?;
1134 scalar(ConstructedValue::Integer(count), budget, &location)
1135}
1136
1137pub fn validate_value_expression(
1143 expression: &ValueExpression,
1144 binding_types: &BTreeMap<String, ValueType>,
1145 budget: &mut Budget,
1146) -> Result<(), QueryError> {
1147 Analysis::new(
1148 Globals::Types(binding_types),
1149 &mut NoQueries { bindings: None },
1150 )
1151 .walk(expression, budget)?;
1152 Ok(())
1153}
1154
1155pub fn free_value_names(
1162 expression: &ValueExpression,
1163 binding_types: &BTreeMap<String, ValueType>,
1164 budget: &mut Budget,
1165) -> Result<Vec<Located<String>>, QueryError> {
1166 let mut queries = NoQueries { bindings: None };
1167 let mut analysis = Analysis::new(Globals::Types(binding_types), &mut queries);
1168 analysis.walk(expression, budget)?;
1169 Ok(analysis.free)
1170}
1171
1172pub fn constructed_value_type(
1179 value: &ConstructedValue,
1180 budget: &mut Budget,
1181 location: Location,
1182) -> Result<ValueType, QueryError> {
1183 budget.within_evaluation(1, location.clone(), |budget| {
1184 budget.charge(1, location.clone())?;
1185 match value {
1186 ConstructedValue::Absent => Ok(ValueType::Absent),
1187 ConstructedValue::Declared { value, declaration } => {
1188 crate::admit_constructed(value, declaration, budget, location.clone())?;
1189 Ok(ValueType::Declared {
1190 actual: Box::new(constructed_value_type(value, budget, location.clone())?),
1191 declaration: copy_declaration(declaration, budget, &location)?,
1192 })
1193 }
1194 ConstructedValue::Boolean(_) => Ok(ValueType::Boolean),
1195 ConstructedValue::Integer(_) => Ok(ValueType::Integer),
1196 ConstructedValue::Float(value) => {
1197 finite(*value, &location)?;
1198 Ok(ValueType::Float)
1199 }
1200 ConstructedValue::String(_) => Ok(ValueType::String),
1201 ConstructedValue::Member(member) => {
1202 member_bytes(member, budget, &location)?;
1203 Ok(ValueType::Member(member.type_id))
1204 }
1205 ConstructedValue::KeyedArray { axes, entries } => {
1206 check_keyed(axes, entries, budget, &location)?;
1207 let axes = axis_types(axes, budget, &location)?;
1208 let mut elements = metadata_vec(entries.len(), budget, &location)?;
1209 for entry in entries {
1210 elements.push(constructed_value_type(
1211 &entry.value,
1212 budget,
1213 location.clone(),
1214 )?);
1215 }
1216 Ok(ValueType::KeyedArray(KeyedArrayType {
1217 axes: Some(axes),
1218 elements,
1219 }))
1220 }
1221 ConstructedValue::Array(values) | ConstructedValue::Tuple(values) => {
1222 let mut types = metadata_vec(values.len(), budget, &location)?;
1223 for value in values {
1224 types.push(constructed_value_type(value, budget, location.clone())?);
1225 }
1226 Ok(if matches!(value, ConstructedValue::Array(_)) {
1227 ValueType::Array(ArrayType::Positions(types))
1228 } else {
1229 ValueType::Tuple(types)
1230 })
1231 }
1232 ConstructedValue::Record(fields) => {
1233 check_value_labels(fields, budget, &location)?;
1234 let mut types = metadata_vec(fields.len(), budget, &location)?;
1235 for (label, value) in fields {
1236 budget.charge(label.len(), location.clone())?;
1237 let ty = constructed_value_type(value, budget, location.clone())?;
1238 types.push((label.to_owned(), ty));
1239 }
1240 Ok(ValueType::Record(types))
1241 }
1242 ConstructedValue::Set {
1243 element_type,
1244 elements,
1245 } => {
1246 check_set(*element_type, elements, budget, &location)?;
1247 Ok(ValueType::Set(*element_type))
1248 }
1249 }
1250 })
1251}
1252
1253pub fn evaluate_value(
1263 expression: &ValueExpression,
1264 bindings: &Bindings<ConstructedValue>,
1265 budget: &mut Budget,
1266) -> Result<ConstructedValue, QueryError> {
1267 evaluate_value_with_host_validation(expression, bindings, budget, |_, _, _| Ok(()))
1268}
1269
1270pub fn evaluate_value_with_host_validation(
1282 expression: &ValueExpression,
1283 bindings: &Bindings<ConstructedValue>,
1284 budget: &mut Budget,
1285 mut check_value: impl FnMut(&ConstructedValue, &mut Budget, &Location) -> Result<(), QueryError>,
1286) -> Result<ConstructedValue, QueryError> {
1287 let mut queries = NoQueries {
1288 bindings: Some(bindings),
1289 };
1290 let mut analysis = Analysis::new(Globals::Available(bindings), &mut queries);
1291 analysis.walk(expression, budget)?;
1292 let mut types = BTreeMap::new();
1293 for name in analysis.free {
1294 let location = Location::Query(name.range.clone());
1295 let mut fallback_only = false;
1296 for candidate in &analysis.fallback_names {
1297 budget.charge(
1298 1 + candidate.value.len().min(name.value.len()),
1299 location.clone(),
1300 )?;
1301 if candidate.value == name.value {
1302 fallback_only = true;
1303 break;
1304 }
1305 }
1306 budget.charge(1, location.clone())?;
1307 admit_diagnostic("undefined binding: ", &name.value, budget, &location)?;
1308 let value = bindings.get(&name.value, name.range)?;
1309 let ty = if !fallback_only {
1310 constructed_value_type(value, budget, location)?
1311 } else {
1312 shallow_type(value, budget, &location)?
1313 };
1314 types.insert(name.value, ty);
1315 }
1316 validate_value_expression(expression, &types, budget)?;
1317 let mut preparation = Preparation::new();
1318 let prepared = preparation.resolve(expression, &mut queries, budget)?;
1319 let mut frame = Frame::new(
1320 preparation.names,
1321 budget,
1322 &Location::Query(expression.range.clone()),
1323 )?;
1324 run(
1325 &prepared,
1326 &mut NoQueries {
1327 bindings: Some(bindings),
1328 },
1329 &mut frame,
1330 budget,
1331 &mut check_value,
1332 )
1333}
1334
1335pub fn copy_constructed_value(
1343 value: &ConstructedValue,
1344 budget: &mut Budget,
1345 location: Location,
1346) -> Result<ConstructedValue, QueryError> {
1347 let (units, bytes) = copy_size(value, budget, &location)?;
1348 budget.charge(bytes, location.clone())?;
1349 budget.charge_results(units, location.clone())?;
1350 copy_admitted(value, budget, &location)
1351}
1352
1353impl ConstructedValue {
1354 pub fn data(&self) -> &Self {
1356 let mut value = self;
1357 while let Self::Declared { value: inner, .. } = value {
1358 value = inner;
1359 }
1360 value
1361 }
1362
1363 pub fn occurs(&self) -> bool {
1365 !matches!(self.data(), Self::Absent)
1366 }
1367
1368 pub fn into_data(self) -> Self {
1370 let mut value = self;
1371 while let Self::Declared { value: inner, .. } = value {
1372 value = *inner;
1373 }
1374 value
1375 }
1376}
1377
1378fn declaration(value: &ConstructedValue) -> Option<&DeclaredType<()>> {
1379 match value {
1380 ConstructedValue::Declared { declaration, .. } => Some(declaration),
1381 _ => None,
1382 }
1383}
1384
1385fn underlying(mut declaration: &DeclaredType<()>) -> &DeclaredType<()> {
1386 while let DeclaredType::Optional(child) = declaration {
1387 declaration = child;
1388 }
1389 declaration
1390}
1391
1392fn declared_facts(
1393 declaration: &DeclaredType<()>,
1394 budget: &mut Budget,
1395 location: &Location,
1396) -> Result<ValueType, QueryError> {
1397 budget.within_evaluation(1, location.clone(), |budget| {
1398 budget.charge(1, location.clone())?;
1399 Ok(match declaration {
1400 DeclaredType::Optional(child) => declared_facts(child, budget, location)?,
1401 DeclaredType::Boolean => ValueType::Boolean,
1402 DeclaredType::Integer => ValueType::Integer,
1403 DeclaredType::Float => ValueType::Float,
1404 DeclaredType::String => ValueType::String,
1405 DeclaredType::Member(id) => ValueType::Member(*id),
1406 DeclaredType::Set(element) => ValueType::Set(Some(*element)),
1407 DeclaredType::Array(child) => ValueType::ArrayKind(one_type(
1408 declared_fact(child, ValueType::Unestablished, budget, location)?,
1409 budget,
1410 location,
1411 )?),
1412 DeclaredType::Sequence(child) => ValueType::Array(ArrayType::Elements(one_type(
1413 declared_fact(child, ValueType::Unestablished, budget, location)?,
1414 budget,
1415 location,
1416 )?)),
1417 DeclaredType::Keyed(child) => ValueType::KeyedArray(KeyedArrayType {
1418 axes: None,
1419 elements: one_type(
1420 declared_fact(child, ValueType::Unestablished, budget, location)?,
1421 budget,
1422 location,
1423 )?,
1424 }),
1425 DeclaredType::Tuple(children) => {
1426 let mut fields = metadata_vec(children.len(), budget, location)?;
1427 for child in children {
1428 fields.push(declared_fact(
1429 child,
1430 ValueType::Unestablished,
1431 budget,
1432 location,
1433 )?);
1434 }
1435 ValueType::Tuple(fields)
1436 }
1437 DeclaredType::Record(children) => {
1438 let mut fields = metadata_vec(children.len(), budget, location)?;
1439 for (label, child) in children {
1440 budget.charge(label.len(), location.clone())?;
1441 fields.push((
1442 label.clone(),
1443 declared_fact(child, ValueType::Unestablished, budget, location)?,
1444 ));
1445 }
1446 ValueType::Record(fields)
1447 }
1448 DeclaredType::Native(()) | DeclaredType::Queue(_) | DeclaredType::IdentitySet(()) => {
1449 return Err(error(location, "native declaration requires a host"));
1450 }
1451 })
1452 })
1453}
1454
1455fn declared_fact(
1456 declaration: &DeclaredType<()>,
1457 actual: ValueType,
1458 budget: &mut Budget,
1459 location: &Location,
1460) -> Result<ValueType, QueryError> {
1461 budget.charge(1, location.clone())?;
1462 Ok(ValueType::Declared {
1463 actual: Box::new(actual),
1464 declaration: copy_declaration(declaration, budget, location)?,
1465 })
1466}
1467
1468fn shape_type(
1471 ty: ValueType,
1472 budget: &mut Budget,
1473 location: &Location,
1474) -> Result<ValueType, QueryError> {
1475 let ValueType::Declared {
1476 actual,
1477 declaration,
1478 } = ty
1479 else {
1480 return Ok(ty);
1481 };
1482 let actual = match *actual {
1483 ValueType::Unestablished => return declared_facts(&declaration, budget, location),
1484 ValueType::ScalarKind
1485 if matches!(
1486 underlying(&declaration),
1487 DeclaredType::Boolean
1488 | DeclaredType::Integer
1489 | DeclaredType::Float
1490 | DeclaredType::String
1491 ) =>
1492 {
1493 return declared_facts(&declaration, budget, location);
1494 }
1495 actual => shape_type(actual, budget, location)?,
1496 };
1497 Ok(match (actual, underlying(&declaration)) {
1498 (ValueType::Array(array), DeclaredType::Array(child) | DeclaredType::Sequence(child)) => {
1499 let positional = matches!(array, ArrayType::Positions(_));
1500 let (ArrayType::Positions(entries) | ArrayType::Elements(entries)) = array;
1501 let mut fields = metadata_vec(entries.len(), budget, location)?;
1502 for entry in entries {
1503 fields.push(declared_fact(child, entry, budget, location)?);
1504 }
1505 if fields.is_empty() {
1506 push_metadata(
1507 &mut fields,
1508 declared_fact(child, ValueType::Unestablished, budget, location)?,
1509 budget,
1510 location,
1511 )?;
1512 return Ok(ValueType::Array(ArrayType::Elements(fields)));
1513 }
1514 ValueType::Array(if positional {
1515 ArrayType::Positions(fields)
1516 } else {
1517 ArrayType::Elements(fields)
1518 })
1519 }
1520 (
1521 ValueType::KeyedArray(mut keyed),
1522 DeclaredType::Array(child) | DeclaredType::Keyed(child),
1523 ) => {
1524 let mut fields = metadata_vec(keyed.elements.len(), budget, location)?;
1525 for entry in keyed.elements {
1526 fields.push(declared_fact(child, entry, budget, location)?);
1527 }
1528 if fields.is_empty() {
1529 push_metadata(
1530 &mut fields,
1531 declared_fact(child, ValueType::Unestablished, budget, location)?,
1532 budget,
1533 location,
1534 )?;
1535 }
1536 keyed.elements = fields;
1537 ValueType::KeyedArray(keyed)
1538 }
1539 (ValueType::Tuple(entries), DeclaredType::Tuple(children)) => {
1540 let mut fields = metadata_vec(entries.len(), budget, location)?;
1541 for (entry, child) in entries.into_iter().zip(children) {
1542 fields.push(declared_fact(child, entry, budget, location)?);
1543 }
1544 ValueType::Tuple(fields)
1545 }
1546 (ValueType::Record(entries), DeclaredType::Record(children)) => {
1547 let mut fields = metadata_vec(entries.len(), budget, location)?;
1548 for (label, entry) in entries {
1549 let child = record_declaration(children, &label, budget, location)?;
1550 fields.push((label, declared_fact(child, entry, budget, location)?));
1551 }
1552 ValueType::Record(fields)
1553 }
1554 (actual, _) => actual,
1555 })
1556}
1557
1558fn record_declaration<'a, K>(
1559 fields: &'a [(String, DeclaredType<K>)],
1560 label: &str,
1561 budget: &mut Budget,
1562 location: &Location,
1563) -> Result<&'a DeclaredType<K>, QueryError> {
1564 for (candidate, declaration) in fields {
1565 budget.charge(1 + label.len().min(candidate.len()), location.clone())?;
1566 if label == candidate {
1567 return Ok(declaration);
1568 }
1569 }
1570 Err(error(location, "record label outside declared schema"))
1571}
1572
1573fn expose(
1574 value: ConstructedValue,
1575 budget: &mut Budget,
1576 location: &Location,
1577) -> Result<ConstructedValue, QueryError> {
1578 let ConstructedValue::Declared { value, declaration } = value else {
1579 return Ok(value);
1580 };
1581 let mut value = expose(*value, budget, location)?;
1582 match (&mut value, underlying(&declaration)) {
1583 (
1584 ConstructedValue::Array(values),
1585 DeclaredType::Array(child) | DeclaredType::Sequence(child),
1586 ) => {
1587 for value in values {
1588 annotate_child(value, child, budget, location)?;
1589 }
1590 }
1591 (ConstructedValue::Tuple(values), DeclaredType::Tuple(children)) => {
1592 for (value, child) in values.iter_mut().zip(children) {
1593 annotate_child(value, child, budget, location)?;
1594 }
1595 }
1596 (ConstructedValue::Record(values), DeclaredType::Record(children)) => {
1597 for (label, value) in values {
1598 annotate_child(
1599 value,
1600 record_declaration(children, label, budget, location)?,
1601 budget,
1602 location,
1603 )?;
1604 }
1605 }
1606 (
1607 ConstructedValue::KeyedArray { entries, .. },
1608 DeclaredType::Array(child) | DeclaredType::Keyed(child),
1609 ) => {
1610 for entry in entries {
1611 annotate_child(&mut entry.value, child, budget, location)?;
1612 }
1613 }
1614 _ => {}
1615 }
1616 Ok(value)
1617}
1618
1619fn annotate_child(
1620 value: &mut ConstructedValue,
1621 declaration: &DeclaredType<()>,
1622 budget: &mut Budget,
1623 location: &Location,
1624) -> Result<(), QueryError> {
1625 let declaration = copy_declaration(declaration, budget, location)?;
1626 let child = std::mem::replace(value, ConstructedValue::Absent);
1627 *value = annotate(child, declaration, budget, location)?;
1628 Ok(())
1629}
1630
1631fn shallow_type(
1632 value: &ConstructedValue,
1633 budget: &mut Budget,
1634 location: &Location,
1635) -> Result<ValueType, QueryError> {
1636 Ok(match value {
1637 ConstructedValue::Declared { declaration, .. } => {
1638 declared_fact(declaration, ValueType::Unestablished, budget, location)?
1639 }
1640 ConstructedValue::Absent => ValueType::Absent,
1641 ConstructedValue::Boolean(_) => ValueType::Boolean,
1642 ConstructedValue::Integer(_) => ValueType::Integer,
1643 ConstructedValue::Float(value) => {
1644 finite(*value, location)?;
1645 ValueType::Float
1646 }
1647 ConstructedValue::String(_) => ValueType::String,
1648 ConstructedValue::Member(member) => ValueType::Member(member.type_id),
1649 ConstructedValue::Array(_) => ValueType::Array(ArrayType::Elements(Vec::new())),
1650 ConstructedValue::Tuple(_) => ValueType::TupleKind,
1651 ConstructedValue::Record(_) => ValueType::RecordKind,
1652 ConstructedValue::Set { .. } => ValueType::Set(None),
1653 ConstructedValue::KeyedArray { .. } => ValueType::KeyedArray(KeyedArrayType {
1654 axes: None,
1655 elements: Vec::new(),
1656 }),
1657 })
1658}
1659
1660fn access_type(
1661 ty: ValueType,
1662 keys: &[ValueType],
1663 label: Option<&str>,
1664 optional: bool,
1665 budget: &mut Budget,
1666 location: &Location,
1667) -> Result<Vec<ValueType>, QueryError> {
1668 let mut child = None;
1669 if let ValueType::Declared { declaration, .. } = &ty {
1670 child = match underlying(declaration) {
1671 DeclaredType::Record(fields) => match label {
1672 Some(label) => Some(copy_declaration(
1673 record_declaration(fields, label, budget, location)?,
1674 budget,
1675 location,
1676 )?),
1677 None => None,
1678 },
1679 DeclaredType::Array(element)
1680 | DeclaredType::Sequence(element)
1681 | DeclaredType::Keyed(element) => Some(copy_declaration(element, budget, location)?),
1682 _ => None,
1683 };
1684 }
1685 let mut result = Vec::new();
1686 match shape_type(ty, budget, location)? {
1687 record @ (ValueType::RecordKind | ValueType::Record(_)) => {
1688 for key in keys {
1689 let key = shape_type(copy_type(key, budget, location)?, budget, location)?;
1690 if !matches!(
1691 key,
1692 ValueType::String | ValueType::ScalarKind | ValueType::Unestablished
1693 ) {
1694 return Err(error(location, "record lookup needs a string label"));
1695 }
1696 }
1697 if let ValueType::Record(fields) = record {
1698 for (candidate, ty) in fields {
1699 budget.charge(
1700 1 + label.map_or(0, |label| label.len().min(candidate.len())),
1701 location.clone(),
1702 )?;
1703 if label.is_none_or(|label| label == candidate) {
1704 push_metadata(&mut result, ty, budget, location)?;
1705 }
1706 }
1707 } else {
1708 push_metadata(&mut result, ValueType::Unestablished, budget, location)?;
1709 }
1710 }
1711 ValueType::Array(ArrayType::Positions(elements) | ArrayType::Elements(elements)) => {
1712 for key in keys {
1713 require_type(key, ScalarRequirement::Integer, location)?;
1714 }
1715 result = elements;
1716 }
1717 ValueType::ArrayKind(elements) => {
1718 let mut facts = None;
1719 merge_key_facts(&mut facts, keys, budget, location)?;
1720 result = elements;
1721 }
1722 ValueType::KeyedArray(keyed) => {
1723 let mut facts = None;
1724 merge_key_facts(&mut facts, keys, budget, location)?;
1725 if let Some(axes) = &keyed.axes {
1726 check_key_facts(&facts, axes, budget, location)?;
1727 }
1728 result = keyed.elements;
1729 }
1730 ValueType::Unestablished => {
1731 push_metadata(&mut result, ValueType::Unestablished, budget, location)?
1732 }
1733 _ => return Err(error(location, "lookup needs a record or array")),
1734 }
1735 if let Some(child) = child {
1736 return one_type(
1737 declared_fact(
1738 &if optional {
1739 DeclaredType::Optional(Box::new(child))
1740 } else {
1741 child
1742 },
1743 ValueType::Unestablished,
1744 budget,
1745 location,
1746 )?,
1747 budget,
1748 location,
1749 );
1750 }
1751 if optional {
1752 push_metadata(&mut result, ValueType::Absent, budget, location)?;
1753 }
1754 Ok(result)
1755}
1756
1757fn optional_access(
1758 value: ConstructedValue,
1759 key: ConstructedValue,
1760 budget: &mut Budget,
1761 location: &Location,
1762) -> Result<(bool, ConstructedValue), QueryError> {
1763 let mut child = None;
1764 if let Some(declaration) = declaration(&value) {
1765 child = match underlying(declaration) {
1766 DeclaredType::Record(fields) => {
1767 let ConstructedValue::String(label) = key.data() else {
1768 return Err(error(location, "record lookup needs a string label"));
1769 };
1770 Some(copy_declaration(
1771 record_declaration(fields, label, budget, location)?,
1772 budget,
1773 location,
1774 )?)
1775 }
1776 DeclaredType::Array(element)
1777 | DeclaredType::Sequence(element)
1778 | DeclaredType::Keyed(element) => Some(copy_declaration(element, budget, location)?),
1779 _ => None,
1780 };
1781 }
1782 let mut selected = None;
1783 match expose(value, budget, location)? {
1784 ConstructedValue::Record(fields) => {
1785 let ConstructedValue::String(label) = key.into_data() else {
1786 return Err(error(location, "record lookup needs a string label"));
1787 };
1788 for (candidate, value) in fields {
1789 budget.charge(1 + label.len().min(candidate.len()), location.clone())?;
1790 if label == candidate {
1791 selected = Some(value);
1792 break;
1793 }
1794 }
1795 }
1796 ConstructedValue::Array(values) => {
1797 let position = integer(key, location)?;
1798 if let Some(index) = usize::try_from(position)
1799 .ok()
1800 .and_then(|position| position.checked_sub(1))
1801 {
1802 budget.charge(values.len().min(index.saturating_add(1)), location.clone())?;
1803 selected = values.into_iter().nth(index);
1804 }
1805 }
1806 ConstructedValue::KeyedArray { axes, entries } => {
1807 let key = value_keys(key.into_data(), budget, location)?;
1808 if key.len() != axes.len() {
1809 return Err(error(location, "key rank mismatch"));
1810 }
1811 for (component, axis) in key.iter().zip(&axes) {
1812 budget.charge(1, location.clone())?;
1813 if key_type(component) != axis.key_type {
1814 return Err(error(location, "incompatible key type"));
1815 }
1816 for candidate in &axis.keys {
1818 compare_keys(component, candidate, budget, location)?;
1819 }
1820 }
1821 for entry in entries {
1822 budget.charge(1, location.clone())?;
1823 if equal_keys(&key, &entry.key, budget, location)? {
1824 selected = Some(entry.value);
1825 break;
1826 }
1827 }
1828 }
1829 _ => return Err(error(location, "lookup needs a record or array")),
1830 }
1831 let present = selected.is_some();
1832 let value = match selected {
1833 Some(value) => value,
1834 None => scalar(ConstructedValue::Absent, budget, location)?,
1835 };
1836 let value = match child {
1837 Some(child) => annotate(
1838 value,
1839 DeclaredType::Optional(Box::new(child)),
1840 budget,
1841 location,
1842 )?,
1843 None => value,
1844 };
1845 Ok((present, value))
1846}
1847
1848fn error(location: &Location, message: impl Into<String>) -> QueryError {
1849 QueryError {
1850 location: location.clone(),
1851 message: message.into(),
1852 category: QueryErrorCategory::Other,
1853 }
1854}
1855
1856fn admit_diagnostic(
1859 prefix: &str,
1860 value: &str,
1861 budget: &mut Budget,
1862 location: &Location,
1863) -> Result<(), QueryError> {
1864 let len = sum(prefix.len(), value.len(), location)?;
1865 allocation::<u8>(len, location)?;
1866 budget.charge(len, location.clone())
1867}
1868
1869fn sum(a: usize, b: usize, location: &Location) -> Result<usize, QueryError> {
1870 a.checked_add(b)
1871 .ok_or_else(|| error(location, "allocation size overflow"))
1872}
1873
1874pub(crate) fn allocation<T>(len: usize, location: &Location) -> Result<(), QueryError> {
1875 if len
1876 .checked_mul(size_of::<T>())
1877 .is_none_or(|size| size > isize::MAX as usize)
1878 {
1879 return Err(error(location, "allocation size overflow"));
1880 }
1881 Ok(())
1882}
1883
1884fn metadata_vec<T>(
1885 len: usize,
1886 budget: &mut Budget,
1887 location: &Location,
1888) -> Result<Vec<T>, QueryError> {
1889 allocation::<T>(len, location)?;
1890 budget.charge(len, location.clone())?;
1891 Ok(Vec::with_capacity(len))
1892}
1893
1894fn push_metadata<T>(
1895 values: &mut Vec<T>,
1896 value: T,
1897 budget: &mut Budget,
1898 location: &Location,
1899) -> Result<(), QueryError> {
1900 allocation::<T>(sum(values.len(), 1, location)?, location)?;
1901 budget.charge(1, location.clone())?;
1902 values.push(value);
1903 Ok(())
1904}
1905
1906fn admit_container<T>(
1907 len: usize,
1908 budget: &mut Budget,
1909 location: &Location,
1910) -> Result<Vec<T>, QueryError> {
1911 allocation::<T>(len, location)?;
1912 budget.charge(sum(len, 1, location)?, location.clone())?;
1913 budget.charge_results(sum(len, 1, location)?, location.clone())?;
1914 Ok(Vec::with_capacity(len))
1915}
1916
1917fn finite(value: f64, location: &Location) -> Result<(), QueryError> {
1918 if !value.is_finite() {
1919 return Err(error(location, "nonfinite float"));
1920 }
1921 Ok(())
1922}
1923
1924fn member_bytes(
1925 member: &NominalMember,
1926 budget: &mut Budget,
1927 location: &Location,
1928) -> Result<usize, QueryError> {
1929 allocation::<u8>(member.type_name.len(), location)?;
1930 allocation::<u8>(member.member_name.len(), location)?;
1931 let bytes = sum(member.type_name.len(), member.member_name.len(), location)?;
1932 budget.charge(sum(1, bytes, location)?, location.clone())?;
1933 Ok(bytes)
1934}
1935
1936pub(crate) fn check_member_pair(
1937 left: &NominalMember,
1938 right: &NominalMember,
1939 budget: &mut Budget,
1940 location: &Location,
1941) -> Result<(), QueryError> {
1942 budget.charge(1, location.clone())?;
1943 if left.type_id != right.type_id {
1944 return Ok(());
1945 }
1946 budget.charge(
1947 left.type_name.len().min(right.type_name.len()),
1948 location.clone(),
1949 )?;
1950 if left.type_name != right.type_name {
1951 return Err(error(location, "inconsistent nominal type name"));
1952 }
1953 if left.member_id == right.member_id {
1954 budget.charge(
1955 left.member_name.len().min(right.member_name.len()),
1956 location.clone(),
1957 )?;
1958 if left.declaration_order != right.declaration_order
1959 || left.member_name != right.member_name
1960 {
1961 return Err(error(location, "inconsistent nominal member metadata"));
1962 }
1963 } else if left.declaration_order == right.declaration_order {
1964 return Err(error(
1965 location,
1966 "distinct nominal members share declaration order",
1967 ));
1968 }
1969 Ok(())
1970}
1971
1972fn copy_member_admitted(member: &NominalMember) -> NominalMember {
1973 NominalMember {
1974 type_id: member.type_id,
1975 member_id: member.member_id,
1976 declaration_order: member.declaration_order,
1977 type_name: member.type_name.to_owned(),
1978 member_name: member.member_name.to_owned(),
1979 }
1980}
1981
1982fn key_value(key: ValueKey) -> ConstructedValue {
1983 match key {
1984 ValueKey::Integer(value) => ConstructedValue::Integer(value),
1985 ValueKey::Member(member) => ConstructedValue::Member(member),
1986 }
1987}
1988
1989fn key_type(key: &ValueKey) -> ValueKeyType {
1990 match key {
1991 ValueKey::Integer(_) => ValueKeyType::Integer,
1992 ValueKey::Member(member) => ValueKeyType::Member(member.type_id),
1993 }
1994}
1995
1996fn key_set_type(ty: ValueKeyType) -> SetElementType {
1997 match ty {
1998 ValueKeyType::Integer => SetElementType::Integer,
1999 ValueKeyType::Member(id) => SetElementType::Member(id),
2000 }
2001}
2002
2003fn compare_keys(
2004 left: &ValueKey,
2005 right: &ValueKey,
2006 budget: &mut Budget,
2007 location: &Location,
2008) -> Result<Ordering, QueryError> {
2009 budget.charge(1, location.clone())?;
2010 match (left, right) {
2011 (ValueKey::Integer(a), ValueKey::Integer(b)) => Ok(a.cmp(b)),
2012 (ValueKey::Member(a), ValueKey::Member(b)) if a.type_id == b.type_id => {
2013 check_member_pair(a, b, budget, location)?;
2014 Ok(if a.member_id == b.member_id {
2015 Ordering::Equal
2016 } else {
2017 a.declaration_order.cmp(&b.declaration_order)
2018 })
2019 }
2020 _ => Err(error(location, "incompatible keyed dimension type")),
2021 }
2022}
2023
2024fn keys_size(
2025 keys: &[ValueKey],
2026 budget: &mut Budget,
2027 location: &Location,
2028) -> Result<(usize, usize), QueryError> {
2029 allocation::<ValueKey>(keys.len(), location)?;
2030 let units = keys
2031 .len()
2032 .checked_mul(2)
2033 .ok_or_else(|| error(location, "key size overflow"))?;
2034 let mut bytes = 0;
2035 for key in keys {
2036 budget.charge(1, location.clone())?;
2037 if let ValueKey::Member(member) = key {
2038 bytes = sum(bytes, member_bytes(member, budget, location)?, location)?;
2039 }
2040 }
2041 Ok((units, bytes))
2042}
2043
2044fn copy_keys_admitted(
2045 keys: &[ValueKey],
2046 budget: &mut Budget,
2047 location: &Location,
2048) -> Result<Vec<ValueKey>, QueryError> {
2049 let mut copied = Vec::with_capacity(keys.len());
2050 for key in keys {
2051 budget.charge(1, location.clone())?;
2052 copied.push(match key {
2053 ValueKey::Integer(value) => ValueKey::Integer(*value),
2054 ValueKey::Member(member) => ValueKey::Member(copy_member_admitted(member)),
2055 });
2056 }
2057 Ok(copied)
2058}
2059
2060fn copy_key(
2061 key: &ValueKey,
2062 budget: &mut Budget,
2063 location: &Location,
2064) -> Result<ValueKey, QueryError> {
2065 budget.charge(1, location.clone())?;
2066 budget.charge_results(1, location.clone())?;
2067 Ok(match key {
2068 ValueKey::Integer(value) => ValueKey::Integer(*value),
2069 ValueKey::Member(member) => {
2070 let bytes = member_bytes(member, budget, location)?;
2071 budget.charge(bytes, location.clone())?;
2072 ValueKey::Member(copy_member_admitted(member))
2073 }
2074 })
2075}
2076
2077fn check_axes(
2078 axes: &[KeyAxis],
2079 budget: &mut Budget,
2080 location: &Location,
2081) -> Result<(), QueryError> {
2082 if axes.is_empty() {
2083 return Err(error(
2084 location,
2085 "keyed arrays need nonzero rank and established axes",
2086 ));
2087 }
2088 for axis in axes {
2089 budget.charge(1, location.clone())?;
2090 for (index, key) in axis.keys.iter().enumerate() {
2091 budget.charge(1, location.clone())?;
2092 if key_type(key) != axis.key_type {
2093 return Err(error(location, "axis key type mismatch"));
2094 }
2095 if let ValueKey::Member(member) = key {
2096 member_bytes(member, budget, location)?;
2097 }
2098 for previous in &axis.keys[..index] {
2099 if compare_keys(previous, key, budget, location)? == Ordering::Equal {
2100 return Err(error(location, "duplicate axis key"));
2101 }
2102 }
2103 }
2104 }
2105 let members = axes
2106 .iter()
2107 .flat_map(|axis| &axis.keys)
2108 .filter_map(|key| match key {
2109 ValueKey::Member(member) => Some(member),
2110 ValueKey::Integer(_) => None,
2111 });
2112 for (index, member) in members.clone().enumerate() {
2113 for previous in members.clone().take(index) {
2114 check_member_pair(previous, member, budget, location)?;
2115 }
2116 }
2117 Ok(())
2118}
2119
2120fn copy_axes(
2121 axes: &[KeyAxis],
2122 budget: &mut Budget,
2123 location: &Location,
2124) -> Result<Vec<KeyAxis>, QueryError> {
2125 check_axes(axes, budget, location)?;
2126 allocation::<KeyAxis>(axes.len(), location)?;
2127 budget.charge(axes.len(), location.clone())?;
2128 budget.charge_results(axes.len(), location.clone())?;
2129 let mut copied = Vec::with_capacity(axes.len());
2130 for axis in axes {
2131 let (units, bytes) = keys_size(&axis.keys, budget, location)?;
2132 budget.charge(bytes, location.clone())?;
2133 budget.charge_results(units, location.clone())?;
2134 copied.push(KeyAxis {
2135 key_type: axis.key_type,
2136 keys: copy_keys_admitted(&axis.keys, budget, location)?,
2137 });
2138 }
2139 Ok(copied)
2140}
2141
2142fn copy_axis_types(
2143 types: &[ValueKeyType],
2144 budget: &mut Budget,
2145 location: &Location,
2146) -> Result<Vec<ValueKeyType>, QueryError> {
2147 if types.is_empty() {
2148 return Err(error(location, "keyed arrays need nonzero rank"));
2149 }
2150 let mut copied = metadata_vec(types.len(), budget, location)?;
2151 for ty in types {
2152 budget.charge(1, location.clone())?;
2153 copied.push(*ty);
2154 }
2155 Ok(copied)
2156}
2157
2158fn axis_types(
2159 axes: &[KeyAxis],
2160 budget: &mut Budget,
2161 location: &Location,
2162) -> Result<Vec<ValueKeyType>, QueryError> {
2163 let mut types = metadata_vec(axes.len(), budget, location)?;
2164 for axis in axes {
2165 budget.charge(1, location.clone())?;
2166 types.push(axis.key_type);
2167 }
2168 Ok(types)
2169}
2170
2171fn same_axis_types(
2172 left: &[ValueKeyType],
2173 right: &[ValueKeyType],
2174 budget: &mut Budget,
2175 location: &Location,
2176) -> Result<bool, QueryError> {
2177 budget.charge(1, location.clone())?;
2178 if left.len() != right.len() {
2179 return Ok(false);
2180 }
2181 for (a, b) in left.iter().zip(right) {
2182 budget.charge(1, location.clone())?;
2183 if a != b {
2184 return Ok(false);
2185 }
2186 }
2187 Ok(true)
2188}
2189
2190fn key_positions(
2191 key: &[ValueKey],
2192 axes: &[KeyAxis],
2193 budget: &mut Budget,
2194 location: &Location,
2195) -> Result<Vec<usize>, QueryError> {
2196 if key.len() != axes.len() {
2197 return Err(error(location, "key rank mismatch"));
2198 }
2199 let mut positions = metadata_vec(key.len(), budget, location)?;
2200 for (key, axis) in key.iter().zip(axes) {
2201 budget.charge(1, location.clone())?;
2202 if key_type(key) != axis.key_type {
2203 return Err(error(location, "incompatible keyed dimension type"));
2204 }
2205 let mut position = None;
2206 for (index, existing) in axis.keys.iter().enumerate() {
2208 if compare_keys(key, existing, budget, location)? == Ordering::Equal {
2209 position = Some(index);
2210 }
2211 }
2212 positions
2213 .push(position.ok_or_else(|| error(location, "key is absent from established axis"))?);
2214 }
2215 Ok(positions)
2216}
2217
2218fn compare_positions(
2219 left: &[usize],
2220 right: &[usize],
2221 budget: &mut Budget,
2222 location: &Location,
2223) -> Result<Ordering, QueryError> {
2224 for (a, b) in left.iter().zip(right) {
2225 budget.charge(1, location.clone())?;
2226 let order = a.cmp(b);
2227 if order != Ordering::Equal {
2228 return Ok(order);
2229 }
2230 }
2231 Ok(Ordering::Equal)
2232}
2233
2234fn equal_keys(
2235 left: &[ValueKey],
2236 right: &[ValueKey],
2237 budget: &mut Budget,
2238 location: &Location,
2239) -> Result<bool, QueryError> {
2240 budget.charge(1, location.clone())?;
2241 if left.len() != right.len() {
2242 return Ok(false);
2243 }
2244 for (a, b) in left.iter().zip(right) {
2245 if compare_keys(a, b, budget, location)? != Ordering::Equal {
2246 return Ok(false);
2247 }
2248 }
2249 Ok(true)
2250}
2251
2252fn check_keyed(
2253 axes: &[KeyAxis],
2254 entries: &[KeyedEntry],
2255 budget: &mut Budget,
2256 location: &Location,
2257) -> Result<(), QueryError> {
2258 check_axes(axes, budget, location)?;
2259 let mut previous: Option<Vec<usize>> = None;
2260 for entry in entries {
2261 budget.charge(1, location.clone())?;
2262 let positions = key_positions(&entry.key, axes, budget, location)?;
2263 if let Some(previous) = previous
2264 && compare_positions(&previous, &positions, budget, location)? != Ordering::Less
2265 {
2266 return Err(error(
2267 location,
2268 "keyed entries must be unique and in row-major order",
2269 ));
2270 }
2271 previous = Some(positions);
2272 }
2273 let members = axes
2274 .iter()
2275 .flat_map(|axis| &axis.keys)
2276 .chain(entries.iter().flat_map(|entry| &entry.key))
2277 .filter_map(|key| match key {
2278 ValueKey::Member(member) => Some(member),
2279 ValueKey::Integer(_) => None,
2280 });
2281 for (index, member) in members.clone().enumerate() {
2282 for previous in members.clone().take(index) {
2283 check_member_pair(previous, member, budget, location)?;
2284 }
2285 }
2286 Ok(())
2287}
2288
2289fn value_keys(
2290 value: ConstructedValue,
2291 budget: &mut Budget,
2292 location: &Location,
2293) -> Result<Vec<ValueKey>, QueryError> {
2294 let value = expose(value, budget, location)?;
2295 let values = match value {
2296 ConstructedValue::Tuple(values) if !values.is_empty() => values,
2297 value @ (ConstructedValue::Integer(_) | ConstructedValue::Member(_)) => {
2298 let mut values = metadata_vec(1, budget, location)?;
2299 values.push(value);
2300 values
2301 }
2302 _ => {
2303 return Err(error(
2304 location,
2305 "key needs an integer, member or nonempty flat tuple",
2306 ));
2307 }
2308 };
2309 allocation::<ValueKey>(values.len(), location)?;
2310 budget.charge(values.len(), location.clone())?;
2311 budget.charge_results(values.len(), location.clone())?;
2312 let mut keys = Vec::with_capacity(values.len());
2313 for value in values {
2314 budget.charge(1, location.clone())?;
2315 keys.push(match value.into_data() {
2316 ConstructedValue::Integer(value) => ValueKey::Integer(value),
2317 ConstructedValue::Member(member) => ValueKey::Member(member),
2318 _ => return Err(error(location, "key components need integers or members")),
2319 });
2320 }
2321 Ok(keys)
2322}
2323
2324fn infer_axes(
2325 entries: &[Located<KeyedEntry>],
2326 budget: &mut Budget,
2327 location: &Location,
2328) -> Result<Vec<KeyAxis>, QueryError> {
2329 let Some(first) = entries.first() else {
2330 return Err(error(location, "unestablished empty keyed axes"));
2331 };
2332 if first.value.key.is_empty() {
2333 return Err(error(location, "keyed arrays need nonzero rank"));
2334 }
2335 allocation::<KeyAxis>(first.value.key.len(), location)?;
2336 budget.charge(first.value.key.len(), location.clone())?;
2337 budget.charge_results(first.value.key.len(), location.clone())?;
2338 let mut axes = Vec::with_capacity(first.value.key.len());
2339 for key in &first.value.key {
2340 axes.push(KeyAxis {
2341 key_type: key_type(key),
2342 keys: Vec::new(),
2343 });
2344 }
2345 for entry in entries {
2346 let key_location = Location::Query(entry.range.clone());
2347 if entry.value.key.len() != axes.len() {
2348 return Err(error(&key_location, "key rank mismatch"));
2349 }
2350 for (key, axis) in entry.value.key.iter().zip(&mut axes) {
2351 if key_type(key) != axis.key_type {
2352 return Err(error(&key_location, "incompatible keyed dimension type"));
2353 }
2354 let mut position = axis.keys.len();
2355 let mut found = false;
2356 for (index, existing) in axis.keys.iter().enumerate() {
2357 match compare_keys(key, existing, budget, &key_location)? {
2358 Ordering::Equal => found = true,
2359 Ordering::Less if position == axis.keys.len() => position = index,
2360 _ => {}
2361 }
2362 }
2363 if !found {
2364 let key = copy_key(key, budget, &key_location)?;
2365 allocation::<ValueKey>(sum(axis.keys.len(), 1, &key_location)?, &key_location)?;
2366 budget.charge(
2367 sum(1, axis.keys.len() - position, &key_location)?,
2368 key_location.clone(),
2369 )?;
2370 budget.charge_results(1, key_location.clone())?;
2371 axis.keys.insert(position, key);
2372 }
2373 }
2374 }
2375 Ok(axes)
2376}
2377
2378fn finish_keyed(
2379 axes: Option<Vec<KeyAxis>>,
2380 mut entries: Vec<Located<KeyedEntry>>,
2381 budget: &mut Budget,
2382 location: &Location,
2383) -> Result<ConstructedValue, QueryError> {
2384 let axes = match axes {
2385 Some(axes) => {
2386 check_axes(&axes, budget, location)?;
2387 axes
2388 }
2389 None => infer_axes(&entries, budget, location)?,
2390 };
2391 let mut positions = metadata_vec(entries.len(), budget, location)?;
2392 for entry in &entries {
2393 positions.push(key_positions(
2394 &entry.value.key,
2395 &axes,
2396 budget,
2397 &Location::Query(entry.range.clone()),
2398 )?);
2399 }
2400 for index in 1..entries.len() {
2403 let mut cursor = index;
2404 while cursor > 0 {
2405 let key_location = Location::Query(entries[cursor].range.clone());
2406 match compare_positions(
2407 &positions[cursor - 1],
2408 &positions[cursor],
2409 budget,
2410 &key_location,
2411 )? {
2412 Ordering::Equal => return Err(error(&key_location, "duplicate full key")),
2413 Ordering::Less => break,
2414 Ordering::Greater => {
2415 budget.charge(2, key_location)?;
2416 entries.swap(cursor - 1, cursor);
2417 positions.swap(cursor - 1, cursor);
2418 cursor -= 1;
2419 }
2420 }
2421 }
2422 }
2423 let mut values = metadata_vec(entries.len(), budget, location)?;
2424 for entry in entries {
2425 values.push(entry.value);
2426 }
2427 check_keyed(&axes, &values, budget, location)?;
2428 Ok(ConstructedValue::KeyedArray {
2429 axes,
2430 entries: values,
2431 })
2432}
2433
2434fn source_axis(
2435 value: ConstructedValue,
2436 budget: &mut Budget,
2437 location: &Location,
2438) -> Result<KeyAxis, QueryError> {
2439 let empty = match value.data() {
2440 ConstructedValue::Array(values)
2441 | ConstructedValue::Set {
2442 elements: values, ..
2443 } => values.is_empty(),
2444 ConstructedValue::KeyedArray { entries, .. } => entries.is_empty(),
2445 _ => false,
2446 };
2447 let declared = if empty && let Some(declaration) = declaration(&value) {
2448 let facts = declared_facts(declaration, budget, location)?;
2449 source_key_type(std::slice::from_ref(&facts), budget, location)?
2450 } else {
2451 None
2452 };
2453 let value = expose(value, budget, location)?;
2454 let (declared, values) = match value {
2455 ConstructedValue::Array(values) => (declared, values),
2456 ConstructedValue::KeyedArray { entries, .. } => {
2457 let mut values = metadata_vec(entries.len(), budget, location)?;
2458 for entry in entries {
2459 budget.charge(1, location.clone())?;
2460 values.push(entry.value);
2461 }
2462 (declared, values)
2463 }
2464 ConstructedValue::Set {
2465 element_type,
2466 elements,
2467 } => {
2468 let ty = match element_type {
2469 Some(SetElementType::Integer) => Some(ValueKeyType::Integer),
2470 Some(SetElementType::Member(id)) => Some(ValueKeyType::Member(id)),
2471 _ => None,
2472 };
2473 (ty.or(declared), elements)
2474 }
2475 _ => {
2476 return Err(error(
2477 location,
2478 "axis source needs an integer or member array/set",
2479 ));
2480 }
2481 };
2482 allocation::<ValueKey>(values.len(), location)?;
2485 budget.charge(values.len(), location.clone())?;
2486 budget.charge_results(values.len(), location.clone())?;
2487 let mut keys = Vec::with_capacity(values.len());
2488 let mut ty = declared;
2489 for value in values {
2490 budget.charge(1, location.clone())?;
2491 let key = match value.into_data() {
2492 ConstructedValue::Integer(value) => ValueKey::Integer(value),
2493 ConstructedValue::Member(member) => ValueKey::Member(member),
2494 _ => {
2495 return Err(error(
2496 location,
2497 "axis source needs integer or member elements",
2498 ));
2499 }
2500 };
2501 if ty.is_some_and(|ty| ty != key_type(&key)) {
2502 return Err(error(location, "incompatible axis source type"));
2503 }
2504 ty = Some(key_type(&key));
2505 keys.push(key);
2506 }
2507 let axis = KeyAxis {
2508 key_type: ty.ok_or_else(|| error(location, "unestablished empty keyed axis"))?,
2509 keys,
2510 };
2511 check_axes(std::slice::from_ref(&axis), budget, location)?;
2512 Ok(axis)
2513}
2514
2515fn scalar_set_type(value: &ConstructedValue) -> Option<SetElementType> {
2516 match value.data() {
2517 ConstructedValue::Boolean(_) => Some(SetElementType::Boolean),
2518 ConstructedValue::Integer(_) => Some(SetElementType::Integer),
2519 ConstructedValue::Float(_) => Some(SetElementType::Float),
2520 ConstructedValue::Member(member) => Some(SetElementType::Member(member.type_id)),
2521 _ => None,
2522 }
2523}
2524
2525fn compare_set_values(
2526 left: &ConstructedValue,
2527 right: &ConstructedValue,
2528 budget: &mut Budget,
2529 location: &Location,
2530) -> Result<Ordering, QueryError> {
2531 budget.charge(1, location.clone())?;
2532 match (left.data(), right.data()) {
2533 (ConstructedValue::Boolean(a), ConstructedValue::Boolean(b)) => Ok(a.cmp(b)),
2534 (ConstructedValue::Integer(a), ConstructedValue::Integer(b)) => Ok(a.cmp(b)),
2535 (ConstructedValue::Float(a), ConstructedValue::Float(b)) => {
2536 finite(*a, location)?;
2537 finite(*b, location)?;
2538 Ok(a.partial_cmp(b).expect("finite floats are ordered"))
2539 }
2540 (ConstructedValue::Member(a), ConstructedValue::Member(b)) => {
2541 check_member_pair(a, b, budget, location)?;
2542 if a.type_id != b.type_id {
2543 return Err(error(location, "incompatible nominal types"));
2544 }
2545 Ok(if a.member_id == b.member_id {
2546 Ordering::Equal
2547 } else {
2548 a.declaration_order.cmp(&b.declaration_order)
2549 })
2550 }
2551 _ => Err(error(
2552 location,
2553 "set elements must have one supported scalar type",
2554 )),
2555 }
2556}
2557
2558fn check_set(
2559 element_type: Option<SetElementType>,
2560 elements: &[ConstructedValue],
2561 budget: &mut Budget,
2562 location: &Location,
2563) -> Result<(), QueryError> {
2564 if element_type.is_none() && !elements.is_empty() {
2565 return Err(error(location, "a nonempty set needs its element type"));
2566 }
2567 for (index, value) in elements.iter().enumerate() {
2568 budget.charge(1, location.clone())?;
2569 if scalar_set_type(value) != element_type {
2570 return Err(error(location, "set element type mismatch"));
2571 }
2572 match value.data() {
2573 ConstructedValue::Float(value) => finite(*value, location)?,
2574 ConstructedValue::Member(member) => {
2575 member_bytes(member, budget, location)?;
2576 }
2577 _ => {}
2578 }
2579 for previous in &elements[..index] {
2580 if compare_set_values(previous, value, budget, location)? == Ordering::Equal {
2581 return Err(error(location, "set elements must be unique"));
2582 }
2583 }
2584 }
2585 Ok(())
2586}
2587
2588fn check_value_labels<T>(
2589 fields: &[(String, T)],
2590 budget: &mut Budget,
2591 location: &Location,
2592) -> Result<(), QueryError> {
2593 for (index, (label, _)) in fields.iter().enumerate() {
2594 for (previous, _) in &fields[..index] {
2595 budget.charge(
2596 sum(1, label.len().min(previous.len()), location)?,
2597 location.clone(),
2598 )?;
2599 if label == previous {
2600 return Err(error(location, "duplicate record label"));
2601 }
2602 }
2603 }
2604 Ok(())
2605}
2606
2607fn copy_size(
2608 value: &ConstructedValue,
2609 budget: &mut Budget,
2610 location: &Location,
2611) -> Result<(usize, usize), QueryError> {
2612 budget.within_evaluation(1, location.clone(), |budget| {
2613 budget.charge(1, location.clone())?;
2614 let mut units = 1;
2615 let mut bytes = 0;
2616 match value {
2617 ConstructedValue::Absent
2618 | ConstructedValue::Boolean(_)
2619 | ConstructedValue::Integer(_) => {}
2620 ConstructedValue::Declared { value, declaration } => {
2621 crate::admit_constructed(value, declaration, budget, location.clone())?;
2622 let (child_units, child_bytes) = copy_size(value, budget, location)?;
2623 units = sum(units, child_units, location)?;
2624 bytes = child_bytes;
2625 }
2627 ConstructedValue::Float(value) => finite(*value, location)?,
2628 ConstructedValue::Member(member) => {
2629 bytes = member_bytes(member, budget, location)?;
2630 }
2631 ConstructedValue::KeyedArray { axes, entries } => {
2632 check_keyed(axes, entries, budget, location)?;
2633 allocation::<KeyAxis>(axes.len(), location)?;
2634 allocation::<KeyedEntry>(entries.len(), location)?;
2635 units = sum(units, sum(axes.len(), entries.len(), location)?, location)?;
2636 for axis in axes {
2637 let (key_units, key_bytes) = keys_size(&axis.keys, budget, location)?;
2638 units = sum(units, key_units, location)?;
2639 bytes = sum(bytes, key_bytes, location)?;
2640 }
2641 for entry in entries {
2642 let (key_units, key_bytes) = keys_size(&entry.key, budget, location)?;
2643 let (child_units, child_bytes) = copy_size(&entry.value, budget, location)?;
2644 units = sum(units, sum(key_units, child_units, location)?, location)?;
2645 bytes = sum(bytes, sum(key_bytes, child_bytes, location)?, location)?;
2646 }
2647 }
2648 ConstructedValue::String(value) => {
2649 allocation::<u8>(value.len(), location)?;
2650 bytes = value.len();
2651 }
2652 ConstructedValue::Array(values)
2653 | ConstructedValue::Tuple(values)
2654 | ConstructedValue::Set {
2655 elements: values, ..
2656 } => {
2657 allocation::<ConstructedValue>(values.len(), location)?;
2658 if let ConstructedValue::Set { element_type, .. } = value {
2659 check_set(*element_type, values, budget, location)?;
2660 }
2661 units = sum(units, values.len(), location)?;
2662 for value in values {
2663 let (child_units, child_bytes) = copy_size(value, budget, location)?;
2664 units = sum(units, child_units, location)?;
2665 bytes = sum(bytes, child_bytes, location)?;
2666 }
2667 }
2668 ConstructedValue::Record(fields) => {
2669 allocation::<(String, ConstructedValue)>(fields.len(), location)?;
2670 check_value_labels(fields, budget, location)?;
2671 units = sum(units, fields.len(), location)?;
2672 for (label, value) in fields {
2673 allocation::<u8>(label.len(), location)?;
2674 let (child_units, child_bytes) = copy_size(value, budget, location)?;
2675 units = sum(units, child_units, location)?;
2676 bytes = sum(bytes, sum(label.len(), child_bytes, location)?, location)?;
2677 }
2678 }
2679 }
2680 Ok((units, bytes))
2681 })
2682}
2683
2684fn copy_admitted(
2685 value: &ConstructedValue,
2686 budget: &mut Budget,
2687 location: &Location,
2688) -> Result<ConstructedValue, QueryError> {
2689 budget.within_evaluation(1, location.clone(), |budget| {
2690 budget.charge(1, location.clone())?;
2691 Ok(match value {
2692 ConstructedValue::Absent => ConstructedValue::Absent,
2693 ConstructedValue::Declared { value, declaration } => ConstructedValue::Declared {
2694 value: Box::new(copy_admitted(value, budget, location)?),
2695 declaration: copy_declaration(declaration, budget, location)?,
2696 },
2697 ConstructedValue::Boolean(value) => ConstructedValue::Boolean(*value),
2698 ConstructedValue::Integer(value) => ConstructedValue::Integer(*value),
2699 ConstructedValue::Float(value) => ConstructedValue::Float(*value),
2700 ConstructedValue::String(value) => ConstructedValue::String(value.to_owned()),
2701 ConstructedValue::Member(member) => {
2702 ConstructedValue::Member(copy_member_admitted(member))
2703 }
2704 ConstructedValue::KeyedArray { axes, entries } => {
2705 let mut copied_axes = Vec::with_capacity(axes.len());
2706 for axis in axes {
2707 budget.charge(1, location.clone())?;
2708 copied_axes.push(KeyAxis {
2709 key_type: axis.key_type,
2710 keys: copy_keys_admitted(&axis.keys, budget, location)?,
2711 });
2712 }
2713 let mut copied_entries = Vec::with_capacity(entries.len());
2714 for entry in entries {
2715 budget.charge(1, location.clone())?;
2716 copied_entries.push(KeyedEntry {
2717 key: copy_keys_admitted(&entry.key, budget, location)?,
2718 value: copy_admitted(&entry.value, budget, location)?,
2719 });
2720 }
2721 ConstructedValue::KeyedArray {
2722 axes: copied_axes,
2723 entries: copied_entries,
2724 }
2725 }
2726 ConstructedValue::Array(values)
2727 | ConstructedValue::Tuple(values)
2728 | ConstructedValue::Set {
2729 elements: values, ..
2730 } => {
2731 let mut copied = Vec::with_capacity(values.len());
2732 for value in values {
2733 copied.push(copy_admitted(value, budget, location)?);
2734 }
2735 match value {
2736 ConstructedValue::Array(_) => ConstructedValue::Array(copied),
2737 ConstructedValue::Tuple(_) => ConstructedValue::Tuple(copied),
2738 ConstructedValue::Set { element_type, .. } => ConstructedValue::Set {
2739 element_type: *element_type,
2740 elements: copied,
2741 },
2742 _ => unreachable!("matched collection"),
2743 }
2744 }
2745 ConstructedValue::Record(fields) => {
2746 let mut copied = Vec::with_capacity(fields.len());
2747 for (label, value) in fields {
2748 copied.push((label.to_owned(), copy_admitted(value, budget, location)?));
2749 }
2750 ConstructedValue::Record(copied)
2751 }
2752 })
2753 })
2754}
2755
2756fn copy_type(
2757 ty: &ValueType,
2758 budget: &mut Budget,
2759 location: &Location,
2760) -> Result<ValueType, QueryError> {
2761 budget.within_evaluation(1, location.clone(), |budget| {
2762 budget.charge(1, location.clone())?;
2763 Ok(match ty {
2764 ValueType::Absent => ValueType::Absent,
2765 ValueType::Declared {
2766 actual,
2767 declaration,
2768 } => ValueType::Declared {
2769 actual: Box::new(copy_type(actual, budget, location)?),
2770 declaration: copy_declaration(declaration, budget, location)?,
2771 },
2772 ValueType::ScalarKind => ValueType::ScalarKind,
2773 ValueType::Boolean => ValueType::Boolean,
2774 ValueType::Integer => ValueType::Integer,
2775 ValueType::Float => ValueType::Float,
2776 ValueType::String => ValueType::String,
2777 ValueType::Member(id) => ValueType::Member(*id),
2778 ValueType::KeyedArray(keyed) => {
2779 let axes = match &keyed.axes {
2780 Some(axes) => Some(copy_axis_types(axes, budget, location)?),
2781 None => None,
2782 };
2783 ValueType::KeyedArray(KeyedArrayType {
2784 axes,
2785 elements: copy_types(&keyed.elements, budget, location)?,
2786 })
2787 }
2788 ValueType::Unestablished => ValueType::Unestablished,
2789 ValueType::TupleKind => ValueType::TupleKind,
2790 ValueType::RecordKind => ValueType::RecordKind,
2791 ValueType::Set(element_type) => ValueType::Set(*element_type),
2792 ValueType::Array(array) => {
2793 let (ArrayType::Positions(entries) | ArrayType::Elements(entries)) = array;
2794 let entries = copy_types(entries, budget, location)?;
2795 ValueType::Array(match array {
2796 ArrayType::Positions(_) => ArrayType::Positions(entries),
2797 ArrayType::Elements(_) => ArrayType::Elements(entries),
2798 })
2799 }
2800 ValueType::ArrayKind(elements) => {
2801 ValueType::ArrayKind(copy_types(elements, budget, location)?)
2802 }
2803 ValueType::Tuple(entries) => ValueType::Tuple(copy_types(entries, budget, location)?),
2804 ValueType::Record(fields) => {
2805 check_value_labels(fields, budget, location)?;
2806 let mut copied = metadata_vec(fields.len(), budget, location)?;
2807 for (label, ty) in fields {
2808 budget.charge(label.len(), location.clone())?;
2809 copied.push((label.to_owned(), copy_type(ty, budget, location)?));
2810 }
2811 ValueType::Record(copied)
2812 }
2813 })
2814 })
2815}
2816
2817fn copy_types(
2818 types: &[ValueType],
2819 budget: &mut Budget,
2820 location: &Location,
2821) -> Result<Vec<ValueType>, QueryError> {
2822 let mut copied = metadata_vec(types.len(), budget, location)?;
2823 for ty in types {
2824 copied.push(copy_type(ty, budget, location)?);
2825 }
2826 Ok(copied)
2827}
2828
2829fn one_type(
2830 ty: ValueType,
2831 budget: &mut Budget,
2832 location: &Location,
2833) -> Result<Vec<ValueType>, QueryError> {
2834 let mut types = metadata_vec(1, budget, location)?;
2835 types.push(ty);
2836 Ok(types)
2837}
2838
2839fn append_types(
2840 target: &mut Vec<ValueType>,
2841 types: Vec<ValueType>,
2842 budget: &mut Budget,
2843 location: &Location,
2844) -> Result<(), QueryError> {
2845 allocation::<ValueType>(sum(target.len(), types.len(), location)?, location)?;
2846 budget.charge(types.len(), location.clone())?;
2847 target.extend(types);
2848 Ok(())
2849}
2850
2851enum Globals<'a> {
2852 Types(&'a BTreeMap<String, ValueType>),
2853 Facts(&'a BTreeMap<String, Vec<ValueType>>),
2854 Available(&'a Bindings<ConstructedValue>),
2855}
2856
2857fn check_loop_terminal<Q>(
2859 kind: &ValueExpressionKind<Q>,
2860 transfer: Option<usize>,
2861 location: &Location,
2862) -> Result<(), QueryError> {
2863 match (kind, transfer) {
2864 (ValueExpressionKind::Break(_), Some(_)) => Ok(()),
2865 (ValueExpressionKind::Continue(values), Some(arity)) if values.len() == arity => Ok(()),
2866 (ValueExpressionKind::Continue(_), Some(_)) => {
2867 Err(error(location, "loop state argument count mismatch"))
2868 }
2869 (ValueExpressionKind::Break(_) | ValueExpressionKind::Continue(_), None) => Err(error(
2870 location,
2871 "loop transfer requires a terminal loop body position",
2872 )),
2873 (
2874 ValueExpressionKind::Block { .. }
2875 | ValueExpressionKind::Conditional { .. }
2876 | ValueExpressionKind::ValueMatch { .. },
2877 Some(_),
2878 )
2879 | (_, None) => Ok(()),
2880 (_, Some(_)) => Err(error(
2881 location,
2882 "loop body path must end in break or continue",
2883 )),
2884 }
2885}
2886
2887struct Analysis<'a, 'h, Q, H> {
2888 queries: &'h mut H,
2889 query: std::marker::PhantomData<Q>,
2890 globals: Globals<'a>,
2891 locals: Vec<(&'a str, Vec<ValueType>)>,
2892 free: Vec<Located<String>>,
2893 fallback_names: Vec<Located<String>>,
2894 fallback: bool,
2895}
2896
2897impl<'a, 'h, Q: 'a, H: ValueQueryHost<Q>> Analysis<'a, 'h, Q, H> {
2898 fn new(globals: Globals<'a>, queries: &'h mut H) -> Self {
2899 Self {
2900 globals,
2901 queries,
2902 query: std::marker::PhantomData,
2903 locals: Vec::new(),
2904 free: Vec::new(),
2905 fallback_names: Vec::new(),
2906 fallback: false,
2907 }
2908 }
2909
2910 fn global_exists(
2911 &self,
2912 name: &str,
2913 range: std::ops::Range<usize>,
2914 budget: &mut Budget,
2915 ) -> Result<bool, QueryError> {
2916 admit_diagnostic(
2917 "undefined binding: ",
2918 name,
2919 budget,
2920 &Location::Query(range.clone()),
2921 )?;
2922 Ok(match self.globals {
2923 Globals::Types(types) => types.contains_key(name),
2924 Globals::Facts(types) => types.contains_key(name),
2925 Globals::Available(bindings) => bindings.get(name, range).is_ok(),
2926 })
2927 }
2928
2929 fn name(
2930 &mut self,
2931 name: &str,
2932 expression: &ValueExpression<Q>,
2933 budget: &mut Budget,
2934 ) -> Result<Vec<ValueType>, QueryError> {
2935 let location = Location::Query(expression.range.clone());
2936 if name == "_" {
2937 return Err(error(&location, "discard binder cannot be read"));
2938 }
2939 for (local, types) in self.locals.iter().rev() {
2940 budget.charge(
2941 sum(1, name.len().min(local.len()), &location)?,
2942 location.clone(),
2943 )?;
2944 if name == *local {
2945 return copy_types(types, budget, &location);
2946 }
2947 }
2948 self.queries
2949 .validate_name(name, &expression.range, budget)?;
2950 admit_diagnostic("undefined binding: ", name, budget, &location)?;
2951 let types = match self.globals {
2952 Globals::Types(types) => one_type(
2953 copy_type(
2954 types
2955 .get(name)
2956 .ok_or_else(|| error(&location, format!("undefined binding: {name}")))?,
2957 budget,
2958 &location,
2959 )?,
2960 budget,
2961 &location,
2962 )?,
2963 Globals::Facts(types) => copy_types(
2964 types
2965 .get(name)
2966 .ok_or_else(|| error(&location, format!("undefined binding: {name}")))?,
2967 budget,
2968 &location,
2969 )?,
2970 Globals::Available(bindings) => {
2971 let value = bindings.get(name, expression.range.clone())?;
2972 let ty = if !self.fallback
2973 && matches!(
2974 value,
2975 ConstructedValue::Array(_)
2976 | ConstructedValue::Tuple(_)
2977 | ConstructedValue::Record(_)
2978 | ConstructedValue::Set { .. }
2979 | ConstructedValue::KeyedArray { .. }
2980 ) {
2981 ValueType::Unestablished
2982 } else {
2983 shallow_type(value, budget, &location)?
2984 };
2985 one_type(ty, budget, &location)?
2986 }
2987 };
2988 let mut found = false;
2989 for previous in &self.free {
2990 budget.charge(
2991 sum(1, name.len().min(previous.value.len()), &location)?,
2992 location.clone(),
2993 )?;
2994 if previous.value == name {
2995 found = true;
2996 break;
2997 }
2998 }
2999 if self.fallback && !found {
3000 budget.charge(name.len(), location.clone())?;
3001 push_metadata(
3002 &mut self.fallback_names,
3003 Located::new(name.to_owned(), expression.range.clone()),
3004 budget,
3005 &location,
3006 )?;
3007 } else if !self.fallback {
3008 let mut selected = None;
3009 for (index, previous) in self.fallback_names.iter().enumerate() {
3010 budget.charge(1 + name.len().min(previous.value.len()), location.clone())?;
3011 if previous.value == name {
3012 selected = Some(index);
3013 break;
3014 }
3015 }
3016 if let Some(index) = selected {
3017 self.fallback_names.remove(index);
3018 }
3019 }
3020 if !found {
3021 budget.charge(name.len(), location.clone())?;
3022 push_metadata(
3023 &mut self.free,
3024 Located::new(name.to_owned(), expression.range.clone()),
3025 budget,
3026 &location,
3027 )?;
3028 }
3029 Ok(types)
3030 }
3031
3032 fn walk(
3033 &mut self,
3034 expression: &'a ValueExpression<Q>,
3035 budget: &mut Budget,
3036 ) -> Result<Vec<ValueType>, QueryError> {
3037 self.walk_control(expression, budget, None)
3038 }
3039
3040 fn walk_control(
3041 &mut self,
3042 expression: &'a ValueExpression<Q>,
3043 budget: &mut Budget,
3044 transfer: Option<usize>,
3045 ) -> Result<Vec<ValueType>, QueryError> {
3046 let location = Location::Query(expression.range.clone());
3047 budget.within_evaluation(1, location.clone(), |budget| {
3048 budget.charge(1, location.clone())?;
3049 check_loop_terminal(&expression.value, transfer, &location)?;
3050 self.node(expression, budget, &location, transfer)
3051 })
3052 }
3053
3054 fn node(
3055 &mut self,
3056 expression: &'a ValueExpression<Q>,
3057 budget: &mut Budget,
3058 location: &Location,
3059 transfer: Option<usize>,
3060 ) -> Result<Vec<ValueType>, QueryError> {
3061 self.queries.prepare_computation(expression, budget)?;
3062 match &expression.value {
3063 ValueExpressionKind::Queue { items, .. } => {
3064 self.walk(items, budget)?;
3065 self.queries.worklist_types(expression, budget)
3066 }
3067 ValueExpressionKind::IdentitySet { .. } => {
3068 self.queries.worklist_types(expression, budget)
3069 }
3070 ValueExpressionKind::PopFront(queue) => {
3071 self.walk(queue, budget)?;
3072 self.queries.worklist_types(expression, budget)
3073 }
3074 ValueExpressionKind::PushBackAll { queue, items } => {
3075 self.walk(queue, budget)?;
3076 self.walk(items, budget)?;
3077 self.queries.worklist_types(expression, budget)
3078 }
3079 ValueExpressionKind::Insert { set, item } => {
3080 self.walk(set, budget)?;
3081 self.walk(item, budget)?;
3082 self.queries.worklist_types(expression, budget)
3083 }
3084 ValueExpressionKind::Call {
3085 function,
3086 arguments,
3087 } => {
3088 let (_, arity) = self.queries.function(function, budget)?;
3089 if arguments.len() != arity {
3090 return Err(error(location, "function argument count mismatch"));
3091 }
3092 for argument in arguments {
3093 self.walk(argument, budget)?;
3094 }
3095 self.queries.function_types(function, budget)
3096 }
3097 ValueExpressionKind::Fold {
3098 items,
3099 initial,
3100 step,
3101 }
3102 | ValueExpressionKind::Scan {
3103 items,
3104 initial,
3105 step,
3106 } => {
3107 let (_, state) = self.queries.accumulator_function(step, budget)?;
3108 let types = self.walk(items, budget)?;
3109 for ty in types {
3110 if !matches!(
3111 shape_type(ty, budget, location)?,
3112 ValueType::Array(_) | ValueType::ArrayKind(_) | ValueType::Unestablished
3113 ) {
3114 return Err(error(
3115 &Location::Query(items.range.clone()),
3116 "named accumulator needs a positional sequence",
3117 ));
3118 }
3119 }
3120 self.walk(initial, budget)?;
3121 if matches!(expression.value, ValueExpressionKind::Scan { .. }) {
3122 one_type(
3123 ValueType::Array(ArrayType::Elements(state)),
3124 budget,
3125 location,
3126 )
3127 } else {
3128 Ok(state)
3129 }
3130 }
3131 ValueExpressionKind::NativeValue(query) => {
3132 self.queries
3133 .native_value_types(query, &self.locals, budget, &expression.range)
3134 }
3135 ValueExpressionKind::Literal(value) => {
3136 let ty = if self.fallback {
3137 shallow_type(value, budget, location)?
3138 } else {
3139 constructed_value_type(value, budget, location.clone())?
3140 };
3141 one_type(ty, budget, location)
3142 }
3143 ValueExpressionKind::Occurs(value) | ValueExpressionKind::Absent(value) => {
3144 self.walk(value, budget)?;
3145 one_type(ValueType::Boolean, budget, location)
3146 }
3147 ValueExpressionKind::Lookup { value, key }
3148 | ValueExpressionKind::HasKey { value, key } => {
3149 let values = self.walk(value, budget)?;
3150 let keys = self.walk(key, budget)?;
3151 let label = match &key.value {
3152 ValueExpressionKind::Literal(ConstructedValue::String(label)) => {
3153 Some(label.as_str())
3154 }
3155 _ => None,
3156 };
3157 let mut result = Vec::new();
3158 for value in values {
3159 append_types(
3160 &mut result,
3161 access_type(value, &keys, label, true, budget, location)?,
3162 budget,
3163 location,
3164 )?;
3165 }
3166 if matches!(expression.value, ValueExpressionKind::HasKey { .. }) {
3167 one_type(ValueType::Boolean, budget, location)
3168 } else {
3169 Ok(result)
3170 }
3171 }
3172 ValueExpressionKind::Default { value, fallback } => {
3173 let left = self.walk(value, budget)?;
3174 let saved = self.fallback;
3175 self.fallback = true;
3176 let right = self.walk(fallback, budget);
3177 self.fallback = saved;
3178 let right = right?;
3179 for a in &left {
3180 for b in &right {
3181 if !compatible_types(a, b, budget, location)? {
3182 return Err(error(
3183 location,
3184 "default needs compatible underlying types",
3185 ));
3186 }
3187 }
3188 }
3189 let mut result = Vec::new();
3190 for ty in left.into_iter().chain(right) {
3191 let ty = match ty {
3192 ValueType::Absent => continue,
3193 ValueType::Declared {
3194 actual,
3195 declaration,
3196 } => {
3197 let declaration =
3198 copy_declaration(underlying(&declaration), budget, location)?;
3199 ValueType::Declared {
3200 actual: Box::new(if matches!(*actual, ValueType::Absent) {
3201 ValueType::Unestablished
3202 } else {
3203 *actual
3204 }),
3205 declaration,
3206 }
3207 }
3208 ty => ty,
3209 };
3210 push_metadata(&mut result, ty, budget, location)?;
3211 }
3212 if result.is_empty() {
3213 one_type(ValueType::Absent, budget, location)
3214 } else {
3215 Ok(result)
3216 }
3217 }
3218 ValueExpressionKind::CurrentInput => {
3219 self.queries.current_input_types(&expression.range, budget)
3220 }
3221 ValueExpressionKind::Name(name) => self.name(name, expression, budget),
3222 ValueExpressionKind::PredicateSubject => {
3223 if !self
3224 .locals
3225 .iter()
3226 .any(|(name, _)| *name == PREDICATE_SUBJECT)
3227 {
3228 return self
3229 .queries
3230 .predicate_subject_types(&expression.range, budget);
3231 }
3232 self.name(PREDICATE_SUBJECT, expression, budget)
3233 }
3234 ValueExpressionKind::SubjectScope { subject, argument } => {
3235 let types = self.walk(subject, budget)?;
3236 self.scoped_types(argument, subject, &types, false, budget)
3237 }
3238 ValueExpressionKind::PredicateScope { subject, predicate } => {
3239 let types = self.walk(subject, budget)?;
3240 let result = self.scoped_types(predicate, subject, &types, false, budget)?;
3241 predicate_types(&result, budget, &predicate.body.range)?;
3242 one_type(ValueType::Boolean, budget, location)
3243 }
3244 ValueExpressionKind::NativeQuery(query) => {
3245 self.queries
3246 .query_types(query, &self.locals, budget, &expression.range)
3247 }
3248 ValueExpressionKind::One(array) => {
3249 let mut result = Vec::new();
3250 for ty in self.walk(array, budget)? {
3251 match shape_type(ty, budget, location)? {
3252 ValueType::Array(
3253 ArrayType::Positions(types) | ArrayType::Elements(types),
3254 )
3255 | ValueType::ArrayKind(types) => {
3256 append_types(&mut result, types, budget, location)?
3257 }
3258 ValueType::Unestablished => {
3259 push_metadata(&mut result, ValueType::Unestablished, budget, location)?
3260 }
3261 _ => return Err(error(location, "one needs a positional array")),
3262 }
3263 }
3264 if result.is_empty() {
3265 push_metadata(&mut result, ValueType::Unestablished, budget, location)?;
3266 }
3267 Ok(result)
3268 }
3269 ValueExpressionKind::Array(children) => {
3270 let mut entries = metadata_vec(children.len(), budget, location)?;
3271 let mut exact = true;
3272 for child in children {
3273 let types = self.walk(child, budget)?;
3274 exact &= types.len() == 1;
3275 entries.push(types);
3276 }
3277 let mut types = Vec::new();
3278 for entries in entries {
3279 append_types(&mut types, entries, budget, location)?;
3280 }
3281 one_type(
3282 ValueType::Array(if exact {
3283 ArrayType::Positions(types)
3284 } else {
3285 ArrayType::Elements(types)
3286 }),
3287 budget,
3288 location,
3289 )
3290 }
3291 ValueExpressionKind::Tuple(children) => {
3292 let positions = self.positions(children.iter(), budget, location)?;
3293 let mut types = metadata_vec(positions.len(), budget, location)?;
3294 for position in positions {
3295 types.push(ValueType::Tuple(position));
3296 }
3297 Ok(types)
3298 }
3299 ValueExpressionKind::Record(fields) => {
3300 for (index, (label, _)) in fields.iter().enumerate() {
3301 for (previous, _) in &fields[..index] {
3302 budget.charge(
3303 sum(1, label.value.len().min(previous.value.len()), location)?,
3304 location.clone(),
3305 )?;
3306 if label.value == previous.value {
3307 return Err(QueryError::query(
3308 label.range.clone(),
3309 "duplicate record label",
3310 ));
3311 }
3312 }
3313 }
3314 let positions =
3315 self.positions(fields.iter().map(|(_, value)| value), budget, location)?;
3316 let mut types = metadata_vec(positions.len(), budget, location)?;
3317 for position in positions {
3318 let mut result = metadata_vec(fields.len(), budget, location)?;
3319 for ((label, _), ty) in fields.iter().zip(position) {
3320 budget.charge(label.value.len(), Location::Query(label.range.clone()))?;
3321 result.push((label.value.to_owned(), ty));
3322 }
3323 types.push(ValueType::Record(result));
3324 }
3325 Ok(types)
3326 }
3327 ValueExpressionKind::Dictionary(entries) => {
3328 for (key, value) in entries {
3329 for ty in self.walk(key, budget)? {
3330 require_type(
3331 &ty,
3332 ScalarRequirement::String,
3333 &Location::Query(key.range.clone()),
3334 )?;
3335 }
3336 self.walk(value, budget)?;
3337 }
3338 one_type(ValueType::RecordKind, budget, location)
3339 }
3340 ValueExpressionKind::DictionaryComprehension { clauses, key, head } => {
3341 let saved = self.locals.len();
3342 let result = (|| {
3343 self.clauses(clauses, budget)?;
3344 for ty in self.walk(key, budget)? {
3345 require_type(
3346 &ty,
3347 ScalarRequirement::String,
3348 &Location::Query(key.range.clone()),
3349 )?;
3350 }
3351 self.walk(head, budget)?;
3352 one_type(ValueType::RecordKind, budget, location)
3353 })();
3354 self.locals.truncate(saved);
3355 result
3356 }
3357 ValueExpressionKind::Set(children) => {
3358 let mut element_type = None;
3359 for child in children {
3360 merge_set_types(
3361 &mut element_type,
3362 &self.walk(child, budget)?,
3363 budget,
3364 location,
3365 )?;
3366 }
3367 one_type(ValueType::Set(element_type), budget, location)
3368 }
3369 ValueExpressionKind::KeyedArray {
3370 axes,
3371 declared_axis_sources,
3372 entries,
3373 } => {
3374 if axes.is_some() && declared_axis_sources.is_some() {
3375 return Err(error(
3376 location,
3377 "conflicting concrete axes and declared axis sources",
3378 ));
3379 }
3380 let source_facts = declared_axis_sources
3381 .as_ref()
3382 .map(|sources| self.axis_sources(sources, budget, location))
3383 .transpose()?;
3384 let mut facts = None;
3385 let established = match axes {
3386 Some(axes) => {
3387 check_axes(axes, budget, location)?;
3388 Some(axis_types(axes, budget, location)?)
3389 }
3390 None => None,
3391 };
3392 let mut elements = Vec::new();
3393 for (key, value) in entries {
3394 let key_location = Location::Query(key.range.clone());
3395 merge_key_facts(&mut facts, &self.walk(key, budget)?, budget, &key_location)?;
3396 if let Some(axes) = &established {
3397 check_key_facts(&facts, axes, budget, &key_location)?;
3398 }
3399 if let Some(sources) = &source_facts {
3400 check_source_facts(&facts, sources, budget, &key_location)?;
3401 }
3402 append_types(&mut elements, self.walk(value, budget)?, budget, location)?;
3403 }
3404 let axes = match established {
3405 Some(axes) => Some(axes),
3406 None => established_facts(source_facts.or(facts), budget, location)?,
3407 };
3408 if entries.is_empty() && axes.is_none() && declared_axis_sources.is_none() {
3409 return Err(error(location, "unestablished empty keyed axes"));
3410 }
3411 one_type(
3412 ValueType::KeyedArray(KeyedArrayType { axes, elements }),
3413 budget,
3414 location,
3415 )
3416 }
3417 ValueExpressionKind::KeyedComprehension {
3418 clauses,
3419 key,
3420 head,
3421 declared_axis_sources,
3422 empty_axis_sources,
3423 } => {
3424 if declared_axis_sources.is_some() && empty_axis_sources.is_some() {
3425 return Err(error(
3426 location,
3427 "conflicting declared and fallback axis sources",
3428 ));
3429 }
3430 let sources = declared_axis_sources
3432 .as_ref()
3433 .or(empty_axis_sources.as_ref());
3434 let source_facts = sources
3435 .map(|sources| self.axis_sources(sources, budget, location))
3436 .transpose()?;
3437 let saved = self.locals.len();
3438 let result = (|| {
3439 self.clauses(clauses, budget)?;
3440 let mut facts = None;
3441 merge_key_facts(
3442 &mut facts,
3443 &self.walk(key, budget)?,
3444 budget,
3445 &Location::Query(key.range.clone()),
3446 )?;
3447 let elements = self.walk(head, budget)?;
3448 if let Some(source_facts) = &source_facts {
3449 check_source_facts(
3450 &facts,
3451 source_facts,
3452 budget,
3453 &Location::Query(key.range.clone()),
3454 )?;
3455 }
3456 let axes = if declared_axis_sources.is_some() {
3457 established_facts(source_facts, budget, location)?
3458 } else {
3459 established_facts(facts, budget, location)?
3460 };
3461 one_type(
3462 ValueType::KeyedArray(KeyedArrayType { axes, elements }),
3463 budget,
3464 location,
3465 )
3466 })();
3467 self.locals.truncate(saved);
3468 result
3469 }
3470 ValueExpressionKind::Unary { operator, operand } => {
3471 let types = self.walk(operand, budget)?;
3472 let mut result = metadata_vec(types.len(), budget, location)?;
3473 for ty in types {
3474 budget.charge(1, location.clone())?;
3475 match operator {
3476 ValueUnaryOperator::Not => {
3477 require_type(&ty, ScalarRequirement::Boolean, location)?;
3478 result.push(ValueType::Boolean);
3479 }
3480 ValueUnaryOperator::Positive | ValueUnaryOperator::Negative => {
3481 require_type(&ty, ScalarRequirement::Numeric, location)?;
3482 result.push(ty);
3483 }
3484 }
3485 }
3486 Ok(result)
3487 }
3488 ValueExpressionKind::Binary {
3489 operator,
3490 left,
3491 right,
3492 } => {
3493 let left = self.walk(left, budget)?;
3494 let right = self.walk(right, budget)?;
3495 let mut result = Vec::new();
3496 for a in &left {
3497 for b in &right {
3498 budget.charge(1, location.clone())?;
3499 let ty = binary_type(*operator, a, b, budget, location)?;
3500 push_metadata(&mut result, ty, budget, location)?;
3501 }
3502 }
3503 Ok(result)
3504 }
3505 ValueExpressionKind::Field { value, field } => {
3506 let types = self.walk(value, budget)?;
3507 let mut result = Vec::new();
3508 for ty in types {
3509 if let ValueType::Declared { declaration, .. } = &ty
3510 && let DeclaredType::Record(fields) = underlying(declaration)
3511 {
3512 let selected = record_declaration(fields, &field.value, budget, location)?;
3513 push_metadata(
3514 &mut result,
3515 declared_fact(selected, ValueType::Unestablished, budget, location)?,
3516 budget,
3517 location,
3518 )?;
3519 continue;
3520 }
3521 let selected = match shape_type(ty, budget, location)? {
3522 ValueType::Record(fields) => {
3523 let mut selected = None;
3524 for (label, ty) in fields {
3525 budget.charge(
3526 sum(1, label.len().min(field.value.len()), location)?,
3527 location.clone(),
3528 )?;
3529 if label == field.value {
3530 selected = Some(ty);
3531 }
3532 }
3533 selected.unwrap_or(ValueType::Unestablished)
3534 }
3535 ValueType::Unestablished | ValueType::RecordKind => {
3536 ValueType::Unestablished
3537 }
3538 _ => return Err(error(location, "field access needs a record")),
3539 };
3540 push_metadata(&mut result, selected, budget, location)?;
3541 }
3542 Ok(result)
3543 }
3544 ValueExpressionKind::TupleAccess { value, position } => {
3545 let types = self.walk(value, budget)?;
3546 let mut result = Vec::new();
3547 for ty in types {
3548 budget.charge(1, location.clone())?;
3549 let selected = match shape_type(ty, budget, location)? {
3550 ValueType::Tuple(entries) => {
3551 budget.charge(entries.len().min(position.value), location.clone())?;
3552 position
3553 .value
3554 .checked_sub(1)
3555 .and_then(|index| entries.into_iter().nth(index))
3556 .unwrap_or(ValueType::Unestablished)
3557 }
3558 ValueType::Unestablished | ValueType::TupleKind => ValueType::Unestablished,
3559 _ => return Err(error(location, "tuple access needs a tuple")),
3560 };
3561 push_metadata(&mut result, selected, budget, location)?;
3562 }
3563 Ok(result)
3564 }
3565 ValueExpressionKind::ArrayAccess { array, index } => {
3566 let arrays = self.walk(array, budget)?;
3567 let indices = self.walk(index, budget)?;
3568 let constant = match index.value {
3569 ValueExpressionKind::Literal(ConstructedValue::Integer(value)) => Some(value),
3570 _ => None,
3571 };
3572 let mut result = Vec::new();
3573 for array in arrays {
3574 if matches!(&array, ValueType::Record(_) | ValueType::RecordKind)
3575 || matches!(&array, ValueType::Declared { declaration, .. }
3576 if matches!(underlying(declaration), DeclaredType::Record(_)))
3577 {
3578 let label = match &index.value {
3579 ValueExpressionKind::Literal(ConstructedValue::String(label)) => {
3580 Some(label.as_str())
3581 }
3582 _ => None,
3583 };
3584 append_types(
3585 &mut result,
3586 access_type(array, &indices, label, false, budget, location)?,
3587 budget,
3588 location,
3589 )?;
3590 continue;
3591 }
3592 match shape_type(array, budget, location)? {
3593 ValueType::ArrayKind(elements) => {
3594 let mut facts = None;
3595 merge_key_facts(&mut facts, &indices, budget, location)?;
3596 append_types(&mut result, elements, budget, location)?;
3597 }
3598 ValueType::KeyedArray(keyed) => {
3599 let mut facts = None;
3600 merge_key_facts(&mut facts, &indices, budget, location)?;
3601 if let Some(axes) = &keyed.axes {
3602 check_key_facts(&facts, axes, budget, location)?;
3603 }
3604 append_types(&mut result, keyed.elements, budget, location)?;
3605 }
3606 ValueType::Array(array) => {
3607 for ty in &indices {
3608 require_type(ty, ScalarRequirement::Integer, location)?;
3609 }
3610 match array {
3611 _ if constant.is_some_and(|position| position <= 0) => {
3612 push_metadata(
3613 &mut result,
3614 ValueType::Unestablished,
3615 budget,
3616 location,
3617 )?;
3618 }
3619 ArrayType::Positions(entries) if constant.is_some() => {
3620 let position =
3621 usize::try_from(constant.expect("constant checked")).ok();
3622 budget.charge(
3623 entries.len().min(position.unwrap_or(0)),
3624 location.clone(),
3625 )?;
3626 let ty = position
3627 .and_then(|position| position.checked_sub(1))
3628 .and_then(|index| entries.into_iter().nth(index))
3629 .unwrap_or(ValueType::Unestablished);
3630 push_metadata(&mut result, ty, budget, location)?;
3631 }
3632 ArrayType::Positions(entries) | ArrayType::Elements(entries) => {
3633 append_types(&mut result, entries, budget, location)?
3634 }
3635 }
3636 }
3637 ValueType::Unestablished => {
3638 for ty in &indices {
3639 if matches!(ty, ValueType::String) {
3640 continue;
3641 }
3642 let mut facts = None;
3643 merge_key_facts(
3644 &mut facts,
3645 std::slice::from_ref(ty),
3646 budget,
3647 location,
3648 )?;
3649 }
3650 push_metadata(&mut result, ValueType::Unestablished, budget, location)?;
3651 }
3652 _ => return Err(error(location, "array access needs an array or record")),
3653 }
3654 }
3655 if result.is_empty() {
3656 push_metadata(&mut result, ValueType::Unestablished, budget, location)?;
3657 }
3658 Ok(result)
3659 }
3660 ValueExpressionKind::IndexSet(array) => {
3661 let mut result = Vec::new();
3662 for ty in self.walk(array, budget)? {
3663 budget.charge(1, location.clone())?;
3664 let element_type = match shape_type(ty, budget, location)? {
3665 ValueType::Array(_) => Some(SetElementType::Integer),
3666 ValueType::KeyedArray(keyed) => match keyed.axes {
3667 Some(axes) if axes.len() == 1 => Some(key_set_type(axes[0])),
3668 Some(_) => return Err(error(location, "index_set needs rank one")),
3669 None => None,
3670 },
3671 ValueType::ArrayKind(_) | ValueType::Unestablished => None,
3672 _ => return Err(error(location, "index_set needs an array")),
3673 };
3674 push_metadata(&mut result, ValueType::Set(element_type), budget, location)?;
3675 }
3676 Ok(result)
3677 }
3678 ValueExpressionKind::Entries(array) => {
3679 let mut result = Vec::new();
3680 for ty in self.walk(array, budget)? {
3681 budget.charge(1, location.clone())?;
3682 let keyed = match shape_type(ty, budget, location)? {
3683 ValueType::KeyedArray(keyed) => keyed,
3684 ValueType::ArrayKind(elements) => KeyedArrayType {
3685 axes: None,
3686 elements,
3687 },
3688 ValueType::Unestablished => {
3689 push_metadata(&mut result, ValueType::Unestablished, budget, location)?;
3690 continue;
3691 }
3692 _ => return Err(error(location, "entries needs a keyed array")),
3693 };
3694 let key = match keyed.axes {
3695 Some(axes) => {
3696 let mut components = metadata_vec(axes.len(), budget, location)?;
3697 for axis in axes {
3698 budget.charge(1, location.clone())?;
3699 components.push(match axis {
3700 ValueKeyType::Integer => ValueType::Integer,
3701 ValueKeyType::Member(id) => ValueType::Member(id),
3702 });
3703 }
3704 if components.len() == 1 {
3705 components.pop().expect("rank one")
3706 } else {
3707 ValueType::Tuple(components)
3708 }
3709 }
3710 None => ValueType::Unestablished,
3711 };
3712 let mut records = metadata_vec(keyed.elements.len(), budget, location)?;
3713 for value in keyed.elements {
3714 let mut fields = metadata_vec(2, budget, location)?;
3715 budget.charge("key".len() + "value".len(), location.clone())?;
3716 fields.push(("key".into(), copy_type(&key, budget, location)?));
3717 fields.push(("value".into(), value));
3718 records.push(ValueType::Record(fields));
3719 }
3720 push_metadata(
3721 &mut result,
3722 ValueType::Array(ArrayType::Elements(records)),
3723 budget,
3724 location,
3725 )?;
3726 }
3727 Ok(result)
3728 }
3729 ValueExpressionKind::ZipExact { left, right } => {
3730 let left = self.walk(left, budget)?;
3731 let right = self.walk(right, budget)?;
3732 let mut result = Vec::new();
3733 for a in &left {
3734 for b in &right {
3735 let ty = zip_exact_type(a, b, budget, location)?;
3736 push_metadata(&mut result, ty, budget, location)?;
3737 }
3738 }
3739 Ok(result)
3740 }
3741 ValueExpressionKind::Reduce { operator, array } => {
3742 for ty in self.walk(array, budget)? {
3743 budget.charge(1, location.clone())?;
3744 let elements = match shape_type(ty, budget, location)? {
3745 ValueType::Array(
3746 ArrayType::Positions(elements) | ArrayType::Elements(elements),
3747 )
3748 | ValueType::ArrayKind(elements) => elements,
3749 ValueType::KeyedArray(keyed) => keyed.elements,
3750 ValueType::Unestablished => continue,
3751 _ => return Err(error(location, "reducer needs an array")),
3752 };
3753 if let Some(requirement) = match operator {
3754 ValueReducer::Length => None,
3755 ValueReducer::Sum => Some(ScalarRequirement::Integer),
3756 ValueReducer::Forall | ValueReducer::Exists => {
3757 Some(ScalarRequirement::Boolean)
3758 }
3759 } {
3760 for element in elements {
3761 budget.charge(1, location.clone())?;
3762 require_type(&element, requirement, location)?;
3763 }
3764 }
3765 }
3766 one_type(
3767 match operator {
3768 ValueReducer::Length | ValueReducer::Sum => ValueType::Integer,
3769 ValueReducer::Forall | ValueReducer::Exists => ValueType::Boolean,
3770 },
3771 budget,
3772 location,
3773 )
3774 }
3775 ValueExpressionKind::Filter { array, predicate } => {
3776 let mut elements = Vec::new();
3777 for ty in self.walk(array, budget)? {
3778 append_types(
3779 &mut elements,
3780 filter_elements(ty, budget, location)?,
3781 budget,
3782 location,
3783 )?;
3784 }
3785 if elements.is_empty() {
3786 push_metadata(&mut elements, ValueType::Unestablished, budget, location)?;
3787 }
3788 let result = self.scoped_types(predicate, array, &elements, true, budget)?;
3789 predicate_types(&result, budget, &predicate.body.range)?;
3790 one_type(
3791 ValueType::Array(ArrayType::Elements(elements)),
3792 budget,
3793 location,
3794 )
3795 }
3796 ValueExpressionKind::Sort {
3797 array, selector, ..
3798 } => {
3799 let mut result = Vec::new();
3800 for ty in self.walk(array, budget)? {
3801 let ty = sort_type(ty, selector.as_ref(), budget, location)?;
3802 push_metadata(&mut result, ty, budget, location)?;
3803 }
3804 Ok(result)
3805 }
3806 ValueExpressionKind::Extremum { array, field, .. } => {
3807 let mut result = Vec::new();
3808 for ty in self.walk(array, budget)? {
3809 let elements = ordering_elements(
3810 ty,
3811 field
3812 .as_ref()
3813 .map(|field| Located::new(field.value.as_str(), field.range.clone())),
3814 budget,
3815 location,
3816 )?;
3817 append_types(&mut result, elements, budget, location)?;
3818 }
3819 push_metadata(&mut result, ValueType::Absent, budget, location)?;
3820 Ok(result)
3821 }
3822 ValueExpressionKind::Unique(array) | ValueExpressionKind::Tally(array) => {
3823 let tally = matches!(expression.value, ValueExpressionKind::Tally(_));
3824 let mut result = Vec::new();
3825 for ty in self.walk(array, budget)? {
3826 let retained = if !tally {
3827 match &ty {
3828 ValueType::Declared { declaration, .. } => {
3829 Some(copy_declaration(declaration, budget, location)?)
3830 }
3831 _ => None,
3832 }
3833 } else {
3834 None
3835 };
3836 let elements = scalar_pipeline_elements(ty, tally, budget, location)?;
3837 let mut ty = if tally {
3838 if elements.is_empty() {
3839 ValueType::Record(Vec::new())
3840 } else {
3841 ValueType::RecordKind
3842 }
3843 } else {
3844 ValueType::Array(ArrayType::Elements(elements))
3845 };
3846 if let Some(declaration) = retained {
3847 ty = ValueType::Declared {
3848 actual: Box::new(ty),
3849 declaration,
3850 };
3851 }
3852 push_metadata(&mut result, ty, budget, location)?;
3853 }
3854 Ok(result)
3855 }
3856 ValueExpressionKind::GroupBy { rows, field } => {
3857 let selector = Location::Query(field.range.clone());
3858 let mut result = Vec::new();
3859 for ty in self.walk(rows, budget)? {
3860 budget.charge(1, location.clone())?;
3861 let elements = match shape_type(ty, budget, location)? {
3862 ValueType::Array(
3863 ArrayType::Positions(elements) | ArrayType::Elements(elements),
3864 )
3865 | ValueType::ArrayKind(elements) => elements,
3866 ValueType::Unestablished => {
3867 push_metadata(&mut result, ValueType::Unestablished, budget, location)?;
3868 continue;
3869 }
3870 _ => return Err(error(location, "group_by needs a positional array")),
3871 };
3872 let mut key_type = None;
3873 for row in &elements {
3874 budget.charge(1, location.clone())?;
3875 let normalized = if matches!(row, ValueType::Declared { .. }) {
3876 Some(shape_type(
3877 copy_type(row, budget, location)?,
3878 budget,
3879 location,
3880 )?)
3881 } else {
3882 None
3883 };
3884 let fields = match normalized.as_ref().unwrap_or(row) {
3885 ValueType::Record(fields) => fields,
3886 ValueType::Unestablished | ValueType::RecordKind => continue,
3887 _ => return Err(error(location, "group_by needs record rows")),
3888 };
3889 for (label, ty) in fields {
3890 budget.charge(
3891 sum(1, label.len().min(field.value.len()), &selector)?,
3892 selector.clone(),
3893 )?;
3894 if label != &field.value || matches!(ty, ValueType::Unestablished) {
3895 continue;
3896 }
3897 if !matches!(
3898 ty,
3899 ValueType::Integer | ValueType::String | ValueType::Member(_)
3900 ) {
3901 return Err(error(
3902 &selector,
3903 "group_by keys need integers, strings or nominal members",
3904 ));
3905 }
3906 if key_type.as_ref().is_some_and(|previous| previous != ty) {
3907 return Err(error(
3908 &selector,
3909 "group_by needs homogeneous key types",
3910 ));
3911 }
3912 if key_type.is_none() {
3913 key_type = Some(copy_type(ty, budget, &selector)?);
3914 }
3915 }
3916 }
3917 let mut groups = Vec::new();
3918 if !elements.is_empty() {
3919 let mut fields = metadata_vec(2, budget, location)?;
3920 budget.charge("key".len() + "items".len(), location.clone())?;
3921 fields.push(("key".into(), key_type.unwrap_or(ValueType::Unestablished)));
3922 fields.push((
3923 "items".into(),
3924 ValueType::Array(ArrayType::Elements(elements)),
3925 ));
3926 push_metadata(&mut groups, ValueType::Record(fields), budget, location)?;
3927 }
3928 push_metadata(
3929 &mut result,
3930 ValueType::Array(ArrayType::Elements(groups)),
3931 budget,
3932 location,
3933 )?;
3934 }
3935 Ok(result)
3936 }
3937 ValueExpressionKind::Require {
3938 condition,
3939 value,
3940 message,
3941 } => {
3942 self.condition(condition, budget)?;
3943 let types = self.walk(value, budget)?;
3944 allocation::<u8>(message.value.len(), location)?;
3945 budget.charge(message.value.len(), Location::Query(message.range.clone()))?;
3946 Ok(types)
3947 }
3948 ValueExpressionKind::Range { lower, upper } => {
3949 for expression in [lower, upper] {
3950 for ty in self.walk(expression, budget)? {
3951 require_type(&ty, ScalarRequirement::Integer, location)?;
3952 }
3953 }
3954 one_type(
3955 ValueType::Set(Some(SetElementType::Integer)),
3956 budget,
3957 location,
3958 )
3959 }
3960 ValueExpressionKind::Conditional {
3961 branches,
3962 otherwise,
3963 } => {
3964 let mut types = Vec::new();
3965 for (condition, branch) in branches {
3966 self.condition(condition, budget)?;
3967 append_types(
3968 &mut types,
3969 self.walk_control(branch, budget, transfer)?,
3970 budget,
3971 location,
3972 )?;
3973 }
3974 append_types(
3975 &mut types,
3976 self.walk_control(otherwise, budget, transfer)?,
3977 budget,
3978 location,
3979 )?;
3980 Ok(types)
3981 }
3982 ValueExpressionKind::Loop { states, body } => {
3983 let mut initial = metadata_vec(states.len(), budget, location)?;
3984 for (_, value) in states {
3985 initial.push(self.walk(value, budget)?);
3986 }
3987 let saved = self.locals.len();
3988 self.queries.enter_scope(budget, &expression.range)?;
3989 let result = (|| {
3990 for ((name, value), facts) in states.iter().zip(initial) {
3991 function_local(name, &self.locals, budget)?;
3992 if self.global_exists(&name.value, name.range.clone(), budget)? {
3993 return Err(error(
3994 &Location::Query(name.range.clone()),
3995 "binding shadows an existing source or let",
3996 ));
3997 }
3998 self.queries.prepare_local(name, value, &facts, budget)?;
3999 push_metadata(
4000 &mut self.locals,
4001 (name.value.as_str(), facts),
4002 budget,
4003 location,
4004 )?;
4005 }
4006 self.queries.prepare_computation(expression, budget)?;
4007 self.walk_control(body, budget, Some(states.len()))
4008 })();
4009 self.locals.truncate(saved);
4010 self.queries.leave_scope();
4011 result
4012 }
4013 ValueExpressionKind::Break(value) => self.walk(value, budget),
4014 ValueExpressionKind::Continue(values) => {
4015 for value in values {
4016 self.walk(value, budget)?;
4017 }
4018 Ok(Vec::new())
4019 }
4020 ValueExpressionKind::Block { bindings, value } => {
4021 let saved = self.locals.len();
4022 self.queries.enter_scope(budget, &expression.range)?;
4023 let result = (|| {
4024 for (pattern, value) in bindings {
4025 let types = self.walk(value, budget)?;
4026 self.pattern(pattern, &types, false, budget)?;
4027 self.queries.prepare_pattern(pattern, value, budget)?;
4028 }
4029 self.walk_control(value, budget, transfer)
4030 })();
4031 self.locals.truncate(saved);
4032 self.queries.leave_scope();
4033 result
4034 }
4035 ValueExpressionKind::ValueMatch { subject, arms } => {
4036 if arms.is_empty()
4037 || !matches!(
4038 arms.last().map(|a| (&a.pattern.value, &a.guard)),
4039 Some((PatternKind::Wildcard, None))
4040 )
4041 {
4042 return Err(error(
4043 location,
4044 "value match needs a final unguarded wildcard",
4045 ));
4046 }
4047 let facts = self.walk(subject, budget)?;
4048 let mut types = Vec::new();
4049 for (index, arm) in arms.iter().enumerate() {
4050 if index + 1 < arms.len()
4051 && matches!(arm.pattern.value, PatternKind::Wildcard)
4052 && arm.guard.is_none()
4053 {
4054 return Err(error(
4055 &Location::Query(arm.pattern.range.clone()),
4056 "unguarded wildcard must be the final arm",
4057 ));
4058 }
4059 let saved = self.locals.len();
4060 self.queries.enter_scope(budget, &arm.pattern.range)?;
4061 let result = (|| {
4062 self.pattern(&arm.pattern, &facts, false, budget)?;
4063 self.queries
4064 .prepare_pattern(&arm.pattern, subject, budget)?;
4065 if let Some(guard) = &arm.guard {
4066 self.condition(guard, budget)?;
4067 }
4068 self.walk_control(&arm.value, budget, transfer)
4069 })();
4070 self.locals.truncate(saved);
4071 self.queries.leave_scope();
4072 append_types(&mut types, result?, budget, location)?;
4073 }
4074 Ok(types)
4075 }
4076 ValueExpressionKind::Comprehension {
4077 constructor,
4078 clauses,
4079 head,
4080 } => {
4081 let saved = self.locals.len();
4082 let result = (|| {
4083 self.clauses(clauses, budget)?;
4084 let types = self.walk(head, budget)?;
4085 let ty = match constructor {
4086 ValueConstructor::Array => ValueType::Array(ArrayType::Elements(types)),
4087 ValueConstructor::Set => {
4088 let mut element_type = None;
4089 merge_set_types(&mut element_type, &types, budget, location)?;
4090 ValueType::Set(element_type)
4091 }
4092 };
4093 one_type(ty, budget, location)
4094 })();
4095 self.locals.truncate(saved);
4096 result
4097 }
4098 }
4099 }
4100
4101 fn scoped_types(
4102 &mut self,
4103 predicate: &'a PredicateArgument<Q>,
4104 source: &'a ValueExpression<Q>,
4105 types: &[ValueType],
4106 element: bool,
4107 budget: &mut Budget,
4108 ) -> Result<Vec<ValueType>, QueryError> {
4109 let range = &predicate.body.range;
4110 let location = Location::Query(range.clone());
4111 let saved = self.locals.len();
4112 self.queries.enter_scope(budget, range)?;
4113 let result = (|| {
4114 push_metadata(
4115 &mut self.locals,
4116 (PREDICATE_SUBJECT, copy_types(types, budget, &location)?),
4117 budget,
4118 &location,
4119 )?;
4120 if let Some(pattern) = &predicate.argument {
4121 self.pattern(pattern, types, true, budget)?;
4122 }
4123 self.queries.prepare_predicate(
4124 predicate.argument.as_ref(),
4125 source,
4126 types,
4127 element,
4128 budget,
4129 )?;
4130 self.walk(&predicate.body, budget)
4131 })();
4132 self.locals.truncate(saved);
4133 self.queries.leave_scope();
4134 result
4135 }
4136
4137 fn pattern(
4138 &mut self,
4139 pattern: &'a Pattern,
4140 facts: &[ValueType],
4141 required: bool,
4142 budget: &mut Budget,
4143 ) -> Result<(), QueryError> {
4144 let names = if required {
4145 pattern::required_facts(pattern, facts, budget)?
4146 } else {
4147 pattern::facts(pattern, facts, budget)?
4148 };
4149 for (name, facts) in names {
4150 let location = Location::Query(name.range.clone());
4151 function_local(name, &self.locals, budget)?;
4152 if self.global_exists(&name.value, name.range.clone(), budget)? {
4153 return Err(error(
4154 &location,
4155 "binding shadows an existing source or let",
4156 ));
4157 }
4158 push_metadata(
4159 &mut self.locals,
4160 (name.value.as_str(), facts),
4161 budget,
4162 &location,
4163 )?;
4164 }
4165 Ok(())
4166 }
4167
4168 fn clauses(
4169 &mut self,
4170 clauses: &'a [Located<ValueGenerator<Q>>],
4171 budget: &mut Budget,
4172 ) -> Result<(), QueryError> {
4173 for clause in clauses {
4174 let clause_location = Location::Query(clause.range.clone());
4175 budget.charge(1, clause_location.clone())?;
4176 match &clause.value {
4177 ValueGenerator::In {
4178 binders,
4179 source,
4180 filter,
4181 } => {
4182 if binders.is_empty() {
4183 return Err(error(&clause_location, "in generator needs a binder"));
4184 }
4185 let source_types = self.walk(source, budget)?;
4186 let mut elements = Vec::new();
4187 for ty in source_types {
4188 match shape_type(ty, budget, &clause_location)? {
4189 ValueType::Array(
4190 ArrayType::Positions(entries) | ArrayType::Elements(entries),
4191 )
4192 | ValueType::ArrayKind(entries) => {
4193 append_types(&mut elements, entries, budget, &clause_location)?
4194 }
4195 ValueType::KeyedArray(keyed) => append_types(
4196 &mut elements,
4197 keyed.elements,
4198 budget,
4199 &clause_location,
4200 )?,
4201 ValueType::Set(element_type) => push_metadata(
4202 &mut elements,
4203 set_value_type(element_type),
4204 budget,
4205 &clause_location,
4206 )?,
4207 ValueType::Unestablished => push_metadata(
4208 &mut elements,
4209 ValueType::Unestablished,
4210 budget,
4211 &clause_location,
4212 )?,
4213 _ => {
4214 return Err(error(
4215 &Location::Query(source.range.clone()),
4216 "in source needs an array or supported set",
4217 ));
4218 }
4219 }
4220 }
4221 if elements.is_empty() {
4222 push_metadata(
4223 &mut elements,
4224 ValueType::Unestablished,
4225 budget,
4226 &clause_location,
4227 )?;
4228 }
4229 for binder in binders {
4230 self.pattern(binder, &elements, false, budget)?;
4231 self.queries
4232 .prepare_generator(binder, source, &elements, true, budget)?;
4233 }
4234 if let Some(filter) = filter {
4235 self.condition(filter, budget)?;
4236 }
4237 }
4238 ValueGenerator::Bind {
4239 binder,
4240 value,
4241 filter,
4242 } => {
4243 let types = self.walk(value, budget)?;
4244 self.pattern(binder, &types, false, budget)?;
4245 self.queries
4246 .prepare_generator(binder, value, &types, false, budget)?;
4247 if let Some(filter) = filter {
4248 self.condition(filter, budget)?;
4249 }
4250 }
4251 }
4252 }
4253 Ok(())
4254 }
4255
4256 fn condition(
4257 &mut self,
4258 condition: &'a ValueExpression<Q>,
4259 budget: &mut Budget,
4260 ) -> Result<(), QueryError> {
4261 let location = Location::Query(condition.range.clone());
4262 for ty in self.walk(condition, budget)? {
4263 budget.charge(1, location.clone())?;
4264 require_type(&ty, ScalarRequirement::Boolean, &location)?;
4265 }
4266 Ok(())
4267 }
4268
4269 fn axis_sources(
4270 &mut self,
4271 sources: &'a [ValueExpression<Q>],
4272 budget: &mut Budget,
4273 location: &Location,
4274 ) -> Result<Vec<Option<ValueKeyType>>, QueryError> {
4275 if sources.is_empty() {
4276 return Err(error(location, "axis sources need nonzero rank"));
4277 }
4278 let mut facts = metadata_vec(sources.len(), budget, location)?;
4279 for source in sources {
4280 let types = self.walk(source, budget)?;
4281 facts.push(source_key_type(
4282 &types,
4283 budget,
4284 &Location::Query(source.range.clone()),
4285 )?);
4286 }
4287 Ok(facts)
4288 }
4289
4290 fn positions(
4294 &mut self,
4295 children: impl Iterator<Item = &'a ValueExpression<Q>>,
4296 budget: &mut Budget,
4297 location: &Location,
4298 ) -> Result<Vec<Vec<ValueType>>, QueryError> {
4299 let mut positions = metadata_vec(1, budget, location)?;
4300 positions.push(Vec::new());
4301 for child in children {
4302 let types = self.walk(child, budget)?;
4303 positions = extend_type_positions(&positions, &types, budget, location)?;
4304 }
4305 Ok(positions)
4306 }
4307}
4308
4309fn zip_exact_type(
4310 left: &ValueType,
4311 right: &ValueType,
4312 budget: &mut Budget,
4313 location: &Location,
4314) -> Result<ValueType, QueryError> {
4315 if matches!(left, ValueType::Declared { .. }) || matches!(right, ValueType::Declared { .. }) {
4316 let left = shape_type(copy_type(left, budget, location)?, budget, location)?;
4317 let right = shape_type(copy_type(right, budget, location)?, budget, location)?;
4318 return zip_exact_type(&left, &right, budget, location);
4319 }
4320 let deferred = [ValueType::Unestablished];
4321 let [left, right] = [left, right].map(|ty| match ty {
4322 ValueType::KeyedArray(keyed) => Ok((keyed.axes.as_deref(), keyed.elements.as_slice())),
4323 ValueType::ArrayKind(elements) => Ok((None, elements.as_slice())),
4324 ValueType::Unestablished => Ok((None, deferred.as_slice())),
4325 _ => Err(error(location, "zip_exact needs keyed arrays")),
4326 });
4327 let (left_axes, a) = left?;
4328 let (right_axes, b) = right?;
4329 if let (Some(a), Some(b)) = (left_axes, right_axes)
4330 && !same_axis_types(a, b, budget, location)?
4331 {
4332 return Err(error(
4333 location,
4334 "zip_exact needs identical axis rank and types",
4335 ));
4336 }
4337 let axes = match left_axes.or(right_axes) {
4338 Some(axes) => Some(copy_axis_types(axes, budget, location)?),
4339 None => None,
4340 };
4341 let mut elements = Vec::new();
4342 for a in a {
4343 for b in b {
4344 let mut pair = metadata_vec(2, budget, location)?;
4345 pair.push(copy_type(a, budget, location)?);
4346 pair.push(copy_type(b, budget, location)?);
4347 push_metadata(&mut elements, ValueType::Tuple(pair), budget, location)?;
4348 }
4349 }
4350 Ok(ValueType::KeyedArray(KeyedArrayType { axes, elements }))
4351}
4352
4353fn key_component_type(
4356 ty: &ValueType,
4357 location: &Location,
4358) -> Result<Option<ValueKeyType>, QueryError> {
4359 match ty {
4360 ValueType::Declared {
4361 actual,
4362 declaration,
4363 } => {
4364 if !matches!(
4365 actual.as_ref(),
4366 ValueType::Unestablished | ValueType::ScalarKind
4367 ) {
4368 return key_component_type(actual, location);
4369 }
4370 match underlying(declaration) {
4371 DeclaredType::Integer => Ok(Some(ValueKeyType::Integer)),
4372 DeclaredType::Member(id) if !matches!(actual.as_ref(), ValueType::ScalarKind) => {
4373 Ok(Some(ValueKeyType::Member(*id)))
4374 }
4375 _ => Err(error(location, "key components need integers or members")),
4376 }
4377 }
4378 ValueType::Integer | ValueType::ScalarKind => Ok(Some(ValueKeyType::Integer)),
4381 ValueType::Member(id) => Ok(Some(ValueKeyType::Member(*id))),
4382 ValueType::Unestablished => Ok(None),
4383 _ => Err(error(location, "key components need integers or members")),
4384 }
4385}
4386
4387fn merge_key_facts(
4388 facts: &mut Option<Vec<Option<ValueKeyType>>>,
4389 types: &[ValueType],
4390 budget: &mut Budget,
4391 location: &Location,
4392) -> Result<(), QueryError> {
4393 for ty in types {
4394 budget.charge(1, location.clone())?;
4395 let normalized = if matches!(ty, ValueType::Declared { .. }) {
4396 Some(shape_type(
4397 copy_type(ty, budget, location)?,
4398 budget,
4399 location,
4400 )?)
4401 } else {
4402 None
4403 };
4404 let ty = normalized.as_ref().unwrap_or(ty);
4405 let components = match ty {
4406 ValueType::Unestablished | ValueType::TupleKind => continue,
4407 ValueType::Tuple(types) if !types.is_empty() => {
4408 let mut result = metadata_vec(types.len(), budget, location)?;
4409 for ty in types {
4410 budget.charge(1, location.clone())?;
4411 result.push(key_component_type(ty, location)?);
4412 }
4413 result
4414 }
4415 ValueType::Integer | ValueType::ScalarKind | ValueType::Member(_) => {
4416 let mut result = metadata_vec(1, budget, location)?;
4417 result.push(key_component_type(ty, location)?);
4418 result
4419 }
4420 _ => {
4421 return Err(error(
4422 location,
4423 "key needs an integer, member or nonempty flat tuple",
4424 ));
4425 }
4426 };
4427 match facts {
4428 Some(previous) => {
4429 if previous.len() != components.len() {
4430 return Err(error(location, "key rank mismatch"));
4431 }
4432 for (previous, ty) in previous.iter_mut().zip(components) {
4433 budget.charge(1, location.clone())?;
4434 if previous.is_some() && ty.is_some() && *previous != ty {
4435 return Err(error(location, "incompatible keyed dimension type"));
4436 }
4437 if previous.is_none() {
4438 *previous = ty;
4439 }
4440 }
4441 }
4442 None => *facts = Some(components),
4443 }
4444 }
4445 Ok(())
4446}
4447
4448fn check_key_facts(
4449 facts: &Option<Vec<Option<ValueKeyType>>>,
4450 axes: &[ValueKeyType],
4451 budget: &mut Budget,
4452 location: &Location,
4453) -> Result<(), QueryError> {
4454 if let Some(facts) = facts {
4455 if facts.len() != axes.len() {
4456 return Err(error(location, "key rank mismatch"));
4457 }
4458 for (fact, axis) in facts.iter().zip(axes) {
4459 budget.charge(1, location.clone())?;
4460 if fact.is_some_and(|fact| fact != *axis) {
4461 return Err(error(location, "incompatible keyed dimension type"));
4462 }
4463 }
4464 }
4465 Ok(())
4466}
4467
4468fn established_facts(
4469 facts: Option<Vec<Option<ValueKeyType>>>,
4470 budget: &mut Budget,
4471 location: &Location,
4472) -> Result<Option<Vec<ValueKeyType>>, QueryError> {
4473 let Some(facts) = facts else {
4474 return Ok(None);
4475 };
4476 let mut axes = metadata_vec(facts.len(), budget, location)?;
4477 for fact in facts {
4478 budget.charge(1, location.clone())?;
4479 let Some(fact) = fact else {
4480 return Ok(None);
4481 };
4482 axes.push(fact);
4483 }
4484 Ok(Some(axes))
4485}
4486
4487fn source_key_type(
4488 types: &[ValueType],
4489 budget: &mut Budget,
4490 location: &Location,
4491) -> Result<Option<ValueKeyType>, QueryError> {
4492 let mut result = None;
4493 for ty in types {
4494 budget.charge(1, location.clone())?;
4495 let normalized = if matches!(ty, ValueType::Declared { .. }) {
4496 Some(shape_type(
4497 copy_type(ty, budget, location)?,
4498 budget,
4499 location,
4500 )?)
4501 } else {
4502 None
4503 };
4504 let ty = normalized.as_ref().unwrap_or(ty);
4505 let keys = match ty {
4506 ValueType::Array(ArrayType::Positions(elements) | ArrayType::Elements(elements))
4507 | ValueType::ArrayKind(elements)
4508 | ValueType::KeyedArray(KeyedArrayType { elements, .. }) => {
4509 let mut key_type = None;
4510 for ty in elements {
4511 budget.charge(1, location.clone())?;
4512 let ty = key_component_type(ty, location)?;
4513 if key_type.is_some() && ty.is_some() && key_type != ty {
4514 return Err(error(location, "incompatible axis source type"));
4515 }
4516 if ty.is_some() {
4517 key_type = ty;
4518 }
4519 }
4520 key_type
4521 }
4522 ValueType::Set(Some(SetElementType::Integer)) => Some(ValueKeyType::Integer),
4523 ValueType::Set(Some(SetElementType::Member(id))) => Some(ValueKeyType::Member(*id)),
4524 ValueType::Set(None) | ValueType::Unestablished => None,
4525 _ => {
4526 return Err(error(
4527 location,
4528 "axis source needs an integer or member array/set",
4529 ));
4530 }
4531 };
4532 if result.is_some() && keys.is_some() && result != keys {
4533 return Err(error(location, "incompatible axis source type"));
4534 }
4535 if keys.is_some() {
4536 result = keys;
4537 }
4538 }
4539 Ok(result)
4540}
4541
4542fn check_source_facts(
4543 facts: &Option<Vec<Option<ValueKeyType>>>,
4544 sources: &[Option<ValueKeyType>],
4545 budget: &mut Budget,
4546 location: &Location,
4547) -> Result<(), QueryError> {
4548 if let Some(facts) = facts {
4549 if facts.len() != sources.len() {
4550 return Err(error(location, "axis source rank mismatch"));
4551 }
4552 for (fact, source) in facts.iter().zip(sources) {
4553 budget.charge(1, location.clone())?;
4554 if fact.is_some() && source.is_some() && *fact != *source {
4555 return Err(error(location, "incompatible axis source type"));
4556 }
4557 }
4559 }
4560 Ok(())
4561}
4562
4563#[derive(Clone, Copy)]
4564enum ScalarRequirement {
4565 String,
4566 Boolean,
4567 Integer,
4568 Numeric,
4569}
4570
4571pub(crate) fn predicate_types(
4572 types: &[ValueType],
4573 budget: &mut Budget,
4574 range: &std::ops::Range<usize>,
4575) -> Result<(), QueryError> {
4576 let location = Location::Query(range.clone());
4577 for ty in types {
4578 budget.charge(1, location.clone())?;
4579 require_type(ty, ScalarRequirement::Boolean, &location)?;
4580 }
4581 Ok(())
4582}
4583
4584fn require_type(
4585 ty: &ValueType,
4586 requirement: ScalarRequirement,
4587 location: &Location,
4588) -> Result<(), QueryError> {
4589 if let ValueType::Declared {
4590 actual,
4591 declaration,
4592 } = ty
4593 {
4594 if !matches!(
4595 actual.as_ref(),
4596 ValueType::Unestablished | ValueType::ScalarKind
4597 ) {
4598 return require_type(actual, requirement, location);
4599 }
4600 let expected = underlying(declaration);
4601 let accepted = match requirement {
4602 ScalarRequirement::String => matches!(expected, DeclaredType::String),
4603 ScalarRequirement::Boolean => matches!(expected, DeclaredType::Boolean),
4604 ScalarRequirement::Integer => matches!(expected, DeclaredType::Integer),
4605 ScalarRequirement::Numeric => {
4606 matches!(expected, DeclaredType::Integer | DeclaredType::Float)
4607 }
4608 };
4609 if accepted {
4610 return Ok(());
4611 }
4612 return Err(error(location, "incompatible declared operand type"));
4613 }
4614 let accepted = matches!(ty, ValueType::Unestablished | ValueType::ScalarKind)
4615 || match requirement {
4616 ScalarRequirement::String => matches!(ty, ValueType::String),
4617 ScalarRequirement::Boolean => matches!(ty, ValueType::Boolean),
4618 ScalarRequirement::Integer => matches!(ty, ValueType::Integer),
4619 ScalarRequirement::Numeric => matches!(ty, ValueType::Integer | ValueType::Float),
4620 };
4621 if !accepted {
4622 return Err(error(
4623 location,
4624 match requirement {
4625 ScalarRequirement::String => "dictionary key needs a present string",
4626 ScalarRequirement::Boolean => "condition needs a fixed Boolean",
4627 ScalarRequirement::Integer => "operation needs integers",
4628 ScalarRequirement::Numeric => "operation needs numeric operands",
4629 },
4630 ));
4631 }
4632 Ok(())
4633}
4634
4635fn set_value_type(ty: Option<SetElementType>) -> ValueType {
4636 match ty {
4637 Some(SetElementType::Boolean) => ValueType::Boolean,
4638 Some(SetElementType::Integer) => ValueType::Integer,
4639 Some(SetElementType::Float) => ValueType::Float,
4640 Some(SetElementType::Member(id)) => ValueType::Member(id),
4641 None => ValueType::Unestablished,
4642 }
4643}
4644
4645fn merge_set_types(
4646 element_type: &mut Option<SetElementType>,
4647 types: &[ValueType],
4648 budget: &mut Budget,
4649 location: &Location,
4650) -> Result<(), QueryError> {
4651 for ty in types {
4652 budget.charge(1, location.clone())?;
4653 let normalized = if matches!(ty, ValueType::Declared { .. }) {
4654 Some(shape_type(
4655 copy_type(ty, budget, location)?,
4656 budget,
4657 location,
4658 )?)
4659 } else {
4660 None
4661 };
4662 let ty = normalized.as_ref().unwrap_or(ty);
4663 let ty = match ty {
4664 ValueType::Boolean => SetElementType::Boolean,
4665 ValueType::Integer => SetElementType::Integer,
4666 ValueType::Float => SetElementType::Float,
4667 ValueType::Member(id) => SetElementType::Member(*id),
4668 ValueType::Unestablished => continue,
4669 _ => {
4670 return Err(error(
4671 location,
4672 "set elements must have one supported scalar type",
4673 ));
4674 }
4675 };
4676 if element_type.is_some_and(|previous| previous != ty) {
4677 return Err(error(
4678 location,
4679 "set elements must have one supported scalar type",
4680 ));
4681 }
4682 *element_type = Some(ty);
4683 }
4684 Ok(())
4685}
4686
4687fn compatible_types(
4688 left: &ValueType,
4689 right: &ValueType,
4690 budget: &mut Budget,
4691 location: &Location,
4692) -> Result<bool, QueryError> {
4693 budget.within_evaluation(1, location.clone(), |budget| {
4694 budget.charge(1, location.clone())?;
4695 if let ValueType::Declared { declaration, .. } = left {
4696 if let ValueType::Declared {
4697 declaration: expected,
4698 ..
4699 } = right
4700 {
4701 return compatible_declarations(declaration, expected, budget, location);
4702 }
4703 let expected = DeclaredType::Optional(Box::new(copy_declaration(
4704 underlying(declaration),
4705 budget,
4706 location,
4707 )?));
4708 check_known_type(right, &expected, budget, location)?;
4709 return Ok(true);
4710 }
4711 if matches!(right, ValueType::Declared { .. }) {
4712 return compatible_types(right, left, budget, location);
4713 }
4714 Ok(match (left, right) {
4715 (ValueType::Absent, _) | (_, ValueType::Absent) => true,
4716 (ValueType::Unestablished, _) | (_, ValueType::Unestablished) => true,
4717 (
4718 ValueType::ScalarKind,
4719 ValueType::ScalarKind
4720 | ValueType::Boolean
4721 | ValueType::Integer
4722 | ValueType::Float
4723 | ValueType::String,
4724 )
4725 | (
4726 ValueType::Boolean | ValueType::Integer | ValueType::Float | ValueType::String,
4727 ValueType::ScalarKind,
4728 ) => true,
4729 (ValueType::Boolean, ValueType::Boolean)
4730 | (ValueType::Integer, ValueType::Integer)
4731 | (ValueType::Float, ValueType::Float)
4732 | (ValueType::String, ValueType::String) => true,
4733 (ValueType::RecordKind, ValueType::RecordKind | ValueType::Record(_))
4734 | (ValueType::Record(_), ValueType::RecordKind)
4735 | (ValueType::TupleKind, ValueType::TupleKind | ValueType::Tuple(_))
4736 | (ValueType::Tuple(_), ValueType::TupleKind) => true,
4737 (ValueType::Member(a), ValueType::Member(b)) => a == b,
4738 (ValueType::ArrayKind(a), ValueType::ArrayKind(b))
4739 | (
4740 ValueType::ArrayKind(a),
4741 ValueType::Array(ArrayType::Positions(b) | ArrayType::Elements(b)),
4742 )
4743 | (
4744 ValueType::Array(ArrayType::Positions(a) | ArrayType::Elements(a)),
4745 ValueType::ArrayKind(b),
4746 ) => compatible_keyed_elements(a, b, budget, location)?,
4747 (ValueType::ArrayKind(a), ValueType::KeyedArray(b)) => {
4748 compatible_keyed_elements(a, &b.elements, budget, location)?
4749 }
4750 (ValueType::KeyedArray(a), ValueType::ArrayKind(b)) => {
4751 compatible_keyed_elements(&a.elements, b, budget, location)?
4752 }
4753 (ValueType::KeyedArray(a), ValueType::KeyedArray(b)) => {
4754 if let (Some(a), Some(b)) = (&a.axes, &b.axes)
4755 && !same_axis_types(a, b, budget, location)?
4756 {
4757 return Ok(false);
4758 }
4759 compatible_keyed_elements(&a.elements, &b.elements, budget, location)?
4760 }
4761 (ValueType::Set(a), ValueType::Set(b)) => a.is_none() || b.is_none() || a == b,
4762 (ValueType::Tuple(a), ValueType::Tuple(b)) => {
4763 if a.len() != b.len() {
4764 return Ok(false);
4765 }
4766 for (a, b) in a.iter().zip(b) {
4767 if !compatible_types(a, b, budget, location)? {
4768 return Ok(false);
4769 }
4770 }
4771 true
4772 }
4773 (ValueType::Array(a), ValueType::Array(b)) => {
4774 match (a, b) {
4775 (ArrayType::Positions(a), ArrayType::Positions(b)) => {
4776 for (a, b) in a.iter().zip(b) {
4777 if !compatible_types(a, b, budget, location)? {
4778 return Ok(false);
4779 }
4780 }
4781 }
4782 (
4783 ArrayType::Positions(a) | ArrayType::Elements(a),
4784 ArrayType::Positions(b) | ArrayType::Elements(b),
4785 ) => {
4786 for a in a {
4787 for b in b {
4788 if !compatible_types(a, b, budget, location)? {
4789 return Ok(false);
4790 }
4791 }
4792 }
4793 }
4794 }
4795 true
4796 }
4797 (ValueType::Record(a), ValueType::Record(b)) => {
4798 if a.len() != b.len() {
4799 return Ok(false);
4800 }
4801 for (label, ty) in a {
4802 let mut other = None;
4803 for (other_label, other_ty) in b {
4804 budget.charge(
4805 sum(1, label.len().min(other_label.len()), location)?,
4806 location.clone(),
4807 )?;
4808 if label == other_label {
4809 other = Some(other_ty);
4810 break;
4811 }
4812 }
4813 let Some(other) = other else {
4814 return Ok(false);
4815 };
4816 if !compatible_types(ty, other, budget, location)? {
4817 return Ok(false);
4818 }
4819 }
4820 true
4821 }
4822 _ => false,
4823 })
4824 })
4825}
4826
4827fn compatible_keyed_elements(
4830 left: &[ValueType],
4831 right: &[ValueType],
4832 budget: &mut Budget,
4833 location: &Location,
4834) -> Result<bool, QueryError> {
4835 if left.is_empty() || right.is_empty() {
4836 return Ok(true);
4837 }
4838 for (types, alternatives) in [(left, right), (right, left)] {
4839 for ty in types {
4840 let mut compatible = false;
4841 for alternative in alternatives {
4842 if compatible_types(ty, alternative, budget, location)? {
4843 compatible = true;
4844 break;
4845 }
4846 }
4847 if !compatible {
4848 return Ok(false);
4849 }
4850 }
4851 }
4852 Ok(true)
4853}
4854
4855fn binary_type(
4856 operator: ValueBinaryOperator,
4857 left: &ValueType,
4858 right: &ValueType,
4859 budget: &mut Budget,
4860 location: &Location,
4861) -> Result<ValueType, QueryError> {
4862 if !matches!(
4863 operator,
4864 ValueBinaryOperator::Equal | ValueBinaryOperator::NotEqual
4865 ) && (matches!(left, ValueType::Declared { .. })
4866 || matches!(right, ValueType::Declared { .. }))
4867 {
4868 let left = shape_type(copy_type(left, budget, location)?, budget, location)?;
4869 let right = shape_type(copy_type(right, budget, location)?, budget, location)?;
4870 return binary_type(operator, &left, &right, budget, location);
4871 }
4872 match operator {
4873 ValueBinaryOperator::Add
4874 | ValueBinaryOperator::Subtract
4875 | ValueBinaryOperator::Multiply
4876 | ValueBinaryOperator::Divide
4877 | ValueBinaryOperator::Less
4878 | ValueBinaryOperator::LessEqual
4879 | ValueBinaryOperator::Greater
4880 | ValueBinaryOperator::GreaterEqual => {
4881 require_type(left, ScalarRequirement::Numeric, location)?;
4882 require_type(right, ScalarRequirement::Numeric, location)?;
4883 Ok(match operator {
4884 ValueBinaryOperator::Less
4885 | ValueBinaryOperator::LessEqual
4886 | ValueBinaryOperator::Greater
4887 | ValueBinaryOperator::GreaterEqual => ValueType::Boolean,
4888 ValueBinaryOperator::Divide => ValueType::Float,
4889 _ if matches!(left, ValueType::Float) || matches!(right, ValueType::Float) => {
4890 ValueType::Float
4891 }
4892 _ if matches!(left, ValueType::ScalarKind)
4893 || matches!(right, ValueType::ScalarKind) =>
4894 {
4895 ValueType::ScalarKind
4896 }
4897 _ if matches!(left, ValueType::Unestablished)
4898 || matches!(right, ValueType::Unestablished) =>
4899 {
4900 ValueType::Unestablished
4901 }
4902 _ => ValueType::Integer,
4903 })
4904 }
4905 ValueBinaryOperator::IntegerDivide | ValueBinaryOperator::Modulo => {
4906 require_type(left, ScalarRequirement::Integer, location)?;
4907 require_type(right, ScalarRequirement::Integer, location)?;
4908 Ok(ValueType::Integer)
4909 }
4910 ValueBinaryOperator::Contains
4911 | ValueBinaryOperator::StartsWith
4912 | ValueBinaryOperator::EndsWith => {
4913 if !matches!(
4914 left,
4915 ValueType::String | ValueType::ScalarKind | ValueType::Unestablished
4916 ) || !matches!(
4917 right,
4918 ValueType::String | ValueType::ScalarKind | ValueType::Unestablished
4919 ) {
4920 return Err(string_operand_error(budget, location));
4921 }
4922 Ok(ValueType::Boolean)
4923 }
4924 ValueBinaryOperator::And | ValueBinaryOperator::Or => {
4925 require_type(left, ScalarRequirement::Boolean, location)?;
4926 require_type(right, ScalarRequirement::Boolean, location)?;
4927 Ok(ValueType::Boolean)
4928 }
4929 ValueBinaryOperator::Equal | ValueBinaryOperator::NotEqual => {
4930 if !compatible_types(left, right, budget, location)? {
4931 return Err(error(location, "equality needs compatible types"));
4932 }
4933 Ok(ValueType::Boolean)
4934 }
4935 ValueBinaryOperator::In => {
4936 let mut element_type = None;
4937 let deferred_primitive = matches!(left, ValueType::ScalarKind)
4938 && matches!(
4939 right,
4940 ValueType::Unestablished
4941 | ValueType::Set(
4942 None | Some(
4943 SetElementType::Boolean
4944 | SetElementType::Integer
4945 | SetElementType::Float
4946 )
4947 )
4948 );
4949 if deferred_primitive {
4950 budget.charge(1, location.clone())?;
4951 } else {
4952 merge_set_types(
4953 &mut element_type,
4954 std::slice::from_ref(left),
4955 budget,
4956 location,
4957 )?;
4958 }
4959 match right {
4960 ValueType::Set(ty)
4961 if ty.is_none() || element_type.is_none() || ty == &element_type =>
4962 {
4963 Ok(ValueType::Boolean)
4964 }
4965 ValueType::Unestablished => Ok(ValueType::Boolean),
4966 _ => Err(error(
4967 location,
4968 "membership needs a compatible supported set",
4969 )),
4970 }
4971 }
4972 ValueBinaryOperator::Concatenate => match (left, right) {
4973 (ValueType::String, ValueType::String)
4974 | (
4975 ValueType::ScalarKind,
4976 ValueType::String | ValueType::ScalarKind | ValueType::Unestablished,
4977 )
4978 | (ValueType::String | ValueType::Unestablished, ValueType::ScalarKind) => {
4979 Ok(ValueType::String)
4980 }
4981 (ValueType::Array(a), ValueType::Array(b)) => {
4982 let (ArrayType::Positions(a_entries) | ArrayType::Elements(a_entries)) = a;
4983 let (ArrayType::Positions(b_entries) | ArrayType::Elements(b_entries)) = b;
4984 let mut entries = copy_types(a_entries, budget, location)?;
4985 append_types(
4986 &mut entries,
4987 copy_types(b_entries, budget, location)?,
4988 budget,
4989 location,
4990 )?;
4991 Ok(ValueType::Array(
4992 if matches!((a, b), (ArrayType::Positions(_), ArrayType::Positions(_))) {
4993 ArrayType::Positions(entries)
4994 } else {
4995 ArrayType::Elements(entries)
4996 },
4997 ))
4998 }
4999 (ValueType::Unestablished, other) | (other, ValueType::Unestablished)
5000 if matches!(
5001 other,
5002 ValueType::Unestablished | ValueType::String | ValueType::Array(_)
5003 ) =>
5004 {
5005 Ok(ValueType::Unestablished)
5006 }
5007 _ => Err(error(
5008 location,
5009 "concatenation needs two strings or two positional arrays",
5010 )),
5011 },
5012 }
5013}
5014
5015pub(crate) fn boolean(value: ConstructedValue, location: &Location) -> Result<bool, QueryError> {
5016 if let ConstructedValue::Boolean(value) = value.into_data() {
5017 Ok(value)
5018 } else {
5019 Err(error(location, "condition needs a fixed Boolean"))
5020 }
5021}
5022
5023fn integer(value: ConstructedValue, location: &Location) -> Result<i64, QueryError> {
5024 if let ConstructedValue::Integer(value) = value.into_data() {
5025 Ok(value)
5026 } else {
5027 Err(error(location, "operation needs integers"))
5028 }
5029}
5030
5031fn scalar(
5032 value: ConstructedValue,
5033 budget: &mut Budget,
5034 location: &Location,
5035) -> Result<ConstructedValue, QueryError> {
5036 budget.charge_results(1, location.clone())?;
5037 Ok(value)
5038}
5039
5040fn exact_float(value: i64, location: &Location) -> Result<f64, QueryError> {
5041 let magnitude = value.unsigned_abs();
5042 let significant_bits = u64::BITS - magnitude.leading_zeros();
5043 if significant_bits > 53 && magnitude.trailing_zeros() < significant_bits - 53 {
5044 return Err(error(
5045 location,
5046 "integer cannot be represented exactly as binary64",
5047 ));
5048 }
5049 Ok(value as f64)
5052}
5053
5054fn numeric_float(value: ConstructedValue, location: &Location) -> Result<f64, QueryError> {
5055 match value.into_data() {
5056 ConstructedValue::Integer(value) => exact_float(value, location),
5057 ConstructedValue::Float(value) => {
5058 finite(value, location)?;
5059 Ok(value)
5060 }
5061 _ => Err(error(location, "operation needs numeric operands")),
5062 }
5063}
5064
5065fn unary(
5066 operator: ValueUnaryOperator,
5067 value: ConstructedValue,
5068 budget: &mut Budget,
5069 location: &Location,
5070) -> Result<ConstructedValue, QueryError> {
5071 let value = match (operator, value.into_data()) {
5072 (ValueUnaryOperator::Not, value) => ConstructedValue::Boolean(!boolean(value, location)?),
5073 (
5074 ValueUnaryOperator::Positive,
5075 value @ (ConstructedValue::Integer(_) | ConstructedValue::Float(_)),
5076 ) => value,
5077 (ValueUnaryOperator::Negative, ConstructedValue::Integer(value)) => {
5078 ConstructedValue::Integer(
5079 value
5080 .checked_neg()
5081 .ok_or_else(|| error(location, "integer overflow"))?,
5082 )
5083 }
5084 (ValueUnaryOperator::Negative, ConstructedValue::Float(value)) => {
5085 ConstructedValue::Float(-value)
5086 }
5087 _ => return Err(error(location, "operation needs numeric operands")),
5088 };
5089 scalar(value, budget, location)
5090}
5091
5092fn string_operand_error(budget: &mut Budget, location: &Location) -> QueryError {
5093 let message = "string matching needs two strings";
5094 match budget.charge(message.len(), location.clone()) {
5095 Ok(()) => error(location, message),
5096 Err(error) => error,
5097 }
5098}
5099
5100fn string_match_ref(
5101 operator: ValueBinaryOperator,
5102 left: &ConstructedValue,
5103 right: &ConstructedValue,
5104 budget: &mut Budget,
5105 location: &Location,
5106) -> Result<ConstructedValue, QueryError> {
5107 let (ConstructedValue::String(text), ConstructedValue::String(search)) =
5108 (left.data(), right.data())
5109 else {
5110 return Err(string_operand_error(budget, location));
5111 };
5112 let work = if search.is_empty() {
5115 0
5116 } else if operator == ValueBinaryOperator::Contains {
5117 sum(text.len(), search.len(), location)?
5118 } else {
5119 text.len().min(search.len())
5120 };
5121 budget.charge(work, location.clone())?;
5122 let matched = match operator {
5123 ValueBinaryOperator::Contains => text.contains(search.as_str()),
5124 ValueBinaryOperator::StartsWith => text.starts_with(search.as_str()),
5125 ValueBinaryOperator::EndsWith => text.ends_with(search.as_str()),
5126 _ => unreachable!("string matching operator"),
5127 };
5128 scalar(ConstructedValue::Boolean(matched), budget, location)
5129}
5130
5131fn binary(
5132 operator: ValueBinaryOperator,
5133 left: ConstructedValue,
5134 right: ConstructedValue,
5135 budget: &mut Budget,
5136 location: &Location,
5137) -> Result<ConstructedValue, QueryError> {
5138 let (left, right) = if matches!(
5139 operator,
5140 ValueBinaryOperator::Equal | ValueBinaryOperator::NotEqual
5141 ) {
5142 (left, right)
5143 } else {
5144 (
5145 expose(left, budget, location)?,
5146 expose(right, budget, location)?,
5147 )
5148 };
5149 match operator {
5150 ValueBinaryOperator::Contains
5151 | ValueBinaryOperator::StartsWith
5152 | ValueBinaryOperator::EndsWith => {
5153 string_match_ref(operator, &left, &right, budget, location)
5154 }
5155 ValueBinaryOperator::Concatenate => match (left, right) {
5156 (ConstructedValue::String(left), ConstructedValue::String(right)) => {
5157 let len = sum(left.len(), right.len(), location)?;
5158 allocation::<u8>(len, location)?;
5159 budget.charge(len, location.clone())?;
5160 budget.charge_results(1, location.clone())?;
5161 let mut value = String::with_capacity(len);
5162 value.push_str(&left);
5163 value.push_str(&right);
5164 Ok(ConstructedValue::String(value))
5165 }
5166 (ConstructedValue::Array(left), ConstructedValue::Array(right)) => {
5167 let len = sum(left.len(), right.len(), location)?;
5168 let mut values = admit_container(len, budget, location)?;
5169 values.extend(left);
5170 values.extend(right);
5171 Ok(ConstructedValue::Array(values))
5172 }
5173 _ => Err(error(
5174 location,
5175 "concatenation needs two strings or two positional arrays",
5176 )),
5177 },
5178 ValueBinaryOperator::Equal | ValueBinaryOperator::NotEqual => {
5179 let equal = equal_values(&left, &right, budget, location)?;
5180 scalar(
5181 ConstructedValue::Boolean(if operator == ValueBinaryOperator::Equal {
5182 equal
5183 } else {
5184 !equal
5185 }),
5186 budget,
5187 location,
5188 )
5189 }
5190 ValueBinaryOperator::In => {
5191 let ConstructedValue::Set {
5192 element_type,
5193 elements,
5194 } = right
5195 else {
5196 return Err(error(location, "membership needs a supported set"));
5197 };
5198 let ty = scalar_set_type(&left)
5199 .ok_or_else(|| error(location, "membership needs a supported scalar"))?;
5200 if element_type.is_some_and(|element_type| element_type != ty) {
5201 return Err(error(location, "membership element type mismatch"));
5202 }
5203 let mut found = false;
5204 for value in &elements {
5205 if compare_set_values(&left, value, budget, location)? == Ordering::Equal {
5206 found = true;
5207 break;
5208 }
5209 }
5210 scalar(ConstructedValue::Boolean(found), budget, location)
5211 }
5212 ValueBinaryOperator::And | ValueBinaryOperator::Or => {
5213 let a = boolean(left, location)?;
5214 let b = boolean(right, location)?;
5215 scalar(
5216 ConstructedValue::Boolean(if operator == ValueBinaryOperator::And {
5217 a && b
5218 } else {
5219 a || b
5220 }),
5221 budget,
5222 location,
5223 )
5224 }
5225 ValueBinaryOperator::IntegerDivide | ValueBinaryOperator::Modulo => {
5226 let a = integer(left, location)?;
5227 let b = integer(right, location)?;
5228 if b == 0 {
5229 return Err(error(location, "division by zero"));
5230 }
5231 let value = if operator == ValueBinaryOperator::IntegerDivide {
5232 a.checked_div(b)
5233 } else {
5234 a.checked_rem(b)
5235 }
5236 .ok_or_else(|| error(location, "integer overflow"))?;
5237 scalar(ConstructedValue::Integer(value), budget, location)
5238 }
5239 ValueBinaryOperator::Add
5240 | ValueBinaryOperator::Subtract
5241 | ValueBinaryOperator::Multiply => {
5242 if let (ConstructedValue::Integer(a), ConstructedValue::Integer(b)) = (&left, &right) {
5243 let value = match operator {
5244 ValueBinaryOperator::Add => a.checked_add(*b),
5245 ValueBinaryOperator::Subtract => a.checked_sub(*b),
5246 ValueBinaryOperator::Multiply => a.checked_mul(*b),
5247 _ => unreachable!("integer arithmetic"),
5248 }
5249 .ok_or_else(|| error(location, "integer overflow"))?;
5250 return scalar(ConstructedValue::Integer(value), budget, location);
5251 }
5252 float_operation(
5253 operator,
5254 numeric_float(left, location)?,
5255 numeric_float(right, location)?,
5256 budget,
5257 location,
5258 )
5259 }
5260 ValueBinaryOperator::Divide => float_operation(
5261 operator,
5262 numeric_float(left, location)?,
5263 numeric_float(right, location)?,
5264 budget,
5265 location,
5266 ),
5267 ValueBinaryOperator::Less
5268 | ValueBinaryOperator::LessEqual
5269 | ValueBinaryOperator::Greater
5270 | ValueBinaryOperator::GreaterEqual => {
5271 let order = if let (ConstructedValue::Integer(a), ConstructedValue::Integer(b)) =
5272 (&left, &right)
5273 {
5274 a.cmp(b)
5275 } else {
5276 numeric_float(left, location)?
5277 .partial_cmp(&numeric_float(right, location)?)
5278 .expect("finite numeric operands")
5279 };
5280 let value = match operator {
5281 ValueBinaryOperator::Less => order == Ordering::Less,
5282 ValueBinaryOperator::LessEqual => order != Ordering::Greater,
5283 ValueBinaryOperator::Greater => order == Ordering::Greater,
5284 ValueBinaryOperator::GreaterEqual => order != Ordering::Less,
5285 _ => unreachable!("comparison"),
5286 };
5287 scalar(ConstructedValue::Boolean(value), budget, location)
5288 }
5289 }
5290}
5291
5292fn float_operation(
5293 operator: ValueBinaryOperator,
5294 a: f64,
5295 b: f64,
5296 budget: &mut Budget,
5297 location: &Location,
5298) -> Result<ConstructedValue, QueryError> {
5299 let (value, mathematically_zero) = match operator {
5300 ValueBinaryOperator::Add => (a + b, a == -b),
5301 ValueBinaryOperator::Subtract => (a - b, a == b),
5302 ValueBinaryOperator::Multiply => (a * b, a == 0.0 || b == 0.0),
5303 ValueBinaryOperator::Divide => {
5304 if b == 0.0 {
5305 return Err(error(location, "division by zero"));
5306 }
5307 (a / b, a == 0.0)
5308 }
5309 _ => unreachable!("floating arithmetic"),
5310 };
5311 finite(value, location)?;
5312 if value == 0.0 && !mathematically_zero {
5313 return Err(error(location, "nonzero floating result rounded to zero"));
5314 }
5315 scalar(ConstructedValue::Float(value), budget, location)
5316}
5317
5318fn equal_values(
5319 left: &ConstructedValue,
5320 right: &ConstructedValue,
5321 budget: &mut Budget,
5322 location: &Location,
5323) -> Result<bool, QueryError> {
5324 budget.within_evaluation(1, location.clone(), |budget| {
5325 budget.charge(1, location.clone())?;
5326 if declaration(left).is_some() || declaration(right).is_some() {
5327 let a = constructed_value_type(left, budget, location.clone())?;
5328 let b = constructed_value_type(right, budget, location.clone())?;
5329 if !compatible_types(&a, &b, budget, location)? {
5330 return Err(error(
5331 location,
5332 "equality needs compatible underlying types",
5333 ));
5334 }
5335 }
5336 Ok(match (left.data(), right.data()) {
5337 (ConstructedValue::Absent, ConstructedValue::Absent) => true,
5338 (ConstructedValue::Absent, _) | (_, ConstructedValue::Absent) => false,
5339 (ConstructedValue::Boolean(a), ConstructedValue::Boolean(b)) => a == b,
5340 (ConstructedValue::Integer(a), ConstructedValue::Integer(b)) => a == b,
5341 (ConstructedValue::Float(a), ConstructedValue::Float(b)) => a == b,
5342 (ConstructedValue::String(a), ConstructedValue::String(b)) => {
5343 budget.charge(a.len().min(b.len()), location.clone())?;
5344 a == b
5345 }
5346 (ConstructedValue::Member(a), ConstructedValue::Member(b)) => {
5347 check_member_pair(a, b, budget, location)?;
5348 if a.type_id != b.type_id {
5349 return Err(error(location, "equality needs compatible nominal types"));
5350 }
5351 a.member_id == b.member_id
5352 }
5353 (
5354 ConstructedValue::KeyedArray {
5355 axes: a,
5356 entries: a_entries,
5357 },
5358 ConstructedValue::KeyedArray {
5359 axes: b,
5360 entries: b_entries,
5361 },
5362 ) => {
5363 if a.len() != b.len() || a_entries.len() != b_entries.len() {
5364 return Ok(false);
5365 }
5366 for (a, b) in a.iter().zip(b) {
5367 budget.charge(1, location.clone())?;
5368 if a.key_type != b.key_type {
5369 return Err(error(location, "equality needs compatible keyed types"));
5370 }
5371 if !equal_keys(&a.keys, &b.keys, budget, location)? {
5372 return Ok(false);
5373 }
5374 }
5375 for (a, b) in a_entries.iter().zip(b_entries) {
5376 budget.charge(1, location.clone())?;
5377 if !equal_keys(&a.key, &b.key, budget, location)?
5378 || !equal_values(&a.value, &b.value, budget, location)?
5379 {
5380 return Ok(false);
5381 }
5382 }
5383 true
5384 }
5385 (ConstructedValue::Array(a), ConstructedValue::Array(b))
5386 | (ConstructedValue::Tuple(a), ConstructedValue::Tuple(b)) => {
5387 if a.len() != b.len() {
5388 return Ok(false);
5389 }
5390 for (a, b) in a.iter().zip(b) {
5391 if !equal_values(a, b, budget, location)? {
5392 return Ok(false);
5393 }
5394 }
5395 true
5396 }
5397 (
5398 ConstructedValue::Set {
5399 element_type: a_type,
5400 elements: a,
5401 },
5402 ConstructedValue::Set {
5403 element_type: b_type,
5404 elements: b,
5405 },
5406 ) => {
5407 if matches!((a_type, b_type), (Some(a), Some(b)) if a != b) {
5408 return Err(error(location, "equality needs compatible set types"));
5409 }
5410 if a.len() != b.len() {
5411 return Ok(false);
5412 }
5413 for value in a {
5414 let mut found = false;
5415 for other in b {
5416 if equal_values(value, other, budget, location)? {
5417 found = true;
5418 break;
5419 }
5420 }
5421 if !found {
5422 return Ok(false);
5423 }
5424 }
5425 true
5426 }
5427 (ConstructedValue::Record(a), ConstructedValue::Record(b)) => {
5428 if a.len() != b.len() {
5429 return Ok(false);
5430 }
5431 for (label, value) in a {
5432 let mut other = None;
5433 for (other_label, other_value) in b {
5434 budget.charge(
5435 sum(1, label.len().min(other_label.len()), location)?,
5436 location.clone(),
5437 )?;
5438 if label == other_label {
5439 other = Some(other_value);
5440 break;
5441 }
5442 }
5443 let Some(other) = other else {
5444 return Ok(false);
5445 };
5446 if !equal_values(value, other, budget, location)? {
5447 return Ok(false);
5448 }
5449 }
5450 true
5451 }
5452 _ => return Err(error(location, "equality needs compatible types")),
5453 })
5454 })
5455}
5456
5457fn run<'a, Q, H: ValueQueryHost<Q> + ?Sized>(
5458 expression: &'a PreparedValue<'a, Q>,
5459 queries: &mut H,
5460 locals: &mut Frame<'a>,
5461 budget: &mut Budget,
5462 check_value: &mut impl FnMut(&ConstructedValue, &mut Budget, &Location) -> Result<(), QueryError>,
5463) -> Result<ConstructedValue, H::Error> {
5464 let value = run_value(expression, queries, locals, budget, check_value)?;
5465 let location = Location::Query(expression.range.clone());
5466 budget.within_evaluation(1, location.clone(), |budget| {
5467 Ok(value.materialize(budget, &location)?)
5468 })
5469}
5470
5471struct PreparedArguments<'a, 'f, Q, F> {
5472 arguments: &'a [PreparedArgument<'a, Q>],
5473 frames: Vec<Frame<'a>>,
5474 check_value: &'f mut F,
5475}
5476impl<Q, E, F> ValueArguments<Q, E> for PreparedArguments<'_, '_, Q, F>
5477where
5478 E: From<QueryError>,
5479 F: FnMut(&ConstructedValue, &mut Budget, &Location) -> Result<(), QueryError>,
5480{
5481 fn evaluate(
5482 &mut self,
5483 expression: &ValueExpression<Q>,
5484 queries: &mut dyn ValueQueryHost<Q, Error = E>,
5485 budget: &mut Budget,
5486 ) -> Result<ConstructedRead, E> {
5487 let index = self
5488 .arguments
5489 .iter()
5490 .position(|argument| std::ptr::eq(argument.source, expression))
5491 .ok_or_else(|| {
5492 QueryError::query(
5493 expression.range.clone(),
5494 "argument was not prepared in this invocation",
5495 )
5496 })?;
5497 let value = run_value(
5498 &self.arguments[index].expression,
5499 queries,
5500 &mut self.frames[index],
5501 budget,
5502 self.check_value,
5503 )?;
5504 Ok(ConstructedRead {
5505 value,
5506 location: Location::Query(expression.range.clone()),
5507 declaration: None,
5508 })
5509 }
5510}
5511
5512fn run_value<'a, Q, H: ValueQueryHost<Q> + ?Sized>(
5513 expression: &'a PreparedValue<'a, Q>,
5514 queries: &mut H,
5515 locals: &mut Frame<'a>,
5516 budget: &mut Budget,
5517 check_value: &mut impl FnMut(&ConstructedValue, &mut Budget, &Location) -> Result<(), QueryError>,
5518) -> Result<RuntimeValue, H::Error> {
5519 execution::run_scalar(expression, queries, locals, budget, check_value)
5520}
5521
5522fn entries(
5523 array: ConstructedValue,
5524 budget: &mut Budget,
5525 location: &Location,
5526) -> Result<ConstructedValue, QueryError> {
5527 let array = expose(array, budget, location)?;
5528 let ConstructedValue::KeyedArray { axes, entries } = array else {
5529 return Err(error(location, "entries needs a keyed array"));
5530 };
5531 check_keyed(&axes, &entries, budget, location)?;
5532 let mut records = admit_container(entries.len(), budget, location)?;
5533 for entry in entries {
5534 budget.within_evaluation(2, location.clone(), |budget| {
5536 copy_size(&entry.value, budget, location).map(|_| ())
5537 })?;
5538 let key = if entry.key.len() == 1 {
5539 budget.charge(1, location.clone())?;
5540 key_value(entry.key.into_iter().next().expect("rank one"))
5541 } else {
5542 let mut components = admit_container(entry.key.len(), budget, location)?;
5543 for key in entry.key {
5544 budget.charge(1, location.clone())?;
5545 components.push(key_value(key));
5546 }
5547 ConstructedValue::Tuple(components)
5548 };
5549 budget.within_evaluation(2, location.clone(), |budget| {
5550 copy_size(&key, budget, location).map(|_| ())
5551 })?;
5552 let mut fields = admit_container(2, budget, location)?;
5553 budget.charge("key".len() + "value".len(), location.clone())?;
5554 fields.push(("key".into(), key));
5555 fields.push(("value".into(), entry.value));
5556 records.push(ConstructedValue::Record(fields));
5557 }
5558 Ok(ConstructedValue::Array(records))
5559}
5560
5561fn zip_exact(
5562 left: ConstructedValue,
5563 right: ConstructedValue,
5564 budget: &mut Budget,
5565 location: &Location,
5566) -> Result<ConstructedValue, QueryError> {
5567 let left = expose(left, budget, location)?;
5568 let right = expose(right, budget, location)?;
5569 let (
5570 ConstructedValue::KeyedArray {
5571 axes,
5572 entries: left,
5573 },
5574 ConstructedValue::KeyedArray {
5575 axes: right_axes,
5576 entries: right,
5577 },
5578 ) = (left, right)
5579 else {
5580 return Err(error(location, "zip_exact needs keyed arrays"));
5581 };
5582 check_keyed(&axes, &left, budget, location)?;
5583 check_keyed(&right_axes, &right, budget, location)?;
5584 budget.charge(1, location.clone())?;
5585 if axes.len() != right_axes.len() {
5586 return Err(error(location, "zip_exact needs identical ordered axes"));
5587 }
5588 for (a, b) in axes.iter().zip(&right_axes) {
5589 budget.charge(1, location.clone())?;
5590 if a.key_type != b.key_type || !equal_keys(&a.keys, &b.keys, budget, location)? {
5591 return Err(error(location, "zip_exact needs identical ordered axes"));
5592 }
5593 }
5594 budget.charge(1, location.clone())?;
5595 if left.len() != right.len() {
5596 return Err(error(location, "zip_exact needs identical present keys"));
5597 }
5598 let mut entries = admit_container(left.len(), budget, location)?;
5599 for (a, b) in left.into_iter().zip(right) {
5600 if !equal_keys(&a.key, &b.key, budget, location)? {
5601 return Err(error(location, "zip_exact needs identical present keys"));
5602 }
5603 budget.within_evaluation(2, location.clone(), |budget| {
5605 copy_size(&a.value, budget, location)?;
5606 copy_size(&b.value, budget, location).map(|_| ())
5607 })?;
5608 let mut pair = admit_container(2, budget, location)?;
5609 pair.push(a.value);
5610 pair.push(b.value);
5611 entries.push(KeyedEntry {
5612 key: a.key,
5613 value: ConstructedValue::Tuple(pair),
5614 });
5615 }
5616 Ok(ConstructedValue::KeyedArray { axes, entries })
5617}
5618
5619fn group_by(
5620 rows: ConstructedValue,
5621 field: &Located<String>,
5622 budget: &mut Budget,
5623 location: &Location,
5624) -> Result<ConstructedValue, QueryError> {
5625 let rows = expose(rows, budget, location)?;
5626 let ConstructedValue::Array(rows) = rows else {
5627 return Err(error(location, "group_by needs a positional array"));
5628 };
5629 let selector = Location::Query(field.range.clone());
5630 let mut groups = admit_container(0, budget, location)?;
5631 let mut key_type = None;
5632 for row in rows {
5633 budget.charge(1, location.clone())?;
5634 let ConstructedValue::Record(fields) = row.data() else {
5635 return Err(error(location, "group_by needs record rows"));
5636 };
5637 budget.within_evaluation(2, location.clone(), |budget| {
5640 copy_size(&row, budget, location).map(|_| ())
5641 })?;
5642 let mut key = None;
5643 for (label, value) in fields {
5644 budget.charge(
5645 sum(1, label.len().min(field.value.len()), &selector)?,
5646 selector.clone(),
5647 )?;
5648 if label == &field.value {
5649 key = Some(value);
5650 break;
5651 }
5652 }
5653 let Some(key) = key else {
5654 admit_diagnostic("missing record field: ", &field.value, budget, &selector)?;
5655 return Err(error(
5656 &selector,
5657 format!("missing record field: {}", field.value),
5658 ));
5659 };
5660 let ty = match key.data() {
5661 ConstructedValue::Integer(_) => ValueType::Integer,
5662 ConstructedValue::String(_) => ValueType::String,
5663 ConstructedValue::Member(member) => ValueType::Member(member.type_id),
5664 _ => {
5665 return Err(error(
5666 &selector,
5667 "group_by keys need integers, strings or nominal members",
5668 ));
5669 }
5670 };
5671 budget.charge(1, selector.clone())?;
5672 if key_type.as_ref().is_some_and(|previous| previous != &ty) {
5673 return Err(error(&selector, "group_by needs homogeneous key types"));
5674 }
5675 key_type = Some(ty);
5676 let mut matched = None;
5677 for (index, group) in groups.iter().enumerate() {
5678 let ConstructedValue::Record(fields) = group else {
5679 unreachable!("group record")
5680 };
5681 if equal_values(key, &fields[0].1, budget, &selector)? {
5682 matched = Some(index);
5683 break;
5684 }
5685 }
5686 let index = match matched {
5687 Some(index) => index,
5688 None => {
5689 let key = copy_constructed_value(key, budget, selector.clone())?;
5690 let mut fields = admit_container(2, budget, location)?;
5691 budget.charge("key".len() + "items".len(), location.clone())?;
5692 fields.push(("key".into(), key));
5693 fields.push((
5694 "items".into(),
5695 ConstructedValue::Array(admit_container(0, budget, location)?),
5696 ));
5697 allocation::<ConstructedValue>(sum(groups.len(), 1, location)?, location)?;
5698 budget.charge(1, location.clone())?;
5699 budget.charge_results(1, location.clone())?;
5700 groups.push(ConstructedValue::Record(fields));
5701 groups.len() - 1
5702 }
5703 };
5704 let ConstructedValue::Record(fields) = &mut groups[index] else {
5705 unreachable!("group record")
5706 };
5707 let ConstructedValue::Array(items) = &mut fields[1].1 else {
5708 unreachable!("group items")
5709 };
5710 allocation::<ConstructedValue>(sum(items.len(), 1, location)?, location)?;
5711 budget.charge(1, location.clone())?;
5712 budget.charge_results(1, location.clone())?;
5713 items.push(row);
5714 }
5715 Ok(ConstructedValue::Array(groups))
5716}
5717
5718fn filter_elements(
5720 ty: ValueType,
5721 budget: &mut Budget,
5722 location: &Location,
5723) -> Result<Vec<ValueType>, QueryError> {
5724 match shape_type(ty, budget, location)? {
5725 ValueType::Array(ArrayType::Positions(elements) | ArrayType::Elements(elements))
5726 | ValueType::ArrayKind(elements) => Ok(elements),
5727 ValueType::Unestablished => one_type(ValueType::Unestablished, budget, location),
5728 _ => Err(error(location, "filter needs a positional array")),
5729 }
5730}
5731
5732fn scalar_pipeline_elements(
5734 ty: ValueType,
5735 tally: bool,
5736 budget: &mut Budget,
5737 location: &Location,
5738) -> Result<Vec<ValueType>, QueryError> {
5739 let elements = match shape_type(ty, budget, location)? {
5740 ValueType::Array(ArrayType::Positions(elements) | ArrayType::Elements(elements))
5741 | ValueType::ArrayKind(elements) => elements,
5742 ValueType::Unestablished => return one_type(ValueType::Unestablished, budget, location),
5743 _ => return Err(error(location, "collection stage needs a positional array")),
5744 };
5745 let mut element_type = None;
5746 for element in &elements {
5747 budget.charge(1, location.clone())?;
5748 let ty = shape_type(copy_type(element, budget, location)?, budget, location)?;
5749 if matches!(ty, ValueType::Unestablished) {
5750 continue;
5751 }
5752 if tally {
5753 if ty != ValueType::String {
5754 return Err(error(location, "tally needs strings"));
5755 }
5756 } else if !matches!(
5757 ty,
5758 ValueType::Boolean
5759 | ValueType::Integer
5760 | ValueType::Float
5761 | ValueType::String
5762 | ValueType::Member(_)
5763 ) {
5764 return Err(error(location, "unique needs homogeneous scalar elements"));
5765 }
5766 if element_type
5767 .as_ref()
5768 .is_some_and(|previous| previous != &ty)
5769 {
5770 return Err(error(location, "unique needs homogeneous scalar elements"));
5771 }
5772 element_type = Some(ty);
5773 }
5774 Ok(elements)
5775}
5776
5777fn unique_value(
5778 mut array: ConstructedValue,
5779 budget: &mut Budget,
5780 location: &Location,
5781) -> Result<ConstructedValue, QueryError> {
5782 if declaration(&array).is_some() {
5783 scalar_pipeline_elements(
5784 constructed_value_type(&array, budget, location.clone())?,
5785 false,
5786 budget,
5787 location,
5788 )?;
5789 }
5790 let mut data = &mut array;
5791 while let ConstructedValue::Declared { value, .. } = data {
5792 data = value;
5793 }
5794 let ConstructedValue::Array(rows) = data else {
5795 return Err(error(location, "unique needs a positional array"));
5796 };
5797 let mut element_type = None;
5798 let mut retained = 0;
5799 for index in 0..rows.len() {
5800 budget.charge(1, location.clone())?;
5801 let ty = match rows[index].data() {
5802 ConstructedValue::Boolean(_) => ValueType::Boolean,
5803 ConstructedValue::Integer(_) => ValueType::Integer,
5804 ConstructedValue::Float(value) => {
5805 finite(*value, location)?;
5806 ValueType::Float
5807 }
5808 ConstructedValue::String(_) => ValueType::String,
5809 ConstructedValue::Member(member) => {
5810 member_bytes(member, budget, location)?;
5811 ValueType::Member(member.type_id)
5812 }
5813 _ => return Err(error(location, "unique needs homogeneous scalar elements")),
5814 };
5815 if element_type
5816 .as_ref()
5817 .is_some_and(|previous| previous != &ty)
5818 {
5819 return Err(error(location, "unique needs homogeneous scalar elements"));
5820 }
5821 element_type = Some(ty);
5822 let mut duplicate = false;
5823 for previous in &rows[..retained] {
5824 if equal_values(previous, &rows[index], budget, location)? {
5825 duplicate = true;
5826 break;
5827 }
5828 }
5829 if !duplicate {
5830 rows.swap(retained, index);
5831 retained += 1;
5832 }
5833 }
5834 rows.truncate(retained);
5835 Ok(array)
5836}
5837
5838fn tally_value(
5839 array: ConstructedValue,
5840 budget: &mut Budget,
5841 location: &Location,
5842) -> Result<ConstructedValue, QueryError> {
5843 if declaration(&array).is_some() {
5844 scalar_pipeline_elements(
5845 constructed_value_type(&array, budget, location.clone())?,
5846 true,
5847 budget,
5848 location,
5849 )?;
5850 }
5851 let ConstructedValue::Array(rows) = array.data() else {
5852 return Err(error(location, "tally needs a positional array"));
5853 };
5854 for row in rows {
5855 budget.charge(1, location.clone())?;
5856 if !matches!(row.data(), ConstructedValue::String(_)) {
5857 return Err(error(location, "tally needs strings"));
5858 }
5859 }
5860 let ConstructedValue::Array(rows) = expose(array, budget, location)? else {
5861 unreachable!("checked positional array")
5862 };
5863 let mut fields: Vec<(String, ConstructedValue)> = metadata_vec(rows.len(), budget, location)?;
5864 budget.charge(1, location.clone())?;
5865 budget.charge_results(1, location.clone())?;
5866 for row in rows {
5867 budget.charge(1, location.clone())?;
5868 let ConstructedValue::String(label) = expose(row, budget, location)? else {
5869 return Err(error(location, "tally needs strings"));
5870 };
5871 let mut existing = None;
5872 for (previous, count) in &mut fields {
5873 budget.charge(
5874 sum(1, previous.len().min(label.len()), location)?,
5875 location.clone(),
5876 )?;
5877 if *previous == label {
5878 existing = Some(count);
5879 break;
5880 }
5881 }
5882 if let Some(ConstructedValue::Integer(count)) = existing {
5883 *count = count
5884 .checked_add(1)
5885 .ok_or_else(|| error(location, "tally count overflow"))?;
5886 continue;
5887 }
5888 budget.charge(2, location.clone())?;
5889 budget.charge_results(2, location.clone())?;
5890 fields.push((label, ConstructedValue::Integer(1)));
5891 }
5892 Ok(ConstructedValue::Record(fields))
5893}
5894
5895fn sort_type(
5896 ty: ValueType,
5897 selector: Option<&Located<SortSelector>>,
5898 budget: &mut Budget,
5899 location: &Location,
5900) -> Result<ValueType, QueryError> {
5901 let retained = match &ty {
5902 ValueType::Declared { declaration, .. } => {
5903 Some(copy_declaration(declaration, budget, location)?)
5904 }
5905 _ => None,
5906 };
5907 let selected = selector.map_or_else(
5908 || location.clone(),
5909 |selector| Location::Query(selector.range.clone()),
5910 );
5911 let field = match selector {
5912 Some(Located {
5913 value: SortSelector::Field(field),
5914 range,
5915 }) => Some(Located::new(field.as_str(), range.clone())),
5916 _ => None,
5917 };
5918 let ty = match shape_type(ty, budget, location)? {
5919 ty @ (ValueType::Array(_) | ValueType::ArrayKind(_)) => {
5920 if matches!(
5921 selector.map(|selector| &selector.value),
5922 Some(SortSelector::Value)
5923 ) {
5924 return Err(error(&selected, "value ordering needs a dictionary"));
5925 }
5926 ValueType::Array(ArrayType::Elements(ordering_elements(
5927 ty, field, budget, location,
5928 )?))
5929 }
5930 ty @ ValueType::Set(_) => {
5931 if selector.is_some() {
5932 return Err(error(&selected, "set sorting uses its scalar values"));
5933 }
5934 ty
5935 }
5936 ValueType::Record(fields) => {
5937 if selector.is_some() {
5938 check_ordering_types(
5939 fields.iter().map(|(_, value)| value),
5940 field,
5941 budget,
5942 &selected,
5943 )?;
5944 }
5945 ValueType::Record(fields)
5946 }
5947 ty @ (ValueType::RecordKind | ValueType::Unestablished) => ty,
5948 _ => return Err(error(location, "sort needs an array, set or dictionary")),
5949 };
5950 Ok(if let Some(declaration) = retained {
5951 ValueType::Declared {
5952 actual: Box::new(ty),
5953 declaration,
5954 }
5955 } else {
5956 ty
5957 })
5958}
5959
5960fn ordering_elements(
5962 ty: ValueType,
5963 field: Option<Located<&str>>,
5964 budget: &mut Budget,
5965 location: &Location,
5966) -> Result<Vec<ValueType>, QueryError> {
5967 let elements = match shape_type(ty, budget, location)? {
5968 ValueType::Array(ArrayType::Positions(elements) | ArrayType::Elements(elements))
5969 | ValueType::ArrayKind(elements) => elements,
5970 ValueType::Unestablished => return one_type(ValueType::Unestablished, budget, location),
5971 _ => return Err(error(location, "ordering needs a positional array")),
5972 };
5973 let selector = field.as_ref().map_or_else(
5974 || location.clone(),
5975 |field| Location::Query(field.range.clone()),
5976 );
5977 check_ordering_types(elements.iter(), field, budget, &selector)?;
5978 Ok(elements)
5979}
5980
5981fn check_ordering_types<'a>(
5982 elements: impl IntoIterator<Item = &'a ValueType>,
5983 field: Option<Located<&str>>,
5984 budget: &mut Budget,
5985 selector: &Location,
5986) -> Result<(), QueryError> {
5987 let mut key_type = None;
5988 for element in elements {
5989 budget.charge(1, selector.clone())?;
5990 let ty = shape_type(copy_type(element, budget, selector)?, budget, selector)?;
5991 let ty = if let Some(field) = &field {
5992 match ty {
5993 ValueType::Record(fields) => {
5994 let mut selected = ValueType::Unestablished;
5995 for (label, ty) in fields {
5996 budget.charge(
5997 sum(1, label.len().min(field.value.len()), selector)?,
5998 selector.clone(),
5999 )?;
6000 if label == field.value {
6001 selected = ty;
6002 break;
6003 }
6004 }
6005 selected
6006 }
6007 ValueType::RecordKind | ValueType::Unestablished | ValueType::Absent => continue,
6008 _ => return Err(error(selector, "field ordering needs record rows")),
6009 }
6010 } else {
6011 ty
6012 };
6013 let ty = shape_type(ty, budget, selector)?;
6014 match ty {
6015 ValueType::Absent | ValueType::Unestablished => continue,
6018 ValueType::Integer | ValueType::Float | ValueType::String | ValueType::Member(_) => {}
6019 _ => {
6020 return Err(error(
6021 selector,
6022 "ordering keys need integers, finite floats, strings or nominal members",
6023 ));
6024 }
6025 }
6026 if key_type.as_ref().is_some_and(|previous| previous != &ty) {
6027 return Err(error(selector, "ordering needs homogeneous key types"));
6028 }
6029 key_type = Some(ty);
6030 }
6031 Ok(())
6032}
6033
6034#[derive(Clone, Copy, PartialEq, Eq)]
6035pub(crate) enum OrderingKeyType {
6036 Integer,
6037 Float,
6038 String,
6039 Member(NominalTypeId),
6040}
6041
6042pub(crate) fn ordering_key<'a>(
6043 row: &'a ConstructedValue,
6044 field: Option<&str>,
6045 budget: &mut Budget,
6046 location: &Location,
6047) -> Result<(&'a ConstructedValue, OrderingKeyType), QueryError> {
6048 budget.charge(1, location.clone())?;
6049 let key = if let Some(field) = field {
6050 let ConstructedValue::Record(fields) = row.data() else {
6051 return Err(error(location, "field ordering needs record rows"));
6052 };
6053 let mut key = None;
6054 for (label, value) in fields {
6055 budget.charge(
6056 sum(1, label.len().min(field.len()), location)?,
6057 location.clone(),
6058 )?;
6059 if label == field {
6060 key = Some(value);
6061 break;
6062 }
6063 }
6064 key.ok_or_else(|| error(location, "missing ordering field"))?
6065 } else {
6066 row
6067 };
6068 let key = key.data();
6069 let ty = match key {
6070 ConstructedValue::Integer(_) => OrderingKeyType::Integer,
6071 ConstructedValue::Float(value) => {
6072 finite(*value, location)?;
6073 OrderingKeyType::Float
6074 }
6075 ConstructedValue::String(_) => OrderingKeyType::String,
6076 ConstructedValue::Member(member) => {
6077 member_bytes(member, budget, location)?;
6078 OrderingKeyType::Member(member.type_id)
6079 }
6080 ConstructedValue::Absent => return Err(error(location, "ordering keys must be present")),
6081 _ => {
6082 return Err(error(
6083 location,
6084 "ordering keys need integers, finite floats, strings or nominal members",
6085 ));
6086 }
6087 };
6088 Ok((key, ty))
6089}
6090
6091pub(crate) fn compare_ordering_keys(
6092 left: &ConstructedValue,
6093 right: &ConstructedValue,
6094 budget: &mut Budget,
6095 location: &Location,
6096) -> Result<Ordering, QueryError> {
6097 budget.charge(1, location.clone())?;
6098 match (left, right) {
6099 (ConstructedValue::Integer(a), ConstructedValue::Integer(b)) => Ok(a.cmp(b)),
6100 (ConstructedValue::Float(a), ConstructedValue::Float(b)) => a
6101 .partial_cmp(b)
6102 .ok_or_else(|| error(location, "nonfinite float")),
6103 (ConstructedValue::String(a), ConstructedValue::String(b)) => {
6104 for (a, b) in a.bytes().zip(b.bytes()) {
6105 budget.charge(1, location.clone())?;
6106 let order = a.cmp(&b);
6107 if order != Ordering::Equal {
6108 return Ok(order);
6109 }
6110 }
6111 Ok(a.len().cmp(&b.len()))
6112 }
6113 (ConstructedValue::Member(a), ConstructedValue::Member(b)) if a.type_id == b.type_id => {
6114 check_member_pair(a, b, budget, location)?;
6115 Ok(a.declaration_order.cmp(&b.declaration_order))
6116 }
6117 _ => Err(error(location, "ordering needs homogeneous key types")),
6118 }
6119}
6120
6121#[derive(Clone, Copy)]
6122enum SortKey<'a> {
6123 Value(&'a ConstructedValue),
6124 Label(&'a str),
6125}
6126
6127#[derive(Clone, Copy)]
6128struct OrderingRow<'a> {
6129 index: usize,
6130 key: SortKey<'a>,
6131}
6132
6133fn sort_value(
6134 mut collection: ConstructedValue,
6135 selector: Option<&Located<SortSelector>>,
6136 descending: bool,
6137 budget: &mut Budget,
6138 location: &Location,
6139) -> Result<ConstructedValue, QueryError> {
6140 if declaration(&collection).is_some() {
6141 sort_type(
6142 constructed_value_type(&collection, budget, location.clone())?,
6143 selector,
6144 budget,
6145 location,
6146 )?;
6147 }
6148 let selected = selector.map_or_else(
6149 || location.clone(),
6150 |selector| Location::Query(selector.range.clone()),
6151 );
6152 let field = match selector.map(|selector| &selector.value) {
6153 Some(SortSelector::Field(field)) => Some(field.as_str()),
6154 _ => None,
6155 };
6156 let data = collection.data();
6157 let count = match data {
6158 ConstructedValue::Array(rows) => {
6159 if matches!(
6160 selector.map(|selector| &selector.value),
6161 Some(SortSelector::Value)
6162 ) {
6163 return Err(error(&selected, "value ordering needs a dictionary"));
6164 }
6165 rows.len()
6166 }
6167 ConstructedValue::Set { elements, .. } => {
6168 if selector.is_some() {
6169 return Err(error(&selected, "set sorting uses its scalar values"));
6170 }
6171 elements.len()
6172 }
6173 ConstructedValue::Record(fields) => fields.len(),
6174 _ => return Err(error(location, "sort needs an array, set or dictionary")),
6175 };
6176 let mut order: Vec<OrderingRow<'_>> = metadata_vec(count, budget, location)?;
6177 let mut key_type = None;
6178 for index in 0..count {
6179 let key = match data {
6180 ConstructedValue::Set { elements, .. } => {
6181 budget.charge(1, selected.clone())?;
6182 SortKey::Value(elements[index].data())
6183 }
6184 ConstructedValue::Record(fields) if selector.is_none() => {
6185 budget.charge(1, selected.clone())?;
6186 SortKey::Label(&fields[index].0)
6187 }
6188 _ => {
6189 let row = match data {
6190 ConstructedValue::Array(rows) => &rows[index],
6191 ConstructedValue::Record(fields) => &fields[index].1,
6192 _ => unreachable!("checked collection kind"),
6193 };
6194 let (key, ty) = ordering_key(row, field, budget, &selected)?;
6195 if key_type.is_some_and(|previous| previous != ty) {
6196 return Err(error(&selected, "ordering needs homogeneous key types"));
6197 }
6198 if let (Some(first), ConstructedValue::Member(member)) = (order.first(), key)
6199 && let SortKey::Value(ConstructedValue::Member(first)) = first.key
6200 {
6201 check_member_pair(first, member, budget, &selected)?;
6202 }
6203 key_type = Some(ty);
6204 SortKey::Value(key)
6205 }
6206 };
6207 order.push(OrderingRow { index, key });
6208 }
6209 let mut scratch = metadata_vec(order.len(), budget, location)?;
6210 scratch.extend_from_slice(&order);
6211 let mut width = 1;
6212 while width < order.len() {
6213 budget.charge(order.len(), location.clone())?;
6215 let mut start = 0;
6216 while start < order.len() {
6217 let middle = start.saturating_add(width).min(order.len());
6218 let end = middle.saturating_add(width).min(order.len());
6219 let (mut left, mut right) = (start, middle);
6220 for slot in &mut scratch[start..end] {
6221 let take_left = if right == end {
6222 true
6223 } else if left == middle {
6224 false
6225 } else {
6226 let order = match (order[left].key, order[right].key) {
6227 (SortKey::Label(left), SortKey::Label(right)) => {
6228 crate::ordering::compare_text(left, right, budget, &selected)?
6229 }
6230 (SortKey::Value(left), SortKey::Value(right))
6231 if matches!(data, ConstructedValue::Set { .. }) =>
6232 {
6233 compare_set_values(left, right, budget, &selected)?
6234 }
6235 (SortKey::Value(left), SortKey::Value(right)) => {
6236 compare_ordering_keys(left, right, budget, &selected)?
6237 }
6238 _ => unreachable!("one collection key kind"),
6239 };
6240 if descending {
6241 order != Ordering::Less
6242 } else {
6243 order != Ordering::Greater
6244 }
6245 };
6246 *slot = if take_left {
6247 let row = order[left];
6248 left += 1;
6249 row
6250 } else {
6251 let row = order[right];
6252 right += 1;
6253 row
6254 };
6255 }
6256 start = end;
6257 }
6258 std::mem::swap(&mut order, &mut scratch);
6259 width = width.saturating_mul(2);
6260 }
6261 let mut destinations = metadata_vec(order.len(), budget, location)?;
6262 destinations.resize(order.len(), 0);
6263 for (destination, row) in order.iter().enumerate() {
6264 budget.charge(1, location.clone())?;
6265 destinations[row.index] = destination;
6266 }
6267 drop(order);
6270 drop(scratch);
6271 let mut data = &mut collection;
6272 while let ConstructedValue::Declared { value, .. } = data {
6273 data = value;
6274 }
6275 for index in 0..count {
6276 while destinations[index] != index {
6277 budget.charge(1, location.clone())?;
6278 let destination = destinations[index];
6279 match data {
6280 ConstructedValue::Array(rows) | ConstructedValue::Set { elements: rows, .. } => {
6281 rows.swap(index, destination)
6282 }
6283 ConstructedValue::Record(fields) => fields.swap(index, destination),
6284 _ => unreachable!("checked collection kind"),
6285 }
6286 destinations.swap(index, destination);
6287 }
6288 }
6289 Ok(collection)
6290}
6291
6292fn extremum_position(
6293 operator: ValueExtremum,
6294 array: &ConstructedValue,
6295 field: Option<&Located<String>>,
6296 budget: &mut Budget,
6297 location: &Location,
6298) -> Result<Option<usize>, QueryError> {
6299 let ConstructedValue::Array(rows) = array.data() else {
6300 return Err(error(location, "ordering needs a positional array"));
6301 };
6302 let selector = field.map_or_else(
6303 || location.clone(),
6304 |field| Location::Query(field.range.clone()),
6305 );
6306 let mut selected: Option<(usize, &ConstructedValue)> = None;
6307 let mut key_type = None;
6308 for (index, row) in rows.iter().enumerate() {
6309 let (key, ty) = ordering_key(
6310 row,
6311 field.map(|field| field.value.as_str()),
6312 budget,
6313 &selector,
6314 )?;
6315 if key_type.is_some_and(|previous| previous != ty) {
6316 return Err(error(&selector, "ordering needs homogeneous key types"));
6317 }
6318 key_type = Some(ty);
6319 let replace = match selected {
6320 None => true,
6321 Some((_, previous)) => {
6322 let order = compare_ordering_keys(key, previous, budget, &selector)?;
6323 order
6324 == if operator == ValueExtremum::Min {
6325 Ordering::Less
6326 } else {
6327 Ordering::Greater
6328 }
6329 }
6330 };
6331 if replace {
6332 selected = Some((index, key));
6333 }
6334 }
6335 Ok(selected.map(|(index, _)| index))
6336}
6337
6338fn reduce_ref(
6339 operator: ValueReducer,
6340 array: &ConstructedValue,
6341 budget: &mut Budget,
6342 location: &Location,
6343) -> Result<ConstructedValue, QueryError> {
6344 let array = array.data();
6345 let (positional, keyed) = match &array {
6347 ConstructedValue::Array(values) => (values.as_slice(), &[][..]),
6348 ConstructedValue::KeyedArray { entries, .. } => (&[][..], entries.as_slice()),
6349 _ => return Err(error(location, "reducer needs an array")),
6350 };
6351 let len = positional.len().max(keyed.len());
6352 let values = positional
6353 .iter()
6354 .chain(keyed.iter().map(|entry| &entry.value));
6355 let value = match operator {
6356 ValueReducer::Length => {
6357 budget.charge(1, location.clone())?;
6358 ConstructedValue::Integer(
6359 i64::try_from(len).map_err(|_| error(location, "length overflow"))?,
6360 )
6361 }
6362 ValueReducer::Sum => {
6363 let mut total = 0_i64;
6364 for value in values {
6365 budget.charge(1, location.clone())?;
6366 let ConstructedValue::Integer(value) = value.data() else {
6367 return Err(error(location, "sum needs integer array elements"));
6368 };
6369 total = total
6370 .checked_add(*value)
6371 .ok_or_else(|| error(location, "integer sum overflow"))?;
6372 }
6373 ConstructedValue::Integer(total)
6374 }
6375 ValueReducer::Forall | ValueReducer::Exists => {
6376 let mut result = operator == ValueReducer::Forall;
6377 for value in values {
6378 budget.charge(1, location.clone())?;
6379 let ConstructedValue::Boolean(value) = value.data() else {
6380 return Err(error(
6381 location,
6382 "quantifier needs fixed Boolean array elements",
6383 ));
6384 };
6385 if operator == ValueReducer::Forall {
6387 result &= value;
6388 } else {
6389 result |= value;
6390 }
6391 }
6392 ConstructedValue::Boolean(result)
6393 }
6394 };
6395 scalar(value, budget, location)
6396}
6397
6398fn take_position(
6399 values: Vec<ConstructedValue>,
6400 position: usize,
6401 budget: &mut Budget,
6402 location: &Location,
6403) -> Result<ConstructedValue, QueryError> {
6404 let index = position
6405 .checked_sub(1)
6406 .filter(|index| *index < values.len())
6407 .ok_or_else(|| error(location, "missing collection position"))?;
6408 budget.charge(sum(index, 1, location)?, location.clone())?;
6411 Ok(values.into_iter().nth(index).expect("position checked"))
6412}
6413
6414fn integer_range(
6415 lower: i64,
6416 upper: i64,
6417 budget: &mut Budget,
6418 location: &Location,
6419) -> Result<ConstructedValue, QueryError> {
6420 let len = if upper < lower {
6421 0
6422 } else {
6423 usize::try_from(i128::from(upper) - i128::from(lower) + 1)
6424 .map_err(|_| error(location, "range cardinality overflow"))?
6425 };
6426 allocation::<ConstructedValue>(len, location)?;
6427 budget.charge(len, location.clone())?;
6428 let units = sum(
6429 1,
6430 len.checked_mul(2)
6431 .ok_or_else(|| error(location, "range allocation overflow"))?,
6432 location,
6433 )?;
6434 budget.charge_results(units, location.clone())?;
6435 let mut elements = Vec::with_capacity(len);
6436 for offset in 0..len {
6437 let value = i64::try_from(i128::from(lower) + offset as i128)
6438 .map_err(|_| error(location, "range value overflow"))?;
6439 elements.push(ConstructedValue::Integer(value));
6440 }
6441 Ok(ConstructedValue::Set {
6442 element_type: Some(SetElementType::Integer),
6443 elements,
6444 })
6445}
6446
6447fn insert_set(
6448 values: &mut Vec<ConstructedValue>,
6449 element_type: &mut Option<SetElementType>,
6450 value: ConstructedValue,
6451 admit_slot: bool,
6452 budget: &mut Budget,
6453 location: &Location,
6454) -> Result<(), QueryError> {
6455 let ty = scalar_set_type(&value)
6456 .ok_or_else(|| error(location, "set elements must have one supported scalar type"))?;
6457 if element_type.is_some_and(|previous| previous != ty) {
6458 return Err(error(location, "set element type mismatch"));
6459 }
6460 *element_type = Some(ty);
6461 match value.data() {
6462 ConstructedValue::Float(value) => finite(*value, location)?,
6463 ConstructedValue::Member(member) => {
6464 for previous in values.iter() {
6465 if let ConstructedValue::Member(previous) = previous.data() {
6466 check_member_pair(previous, member, budget, location)?;
6467 }
6468 }
6469 }
6470 _ => {}
6471 }
6472 for existing in values.iter() {
6473 if compare_set_values(&value, existing, budget, location)? == Ordering::Equal {
6474 return Ok(());
6475 }
6476 }
6477 allocation::<ConstructedValue>(sum(values.len(), 1, location)?, location)?;
6478 if admit_slot {
6479 budget.charge_results(1, location.clone())?;
6480 }
6481 values.push(value);
6482 Ok(())
6483}
6484
6485pub fn copy_value_types(
6490 types: &[ValueType],
6491 budget: &mut Budget,
6492 location: Location,
6493) -> Result<Vec<ValueType>, QueryError> {
6494 copy_types(types, budget, &location)
6495}
6496
6497pub(crate) fn extend_type_positions(
6499 positions: &[Vec<ValueType>],
6500 types: &[ValueType],
6501 budget: &mut Budget,
6502 location: &Location,
6503) -> Result<Vec<Vec<ValueType>>, QueryError> {
6504 let count = positions
6505 .len()
6506 .checked_mul(types.len())
6507 .ok_or_else(|| error(location, "type possibilities overflow"))?;
6508 let mut expanded = metadata_vec(count, budget, location)?;
6509 for position in positions {
6510 for ty in types {
6511 let mut entries = copy_types(position, budget, location)?;
6512 let ty = copy_type(ty, budget, location)?;
6513 push_metadata(&mut entries, ty, budget, location)?;
6514 expanded.push(entries);
6515 }
6516 }
6517 Ok(expanded)
6518}
6519
6520pub(crate) fn function_facts<K>(
6521 declaration: &DeclaredType<K>,
6522 budget: &mut Budget,
6523 location: &Location,
6524) -> Result<Vec<ValueType>, QueryError> {
6525 if crate::runtime_types::contains_native(declaration, budget, location)? {
6526 one_type(ValueType::Unestablished, budget, location)
6527 } else {
6528 let declaration =
6529 crate::runtime_types::constructed_declaration(declaration, budget, location)?;
6530 one_type(
6531 declared_fact(&declaration, ValueType::Unestablished, budget, location)?,
6532 budget,
6533 location,
6534 )
6535 }
6536}
6537
6538pub(crate) fn prepare_function_body<'a, Q, K, H: ValueQueryHost<Q>>(
6539 declaration: crate::functions::DeclarationRef<'a, Q, K>,
6540 queries: &mut H,
6541 budget: &mut Budget,
6542) -> Result<execution::Body<'a, Q, K>, QueryError> {
6543 let globals = BTreeMap::new();
6544 let mut analysis = Analysis::new(Globals::Facts(&globals), queries);
6545 let mut preparation = Preparation::new();
6546 for parameter in declaration.parameters.iter() {
6547 let types = function_facts(
6548 parameter.declaration,
6549 budget,
6550 &Location::Query(parameter.range.clone()),
6551 )?;
6552 match parameter.pattern {
6553 pattern::PatternRef::Name(name) => {
6554 function_local(name, &analysis.locals, budget)?;
6555 push_metadata(
6556 &mut analysis.locals,
6557 (name.value.as_str(), types),
6558 budget,
6559 &Location::Query(name.range.clone()),
6560 )?;
6561 push_metadata(
6562 &mut preparation.locals,
6563 name.value.as_str(),
6564 budget,
6565 &Location::Query(name.range.clone()),
6566 )?;
6567 }
6568 pattern::PatternRef::Pattern(pattern) => {
6569 analysis.pattern(pattern, &types, false, budget)?;
6570 pattern::names(pattern, &mut preparation.locals, budget)?;
6571 }
6572 }
6573 }
6574 let mut bindings = metadata_vec(
6575 declaration.bindings.len(),
6576 budget,
6577 &Location::Query(declaration.name.range.clone()),
6578 )?;
6579 for (name, value) in declaration.bindings {
6580 function_local(name, &analysis.locals, budget)?;
6581 let types = analysis.walk(value, budget)?;
6582 let expression = preparation.resolve(value, analysis.queries, budget)?;
6583 analysis
6584 .queries
6585 .prepare_local(name, value, &types, budget)?;
6586 push_metadata(
6587 &mut analysis.locals,
6588 (name.value.as_str(), types),
6589 budget,
6590 &Location::Query(name.range.clone()),
6591 )?;
6592 push_metadata(
6593 &mut preparation.locals,
6594 name.value.as_str(),
6595 budget,
6596 &Location::Query(name.range.clone()),
6597 )?;
6598 bindings.push((name.value.as_str(), expression));
6599 }
6600 analysis.walk(declaration.value, budget)?;
6601 let value = preparation.resolve(declaration.value, analysis.queries, budget)?;
6602 Ok(execution::Body {
6603 declaration,
6604 bindings,
6605 value,
6606 })
6607}
6608fn function_local(
6609 name: &Located<String>,
6610 locals: &[(&str, Vec<ValueType>)],
6611 budget: &mut Budget,
6612) -> Result<(), QueryError> {
6613 let location = Location::Query(name.range.clone());
6614 budget.charge(name.value.len() + 1, location.clone())?;
6615 if name.value == "_" {
6616 return Err(error(&location, "discard name cannot be bound"));
6617 }
6618 for (previous, _) in locals {
6619 budget.charge(1 + previous.len().min(name.value.len()), location.clone())?;
6620 if *previous == name.value {
6621 return Err(error(&location, "binding is already defined"));
6622 }
6623 }
6624 Ok(())
6625}
6626pub(crate) fn run_functions(
6627 declarations: &[crate::FunctionDeclaration],
6628 entry: &ValueExpression,
6629 bindings: &Bindings<ConstructedValue>,
6630 budget: &mut Budget,
6631 limits: crate::FunctionLimits,
6632) -> Result<ConstructedValue, crate::FunctionError<QueryError>> {
6633 let mut retained = Vec::new();
6634 for d in declarations {
6635 crate::projection::admit_slots::<crate::functions::DeclarationRef<'_, (), ()>>(
6636 1,
6637 budget,
6638 &d.name.range,
6639 )?;
6640 retained.push(d.as_borrowed().into());
6641 }
6642 run_function_refs(&retained, entry, bindings, budget, limits)
6643}
6644pub(crate) fn run_function_refs(
6645 declarations: &[crate::functions::DeclarationRef<'_, (), ()>],
6646 entry: &ValueExpression,
6647 bindings: &Bindings<ConstructedValue>,
6648 budget: &mut Budget,
6649 limits: crate::FunctionLimits,
6650) -> Result<ConstructedValue, crate::FunctionError<QueryError>> {
6651 let mut queries = NoQueries {
6652 bindings: Some(bindings),
6653 };
6654 let mut resolver = crate::functions::Resolver {
6655 queries: &mut queries,
6656 declarations,
6657 };
6658 let location = Location::Query(entry.range.clone());
6659 let mut bodies = metadata_vec(declarations.len(), budget, &location)?;
6660 for declaration in declarations {
6661 bodies.push(prepare_function_body(*declaration, &mut resolver, budget)?);
6662 }
6663 Analysis::new(Globals::Available(bindings), &mut resolver).walk(entry, budget)?;
6664 let mut preparation = Preparation::new();
6665 let entry = preparation.resolve(entry, &mut resolver, budget)?;
6666 let names = preparation.names;
6667 let mut frame = Frame::new(names.clone(), budget, &location)?;
6668 let mut calls = crate::functions::CallTrace::default();
6669 let mut check = |_: &ConstructedValue, _: &mut Budget, _: &Location| Ok(());
6670 let mut host = execution::ScalarHost {
6671 queries: &mut queries,
6672 frame: &mut frame,
6673 check: &mut check,
6674 query: std::marker::PhantomData,
6675 calls: &mut calls,
6676 };
6677 let result = execution::execute(
6678 &entry,
6679 &names,
6680 &bodies,
6681 &mut host,
6682 budget,
6683 limits.active_depth,
6684 0,
6685 )
6686 .and_then(|v| v.materialize(budget, &location));
6687 result.map_err(|error| crate::FunctionError {
6688 error,
6689 omitted_calls: calls.omitted,
6690 calls: calls
6691 .frames
6692 .into_iter()
6693 .map(|(id, range)| crate::FunctionCall {
6694 function: declarations[id].name.value.chars().take(64).collect(),
6695 range,
6696 })
6697 .collect(),
6698 })
6699}