Skip to content

Commit

Permalink
Implements call-by-name properly.
Browse files Browse the repository at this point in the history
  • Loading branch information
remexre committed Jun 12, 2018
1 parent 8be4005 commit 6c21e0f
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 105 deletions.
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ authors = ["Nathan Ringo <[email protected]>"]
description = "A simple term-rewriting interpreter that displays intermediate expressions."
license = "Apache-2.0/MIT"
name = "evaltrees"
version = "0.0.1"
version = "0.1.0-pre"

[dependencies]
display_attr = "0.1.1"
Expand Down
200 changes: 106 additions & 94 deletions src/eval/name.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use failure::Error;
use symbol::Symbol;

use ast::{Decl, Expr, Literal, Op, PrintStyle};
use eval::util::{apply, beta_number, reducible};
use eval::util::{apply, arg_normal_enough, beta_number, reducible};
use eval::Evaluator;

/// Call-by-name evaluation.
Expand Down Expand Up @@ -60,15 +61,24 @@ fn step<Aux: Clone>(expr: Expr<Aux>, decls: &[Decl<Aux>]) -> Result<Expr<Aux>, E
Expr::Op(Op::App, l, r, aux) => match beta {
Some(n) if n > 0 => Expr::Op(Op::App, l, r, aux),
Some(0) => {
debug!("rip strictness analysis");
let mut args = vec![*r];
let mut func = *l;
while let Expr::Op(Op::App, f, a, _) = func {
let mut r_types = vec![aux];
while let Expr::Op(Op::App, f, a, ty) = func {
args.push(*a);
func = *f;
r_types.push(ty);
}
args.reverse();
apply(func, args, decls)?
let func_name = match func {
Expr::Variable(var, _) => var,
func => panic!("Invalid callable expression: {}", func),
};
if let Some(n) = check_arg_normalization(func_name, &args, decls) {
normalize_arg(n, func, args, r_types, decls)?
} else {
apply(func_name, args, decls)?
}
}
_ => Expr::Op(Op::App, Box::new(step(*l, decls)?), r, aux),
},
Expand Down Expand Up @@ -97,6 +107,19 @@ fn step<Aux: Clone>(expr: Expr<Aux>, decls: &[Decl<Aux>]) -> Result<Expr<Aux>, E
Ok(expr)
}

fn check_arg_normalization<Aux: Clone>(
func: Symbol,
args: &[Expr<Aux>],
decls: &[Decl<Aux>],
) -> Option<usize> {
for (i, a) in args.iter().enumerate() {
if !arg_normal_enough(a, i, func, decls) {
return Some(i);
}
}
None
}

fn math_op<Aux: Clone, F: Fn(usize, usize) -> Result<usize, Error>>(
op: Op,
l: Box<Expr<Aux>>,
Expand All @@ -116,30 +139,52 @@ fn math_op<Aux: Clone, F: Fn(usize, usize) -> Result<usize, Error>>(
}
}

fn normalize_arg<Aux: Clone>(
n: usize,
func: Expr<Aux>,
mut args: Vec<Expr<Aux>>,
mut r_types: Vec<Aux>,
decls: &[Decl<Aux>],
) -> Result<Expr<Aux>, Error> {
assert_eq!(args.len(), r_types.len());
args.reverse();
let mut out = func;
let mut i = 0;
while let Some(arg) = args.pop() {
let arg = if i == n { step(arg, decls)? } else { arg };
i += 1;
out = Expr::Op(
Op::App,
Box::new(out),
Box::new(arg),
r_types.pop().unwrap(),
);
}
assert_eq!(n, 0);
Ok(out)
}

#[cfg(test)]
mod tests {
use super::*;
use ast::{Pattern, Type};
use ast::Pattern;

#[test]
fn app() {
let mut evaluator = CallByName::new(vec![
Decl {
name: "f".into(),
args: vec![
Pattern::Binding("x".into(), Type::Int),
Pattern::Binding("y".into(), Type::Int),
Pattern::Binding("x".into(), ()),
Pattern::Binding("y".into(), ()),
],
body: Expr::Op(
Op::Add,
Box::new(Expr::Variable("x".into(), Type::Int)),
Box::new(Expr::Variable("y".into(), Type::Int)),
Type::Int,
),
aux: Type::Func(
Box::new(Type::Int),
Box::new(Type::Func(Box::new(Type::Int), Box::new(Type::Int))),
Box::new(Expr::Variable("x".into(), ())),
Box::new(Expr::Variable("y".into(), ())),
(),
),
aux: (),
},
Decl {
name: "".into(),
Expand All @@ -148,30 +193,24 @@ mod tests {
Op::App,
Box::new(Expr::Op(
Op::App,
Box::new(Expr::Variable(
"f".into(),
Type::Func(
Box::new(Type::Int),
Box::new(Type::Func(Box::new(Type::Int), Box::new(Type::Int))),
),
)),
Box::new(Expr::Variable("f".into(), ())),
Box::new(Expr::Op(
Op::Add,
Box::new(Expr::Literal(Literal::Int(1), Type::Int)),
Box::new(Expr::Literal(Literal::Int(2), Type::Int)),
Type::Int,
Box::new(Expr::Literal(Literal::Int(1), ())),
Box::new(Expr::Literal(Literal::Int(2), ())),
(),
)),
Type::Int,
(),
)),
Box::new(Expr::Op(
Op::Add,
Box::new(Expr::Literal(Literal::Int(3), Type::Int)),
Box::new(Expr::Literal(Literal::Int(4), Type::Int)),
Type::Int,
Box::new(Expr::Literal(Literal::Int(3), ())),
Box::new(Expr::Literal(Literal::Int(4), ())),
(),
)),
Type::Int,
(),
),
aux: Type::Int,
aux: (),
},
]);

Expand Down Expand Up @@ -205,68 +244,50 @@ mod tests {
Decl {
name: "f".into(),
args: vec![
Pattern::Literal(Literal::Nil, Type::List(Box::new(Type::Int))),
Pattern::Binding("y".into(), Type::Int),
Pattern::Literal(Literal::Nil, ()),
Pattern::Binding("y".into(), ()),
],
body: Expr::Variable("y".into(), Type::Int),
aux: Type::Func(
Box::new(Type::List(Box::new(Type::Int))),
Box::new(Type::Func(Box::new(Type::Int), Box::new(Type::Int))),
),
body: Expr::Variable("y".into(), ()),
aux: (),
},
Decl {
name: "f".into(),
args: vec![
Pattern::Cons(
Box::new(Pattern::Binding("h".into(), Type::Int)),
Box::new(Pattern::Binding("t".into(), Type::Int)),
Type::List(Box::new(Type::Int)),
Box::new(Pattern::Binding("h".into(), ())),
Box::new(Pattern::Binding("t".into(), ())),
(),
),
Pattern::Binding("y".into(), Type::Int),
Pattern::Binding("y".into(), ()),
],
body: Expr::Op(
Op::Add,
Box::new(Expr::Variable("h".into(), Type::Int)),
Box::new(Expr::Variable("h".into(), ())),
Box::new(Expr::Op(
Op::App,
Box::new(Expr::Op(
Op::App,
Box::new(Expr::Variable(
"f".into(),
Type::Func(
Box::new(Type::List(Box::new(Type::Int))),
Box::new(Type::Func(Box::new(Type::Int), Box::new(Type::Int))),
),
)),
Box::new(Expr::Variable("t".into(), Type::Int)),
Type::Func(Box::new(Type::Int), Box::new(Type::Int)),
Box::new(Expr::Variable("f".into(), ())),
Box::new(Expr::Variable("t".into(), ())),
(),
)),
Box::new(Expr::Variable("y".into(), Type::Int)),
Type::Int,
Box::new(Expr::Variable("y".into(), ())),
(),
)),
Type::Func(
Box::new(Type::List(Box::new(Type::Int))),
Box::new(Type::Func(Box::new(Type::Int), Box::new(Type::Int))),
),
),
aux: Type::Func(
Box::new(Type::List(Box::new(Type::Int))),
Box::new(Type::Func(Box::new(Type::Int), Box::new(Type::Int))),
(),
),
aux: (),
},
Decl {
name: "g".into(),
args: vec![Pattern::Binding("x".into(), Type::Int)],
args: vec![Pattern::Binding("x".into(), ())],
body: Expr::Op(
Op::Cons,
Box::new(Expr::Variable("x".into(), Type::Int)),
Box::new(Expr::Literal(Literal::Nil, Type::List(Box::new(Type::Int)))),
Type::List(Box::new(Type::Int)),
),
aux: Type::Func(
Box::new(Type::Int),
Box::new(Type::List(Box::new(Type::Int))),
Box::new(Expr::Variable("x".into(), ())),
Box::new(Expr::Literal(Literal::Nil, ())),
(),
),
aux: (),
},
Decl {
name: "".into(),
Expand All @@ -275,41 +296,29 @@ mod tests {
Op::App,
Box::new(Expr::Op(
Op::App,
Box::new(Expr::Variable(
"f".into(),
Type::Func(
Box::new(Type::List(Box::new(Type::Int))),
Box::new(Type::Func(Box::new(Type::Int), Box::new(Type::Int))),
),
)),
Box::new(Expr::Variable("f".into(), ())),
Box::new(Expr::Op(
Op::App,
Box::new(Expr::Variable(
"g".into(),
Type::Func(
Box::new(Type::Int),
Box::new(Type::List(Box::new(Type::Int))),
),
)),
Box::new(Expr::Variable("g".into(), ())),
Box::new(Expr::Op(
Op::Add,
Box::new(Expr::Literal(Literal::Int(1), Type::Int)),
Box::new(Expr::Literal(Literal::Int(2), Type::Int)),
Type::Int,
Box::new(Expr::Literal(Literal::Int(1), ())),
Box::new(Expr::Literal(Literal::Int(2), ())),
(),
)),
Type::List(Box::new(Type::Int)),
(),
)),
Type::Func(Box::new(Type::Int), Box::new(Type::Int)),
(),
)),
Box::new(Expr::Op(
Op::Add,
Box::new(Expr::Literal(Literal::Int(3), Type::Int)),
Box::new(Expr::Literal(Literal::Int(4), Type::Int)),
Type::Int,
Box::new(Expr::Literal(Literal::Int(3), ())),
Box::new(Expr::Literal(Literal::Int(4), ())),
(),
)),
Type::Int,
(),
),
aux: Type::Int,
aux: (),
},
]);

Expand All @@ -335,7 +344,10 @@ mod tests {
assert!(!evaluator.normal_form());

assert!(evaluator.step().is_ok());
assert_eq!(format!("{}", evaluator), "Add(Add(1, 2), Add(3, 4))");
assert_eq!(
format!("{}", evaluator),
"Add(3, App(App(f, []), Add(3, 4)))"
);
assert!(!evaluator.normal_form());

assert!(evaluator.step().is_ok());
Expand Down
12 changes: 4 additions & 8 deletions src/eval/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,10 @@ pub fn beta_number<Aux>(expr: &Expr<Aux>, decls: &[Decl<Aux>]) -> Option<isize>

/// Performs "normal" (for call-by-name and call-by-value) function application.
pub fn apply<Aux: Clone>(
func: Expr<Aux>,
func: Symbol,
args: Vec<Expr<Aux>>,
decls: &[Decl<Aux>],
) -> Result<Expr<Aux>, Error> {
let func = match func {
Expr::Variable(var, _) => var,
func => panic!("Invalid callable expression: {}", func),
};
let (decl, args) = decls
.iter()
.filter(|decl| decl.name == func)
Expand All @@ -80,8 +76,8 @@ pub fn apply<Aux: Clone>(
Ok(apply_replacement(&decl.body, &args))
}

/// Returns whether the given expression is normalized enough to be a valid n-from-the-right-th
/// argument to the decl with the given name. Used in call-by-name and lazy evaluation.
/// Returns whether the given expression is normalized enough to be a valid nth argument to the
/// decl with the given name. Used in call-by-name and lazy evaluation.
pub fn arg_normal_enough<Aux: Clone>(
value: &Expr<Aux>,
n: usize,
Expand All @@ -91,7 +87,7 @@ pub fn arg_normal_enough<Aux: Clone>(
let pats = decls
.iter()
.filter(|decl| decl.name == name)
.map(|decl| &decl.args[decl.args.len() - n]);
.map(|decl| &decl.args[n]);
for pat in pats {
if arg_normal_enough_for_pat(value, pat, decls) {
if pat.matches(value).is_some() {
Expand Down
4 changes: 4 additions & 0 deletions src/eval/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ fn step<Aux: Clone>(expr: Expr<Aux>, decls: &[Decl<Aux>]) -> Result<Expr<Aux>, E
func = *f;
}
args.reverse();
let func = match func {
Expr::Variable(var, _) => var,
func => panic!("Invalid callable expression: {}", func),
};
apply(func, args, decls)?
}
_ => Expr::Op(Op::App, Box::new(step(*l, decls)?), r, aux),
Expand Down

0 comments on commit 6c21e0f

Please sign in to comment.