Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MultiPatterns #168

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ mod explain;
mod extract;
mod language;
mod machine;
mod multipattern;
mod pattern;
mod rewrite;
mod run;
Expand Down Expand Up @@ -84,6 +85,7 @@ pub use {
explain::{Explanation, FlatExplanation, FlatTerm, TreeExplanation, TreeTerm},
extract::*,
language::*,
multipattern::*,
pattern::{ENodeOrVar, Pattern, PatternAst, SearchMatches},
rewrite::{Applier, Condition, ConditionEqual, ConditionalApplier, Rewrite, Searcher},
run::*,
Expand Down
130 changes: 91 additions & 39 deletions src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ enum Instruction<L> {
Bind { node: L, i: Reg, out: Reg },
Compare { i: Reg, j: Reg },
Lookup { term: Vec<ENodeOrReg<L>>, i: Reg },
Scan { out: Reg },
}

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -98,6 +99,15 @@ impl Machine {
self.run(egraph, remaining_instructions, subst, yield_fn)
});
}
Instruction::Scan { out } => {
let remaining_instructions = instructions.as_slice();
for class in egraph.classes() {
self.reg.truncate(out.0 as usize);
self.reg.push(class.id);
self.run(egraph, remaining_instructions, subst, yield_fn)
}
return;
}
Instruction::Compare { i, j } => {
if egraph.find(self.reg(*i)) != egraph.find(self.reg(*j)) {
return;
Expand Down Expand Up @@ -132,52 +142,29 @@ impl Machine {
}
}

struct Compiler<'a, L> {
pattern: &'a PatternAst<L>,
struct Compiler<L> {
v2r: IndexMap<Var, Reg>,
free_vars: Vec<HashSet<Var>>,
subtree_size: Vec<usize>,
todo_nodes: HashMap<(Id, Reg), L>,
instructions: Vec<Instruction<L>>,
next_reg: Reg,
}

impl<'a, L: Language> Compiler<'a, L> {
fn new(pattern: &'a PatternAst<L>) -> Self {
let len = pattern.as_ref().len();
let mut free_vars: Vec<HashSet<Var>> = Vec::with_capacity(len);
let mut subtree_size = Vec::with_capacity(len);

for node in pattern.as_ref() {
let mut free = HashSet::default();
let mut size = 0;
match node {
ENodeOrVar::ENode(n) => {
size = 1;
for &child in n.children() {
free.extend(&free_vars[usize::from(child)]);
size += subtree_size[usize::from(child)];
}
}
ENodeOrVar::Var(v) => {
free.insert(*v);
}
}
free_vars.push(free);
subtree_size.push(size);
}

impl<L: Language> Compiler<L> {
fn new() -> Self {
Self {
pattern,
free_vars,
subtree_size,
free_vars: Default::default(),
subtree_size: Default::default(),
v2r: Default::default(),
todo_nodes: Default::default(),
instructions: Default::default(),
next_reg: Reg(0),
}
}

fn add_todo(&mut self, id: Id, reg: Reg) {
match &self.pattern[id] {
fn add_todo(&mut self, pattern: &PatternAst<L>, id: Id, reg: Reg) {
match &pattern[id] {
ENodeOrVar::Var(v) => {
if let Some(&j) = self.v2r.get(v) {
self.instructions.push(Instruction::Compare { i: reg, j })
Expand All @@ -191,6 +178,31 @@ impl<'a, L: Language> Compiler<'a, L> {
}
}

fn load_pattern(&mut self, pattern: &PatternAst<L>) {
let len = pattern.as_ref().len();
self.free_vars = Vec::with_capacity(len);
self.subtree_size = Vec::with_capacity(len);

for node in pattern.as_ref() {
let mut free = HashSet::default();
let mut size = 0;
match node {
ENodeOrVar::ENode(n) => {
size = 1;
for &child in n.children() {
free.extend(&self.free_vars[usize::from(child)]);
size += self.subtree_size[usize::from(child)];
}
}
ENodeOrVar::Var(v) => {
free.insert(*v);
}
}
self.free_vars.push(free);
self.subtree_size.push(size);
}
}

fn next(&mut self) -> Option<((Id, Reg), L)> {
// we take the max todo according to this key
// - prefer grounded
Expand Down Expand Up @@ -222,15 +234,42 @@ impl<'a, L: Language> Compiler<'a, L> {
.all(|v| self.v2r.contains_key(v))
}

fn compile(mut self) -> Program<L> {
let last_i = self.pattern.as_ref().len() - 1;
let mut next_out = Reg(1);
fn compile(&mut self, patternbinder: Option<Var>, pattern: &PatternAst<L>) {
self.load_pattern(pattern);
let last_i = pattern.as_ref().len() - 1;

let mut next_out = self.next_reg;

// Check if patternbinder already bound in v2r
// Behavior common to creating a new pattern
let add_new_pattern = |comp: &mut Compiler<L>| {
if !comp.instructions.is_empty() {
// After first pattern needs scan
comp.instructions
.push(Instruction::Scan { out: comp.next_reg });
}
comp.add_todo(pattern, Id::from(last_i), comp.next_reg);
};

self.add_todo(Id::from(last_i), Reg(0));
if let Some(v) = patternbinder {
if let Some(&i) = self.v2r.get(&v) {
// patternbinder already bound
self.add_todo(pattern, Id::from(last_i), i);
} else {
// patternbinder is new variable
next_out.0 += 1;
add_new_pattern(self);
self.v2r.insert(v, self.next_reg); //add to known variables.
}
} else {
// No pattern binder
next_out.0 += 1;
add_new_pattern(self);
}

while let Some(((id, reg), node)) = self.next() {
if self.is_ground_now(id) && !node.is_leaf() {
let extracted = self.pattern.extract(id);
let extracted = pattern.extract(id);
self.instructions.push(Instruction::Lookup {
i: reg,
term: extracted
Expand All @@ -255,11 +294,14 @@ impl<'a, L: Language> Compiler<'a, L> {
});

for (i, &child) in node.children().iter().enumerate() {
self.add_todo(child, Reg(out.0 + i as u32));
self.add_todo(pattern, child, Reg(out.0 + i as u32));
}
}
}
self.next_reg = next_out;
}

fn extract(self) -> Program<L> {
let mut subst = Subst::default();
for (v, r) in self.v2r {
subst.insert(v, Id::from(r.0 as usize));
Expand All @@ -273,11 +315,21 @@ impl<'a, L: Language> Compiler<'a, L> {

impl<L: Language> Program<L> {
pub(crate) fn compile_from_pat(pattern: &PatternAst<L>) -> Self {
let program = Compiler::new(pattern).compile();
let mut compiler = Compiler::new();
compiler.compile(None, pattern);
let program = compiler.extract();
log::debug!("Compiled {:?} to {:?}", pattern.as_ref(), program);
program
}

pub(crate) fn compile_from_multi_pat(patterns: &[(Var, PatternAst<L>)]) -> Self {
let mut compiler = Compiler::new();
for (var, pattern) in patterns {
compiler.compile(Some(*var), pattern);
}
compiler.extract()
}

pub fn run<A>(&self, egraph: &EGraph<L, A>, eclass: Id) -> Vec<Subst>
where
A: Analysis<L>,
Expand Down
30 changes: 25 additions & 5 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,8 +285,8 @@ macro_rules! rewrite {
$lhs:tt => $rhs:tt
$(if $cond:expr)*
) => {{
let searcher = $crate::__rewrite!(@parse $lhs);
let core_applier = $crate::__rewrite!(@parse $rhs);
let searcher = $crate::__rewrite!(@parse Pattern $lhs);
let core_applier = $crate::__rewrite!(@parse Pattern $rhs);
let applier = $crate::__rewrite!(@applier core_applier; $($cond,)*);
$crate::Rewrite::new($name.to_string(), searcher, applier).unwrap()
}};
Expand All @@ -304,13 +304,33 @@ macro_rules! rewrite {
}};
}

/** A macro to easily make [`Rewrite`]s using [`MultiPattern`]s.

Similar to the [`rewrite!`] macro,
this macro uses the form `multi_rewrite!(name; multipattern => multipattern)`.
String literals will be parsed a [`MultiPattern`]s.

**/
#[macro_export]
macro_rules! multi_rewrite {
// limited multipattern support
(
$name:expr;
$lhs:tt => $rhs:tt
) => {{
let searcher = $crate::__rewrite!(@parse MultiPattern $lhs);
let applier = $crate::__rewrite!(@parse MultiPattern $rhs);
$crate::Rewrite::new($name.to_string(), searcher, applier).unwrap()
}};
}

#[doc(hidden)]
#[macro_export]
macro_rules! __rewrite {
(@parse $rhs:literal) => {
$rhs.parse::<$crate::Pattern<_>>().unwrap()
(@parse $t:ident $rhs:literal) => {
$rhs.parse::<$crate::$t<_>>().unwrap()
};
(@parse $rhs:expr) => { $rhs };
(@parse $t:ident $rhs:expr) => { $rhs };
(@applier $applier:expr;) => { $applier };
(@applier $applier:expr; $cond:expr, $($conds:expr,)*) => {
$crate::ConditionalApplier {
Expand Down
Loading