diff options
| author | Arnaud Bailly <arnaud.bailly@iohk.io> | 2024-09-25 08:45:05 +0200 |
|---|---|---|
| committer | Arnaud Bailly <arnaud.bailly@iohk.io> | 2024-09-25 08:45:05 +0200 |
| commit | d98451ea1e703dfbab4cdc8cc451fd55fed89c06 (patch) | |
| tree | 09d37fba2f603922e4617942b8182ad6fbdbbd4d /rust/src | |
| parent | bb16c10c4c851018ee5aa905a719abdfd8fdc861 (diff) | |
| download | lambda-nantes-d98451ea1e703dfbab4cdc8cc451fd55fed89c06.tar.gz | |
Evaluate in normal order until normal form is reached
Diffstat (limited to 'rust/src')
| -rw-r--r-- | rust/src/lambda.rs | 45 |
1 files changed, 31 insertions, 14 deletions
diff --git a/rust/src/lambda.rs b/rust/src/lambda.rs index 9b2a6f2..a6da87b 100644 --- a/rust/src/lambda.rs +++ b/rust/src/lambda.rs @@ -10,25 +10,36 @@ use parser::*; pub fn run(arg: &str) -> String { let content = read_to_string(arg).unwrap(); let value = parse(&content.to_string()); - let result = interpret(&value); + let result = eval(&value); result.to_string() } -fn interpret(arg: &Value) -> Value { +fn eval(arg: &Value) -> Value { match arg { Value::App(l, r) => { - let result = apply(&interpret(l), &interpret(r)); - if result == *arg { - result + let result = apply(&eval(l), r); + if is_reducible(&result) { + eval(&result) } else { - interpret(&result) + result } } other => other.clone(), } } +fn is_reducible(result: &Value) -> bool { + match result { + Value::App(l, r) => match **l { + Value::Lam(_, _) => true, + _ => is_reducible(l) || is_reducible(r), + }, + Value::Lam(_, _) => false, + _ => false, + } +} + fn apply(l: &Value, r: &Value) -> Value { if let Value::Lam(v, body) = l { subst(v, body, r) @@ -60,24 +71,24 @@ fn gensym() -> String { #[cfg(test)] mod lambda_test { - use crate::{interpret, parse, Value}; + use crate::{eval, parse, Value}; #[test] fn evaluating_a_non_reducible_value_yields_itself() { let value = parse("(foo 12)"); - assert_eq!(value, interpret(&value)); + assert_eq!(value, eval(&value)); } #[test] fn evaluating_application_on_an_abstraction_reduces_it() { let value = parse("((lam x x) 12)"); - assert_eq!(Value::Num(12), interpret(&value)); + assert_eq!(Value::Num(12), eval(&value)); } #[test] fn substitution_occurs_within_abstraction_body() { let value = parse("(((lam x (lam y x)) 13) 12)"); - assert_eq!(Value::Num(13), interpret(&value)); + assert_eq!(Value::Num(13), eval(&value)); } #[test] @@ -85,26 +96,32 @@ mod lambda_test { let value = parse("(((lam x (lam y (y x))) 13) 12)"); assert_eq!( Value::App(Box::new(Value::Num(12)), Box::new(Value::Num(13))), - interpret(&value) + eval(&value) ); } #[test] fn substitution_does_not_capture_free_variables() { let value = parse("(((lam x (lam x x)) 13) 12)"); - assert_eq!(Value::Num(12), interpret(&value)); + assert_eq!(Value::Num(12), eval(&value)); } #[test] fn interpretation_applies_to_both_sides_of_application() { let value = parse("((lam x x) ((lam x x) 12))"); - assert_eq!(Value::Num(12), interpret(&value)); + assert_eq!(Value::Num(12), eval(&value)); } #[test] fn reduction_is_applied_until_normal_form_is_reached() { let value = parse("((((lam y (lam x (lam y (x y)))) 13) (lam x x)) 11)"); - assert_eq!(Value::Num(11), interpret(&value)); + assert_eq!(Value::Num(11), eval(&value)); } + #[test] + fn reduction_always_select_leftmost_outermost_redex() { + // this should not terminate if we evaluate the rightmost redex first + let value = parse("((lam x 1) ((lam x (x x)) (lam x (x x))))"); + assert_eq!(Value::Num(1), eval(&value)); + } } |
