Skip to content

Commit

Permalink
Merge pull request #40 from ecstatic-morse/passthrough-leaper
Browse files Browse the repository at this point in the history
Add a helper function for "passthrough" leapers
  • Loading branch information
nikomatsakis authored Aug 23, 2021
2 parents 5bda2f0 + dfda7fc commit 0973cdd
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub use crate::{
extend_with::ExtendWith,
filter_anti::FilterAnti,
filter_with::FilterWith,
filters::{PrefixFilter, ValueFilter},
filters::{passthrough, PrefixFilter, ValueFilter},
Leaper, Leapers, RelationLeaper,
},
variable::Variable,
Expand Down
26 changes: 26 additions & 0 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,29 @@ fn leapjoin_from_extend() {

assert_eq!(variable.elements, vec![(2, 2), (2, 4)]);
}

#[test]
fn passthrough_leaper() {
let mut iteration = Iteration::new();

let variable = iteration.variable::<(u32, u32)>("variable");
variable.extend((0..10).map(|i| (i, i)));

while iteration.changed() {
variable.from_leapjoin(
&variable,
(
crate::passthrough(), // Without this, the test would fail at runtime.
crate::PrefixFilter::from(|&(i, _)| i <= 20),
),
|&(i, j), ()| (2*i, 2*j),
);
}

let variable = variable.complete();

let mut expected: Vec<_> = (0..10).map(|i| (i, i)).collect();
expected.extend((10..20).filter_map(|i| (i%2 == 0).then(|| (i, i))));
expected.extend((20..=40).filter_map(|i| (i%4 == 0).then(|| (i, i))));
assert_eq!(&*variable, &expected);
}
47 changes: 47 additions & 0 deletions src/treefrog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,53 @@ pub(crate) mod filters {
}
}

pub struct Passthrough<Tuple> {
phantom: ::std::marker::PhantomData<Tuple>,
}

impl<Tuple> Passthrough<Tuple> {
fn new() -> Self {
Passthrough {
phantom: ::std::marker::PhantomData,
}
}
}

impl<'leap, Tuple> Leaper<'leap, Tuple, ()> for Passthrough<Tuple> {
/// Estimates the number of proposed values.
fn count(&mut self, _prefix: &Tuple) -> usize {
1
}
/// Populates `values` with proposed values.
fn propose(&mut self, _prefix: &Tuple, values: &mut Vec<&'leap ()>) {
values.push(&())
}
/// Restricts `values` to proposed values.
fn intersect(&mut self, _prefix: &Tuple, _values: &mut Vec<&'leap ()>) {
// `Passthrough` never removes values (although if we're here it indicates that the user
// didn't need a `Passthrough` in the first place)
}
}

/// Returns a leaper that proposes a single copy of each tuple from the main input.
///
/// Use this when you don't need any "extend" leapers in a join, only "filter"s. For example,
/// in the following datalog rule, all terms in the second and third predicate are bound in the
/// first one (the "main input" to our leapjoin).
///
/// ```prolog
/// error(loan, point) :-
/// origin_contains_loan_at(origin, loan, point), % main input
/// origin_live_at(origin, point),
/// loan_invalidated_at(loan, point).
/// ```
///
/// Without a passthrough leaper, neither the filter for `origin_live_at` nor the one for
/// `loan_invalidated_at` would propose any tuples, and the leapjoin would panic at runtime.
pub fn passthrough<Tuple>() -> Passthrough<Tuple> {
Passthrough::new()
}

/// A treefrog leaper based on a predicate of prefix and value.
/// Use like `ValueFilter::from(|tuple, value| ...)`. The closure
/// should return true if `value` ought to be retained. The
Expand Down

0 comments on commit 0973cdd

Please sign in to comment.