jaq_interpret/
filter.rs

1use crate::box_iter::{box_once, flat_map_with, map_with, BoxIter};
2use crate::results::{fold, recurse, then, Fold, Results};
3use crate::val::{Val, ValR2, ValR2s, ValT};
4use crate::{rc_lazy_list, Bind, Ctx, Error};
5use alloc::{boxed::Box, string::String, vec::Vec};
6use core::ops::ControlFlow;
7use dyn_clone::DynClone;
8use jaq_syn::filter::FoldType;
9use jaq_syn::{MathOp, OrdOp};
10
11/// Function from a value to a stream of value results.
12#[derive(Debug, Clone)]
13pub struct Owned<V = Val>(Id, Lut<V>);
14
15/// Look-up table for indices stored in ASTs.
16#[derive(Clone, Debug)]
17struct Lut<V = Val> {
18    defs: Box<[Ast]>,
19    natives: Box<[Native<V>]>,
20}
21
22impl<V> Default for Owned<V> {
23    fn default() -> Self {
24        Self::new(Id(0), [Ast::Id].into(), [].into())
25    }
26}
27
28#[derive(Debug)]
29pub struct Ref<'a, V = Val>(Id, &'a Lut<V>);
30
31impl<'a, V> Clone for Ref<'a, V> {
32    fn clone(&self) -> Self {
33        *self
34    }
35}
36
37impl<'a, V> Copy for Ref<'a, V> {}
38
39#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
40pub struct Id(pub usize);
41
42#[derive(Clone, Debug)]
43pub enum CallTyp {
44    /// everything that is not tail-recursive
45    Normal,
46    /// set up a tail-recursion handler
47    Catch,
48    /// throw a tail-recursion exception to be caught by the handler
49    Throw,
50}
51
52#[derive(Clone, Debug, PartialEq, Eq)]
53pub struct TailCall<V>(Id, crate::Vars<V>, V);
54
55#[derive(Clone, Debug)]
56pub(crate) struct Call {
57    pub id: Id,
58    pub typ: CallTyp,
59    pub skip: usize,
60    pub args: Box<[Bind<Id, Id>]>,
61}
62
63impl<V> Owned<V> {
64    pub(crate) fn new(main: Id, defs: Box<[Ast]>, natives: Box<[Native<V>]>) -> Self {
65        Self(main, Lut { defs, natives })
66    }
67}
68
69/// Abstract syntax tree for filters.
70#[derive(Clone, Debug)]
71pub(crate) enum Ast {
72    Id,
73    ToString,
74
75    Int(isize),
76    Num(String),
77    Str(String),
78    Array(Id),
79    ObjEmpty,
80    ObjSingle(Id, Id),
81
82    Try(Id, Id),
83    Neg(Id),
84    Pipe(Id, bool, Id),
85    Comma(Id, Id),
86    Alt(Id, Id),
87    Ite(Id, Id, Id),
88    /// `reduce`, `for`, and `foreach`
89    ///
90    /// The first field indicates whether to yield intermediate results
91    /// (`false` for `reduce` and `true` for `foreach`).
92    ///
93    ///  Assuming that `xs` evaluates to `x0`, `x1`, ..., `xn`,
94    /// `reduce xs as $x (init; f)` evaluates to
95    ///
96    /// ~~~ text
97    /// init
98    /// | x0 as $x | f
99    /// | ...
100    /// | xn as $x | f
101    /// ~~~
102    ///
103    /// and `for xs as $x (init; f)` evaluates to
104    ///
105    /// ~~~ text
106    /// init
107    /// | ., (x0 as $x | f
108    /// | ...
109    /// | ., (xn as $x | f)...)
110    /// ~~~
111    Fold(FoldType, Id, Id, Id),
112
113    Path(Id, crate::path::Path<Id>),
114
115    Update(Id, Id),
116    UpdateMath(Id, MathOp, Id),
117    Assign(Id, Id),
118
119    Logic(Id, bool, Id),
120    Math(Id, MathOp, Id),
121    Ord(Id, OrdOp, Id),
122
123    Var(usize),
124    Call(Call),
125
126    Native(usize, Box<[Id]>),
127}
128
129// we can unfortunately not make a `Box<dyn ... + Clone>`
130// that is why we have to go through the pain of making a new trait here
131pub trait Update<'a, V = Val>: Fn(V) -> ValR2s<'a, V> + DynClone {}
132
133impl<'a, V, T: Fn(V) -> ValR2s<'a, V> + Clone> Update<'a, V> for T {}
134
135dyn_clone::clone_trait_object!(<'a, V> Update<'a, V>);
136
137/// Enhance the context `ctx` with variables bound to the outputs of `args` executed on `cv`,
138/// and return the enhanced contexts together with the original value of `cv`.
139///
140/// This is used when we call filters with variable arguments.
141fn bind_vars<'a, V: ValT>(
142    mut args: impl Iterator<Item = Bind<Ref<'a, V>, Ref<'a, V>>> + Clone + 'a,
143    ctx: Ctx<'a, V>,
144    cv: Cv<'a, V>,
145) -> Results<'a, Cv<'a, V>, Error<V>> {
146    match args.next() {
147        Some(Bind::Var(arg)) => flat_map_with(
148            arg.run(cv.clone()),
149            (ctx, cv, args),
150            |y, (ctx, cv, args)| then(y, |y| bind_vars(args, ctx.cons_var(y), cv)),
151        ),
152        Some(Bind::Fun(Ref(arg, _defs))) => bind_vars(args, ctx.cons_fun((arg, cv.0.clone())), cv),
153        None => box_once(Ok((ctx, cv.1))),
154    }
155}
156
157fn run_cvs<'a, V: ValT>(f: Ref<'a, V>, cvs: Results<'a, Cv<'a, V>, Error<V>>) -> ValR2s<'a, V> {
158    Box::new(cvs.flat_map(move |cv| then(cv, |cv| f.run(cv))))
159}
160
161fn reduce<'a, T, V, F>(xs: Results<'a, T, Error<V>>, init: V, f: F) -> ValR2s<V>
162where
163    T: Clone + 'a,
164    V: Clone + 'a,
165    F: Fn(T, V) -> ValR2s<'a, V> + 'a,
166{
167    let xs = rc_lazy_list::List::from_iter(xs);
168    Box::new(fold(false, xs, Fold::Input(init), f))
169}
170
171type Cv<'c, V = Val> = (Ctx<'c, V>, V);
172
173/// A filter which is implemented using function pointers.
174#[derive(Clone)]
175pub struct Native<V = Val> {
176    run: RunPtr<V>,
177    update: UpdatePtr<V>,
178}
179
180/// Run function pointer.
181pub type RunPtr<V = Val> = for<'a> fn(Args<'a, V>, Cv<'a, V>) -> ValR2s<'a, V>;
182/// Update function pointer.
183pub type UpdatePtr<V = Val> =
184    for<'a> fn(Args<'a, V>, Cv<'a, V>, Box<dyn Update<'a, V> + 'a>) -> ValR2s<'a, V>;
185
186impl<V> Native<V> {
187    /// Create a native filter from a run function, without support for updates.
188    pub const fn new(run: RunPtr<V>) -> Self {
189        Self::with_update(run, |_, _, _| box_once(Err(Error::PathExp)))
190    }
191
192    /// Create a native filter from a run function and an update function (used for `filter |= ...`).
193    // TODO for v2.0: change run to self
194    pub const fn with_update(run: RunPtr<V>, update: UpdatePtr<V>) -> Self {
195        Self { run, update }
196    }
197}
198
199// TODO for v2.0: remove this
200impl<V> core::fmt::Debug for Native<V> {
201    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
202        f.debug_struct("Native").finish()
203    }
204}
205
206/// Arguments passed to a native filter.
207pub struct Args<'a, V = Val>(&'a [Id], &'a Lut<V>);
208
209impl<'a, V> Clone for Args<'a, V> {
210    fn clone(&self) -> Self {
211        *self
212    }
213}
214
215impl<'a, V> Copy for Args<'a, V> {}
216
217impl<'a, V: ValT> Args<'a, V> {
218    /// Obtain the n-th argument passed to the filter, crash if it is not given.
219    // This function returns an `impl` in order not to expose the `Filter` type publicly.
220    // It would be more elegant to implement `Index<usize>` here instead,
221    // but because of returning `impl`, we cannot do this right now, see:
222    // <https://github.com/rust-lang/rust/issues/63063>.
223    // TODO for v2.0: make this take `&self`?
224    pub fn get(self, i: usize) -> impl FilterT<'a, V> {
225        Ref(self.0[i], self.1)
226    }
227}
228
229impl<'a, V: ValT> FilterT<'a, V> for &'a Owned<V> {
230    fn run(self, cv: Cv<'a, V>) -> ValR2s<'a, V> {
231        Ref(self.0, &self.1).run(cv)
232    }
233
234    fn update(self, cv: Cv<'a, V>, f: Box<dyn Update<'a, V> + 'a>) -> ValR2s<'a, V> {
235        Ref(self.0, &self.1).update(cv, f)
236    }
237}
238
239impl<'a, V: ValT> FilterT<'a, V> for Ref<'a, V> {
240    fn run(self, cv: Cv<'a, V>) -> ValR2s<'a, V> {
241        use alloc::string::ToString;
242        use core::iter::{once, once_with};
243        // wrap a filter AST with the filter definitions
244        let w = move |id: &Id| Ref(*id, self.1);
245        match &self.1.defs[self.0 .0] {
246            Ast::Id => box_once(Ok(cv.1)),
247            Ast::ToString => Box::new(once_with(move || match cv.1.as_str() {
248                Some(_) => Ok(cv.1),
249                None => Ok(V::from(cv.1.to_string())),
250            })),
251            Ast::Int(n) => box_once(Ok(V::from(*n))),
252            Ast::Num(x) => box_once(V::from_num(x)),
253            Ast::Str(s) => Box::new(once_with(move || Ok(V::from(s.clone())))),
254            Ast::Array(f) => Box::new(once_with(move || w(f).run(cv).collect::<Result<_, _>>())),
255            Ast::ObjEmpty => box_once(V::from_map([])),
256            Ast::ObjSingle(k, v) => {
257                Box::new(Self::cartesian(w(k), w(v), cv).map(|(k, v)| V::from_map([(k?, v?)])))
258            }
259            Ast::Try(f, c) => Box::new(w(f).run((cv.0.clone(), cv.1)).flat_map(move |y| {
260                y.map_or_else(
261                    |e| w(c).run((cv.0.clone(), e.as_val())),
262                    |v| box_once(Ok(v)),
263                )
264            })),
265            Ast::Neg(f) => Box::new(w(f).run(cv).map(|v| -v?)),
266
267            // `l | r`
268            Ast::Pipe(l, false, r) => {
269                let l = w(l).run((cv.0.clone(), cv.1));
270                flat_map_with(l, cv.0, move |y, ctx| then(y, |y| w(r).run((ctx, y))))
271            }
272            // `l as $x | r`
273            Ast::Pipe(l, true, r) => w(l).pipe(cv, move |cv, y| w(r).run((cv.0.cons_var(y), cv.1))),
274
275            Ast::Comma(l, r) => Box::new(w(l).run(cv.clone()).chain(w(r).run(cv))),
276            Ast::Alt(l, r) => {
277                let mut l = w(l)
278                    .run(cv.clone())
279                    .filter(|v| v.as_ref().map_or(true, |v| v.as_bool()));
280                match l.next() {
281                    Some(head) => Box::new(once(head).chain(l)),
282                    None => w(r).run(cv),
283                }
284            }
285            Ast::Ite(if_, then_, else_) => w(if_).pipe(cv, move |cv, v| {
286                w(if v.as_bool() { then_ } else { else_ }).run(cv)
287            }),
288            Ast::Path(f, path) => {
289                let path = path.map_ref(|i| {
290                    let cv = cv.clone();
291                    crate::into_iter::collect_if_once(move || w(i).run(cv))
292                });
293                flat_map_with(w(f).run(cv), path, |y, path| {
294                    then(y, |y| {
295                        flat_map_with(path.explode(), y, |path, y| then(path, |path| path.run(y)))
296                    })
297                })
298            }
299
300            Ast::Update(path, f) => w(path).update(
301                (cv.0.clone(), cv.1),
302                Box::new(move |v| w(f).run((cv.0.clone(), v))),
303            ),
304            Ast::UpdateMath(path, op, f) => w(f).pipe(cv, move |cv, y| {
305                w(path).update(cv, Box::new(move |x| box_once(op.run(x, y.clone()))))
306            }),
307            Ast::Assign(path, f) => w(f).pipe(cv, move |cv, y| {
308                w(path).update(cv, Box::new(move |_| box_once(Ok(y.clone()))))
309            }),
310
311            Ast::Logic(l, stop, r) => w(l).pipe(cv, move |cv, l| {
312                if l.as_bool() == *stop {
313                    box_once(Ok(V::from(*stop)))
314                } else {
315                    Box::new(w(r).run(cv).map(|r| Ok(V::from(r?.as_bool()))))
316                }
317            }),
318            Ast::Math(l, op, r) => {
319                Box::new(Self::cartesian(w(l), w(r), cv).map(|(x, y)| op.run(x?, y?)))
320            }
321            Ast::Ord(l, op, r) => Box::new(
322                Self::cartesian(w(l), w(r), cv).map(|(x, y)| Ok(V::from(op.run(&x?, &y?)))),
323            ),
324
325            Ast::Fold(typ, xs, init, f) => {
326                let xs = rc_lazy_list::List::from_iter(w(xs).run(cv.clone()));
327                let init = w(init).run(cv.clone());
328                let f = move |x, v| w(f).run((cv.0.clone().cons_var(x), v));
329                use Fold::{Input, Output};
330                match typ {
331                    FoldType::Reduce => Box::new(fold(false, xs, Output(init), f)),
332                    FoldType::For => Box::new(fold(true, xs, Output(init), f)),
333                    FoldType::Foreach => flat_map_with(init, xs, move |i, xs| {
334                        then(i, |i| Box::new(fold(true, xs, Input(i), f.clone())))
335                    }),
336                }
337            }
338
339            Ast::Var(v) => match cv.0.vars.get(*v).unwrap() {
340                Bind::Var(v) => box_once(Ok(v.clone())),
341                Bind::Fun(f) => w(&f.0).run((cv.0.with_vars(f.1.clone()), cv.1)),
342            },
343            Ast::Call(call) => {
344                let def = w(&call.id);
345                let ctx = cv.0.clone().skip_vars(call.skip);
346                let inputs = cv.0.inputs;
347                let cvs = bind_vars(call.args.iter().map(move |a| a.as_ref().map(w)), ctx, cv);
348                match call.typ {
349                    CallTyp::Normal => run_cvs(def, cvs),
350                    CallTyp::Catch => Box::new(crate::Stack::new(
351                        Vec::from([run_cvs(def, cvs)]),
352                        move |r| match r {
353                            Err(Error::TailCall(TailCall(id, vars, v))) if id == call.id => {
354                                ControlFlow::Continue(def.run((Ctx { inputs, vars }, v)))
355                            }
356                            Ok(_) | Err(_) => ControlFlow::Break(r),
357                        },
358                    )),
359                    CallTyp::Throw => Box::new(cvs.map(move |cv| {
360                        cv.and_then(|cv| Err(Error::TailCall(TailCall(call.id, cv.0.vars, cv.1))))
361                    })),
362                }
363            }
364
365            Ast::Native(id, args) => (self.1.natives[*id].run)(Args(args, self.1), cv),
366        }
367    }
368
369    fn update(self, cv: Cv<'a, V>, f: Box<dyn Update<'a, V> + 'a>) -> ValR2s<'a, V> {
370        let err = box_once(Err(Error::PathExp));
371        let w = move |id: &Id| Ref(*id, self.1);
372        match &self.1.defs[self.0 .0] {
373            Ast::ToString => err,
374            Ast::Int(_) | Ast::Num(_) | Ast::Str(_) => err,
375            Ast::Array(_) | Ast::ObjEmpty | Ast::ObjSingle(..) => err,
376            Ast::Neg(_) | Ast::Logic(..) | Ast::Math(..) | Ast::Ord(..) => err,
377            Ast::Update(..) | Ast::UpdateMath(..) | Ast::Assign(..) => err,
378
379            // these are up for grabs to implement :)
380            Ast::Try(..) | Ast::Alt(..) | Ast::Fold(..) => {
381                unimplemented!("updating with this operator is not supported yet")
382            }
383
384            Ast::Id => f(cv.1),
385            Ast::Path(l, path) => {
386                let path = path.map_ref(|i| {
387                    let cv = cv.clone();
388                    crate::into_iter::collect_if_once(move || w(i).run(cv))
389                });
390                let f = move |v| {
391                    let mut paths = path.clone().explode();
392                    box_once(paths.try_fold(v, |acc, path| path?.update(acc, &f)))
393                };
394                w(l).update(cv, Box::new(f))
395            }
396            Ast::Pipe(l, false, r) => w(l).update(
397                (cv.0.clone(), cv.1),
398                Box::new(move |v| w(r).update((cv.0.clone(), v), f.clone())),
399            ),
400            Ast::Pipe(l, true, r) => reduce(w(l).run(cv.clone()), cv.1, move |x, v| {
401                w(r).update((cv.0.clone().cons_var(x), v), f.clone())
402            }),
403            Ast::Comma(l, r) => {
404                let l = w(l).update((cv.0.clone(), cv.1), f.clone());
405                Box::new(
406                    l.flat_map(move |v| then(v, |v| w(r).update((cv.0.clone(), v), f.clone()))),
407                )
408            }
409            Ast::Ite(if_, then_, else_) => reduce(w(if_).run(cv.clone()), cv.1, move |x, v| {
410                w(if x.as_bool() { then_ } else { else_ }).update((cv.0.clone(), v), f.clone())
411            }),
412
413            Ast::Var(v) => match cv.0.vars.get(*v).unwrap() {
414                Bind::Var(_) => err,
415                Bind::Fun(l) => w(&l.0).update((cv.0.with_vars(l.1.clone()), cv.1), f),
416            },
417            Ast::Call(call) => {
418                let def = w(&call.id);
419                let init = cv.1.clone();
420                let ctx = cv.0.clone().skip_vars(call.skip);
421                let cvs = bind_vars(call.args.iter().map(move |a| a.as_ref().map(w)), ctx, cv);
422                reduce(cvs, init, move |cv, v| def.update((cv.0, v), f.clone()))
423            }
424
425            Ast::Native(id, args) => (self.1.natives[*id].update)(Args(args, self.1), cv, f),
426        }
427    }
428}
429
430type Triple<T> = (T, T, T);
431
432/// Function from a value to a stream of value results.
433// TODO for v2.0: Remove `Clone` bound and make sub-trait requiring `Copy` for derived functions
434// try to implement functions on &self?
435pub trait FilterT<'a, V: ValT = Val>: Clone + 'a {
436    /// `f.run((c, v))` returns the output of `v | f` in the context `c`.
437    fn run(self, cv: Cv<'a, V>) -> ValR2s<'a, V>;
438
439    /// `p.update((c, v), f)` returns the output of `v | p |= f` in the context `c`.
440    fn update(self, cv: Cv<'a, V>, f: Box<dyn Update<'a, V> + 'a>) -> ValR2s<'a, V>;
441
442    /// For every value `v` returned by `self.run(cv)`, call `f(cv, v)` and return all results.
443    ///
444    /// This has a special optimisation for the case where only a single `v` is returned.
445    /// In that case, we can consume `cv` instead of cloning it.
446    fn pipe<T: 'a, F>(self, cv: Cv<'a, V>, f: F) -> Results<'a, T, Error<V>>
447    where
448        F: Fn(Cv<'a, V>, V) -> Results<'a, T, Error<V>> + 'a,
449    {
450        flat_map_with(self.run(cv.clone()), cv, move |y, cv| then(y, |y| f(cv, y)))
451    }
452
453    /// Run `self` and `r` and return the cartesian product of their outputs.
454    fn cartesian(self, r: Self, cv: Cv<'a, V>) -> BoxIter<'a, (ValR2<V>, ValR2<V>)> {
455        flat_map_with(self.run(cv.clone()), cv, move |l, cv| {
456            map_with(r.clone().run(cv), l, |r, l| (l, r))
457        })
458    }
459
460    /// Run `self`, `m`, and `r`, and return the cartesian product of their outputs.
461    fn cartesian3(self, m: Self, r: Self, cv: Cv<'a, V>) -> BoxIter<'a, Triple<ValR2<V>>> {
462        flat_map_with(self.run(cv.clone()), cv, move |l, cv| {
463            let r = r.clone();
464            flat_map_with(m.clone().run(cv.clone()), (l, cv), move |m, (l, cv)| {
465                map_with(r.clone().run(cv), (l, m), |r, (l, m)| (l, m, r))
466            })
467        })
468    }
469
470    /// Return the output of `recurse(f)` if `inner` and `outer` are true.
471    ///
472    /// This function implements a generalisation of `recurse(f)`:
473    /// if `inner` is true, it returns values for which `f` yields at least one output, and
474    /// if `outer` is true, it returns values for which `f` yields no output.
475    /// This is useful to implement `while` and `until`.
476    #[deprecated(since = "1.2.0")]
477    fn recurse(self, inner: bool, outer: bool, cv: Cv<'a, V>) -> ValR2s<V> {
478        let f = move |v| self.clone().run((cv.0.clone(), v));
479        Box::new(recurse(inner, outer, box_once(Ok(cv.1)), f))
480    }
481
482    /// Return the output of `recurse(l) |= f`.
483    #[deprecated(since = "1.2.0")]
484    fn recurse_update(self, cv: Cv<'a, V>, f: Box<dyn Update<'a, V> + 'a>) -> ValR2s<'a, V> {
485        // implemented by the expansion of `def recurse(l): ., (l | recurse(l))`
486        Box::new(f(cv.1).flat_map(move |v| {
487            then(v, |v| {
488                let (c, f) = (cv.0.clone(), f.clone());
489                let slf = self.clone();
490                let rec = move |v| slf.clone().recurse_update((c.clone(), v), f.clone());
491                (self.clone()).update((cv.0.clone(), v), Box::new(rec))
492            })
493        }))
494    }
495}