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

Add a replace action #226

Closed
wants to merge 4 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
37 changes: 37 additions & 0 deletions src/ast/desugar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,32 @@ fn flatten_actions(actions: &Vec<Action>, desugar: &mut Desugar) -> Vec<NormActi
);
res.push(set);
}
Action::Replace {
old_constructor: constructor,
new_constructor: other_constructor,
old_args,
new_args,
new_output,
} => {
let replace = NormAction::Replace(
NormExpr::Call(
*constructor,
old_args
.iter()
.map(|ex| add_expr(ex.clone(), &mut res))
.collect(),
),
NormExpr::Call(
*other_constructor,
new_args
.iter()
.map(|ex| add_expr(ex.clone(), &mut res))
.collect(),
),
add_expr(new_output.clone(), &mut res),
);
res.push(replace);
}
Action::Extract(expr, variants) => {
let added = add_expr(expr.clone(), &mut res);
let added_variants = add_expr(variants.clone(), &mut res);
Expand Down Expand Up @@ -385,6 +411,17 @@ fn add_semi_naive_rule(desugar: &mut Desugar, rule: Rule) -> Option<Rule> {
new_head_atoms.push(Fact::Eq(vec![fresh_var, expr]));
};
}
Action::Replace { new_output, .. } => {
var_set.extend(new_output.vars());
if let Expr::Call(_, _) = new_output {
add_new_rule = true;

let fresh_symbol = desugar.get_fresh();
let fresh_var = Expr::Var(fresh_symbol);
let new_output = std::mem::replace(new_output, fresh_var.clone());
new_head_atoms.push(Fact::Eq(vec![fresh_var, new_output]));
};
}
Action::Let(symbol, expr) if var_set.contains(symbol) => {
var_set.extend(expr.vars());
if let Expr::Call(_, _) = expr {
Expand Down
127 changes: 127 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,21 @@ pub enum Action {
Let(Symbol, Expr),
Set(Symbol, Vec<Expr>, Expr),
Delete(Symbol, Vec<Expr>),
/// Replace a row in a table with another row.
/// Equivalent to `delete` followed by `set`,
/// except that it is aware when the new row is
/// the same as the old and can therefore saturate
/// in this case.
///
/// If the constructors are different, simply deletes
/// from one table and adds to another.
Replace {
old_constructor: Symbol,
new_constructor: Symbol,
old_args: Vec<Expr>,
new_args: Vec<Expr>,
new_output: Expr,
},
Union(Expr, Expr),
Extract(Expr, Expr),
Panic(String),
Expand All @@ -758,6 +773,7 @@ pub enum NormAction {
Extract(Symbol, Symbol),
Set(NormExpr, Symbol),
Delete(NormExpr),
Replace(NormExpr, NormExpr, Symbol),
Union(Symbol, Symbol),
Panic(String),
}
Expand All @@ -779,6 +795,17 @@ impl NormAction {
NormAction::Delete(NormExpr::Call(symbol, args)) => {
Action::Delete(*symbol, args.iter().map(|s| Expr::Var(*s)).collect())
}
NormAction::Replace(
NormExpr::Call(head, body),
NormExpr::Call(head2, body2),
output,
) => Action::Replace {
old_constructor: *head,
new_constructor: *head2,
old_args: body.iter().map(|s| Expr::Var(*s)).collect(),
new_args: body2.iter().map(|s| Expr::Var(*s)).collect(),
new_output: Expr::Var(*output),
},
NormAction::Union(lhs, rhs) => Action::Union(Expr::Var(*lhs), Expr::Var(*rhs)),
NormAction::Panic(msg) => Action::Panic(msg.clone()),
}
Expand All @@ -790,6 +817,9 @@ impl NormAction {
NormAction::LetVar(symbol, other) => NormAction::LetVar(*symbol, *other),
NormAction::LetLit(symbol, lit) => NormAction::LetLit(*symbol, lit.clone()),
NormAction::Set(expr, other) => NormAction::Set(f(expr), *other),
NormAction::Replace(expr1, expr2, other) => {
NormAction::Replace(f(expr1), f(expr2), *other)
}
NormAction::Extract(var, variants) => NormAction::Extract(*var, *variants),
NormAction::Delete(expr) => NormAction::Delete(f(expr)),
NormAction::Union(lhs, rhs) => NormAction::Union(*lhs, *rhs),
Expand All @@ -810,6 +840,11 @@ impl NormAction {
NormAction::Set(expr, other) => {
NormAction::Set(expr.map_def_use(fvar, false), fvar(*other, false))
}
NormAction::Replace(expr1, expr2, other) => NormAction::Replace(
expr1.map_def_use(fvar, false),
expr2.map_def_use(fvar, false),
fvar(*other, false),
),
NormAction::Extract(var, variants) => {
NormAction::Extract(fvar(*var, false), fvar(*variants, false))
}
Expand All @@ -825,6 +860,20 @@ impl ToSexp for Action {
match self {
Action::Let(lhs, rhs) => list!("let", lhs, rhs),
Action::Set(lhs, args, rhs) => list!("set", list!(lhs, ++ args), rhs),
Action::Replace {
old_constructor: constructor,
new_constructor: other_constructor,
old_args,
new_args,
new_output,
} => {
list!(
"replace",
list!(constructor, ++ old_args),
list!(other_constructor, ++ new_args),
new_output
)
}
Action::Union(lhs, rhs) => list!("union", lhs, rhs),
Action::Delete(lhs, args) => list!("delete", list!(lhs, ++ args)),
Action::Extract(expr, variants) => list!("extract", expr, variants),
Expand All @@ -842,6 +891,27 @@ impl Action {
let right = f(rhs);
Action::Set(*lhs, args.iter().map(f).collect(), right)
}
Action::Replace {
old_constructor: constructor,
new_constructor: other_constructor,
old_args,
new_args,
new_output,
} => {
// use for loop because of borrow error
// with passing f to map
let mut mapped_old_args: Vec<Expr> = vec![];
for entry in old_args {
mapped_old_args.push(f(entry));
}
Action::Replace {
old_constructor: *constructor,
new_constructor: *other_constructor,
old_args: mapped_old_args,
new_output: f(new_output),
new_args: new_args.iter().map(f).collect(),
}
}
Action::Delete(lhs, args) => Action::Delete(*lhs, args.iter().map(f).collect()),
Action::Union(lhs, rhs) => Action::Union(f(lhs), f(rhs)),
Action::Extract(expr, variants) => Action::Extract(f(expr), f(variants)),
Expand All @@ -858,6 +928,19 @@ impl Action {
args.iter().map(|e| e.subst(canon)).collect(),
rhs.subst(canon),
),
Action::Replace {
old_constructor: constructor,
new_constructor: other_constructor,
old_args,
new_args,
new_output,
} => Action::Replace {
old_constructor: *constructor,
new_constructor: *other_constructor,
old_args: old_args.iter().map(|e| e.subst(canon)).collect(),
new_args: new_args.iter().map(|e| e.subst(canon)).collect(),
new_output: new_output.subst(canon),
},
Action::Delete(lhs, args) => {
Action::Delete(*lhs, args.iter().map(|e| e.subst(canon)).collect())
}
Expand Down Expand Up @@ -985,6 +1068,9 @@ impl NormRule {
res
}

// TODO this function isn't actually correct right now
// because actions can fail halfway-through.
// If we fix that bug, we can run this.
pub fn resugar_actions(&self, subst: &mut HashMap<Symbol, Expr>) -> Vec<Action> {
let mut used = HashSet::<Symbol>::default();
let mut head = Vec::<Action>::default();
Expand All @@ -1001,6 +1087,8 @@ impl NormRule {
let substituted = new_expr.subst(subst);

// TODO sometimes re-arranging actions is bad
// this is because actions can fail
// halfway through in the current semantics
if substituted.ast_size() > 1 {
head.push(Action::Let(*symbol, substituted));
} else {
Expand Down Expand Up @@ -1040,6 +1128,45 @@ impl NormRule {
_ => panic!("Expected call in set"),
}
}
NormAction::Replace(expr1, expr2, output_var) => {
let new_expr1 = expr1.to_expr();
new_expr1.map(&mut |subexpr| {
if let Expr::Var(v) = subexpr {
used.insert(*v);
}
subexpr.clone()
});
let new_expr2 = expr2.to_expr();
new_expr2.map(&mut |subexpr| {
if let Expr::Var(v) = subexpr {
used.insert(*v);
}
subexpr.clone()
});
let new_output = subst
.get(output_var)
.unwrap_or(&Expr::Var(*output_var))
.clone();
used.insert(*output_var);
let substituted1 = new_expr1.subst(subst);
let substituted2 = new_expr2.subst(subst);
match (substituted1, substituted2) {
(
Expr::Call(constructor, old_args),
Expr::Call(other_constructor, new_args),
) => {
assert!(constructor == other_constructor);
head.push(Action::Replace {
old_constructor: constructor,
new_constructor: other_constructor,
old_args,
new_args,
new_output,
});
}
_ => panic!("Expected call in replace"),
}
}
NormAction::Delete(expr) => {
let new_expr = expr.to_expr();
new_expr.map(&mut |subexpr| {
Expand Down
2 changes: 2 additions & 0 deletions src/ast/parse.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ Cost: Option<usize> = {
NonLetAction: Action = {
LParen "set" LParen <f: Ident> <args:Expr*> RParen <v:Expr> RParen => Action::Set ( f, args, v ),
LParen "delete" LParen <f: Ident> <args:Expr*> RParen RParen => Action::Delete ( f, args),
LParen "replace" LParen <old_constructor: Ident> <old_args:Expr*> RParen
LParen <new_constructor: Ident> <new_args:Expr*> RParen <new_output:Expr> RParen => Action::Replace { new_constructor, old_constructor, old_args, new_args, new_output},
LParen "union" <e1:Expr> <e2:Expr> RParen => Action::Union(<>),
LParen "panic" <msg:String> RParen => Action::Panic(msg),
LParen "extract" <expr:Expr> RParen => Action::Extract(expr, Expr::Lit(Literal::Int(0))),
Expand Down
Loading