diff options
| -rw-r--r-- | lambda-calcul/rust/src/ast.rs | 4 | ||||
| -rw-r--r-- | lambda-calcul/rust/src/lambda.rs | 73 | ||||
| -rw-r--r-- | lambda-calcul/rust/src/web.rs | 17 |
3 files changed, 73 insertions, 21 deletions
diff --git a/lambda-calcul/rust/src/ast.rs b/lambda-calcul/rust/src/ast.rs index db3b4a7..18aaaa7 100644 --- a/lambda-calcul/rust/src/ast.rs +++ b/lambda-calcul/rust/src/ast.rs @@ -7,7 +7,8 @@ use std::fmt::{self, Display}; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub enum Type { - Int + Num, + Bool, } impl Display for Type { @@ -19,6 +20,7 @@ impl Display for Type { #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub enum TypeError { UnknownType(Value), + UnboundVariable(String), } #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] diff --git a/lambda-calcul/rust/src/lambda.rs b/lambda-calcul/rust/src/lambda.rs index ed1515a..c624b19 100644 --- a/lambda-calcul/rust/src/lambda.rs +++ b/lambda-calcul/rust/src/lambda.rs @@ -10,12 +10,12 @@ use std::collections::HashMap; use crate::ast::*; #[derive(Debug, PartialEq)] -pub struct Environment<'a> { - parent: Box<Option<&'a Environment<'a>>>, - bindings: HashMap<String, Value>, +pub struct Environment<'a, T> { + parent: Box<Option<&'a Environment<'a, T>>>, + bindings: HashMap<String, T>, } -impl<'a> Environment<'a> { +impl<'a, T: Clone> Environment<'a, T> { pub fn new() -> Self { Environment { parent: Box::new(None), @@ -23,7 +23,7 @@ impl<'a> Environment<'a> { } } - fn bind(&mut self, var: &str, value: &Value) { + fn bind(&mut self, var: &str, value: &T) { self.bindings.insert(var.to_string(), value.clone()); } @@ -34,7 +34,7 @@ impl<'a> Environment<'a> { } } - fn lookup(&self, var: &str) -> Option<&Value> { + fn lookup(&self, var: &str) -> Option<&T> { self.bindings.get(var).or_else(|| match *self.parent { Some(parent) => parent.lookup(var), None => None, @@ -42,7 +42,7 @@ impl<'a> Environment<'a> { } } -impl<'a> Default for Environment<'a> { +impl<'a, T: Clone> Default for Environment<'a, T> { fn default() -> Self { Self::new() } @@ -58,7 +58,7 @@ pub fn eval_all(values: &[Value]) -> Vec<Value> { /// /// call-by-name reduces the leftmost outermost redex first, which is /// not under a lambda abstraction. -pub fn eval_whnf(arg: &Value, env: &mut Environment) -> Value { +pub fn eval_whnf(arg: &Value, env: &mut Environment<Value>) -> Value { match arg { Value::Def(var, value) => { env.bind(var, value); @@ -98,14 +98,22 @@ fn subst(var: &str, body: &Value, e: &Value) -> Value { pub fn type_of_all(values: &[Value]) -> Vec<Type> { let mut env = Environment::new(); - values.iter().map(|v| type_of(v, &mut env).unwrap()).collect() + values + .iter() + .map(|v| type_of(v, &mut env).unwrap()) + .collect() } -fn type_of(v: &Value, env: &mut Environment) -> Result<Type, TypeError> { +fn type_of(v: &Value, env: &mut Environment<Type>) -> Result<Type, TypeError> { match v { - Value::Num(_) => Ok(Type::Int), + Value::Num(_) => Ok(Type::Num), + Value::Bool(_) => Ok(Type::Bool), + Value::Sym(s) => match env.lookup(s) { + Some(ty) => Ok(ty.clone()), + None => Err(TypeError::UnboundVariable(s.clone())), + }, _ => Err(TypeError::UnknownType(v.clone())), - } + } } pub fn gensym() -> String { @@ -138,10 +146,15 @@ pub fn generate_expr(size: u32, runner: &mut TestRunner) -> Value { pub fn generate_expression_to_type(size: u32, runner: &mut TestRunner) -> Vec<Value> { match size { - _ => { + 0 => { let n = any::<u16>().new_tree(runner).unwrap().current(); vec![Value::Num(n.into())] - } + }, + _ => { + let n = any::<bool>().new_tree(runner).unwrap().current(); + vec![Value::Bool(n.into())] + }, + } } @@ -224,7 +237,7 @@ fn gen_terms(u: u32) -> impl Strategy<Value = Value> { mod lambda_test { use crate::{ast::Type, parser::parse}; - use super::{Environment, Value, eval_all, eval_whnf, type_of}; + use super::{eval_all, eval_whnf, type_of, Environment, Value}; fn parse1(string: &str) -> Value { parse(string).pop().unwrap() @@ -315,6 +328,34 @@ mod lambda_test { fn type_of_a_number_is_int() { let value = parse1("12"); let ty = type_of(&value, &mut Environment::new()).unwrap(); - assert_eq!(Type::Int, ty); + assert_eq!(Type::Num, ty); + } + + #[test] + fn type_of_a_boolean_is_bool() { + let value = parse1("true"); + let ty = type_of(&value, &mut Environment::new()).unwrap(); + assert_eq!(Type::Bool, ty); + } + + #[test] + fn type_of_a_bound_variable_is_looked_up_in_context() { + let value = parse1("x"); + let mut ctx = Environment::new(); + ctx.bind("x", &Type::Bool); + + let ty = type_of(&value, &mut ctx).unwrap(); + + assert_eq!(Type::Bool, ty); + } + + #[test] + fn type_of_an_unbound_variable_yields_an_error() { + let value = parse1("x"); + let mut ctx = Environment::new(); + + let ty = type_of(&value, &mut ctx); + + assert!(ty.is_err()); } } diff --git a/lambda-calcul/rust/src/web.rs b/lambda-calcul/rust/src/web.rs index 27aed21..5ad48bd 100644 --- a/lambda-calcul/rust/src/web.rs +++ b/lambda-calcul/rust/src/web.rs @@ -13,7 +13,10 @@ use std::{collections::HashMap, sync::Arc}; use tokio::task::{self, JoinHandle}; use uuid::Uuid; -use lambda::lambda::{Environment, eval_all, eval_whnf, generate_expr, generate_expression_to_type, generate_exprs, gensym, type_of_all}; +use lambda::lambda::{ + eval_all, eval_whnf, generate_expr, generate_expression_to_type, generate_exprs, gensym, + type_of_all, Environment, +}; use lambda::parser::{parse, parse_total}; #[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] @@ -96,6 +99,8 @@ enum TestResult { } impl Client { + const TYPING_GRADE: u8 = 20; + fn new(url: String, name: String, delay: Duration, encoding: AstEncoding) -> Self { let id = Uuid::new_v4(); let runner = TestRunner::new_with_rng( @@ -119,7 +124,7 @@ impl Client { } fn generate_expr(&mut self) -> (Vec<Value>, Vec<Value>) { - if self.grade >= 20 { + if self.grade >= Self::TYPING_GRADE { self.generate_expression_to_type() } else if self.grade >= 10 { self.generate_exprs() @@ -131,8 +136,12 @@ impl Client { } fn generate_expression_to_type(&mut self) -> (Vec<Value>, Vec<Value>) { - let input = generate_expression_to_type(self.grade.into(), &mut self.runner); - let expected = type_of_all(&input).iter().map(|t| Value::Type(t.clone())).collect(); + let input = + generate_expression_to_type((self.grade - Self::TYPING_GRADE).into(), &mut self.runner); + let expected = type_of_all(&input) + .iter() + .map(|t| Value::Type(t.clone())) + .collect(); (input, expected) } |
