diff options
-rw-r--r-- | src/ast.rs | 2 | ||||
-rw-r--r-- | src/parser.rs | 2 | ||||
-rw-r--r-- | src/simple.rs | 32 | ||||
-rw-r--r-- | tests/test_execution.rs | 21 |
4 files changed, 42 insertions, 15 deletions
@@ -25,7 +25,7 @@ pub type Value = u64; pub enum Type { Empty, Unit, - Bool, + Boolean, Natural, Integer, // Float, diff --git a/src/parser.rs b/src/parser.rs index 2f918a8..719ee3f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -34,7 +34,7 @@ pub fn parse_str(input: &str) -> Result<Expression, peg::error::ParseError<peg:: match k.as_str() { "empty" => Type::Empty, "unit" => Type::Unit, - "bool" => Type::Bool, + "bool" => Type::Boolean, "nat" => Type::Natural, "int" => Type::Integer, _ => panic!("invalid type"), // fixme: raise an error diff --git a/src/simple.rs b/src/simple.rs index 9393c00..b7ef94a 100644 --- a/src/simple.rs +++ b/src/simple.rs @@ -12,27 +12,33 @@ pub fn check(context: Context, expression: Expression) { /// Evaluates an expression given a context (of variables) to a term. /// Panics on non-evaluatable code. -pub fn execute(context: Context, expression: Expression) -> Term { +pub fn execute(context: Context, expression: Expression) -> Result<Term, &'static str> { match expression { - Expression::Annotation { expr, .. } => return execute(context, *expr), - Expression::Constant { term } => return term, - Expression::Variable { id } => return context[&id], - Expression::Abstraction { .. } => panic!("attempting to execute an abstraction"), + Expression::Annotation { expr, .. } => execute(context, *expr), + Expression::Constant { term } => Ok(term), + Expression::Variable { id } => context.get(&id).ok_or("no such variable in context").map(|x| *x), + Expression::Abstraction { .. } => Err("attempting to execute an abstraction"), Expression::Application { func, arg } => { match *func { Expression::Abstraction { param, func } => { - let mut context = context; - context.insert(param, execute(context.clone(), *arg)); - return execute(context, *func); + let result = execute(context.clone(), *arg); + match result { + Ok(value) => { + let mut context = context; + context.insert(param, value); + return execute(context, *func); + }, + Err(e) => Err(e) + } }, - _ => panic!("attempting to execute an application to nothing") + _ => Err("attempting to execute an application to nothing") } }, Expression::Conditional { if_cond, if_then, if_else } => { - match execute(context.clone(), *if_cond).val { - 1 => execute(context, *if_then), - 0 => execute(context, *if_else), - _ => panic!("invalid type for a conditional") + match execute(context.clone(), *if_cond) { + Ok(Term { val: 1, .. }) => execute(context, *if_then), + Ok(Term { val: 0, .. }) => execute(context, *if_else), + _ => Err("invalid type for a conditional") } }, } diff --git a/tests/test_execution.rs b/tests/test_execution.rs new file mode 100644 index 0000000..dd1b038 --- /dev/null +++ b/tests/test_execution.rs @@ -0,0 +1,21 @@ +use chrysanthemum::ast::*; +use chrysanthemum::simple::*; +use chrysanthemum::util::*; + +#[test] +fn test_simple() { + assert_eq!(execute(Context::new(), Const(0, Type::Boolean)), Ok(Term(0, Type::Boolean))); + assert_eq!(execute(Context::new(), Const(123, Type::Natural)), Ok(Term(123, Type::Natural))); + assert_eq!(execute(Context::new(), Const(123, Type::Integer)), Ok(Term(123, Type::Integer))); + assert!(execute(Context::new(), Var("x")).is_err()); +} + +#[test] +fn test_complex() { + let mut context = Context::new(); + context.insert(String::from("x"), Term(413, Type::Natural)); + context.insert(String::from("y"), Term(1, Type::Boolean)); + assert_eq!(execute(context.clone(), Var("x")), Ok(Term(413, Type::Natural))); + assert_eq!(execute(context.clone(), Cond(Var("y"), Const(612, Type::Integer), Var("x"))), Ok(Term(612, Type::Integer))); + assert_eq!(execute(context.clone(), App(Abs("z", Cond(Const(0, Type::Boolean), Var("x"), Var("z"))), Const(1025, Type::Integer))), Ok(Term(1025, Type::Integer))); +} |