Skip to content

Commit 005701b

Browse files
committed
Implement path filter!
1 parent c359397 commit 005701b

File tree

4 files changed

+150
-138
lines changed

4 files changed

+150
-138
lines changed

jaq-core/src/filter.rs

Lines changed: 136 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,18 @@ fn bind_run<'a, F: FilterT, T: Clone + 'a>(
213213
}
214214
}
215215

216+
fn label_run<'a, V, T: 'a>(
217+
cv: Cv<'a, V, T>,
218+
run: impl Fn(Cv<'a, V, T>) -> Results<'a, T, V>,
219+
) -> Results<'a, T, V> {
220+
let ctx = cv.0.cons_label();
221+
let labels = ctx.labels;
222+
Box::new(run((ctx, cv.1)).map_while(move |y| match y {
223+
Err(Exn(exn::Inner::Break(b))) if b == labels => None,
224+
y => Some(y),
225+
}))
226+
}
227+
216228
fn fold_run<'a, V: Clone, T: Clone + 'a>(
217229
xs: impl Iterator<Item = Result<Ctx<'a, V>, Exn<'a, V>>> + Clone + 'a,
218230
cv: Cv<'a, V, T>,
@@ -293,11 +305,15 @@ fn lazy_is_lazy() {
293305

294306
/// Combination of context and input value.
295307
pub type Cv<'c, V, T = V> = (Ctx<'c, V>, T);
308+
/// Combination of context and input value with a path.
309+
type Cvp<'a, V> = Cv<'a, V, (V, RcList<V>)>;
310+
type ValPathXs<'a, V> = Results<'a, (V, RcList<V>), V>;
296311

297312
/// A filter which is implemented using function pointers.
298313
#[derive(Clone)]
299314
pub struct Native<V> {
300315
run: RunPtr<V>,
316+
paths: PathsPtr<V>,
301317
update: UpdatePtr<V>,
302318
}
303319

@@ -308,6 +324,8 @@ pub struct Native<V> {
308324
/// That would also allow to eliminate `F` from `FilterT`.
309325
pub type RunPtr<V, F = Native<V>> = for<'a> fn(&'a Lut<F>, Cv<'a, V>) -> ValXs<'a, V>;
310326
/// Update function pointer.
327+
pub type PathsPtr<V, F = Native<V>> = for<'a> fn(&'a Lut<F>, Cvp<'a, V>) -> ValPathXs<'a, V>;
328+
/// Update function pointer.
311329
pub type UpdatePtr<V, F = Native<V>> =
312330
for<'a> fn(&'a Lut<F>, Cv<'a, V>, BoxUpdate<'a, V>) -> ValXs<'a, V>;
313331

@@ -316,6 +334,7 @@ impl<V> Native<V> {
316334
pub const fn new(run: RunPtr<V, Self>) -> Self {
317335
Self {
318336
run,
337+
paths: |_, _| box_once(Err(Exn::from(Error::path_expr()))),
319338
update: |_, _, _| box_once(Err(Exn::from(Error::path_expr()))),
320339
}
321340
}
@@ -333,6 +352,10 @@ impl<V: ValT> FilterT for Native<V> {
333352
(self.run)(lut, cv)
334353
}
335354

355+
fn paths<'a>(&'a self, lut: &'a Lut<Self>, cv: Cvp<'a, V>) -> ValPathXs<'a, V> {
356+
(self.paths)(lut, cv)
357+
}
358+
336359
fn update<'a>(
337360
&'a self,
338361
lut: &'a Lut<Self>,
@@ -343,121 +366,6 @@ impl<V: ValT> FilterT for Native<V> {
343366
}
344367
}
345368

346-
type Cp<'a, V> = Cv<'a, V, (V, RcList<V>)>;
347-
use crate::box_iter::BoxIter;
348-
pub type ValPXs<'a, V> = BoxIter<'a, Result<(V, RcList<V>), crate::Exn<'a, V>>>;
349-
350-
impl Id {
351-
fn paths<'a, F: FilterT<F>>(&'a self, lut: &'a Lut<F>, cv: Cp<'a, F::V>) -> ValPXs<'a, F::V> {
352-
// TODO: capture value in path_expr?
353-
let err = box_once(Err(Exn::from(Error::path_expr())));
354-
let proj = |cv: &Cp<'a, F::V>| (cv.0.clone(), cv.1 .0.clone());
355-
match &lut.terms[self.0] {
356-
Ast::ToString => err,
357-
Ast::Int(_) | Ast::Num(_) | Ast::Str(_) => err,
358-
Ast::Arr(_) | Ast::ObjEmpty | Ast::ObjSingle(..) => err,
359-
Ast::Neg(_) | Ast::Logic(..) | Ast::Math(..) | Ast::Cmp(..) => err,
360-
Ast::Update(..) | Ast::UpdateMath(..) | Ast::UpdateAlt(..) | Ast::Assign(..) => err,
361-
Ast::Id => box_once(Ok(cv.1)),
362-
Ast::Pipe(l, None, r) => {
363-
flat_map_then_with(l.paths(lut, (cv.0.clone(), cv.1)), cv.0, move |y, ctx| {
364-
r.paths(lut, (ctx, y))
365-
})
366-
}
367-
Ast::Pipe(l, Some(pat), r) => {
368-
flat_map_then_with(l.run(lut, proj(&cv)), cv, move |y, cv| {
369-
bind_run(pat, r, lut, cv, y, |f, lut, cv| f.paths(lut, cv))
370-
})
371-
}
372-
Ast::Comma(l, r) => Box::new(l.paths(lut, cv.clone()).chain(lazy(|| r.paths(lut, cv)))),
373-
Ast::Alt(l, r) => {
374-
let any_true = l
375-
.run(lut, proj(&cv))
376-
.any(|v| v.as_ref().map_or(true, ValT::as_bool));
377-
if any_true { l } else { r }.paths(lut, cv)
378-
}
379-
Ast::Ite(if_, then_, else_) => {
380-
flat_map_then_with(if_.run(lut, proj(&cv)), cv, move |v, cv| {
381-
if v.as_bool() { then_ } else { else_ }.paths(lut, cv)
382-
})
383-
}
384-
Ast::TryCatch(f, c) => {
385-
Box::new(f.paths(lut, (cv.0.clone(), cv.1)).flat_map(move |y| {
386-
match y {
387-
Err(Exn(exn::Inner::Err(e))) => Box::new(
388-
c.run(lut, (cv.0.clone(), e.into_val()))
389-
.map(|_| Err(Exn::from(Error::path_expr()))),
390-
),
391-
y => box_once(y),
392-
}
393-
}))
394-
}
395-
Ast::Path(f, path) => {
396-
let path = path.map_ref(|i| {
397-
let cv = (cv.0.clone(), cv.1 .0.clone());
398-
crate::into_iter::collect_if_once(move || i.run(lut, cv))
399-
});
400-
flat_map_then_with(f.paths(lut, cv), path, |y, path| {
401-
flat_map_then_with(path.explode(), y, |path, y| {
402-
Box::new(path.paths(y).map(|r| r.map_err(Exn::from)))
403-
})
404-
})
405-
}
406-
Ast::Var(v) => match cv.0.vars.get(*v).unwrap() {
407-
Bind::Var(_) => err,
408-
Bind::Fun(l) => l.0.paths(lut, (cv.0.with_vars(l.1.clone()), cv.1)),
409-
Bind::Label(l) => box_once(Err(Exn(exn::Inner::Break(*l)))),
410-
},
411-
Ast::Fold(xs, pat, init, update, fold_type) => {
412-
let xs = rc_lazy_list::List::from_iter(run_and_bind(xs, lut, proj(&cv), pat));
413-
fold_run(xs, cv, init, update, fold_type, |f, cv| f.paths(lut, cv))
414-
}
415-
Ast::CallDef(id, args, skip, tailrec) => {
416-
use core::ops::ControlFlow;
417-
let with_vars = move |vars| Ctx {
418-
vars,
419-
labels: cv.0.labels,
420-
inputs: cv.0.inputs,
421-
};
422-
let cvs = bind_vars(args, lut, cv.0.clone().skip_vars(*skip), cv, |vp| {
423-
vp.0.clone()
424-
});
425-
match tailrec {
426-
None => flat_map_then(cvs, |cv| id.paths(lut, cv)),
427-
Some(Tailrec::Catch) => Box::new(crate::Stack::new(
428-
[flat_map_then(cvs, |cv| id.paths(lut, cv))].into(),
429-
move |r| match r {
430-
Err(Exn(exn::Inner::TailCall(id_, vars, v, Some(p)))) if id == id_ => {
431-
ControlFlow::Continue(id.paths(lut, (with_vars(vars), (v, p))))
432-
}
433-
Ok(_) | Err(_) => ControlFlow::Break(r),
434-
},
435-
)),
436-
Some(Tailrec::Throw) => Box::new(cvs.map(move |cv| {
437-
cv.and_then(|cv| {
438-
Err(Exn(exn::Inner::TailCall(
439-
id,
440-
cv.0.vars,
441-
cv.1 .0,
442-
Some(cv.1 .1),
443-
)))
444-
})
445-
})),
446-
}
447-
}
448-
Ast::Label(id) => {
449-
let ctx = cv.0.cons_label();
450-
let labels = ctx.labels;
451-
Box::new(id.paths(lut, (ctx, cv.1)).map_while(move |y| match y {
452-
Err(Exn(exn::Inner::Break(b))) if b == labels => None,
453-
y => Some(y),
454-
}))
455-
}
456-
_ => todo!(),
457-
}
458-
}
459-
}
460-
461369
impl<F: FilterT<F>> FilterT<F> for Id {
462370
type V = F::V;
463371

@@ -529,18 +437,12 @@ impl<F: FilterT<F>> FilterT<F> for Id {
529437
Box::new(move |v| f.run(lut, (cv.0.clone(), v))),
530438
),
531439
Ast::UpdateMath(path, op, f) => f.pipe(lut, cv, move |cv, y| {
532-
path.update(
533-
lut,
534-
cv,
535-
Box::new(move |x| box_once(op.run(x, y.clone()).map_err(Exn::from))),
536-
)
440+
let u = move |x: F::V| box_once(op.run(x, y.clone()).map_err(Exn::from));
441+
path.update(lut, cv, Box::new(u))
537442
}),
538443
Ast::UpdateAlt(path, f) => f.pipe(lut, cv, move |cv, y| {
539-
path.update(
540-
lut,
541-
cv,
542-
Box::new(move |x| box_once(Ok(if x.as_bool() { x } else { y.clone() }))),
543-
)
444+
let u = move |x: F::V| box_once(Ok(if x.as_bool() { x } else { y.clone() }));
445+
path.update(lut, cv, Box::new(u))
544446
}),
545447
Ast::Assign(path, f) => f.pipe(lut, cv, move |cv, y| {
546448
path.update(lut, cv, Box::new(move |_| box_once(Ok(y.clone()))))
@@ -598,14 +500,108 @@ impl<F: FilterT<F>> FilterT<F> for Id {
598500
let cvs = bind_vars(args, lut, Ctx::new([], cv.0.inputs), cv, Clone::clone);
599501
flat_map_then(cvs, |cv| lut.funs[*id].run(lut, cv))
600502
}
601-
Ast::Label(id) => {
602-
let ctx = cv.0.cons_label();
603-
let labels = ctx.labels;
604-
Box::new(id.run(lut, (ctx, cv.1)).map_while(move |y| match y {
605-
Err(Exn(exn::Inner::Break(b))) if b == labels => None,
606-
y => Some(y),
503+
Ast::Label(id) => label_run(cv, |cv| id.run(lut, cv)),
504+
}
505+
}
506+
507+
fn paths<'a>(&'a self, lut: &'a Lut<F>, cv: Cvp<'a, F::V>) -> ValPathXs<'a, F::V> {
508+
// TODO: capture value in path_expr?
509+
let err = box_once(Err(Exn::from(Error::path_expr())));
510+
let proj_cv = |cv: &Cvp<'a, F::V>| (cv.0.clone(), cv.1 .0.clone());
511+
let proj_val = |(val, _path): &(F::V, _)| val.clone();
512+
match &lut.terms[self.0] {
513+
Ast::ToString => err,
514+
Ast::Int(_) | Ast::Num(_) | Ast::Str(_) => err,
515+
Ast::Arr(_) | Ast::ObjEmpty | Ast::ObjSingle(..) => err,
516+
Ast::Neg(_) | Ast::Logic(..) | Ast::Math(..) | Ast::Cmp(..) => err,
517+
Ast::Update(..) | Ast::UpdateMath(..) | Ast::UpdateAlt(..) | Ast::Assign(..) => err,
518+
Ast::Id => box_once(Ok(cv.1)),
519+
Ast::Pipe(l, None, r) => {
520+
flat_map_then_with(l.paths(lut, (cv.0.clone(), cv.1)), cv.0, move |y, ctx| {
521+
r.paths(lut, (ctx, y))
522+
})
523+
}
524+
Ast::Pipe(l, Some(pat), r) => {
525+
flat_map_then_with(l.run(lut, proj_cv(&cv)), cv, move |y, cv| {
526+
bind_run(pat, r, lut, cv, y, |f, lut, cv| f.paths(lut, cv))
527+
})
528+
}
529+
Ast::Comma(l, r) => Box::new(l.paths(lut, cv.clone()).chain(lazy(|| r.paths(lut, cv)))),
530+
Ast::Alt(l, r) => {
531+
let any_true = l
532+
.run(lut, proj_cv(&cv))
533+
.any(|v| v.as_ref().map_or(true, ValT::as_bool));
534+
if any_true { l } else { r }.paths(lut, cv)
535+
}
536+
Ast::Ite(if_, then_, else_) => {
537+
flat_map_then_with(if_.run(lut, proj_cv(&cv)), cv, move |v, cv| {
538+
if v.as_bool() { then_ } else { else_ }.paths(lut, cv)
539+
})
540+
}
541+
Ast::TryCatch(f, c) => {
542+
Box::new(f.paths(lut, (cv.0.clone(), cv.1)).flat_map(move |y| {
543+
match y {
544+
Err(Exn(exn::Inner::Err(e))) => Box::new(
545+
c.run(lut, (cv.0.clone(), e.into_val()))
546+
.map(|_| Err(Exn::from(Error::path_expr()))),
547+
),
548+
y => box_once(y),
549+
}
607550
}))
608551
}
552+
Ast::Path(f, path) => {
553+
let path = path.map_ref(|i| {
554+
let cv = (cv.0.clone(), cv.1 .0.clone());
555+
crate::into_iter::collect_if_once(move || i.run(lut, cv))
556+
});
557+
flat_map_then_with(f.paths(lut, cv), path, |y, path| {
558+
flat_map_then_with(path.explode(), y, |path, y| {
559+
Box::new(path.paths(y).map(|r| r.map_err(Exn::from)))
560+
})
561+
})
562+
}
563+
Ast::Var(v) => match cv.0.vars.get(*v).unwrap() {
564+
Bind::Var(_) => err,
565+
Bind::Fun(l) => l.0.paths(lut, (cv.0.with_vars(l.1.clone()), cv.1)),
566+
Bind::Label(l) => box_once(Err(Exn(exn::Inner::Break(*l)))),
567+
},
568+
Ast::Fold(xs, pat, init, update, fold_type) => {
569+
let xs = rc_lazy_list::List::from_iter(run_and_bind(xs, lut, proj_cv(&cv), pat));
570+
fold_run(xs, cv, init, update, fold_type, |f, cv| f.paths(lut, cv))
571+
}
572+
Ast::CallDef(id, args, skip, tailrec) => {
573+
use core::ops::ControlFlow;
574+
let with_vars = move |vars| Ctx {
575+
vars,
576+
labels: cv.0.labels,
577+
inputs: cv.0.inputs,
578+
};
579+
let cvs = bind_vars(args, lut, cv.0.clone().skip_vars(*skip), cv, proj_val);
580+
match tailrec {
581+
None => flat_map_then(cvs, |cv| id.paths(lut, cv)),
582+
Some(Tailrec::Catch) => Box::new(crate::Stack::new(
583+
[flat_map_then(cvs, |cv| id.paths(lut, cv))].into(),
584+
move |r| match r {
585+
Err(Exn(exn::Inner::TailCall(id_, vars, v, Some(p)))) if id == id_ => {
586+
ControlFlow::Continue(id.paths(lut, (with_vars(vars), (v, p))))
587+
}
588+
Ok(_) | Err(_) => ControlFlow::Break(r),
589+
},
590+
)),
591+
Some(Tailrec::Throw) => Box::new(cvs.map(move |cv| {
592+
cv.and_then(|cv| {
593+
let val = cv.1 .0;
594+
let path = Some(cv.1 .1);
595+
Err(Exn(exn::Inner::TailCall(id, cv.0.vars, val, path)))
596+
})
597+
})),
598+
}
599+
}
600+
Ast::Label(id) => label_run(cv, |cv| id.paths(lut, cv)),
601+
Ast::Native(id, args) => {
602+
let cvs = bind_vars(args, lut, Ctx::new([], cv.0.inputs), cv, proj_val);
603+
flat_map_then(cvs, |cv| lut.funs[*id].paths(lut, cv))
604+
}
609605
}
610606
}
611607

@@ -697,10 +693,17 @@ pub trait FilterT<F: FilterT<F, V = Self::V> = Self> {
697693
/// This is an associated type because it is strictly determined by `F`.
698694
type V: ValT;
699695

700-
/// `f.run((c, v))` returns the output of `v | f` in the context `c`.
696+
/// `f.run(lut, (c, v))` returns the output of `v | f` in the context `c`.
701697
fn run<'a>(&'a self, lut: &'a Lut<F>, cv: Cv<'a, Self::V>) -> ValXs<'a, Self::V>;
702698

703-
/// `p.update((c, v), f)` returns the output of `v | p |= f` in the context `c`.
699+
/// `f.paths(lut, (c, (v, p)))` returns the outputs and paths of `v | f` in the context `c`,
700+
/// where `v` is assumed to be at path `p`.
701+
///
702+
/// In particular, `v | path(f)` in context `c` yields the same paths as
703+
/// `f.paths(lut, (c, (v, Default::default())))`.
704+
fn paths<'a>(&'a self, lut: &'a Lut<F>, cv: Cvp<'a, Self::V>) -> ValPathXs<'a, Self::V>;
705+
706+
/// `p.update(lut, (c, v), f)` returns the output of `v | p |= f` in the context `c`.
704707
fn update<'a>(
705708
&'a self,
706709
lut: &'a Lut<F>,

jaq-core/src/path.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Paths and their parts.
22
33
use crate::box_iter::{self, box_once, flat_map_with, map_with, then, BoxIter};
4-
use crate::val::{ValPR, ValR, ValT, ValX, ValXs};
4+
use crate::val::{ValR, ValT, ValX, ValXs};
55
use crate::RcList;
66
use alloc::{boxed::Box, vec::Vec};
77

@@ -79,7 +79,7 @@ impl<'a, V: ValT + 'a> Path<V> {
7979
run(self.0.into_iter(), v, |part, v| part.run(v))
8080
}
8181

82-
pub(crate) fn paths(self, vp: (V, RcList<V>)) -> BoxIter<'a, ValPR<V>> {
82+
pub(crate) fn paths(self, vp: (V, RcList<V>)) -> Results<'a, (V, RcList<V>), V> {
8383
run(self.0.into_iter(), vp, |part, vp| part.paths(vp))
8484
}
8585

@@ -134,7 +134,7 @@ impl<'a, V: ValT + 'a> Part<V> {
134134
}
135135
}
136136

137-
fn paths(&self, (v, p): (V, RcList<V>)) -> BoxIter<'a, ValPR<V>> {
137+
fn paths(&self, (v, p): (V, RcList<V>)) -> Results<'a, (V, RcList<V>), V> {
138138
let cons = |p: RcList<V>| |v: V| (v, p.cons(V::from(self.as_ref().map(Clone::clone))));
139139
match self {
140140
Self::Index(idx) => box_once(v.index(idx).map(cons(p))),

jaq-core/src/val.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
66
use crate::box_iter::BoxIter;
77
use crate::path::{self, Opt};
8-
use crate::RcList;
98
use core::fmt::Display;
109
use core::ops::{Add, Div, Mul, Neg, Rem, Sub};
1110

@@ -15,7 +14,6 @@ use core::str::FromStr;
1514

1615
/// Value or eRror.
1716
pub type ValR<V> = Result<V, crate::Error<V>>;
18-
pub type ValPR<V> = Result<(V, RcList<V>), crate::Error<V>>;
1917
/// Value or eXception.
2018
pub type ValX<'a, V> = Result<V, crate::Exn<'a, V>>;
2119
/// Stream of values and eXceptions.

0 commit comments

Comments
 (0)