From b849745636c17a9015198c2a1865b8dbb43ff4a3 Mon Sep 17 00:00:00 2001 From: Arnaud Bailly Date: Wed, 25 Sep 2024 12:12:53 +0200 Subject: Prepare parser to return multiple values --- rust/src/lambda.rs | 35 +++++++++++++++++++++-------------- rust/src/main.rs | 8 ++++++-- rust/src/parser.rs | 29 ++++++++++++++++------------- 3 files changed, 43 insertions(+), 29 deletions(-) (limited to 'rust') diff --git a/rust/src/lambda.rs b/rust/src/lambda.rs index a6da87b..41c0c26 100644 --- a/rust/src/lambda.rs +++ b/rust/src/lambda.rs @@ -7,12 +7,15 @@ use ast::*; mod parser; use parser::*; -pub fn run(arg: &str) -> String { - let content = read_to_string(arg).unwrap(); - let value = parse(&content.to_string()); - let result = eval(&value); - - result.to_string() +pub fn eval_file(file_name: &str) -> String { + let content = read_to_string(file_name).unwrap(); + let values = parse(&content.to_string()); + values + .iter() + .map(eval) + .map(|v| v.to_string()) + .collect::>() + .join(" ") } fn eval(arg: &Value) -> Value { @@ -73,27 +76,31 @@ fn gensym() -> String { mod lambda_test { use crate::{eval, parse, Value}; + fn parse1(string: &str) -> Value { + parse(string).pop().unwrap() + } + #[test] fn evaluating_a_non_reducible_value_yields_itself() { - let value = parse("(foo 12)"); + let value = parse1("(foo 12)"); assert_eq!(value, eval(&value)); } #[test] fn evaluating_application_on_an_abstraction_reduces_it() { - let value = parse("((lam x x) 12)"); + let value = parse1("((lam x x) 12)"); assert_eq!(Value::Num(12), eval(&value)); } #[test] fn substitution_occurs_within_abstraction_body() { - let value = parse("(((lam x (lam y x)) 13) 12)"); + let value = parse1("(((lam x (lam y x)) 13) 12)"); assert_eq!(Value::Num(13), eval(&value)); } #[test] fn substitution_occurs_within_application_body() { - let value = parse("(((lam x (lam y (y x))) 13) 12)"); + let value = parse1("(((lam x (lam y (y x))) 13) 12)"); assert_eq!( Value::App(Box::new(Value::Num(12)), Box::new(Value::Num(13))), eval(&value) @@ -102,26 +109,26 @@ mod lambda_test { #[test] fn substitution_does_not_capture_free_variables() { - let value = parse("(((lam x (lam x x)) 13) 12)"); + let value = parse1("(((lam x (lam x x)) 13) 12)"); 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))"); + let value = parse1("((lam x x) ((lam x x) 12))"); 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)"); + let value = parse1("((((lam y (lam x (lam y (x y)))) 13) (lam x x)) 11)"); 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))))"); + let value = parse1("((lam x 1) ((lam x (x x)) (lam x (x x))))"); assert_eq!(Value::Num(1), eval(&value)); } } diff --git a/rust/src/main.rs b/rust/src/main.rs index cf9477c..ffd92f2 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -1,7 +1,11 @@ use std::env::args; -use lambda::run; +use lambda::eval_file; fn main() { - println!("result: {}", run(&args().nth(1).unwrap())); + if args().count() > 1 { + for file in args().skip(1) { + println!("{}", eval_file(&file)); + } + } } diff --git a/rust/src/parser.rs b/rust/src/parser.rs index 9dc0bb8..c54386c 100644 --- a/rust/src/parser.rs +++ b/rust/src/parser.rs @@ -50,12 +50,15 @@ impl Parser { } } -pub fn parse(arg: &str) -> Value { +pub fn parse(arg: &str) -> Vec { let tokens = tokenize(arg); let mut parser = Parser { tokens, index: 0 }; - parse_expression(&mut parser) + let mut result = Vec::new(); + let expr = parse_expression(&mut parser) .map_err(|e| panic!("Syntax error: {}", e)) - .unwrap() + .unwrap(); + result.push(expr); + result } fn parse_expression(parser: &mut Parser) -> Result { @@ -168,26 +171,26 @@ mod tests { #[test] fn parse_integer_as_number(i in -1000i32..1000) { let result = parse(&i.to_string()); - assert_eq!(Num(i), result); + assert_eq!(vec![Num(i)], result); } } #[test] fn parse_truth_values_as_booleans() { - assert_eq!(Bool(true), parse("true")); - assert_eq!(Bool(false), parse("false")); + assert_eq!(vec![Bool(true)], parse("true")); + assert_eq!(vec![Bool(false)], parse("false")); } #[test] fn parse_identifiers_values_as_symbols() { - assert_eq!(Sym("foo".to_string()), parse("foo")); + assert_eq!(vec![Sym("foo".to_string())], parse("foo")); } #[test] fn ignores_whitespace() { - assert_eq!(Sym("foo".to_string()), parse(" foo \n\r")); - assert_eq!(Num(-42), parse("\n-42")); + assert_eq!(vec![Sym("foo".to_string())], parse(" foo \n\r")); + assert_eq!(vec![Num(-42)], parse("\n-42")); } #[test] @@ -226,7 +229,7 @@ mod tests { #[test] fn parse_application_of_two_values() { assert_eq!( - App(Box::new(Sym("foo".to_string())), Box::new(Num(42))), + vec![App(Box::new(Sym("foo".to_string())), Box::new(Num(42)))], parse("(foo 42)") ); } @@ -234,13 +237,13 @@ mod tests { #[test] fn parse_abstraction() { assert_eq!( - Lam( + vec![Lam( "x".to_string(), Box::new(App( Box::new(Sym("x".to_string())), Box::new(Sym("x".to_string())) )) - ), + )], parse("(lam x (x x))") ); } @@ -271,7 +274,7 @@ mod tests { #[test] fn parse_is_inverse_to_display(values in any::>()) { let result : Vec = values.iter().map(|v:&Value| v.to_string()).collect(); - assert_eq!(values, result.iter().map(|s| parse(s)).collect::>()); + assert_eq!(values, result.iter().flat_map(|s| parse(s)).collect::>()); } } } -- cgit v1.2.3