1#![doc = include_str!("../README.md")]
2
3mod eval;
4mod parser;
5mod program;
6mod value;
7
8use std::fmt;
9use std::ops::Range;
10
11pub use eval::{
12 Budget, EvaluationError, Host, Limits, NativeInput, NativeResult, NativeValue, ProgramLimits,
13 Truth, evaluate,
14};
15pub use parser::{ArgumentReader, parse_pipeline};
16pub use program::{
17 Bindings, EmissionPolicy, Expression, ExpressionKind, ExpressionStep, Program, ProgramHost,
18 Statement, evaluate_expression, evaluate_program,
19};
20pub use value::{
21 ArrayType, ConstructedValue, KeyAxis, KeyedArrayType, KeyedEntry, NominalMember,
22 NominalMemberId, NominalTypeId, SetElementType, ValueBinaryOperator, ValueConstructor,
23 ValueExpression, ValueExpressionKind, ValueGenerator, ValueKey, ValueKeyType, ValueType,
24 ValueUnaryOperator, constructed_value_type, copy_constructed_value, evaluate_value,
25 free_value_names, validate_value_expression,
26};
27
28pub const MAX_NESTING: usize = 64;
31
32#[derive(Clone, Debug, PartialEq, Eq)]
35pub enum Location {
36 Query(Range<usize>),
37 Source(Range<usize>),
38}
39
40#[derive(Clone, Debug, PartialEq, Eq)]
41pub struct QueryError {
42 pub location: Location,
43 pub message: String,
44}
45
46impl QueryError {
47 pub fn query(range: Range<usize>, message: impl Into<String>) -> Self {
48 Self {
49 location: Location::Query(range),
50 message: message.into(),
51 }
52 }
53
54 pub fn source(range: Range<usize>, message: impl Into<String>) -> Self {
55 Self {
56 location: Location::Source(range),
57 message: message.into(),
58 }
59 }
60}
61
62impl fmt::Display for QueryError {
63 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
64 write!(formatter, "{:?}: {}", self.location, self.message)
65 }
66}
67
68impl std::error::Error for QueryError {}
69
70#[derive(Clone, Debug, PartialEq, Eq)]
73pub struct Located<T> {
74 pub value: T,
75 pub range: Range<usize>,
76}
77
78impl<T> Located<T> {
79 pub fn new(value: T, range: Range<usize>) -> Self {
80 Self { value, range }
81 }
82}
83
84pub type Predicate<L> = Located<PredicateKind<L>>;
85
86#[derive(Clone, Debug, PartialEq, Eq)]
87pub enum PredicateKind<L> {
88 Leaf(L),
89 Not(Box<Predicate<L>>),
90 Group(Box<Predicate<L>>),
91 And(Vec<Predicate<L>>),
92 Or(Vec<Predicate<L>>),
93}
94
95#[derive(Clone, Debug, PartialEq, Eq)]
98pub enum Stage<L, S = ()> {
99 Filter(Predicate<L>),
100 Head(usize),
101 Count,
102 Unique,
103 Tally,
104 Host(S),
105}
106
107#[derive(Clone, Debug, PartialEq, Eq)]
109pub struct Pipeline<L, S = ()> {
110 stages: Vec<Located<Stage<L, S>>>,
111 range: Range<usize>,
112 nesting: Nesting,
113}
114
115#[derive(Clone, Copy, Debug, PartialEq, Eq)]
116enum Nesting {
117 Nodes,
118 GroupNot,
119}
120
121impl Nesting {
122 fn pipeline_depth(self) -> usize {
123 usize::from(self == Self::Nodes)
124 }
125
126 fn predicate_depth<L>(self, predicate: &Predicate<L>) -> usize {
127 usize::from(
128 self == Self::Nodes
129 || matches!(
130 predicate.value,
131 PredicateKind::Group(_) | PredicateKind::Not(_)
132 ),
133 )
134 }
135}
136
137impl<L, S> Pipeline<L, S> {
138 pub fn new(
142 stages: Vec<Located<Stage<L, S>>>,
143 range: Range<usize>,
144 nesting_limit: usize,
145 ) -> Result<Self, QueryError> {
146 Self::lower(stages, range, nesting_limit, Nesting::Nodes)
147 }
148
149 pub fn with_group_not_nesting(
153 stages: Vec<Located<Stage<L, S>>>,
154 range: Range<usize>,
155 nesting_limit: usize,
156 ) -> Result<Self, QueryError> {
157 Self::lower(stages, range, nesting_limit, Nesting::GroupNot)
158 }
159
160 fn lower(
161 stages: Vec<Located<Stage<L, S>>>,
162 range: Range<usize>,
163 nesting_limit: usize,
164 nesting: Nesting,
165 ) -> Result<Self, QueryError> {
166 let nesting_limit = nesting_limit.min(MAX_NESTING);
167 check_depth(nesting.pipeline_depth(), nesting_limit, &range)?;
168 let mut terminal = None;
169 for stage in &stages {
170 if let Some(name) = terminal {
171 return Err(QueryError::query(
172 stage.range.clone(),
173 format!("a stage cannot follow {name}"),
174 ));
175 }
176 match &stage.value {
177 Stage::Filter(predicate) => validate_predicate(predicate, nesting, nesting_limit)?,
178 Stage::Head(_) | Stage::Unique | Stage::Host(_) => {}
179 Stage::Count => terminal = Some("count"),
180 Stage::Tally => terminal = Some("tally"),
181 }
182 }
183 Ok(Self {
184 stages,
185 range,
186 nesting,
187 })
188 }
189
190 pub fn stages(&self) -> &[Located<Stage<L, S>>] {
191 &self.stages
192 }
193
194 pub fn range(&self) -> Range<usize> {
195 self.range.clone()
196 }
197}
198
199fn check_depth(depth: usize, limit: usize, range: &Range<usize>) -> Result<(), QueryError> {
200 if depth > limit {
201 return Err(QueryError::query(range.clone(), "nesting limit exceeded"));
202 }
203 Ok(())
204}
205
206fn validate_predicate<L>(
207 predicate: &Predicate<L>,
208 nesting: Nesting,
209 limit: usize,
210) -> Result<(), QueryError> {
211 let mut pending = vec![(predicate, nesting.pipeline_depth())];
212 while let Some((predicate, parent_depth)) = pending.pop() {
213 let depth = parent_depth + nesting.predicate_depth(predicate);
214 check_depth(depth, limit, &predicate.range)?;
215 match &predicate.value {
216 PredicateKind::Leaf(_) => {}
217 PredicateKind::Not(child) | PredicateKind::Group(child) => {
218 pending.push((child, depth));
219 }
220 PredicateKind::And(children) | PredicateKind::Or(children) => {
221 if children.is_empty() {
222 return Err(QueryError::query(
223 predicate.range.clone(),
224 "a Boolean combination needs at least one predicate",
225 ));
226 }
227 pending.extend(children.iter().rev().map(|child| (child, depth)));
228 }
229 }
230 }
231 Ok(())
232}