From 6c21e0f693aeeda63be56db37355277ee6f17978 Mon Sep 17 00:00:00 2001 From: Nathan Ringo Date: Mon, 11 Jun 2018 21:03:47 -0500 Subject: [PATCH] Implements call-by-name properly. --- Cargo.lock | 4 +- Cargo.toml | 2 +- src/eval/name.rs | 200 ++++++++++++++++++++++++---------------------- src/eval/util.rs | 12 +-- src/eval/value.rs | 4 + 5 files changed, 117 insertions(+), 105 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5caa489..a00d0ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -186,7 +186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "evaltrees" -version = "0.0.1" +version = "0.1.0-pre" dependencies = [ "display_attr 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -204,7 +204,7 @@ dependencies = [ name = "evaltrees-cli" version = "0.0.1" dependencies = [ - "evaltrees 0.0.1", + "evaltrees 0.1.0-pre", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "human-panic 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "linefeed 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 512b790..6e7024c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ authors = ["Nathan Ringo "] 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" diff --git a/src/eval/name.rs b/src/eval/name.rs index 9b4df9f..a69a84f 100644 --- a/src/eval/name.rs +++ b/src/eval/name.rs @@ -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. @@ -60,15 +61,24 @@ fn step(expr: Expr, decls: &[Decl]) -> Result, 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), }, @@ -97,6 +107,19 @@ fn step(expr: Expr, decls: &[Decl]) -> Result, E Ok(expr) } +fn check_arg_normalization( + func: Symbol, + args: &[Expr], + decls: &[Decl], +) -> Option { + for (i, a) in args.iter().enumerate() { + if !arg_normal_enough(a, i, func, decls) { + return Some(i); + } + } + None +} + fn math_op Result>( op: Op, l: Box>, @@ -116,10 +139,35 @@ fn math_op Result>( } } +fn normalize_arg( + n: usize, + func: Expr, + mut args: Vec>, + mut r_types: Vec, + decls: &[Decl], +) -> Result, 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() { @@ -127,19 +175,16 @@ mod tests { 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(), @@ -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: (), }, ]); @@ -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(), @@ -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: (), }, ]); @@ -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()); diff --git a/src/eval/util.rs b/src/eval/util.rs index 0a1bcfc..2bd7f3e 100644 --- a/src/eval/util.rs +++ b/src/eval/util.rs @@ -63,14 +63,10 @@ pub fn beta_number(expr: &Expr, decls: &[Decl]) -> Option /// Performs "normal" (for call-by-name and call-by-value) function application. pub fn apply( - func: Expr, + func: Symbol, args: Vec>, decls: &[Decl], ) -> Result, 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) @@ -80,8 +76,8 @@ pub fn apply( 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( value: &Expr, n: usize, @@ -91,7 +87,7 @@ pub fn arg_normal_enough( 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() { diff --git a/src/eval/value.rs b/src/eval/value.rs index 90f6d21..f32370e 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -73,6 +73,10 @@ fn step(expr: Expr, decls: &[Decl]) -> Result, 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),