diff options
| author | Arnaud Bailly <arnaud.bailly@iohk.io> | 2024-10-09 09:38:52 +0200 |
|---|---|---|
| committer | Arnaud Bailly <arnaud.bailly@iohk.io> | 2024-10-09 09:38:52 +0200 |
| commit | e4af8559784e851047702045ff677c9a91897fdd (patch) | |
| tree | 36cb8b81a521f653ed17703639682de24ce6405f | |
| parent | 67502b58c733b755f658f526aff0536828cd7757 (diff) | |
| download | lambda-nantes-e4af8559784e851047702045ff677c9a91897fdd.tar.gz | |
[wip] client generates applications with more than 2 terms
| -rw-r--r-- | rust/src/ast.rs | 37 | ||||
| -rw-r--r-- | rust/src/lambda.rs | 14 | ||||
| -rw-r--r-- | rust/src/web.rs | 24 |
3 files changed, 74 insertions, 1 deletions
diff --git a/rust/src/ast.rs b/rust/src/ast.rs index e70f323..29d548f 100644 --- a/rust/src/ast.rs +++ b/rust/src/ast.rs @@ -22,7 +22,13 @@ impl Display for Value { Value::Num(i) => write!(f, "{}", i), Value::Bool(b) => write!(f, "{}", b), Value::Sym(s) => write!(f, "{}", s), - Value::App(l, r) => write!(f, "({} {})", l, r), + Value::App(l, r) => { + if let App(l1, l2) = *l.clone() { + write!(f, "({} {} {})", l1, l2, r) + } else { + write!(f, "({} {})", l, r) + } + } Value::Lam(var, body) => write!(f, "(lam {} {})", var, body), Value::Def(var, value) => write!(f, "(def {} {})", var, value), Value::Let(var, value, body) => write!(f, "(let ({} {}) {})", var, value, body), @@ -71,3 +77,32 @@ impl Arbitrary for Value { .boxed() } } + +#[cfg(test)] +mod ast_tests { + + use super::{ + identifier, + Value::{self, *}, + }; + use proptest::collection::vec; + use proptest::prelude::*; + + fn any_sym() -> impl Strategy<Value = Value> { + identifier().prop_map(Sym) + } + + proptest! { + + #[test] + fn display_multiple_applications_as_a_sequence(atoms in vec("[a-z]".prop_map(Sym), 2..10)) { + let init = atoms.first().unwrap().clone(); + let value = atoms.iter().skip(1).fold(init, |acc, expr| { + Value::App(Box::new(acc.clone()), Box::new(expr.clone())) + }); + assert_eq!(value.to_string(), + format!("({})", + atoms.iter().map(|v| v.to_string()).collect::<Vec<String>>().join(" "))); + } + } +} diff --git a/rust/src/lambda.rs b/rust/src/lambda.rs index e6102d8..ee5cb1f 100644 --- a/rust/src/lambda.rs +++ b/rust/src/lambda.rs @@ -115,6 +115,8 @@ pub fn generate_expr(size: u32, runner: &mut TestRunner) -> Value { 4 => simple_app().new_tree(runner).unwrap().current(), 5 => nested_simple_app().new_tree(runner).unwrap().current(), 6 => simple_lambda().new_tree(runner).unwrap().current(), + 7 => app_to_lambda().new_tree(runner).unwrap().current(), + 8 => multi_app().new_tree(runner).unwrap().current(), _ => todo!(), } } @@ -124,6 +126,11 @@ fn simple_app() -> impl Strategy<Value = Value> { (leaf.clone(), leaf.clone()).prop_map(|(l, r)| Value::App(Box::new(l), Box::new(r))) } +fn multi_app() -> impl Strategy<Value = Value> { + let leaf = prop_oneof![any_num(), any_sym()]; + (leaf.clone(), leaf.clone()).prop_map(|(l, r)| Value::App(Box::new(l), Box::new(r))) +} + fn any_num() -> impl Strategy<Value = Value> { any::<i32>().prop_map(Value::Num) } @@ -140,9 +147,16 @@ fn any_sym() -> impl Strategy<Value = Value> { } fn simple_lambda() -> impl Strategy<Value = Value> { + // TODO: there's nothing to guarantee the variable appears in the body (ascii_identifier(), nested_simple_app()).prop_map(|(v, b)| Value::Lam(v, Box::new(b))) } +fn app_to_lambda() -> impl Strategy<Value = Value> { + let lam = simple_lambda(); + let arg = prop_oneof![any_num(), any_sym(), nested_simple_app()]; + (lam, arg).prop_map(|(l, a)| Value::App(Box::new(l), Box::new(a))) +} + #[cfg(test)] mod lambda_test { use crate::parser::parse; diff --git a/rust/src/web.rs b/rust/src/web.rs index de87ad6..8fa9452 100644 --- a/rust/src/web.rs +++ b/rust/src/web.rs @@ -465,6 +465,30 @@ mod app_tests { } #[test] + async fn client_generates_application_with_lambda_terms_at_level_7() { + let mut client = client(); + client.grade = 7; + + let (input, _) = client.generate_expr(); + + let parsed = parse(&input); + match &parsed[..] { + [Value::App(t1, _)] if matches!(**t1, Value::Lam(_, _)) => (), + _ => panic!("Expected symbol, got {:?}", parsed), + } + } + + #[test] + async fn client_generates_applications_with_more_than_2_terms_at_level_8() { + let mut client = client(); + client.grade = 8; + + let (input, _) = client.generate_expr(); + + assert!(input.split(' ').count() > 2); + } + + #[test] async fn client_increases_grade_on_successful_test() { let mut client = client(); let expected = "1".to_string(); |
