add (define) and some tests for it

This commit is contained in:
Aaron Blankstein
2019-01-16 14:31:37 -06:00
parent 089ed92053
commit e0173822a6
3 changed files with 120 additions and 34 deletions

View File

@@ -0,0 +1,54 @@
use super::super::types::{ValueType, DefinedFunction};
use super::super::representations::SymbolicExpression;
use super::super::representations::SymbolicExpression::{Atom,List};
use super::super::{Context,CallStack,eval};
pub enum DefineResult {
Variable(String, ValueType),
Function(String, DefinedFunction)
}
pub fn handle_define_variable(variable: &String, expression: &SymbolicExpression, context: &Context) -> DefineResult {
let mut call_stack = CallStack::new();
let value = eval(expression, context, &mut call_stack, context);
DefineResult::Variable(variable.clone(), value)
}
pub fn handle_define_function(signature: &[SymbolicExpression], expression: &SymbolicExpression, _context: &Context) -> DefineResult {
let coerced_atoms: Result<Vec<_>, _> = signature.iter().map(|x| {
if let Atom(name) = x {
Ok(name)
} else {
Err("Non-atomic argument to method signature in define".to_string())
}
}).collect();
if let Ok(names) = coerced_atoms {
if let Some((function_name, arg_names)) = names.split_first() {
let function = DefinedFunction {
arguments: arg_names.iter().map(|x| (*x).clone()).collect(),
body: expression.clone()
};
DefineResult::Function((*function_name).clone(), function)
} else {
panic!("Must supply atleast a name argument to define a function")
}
} else {
panic!("Non-atomic argument to method signature in define")
}
}
pub fn evaluate_define(expression: &SymbolicExpression, context: &Context) -> Option<DefineResult> {
if let SymbolicExpression::List(elements) = expression {
if elements.len() != 3 || elements[0] != Atom("define".to_string()) {
None
} else {
match elements[1] {
Atom(ref variable) => Some(handle_define_variable(variable, &elements[2], context)),
List(ref function_signature) => Some(handle_define_function(&function_signature, &elements[2], context))
}
}
} else {
None
}
}

View File

@@ -0,0 +1,65 @@
extern crate blockstack_vm;
use blockstack_vm::types::ValueType;
use blockstack_vm::parser::parse;
use blockstack_vm::eval_all;
#[test]
fn test_defines() {
let tests = parse(&
"(define x 10)
(define y 15)
(define (f a b) (+ x y a b))
(f 3 1)");
if let Ok(to_eval) = tests {
assert_eq!(Ok(ValueType::IntType(29)), eval_all(&to_eval));
} else {
assert!(false, "Failed to parse function bodies.");
}
}
#[test]
#[should_panic]
fn test_recursive_panic() {
let tests = parse(&
"(define (factorial a)
(if (eq? a 0)
1
(* a (factorial (- a 1)))))
(factorial 10)");
if let Ok(to_eval) = tests {
assert_eq!(Ok(ValueType::IntType(29)), eval_all(&to_eval));
} else {
assert!(false, "Failed to parse function bodies.");
}
}
#[test]
#[should_panic]
fn test_define_parse_panic() {
let tests = parse(&
"(define () 1)");
if let Ok(to_eval) = tests {
assert_eq!(Ok(ValueType::IntType(29)), eval_all(&to_eval));
} else {
assert!(false, "Failed to parse function bodies.");
}
}
#[test]
#[should_panic]
fn test_define_parse_panic_2() {
let tests = parse(&
"(define (a b (d)) 1)");
if let Ok(to_eval) = tests {
assert_eq!(Ok(ValueType::IntType(29)), eval_all(&to_eval));
} else {
assert!(false, "Failed to parse function bodies.");
}
}

View File

@@ -1,6 +1,6 @@
extern crate blockstack_vm;
use blockstack_vm::{eval, eval_all};
use blockstack_vm::eval;
use blockstack_vm::{Context, CallStack};
use blockstack_vm::types::{ValueType, DefinedFunction};
use blockstack_vm::representations::SymbolicExpression;
@@ -138,36 +138,3 @@ fn test_simple_arithmetic_functions() {
assert!(false, "Failed to parse function bodies.");
}
}
#[test]
fn test_defines() {
let tests = parse(&
"(define x 10)
(define y 15)
(define (f a b) (+ x y a b))
(f 3 1)");
if let Ok(to_eval) = tests {
assert_eq!(Ok(ValueType::IntType(29)), eval_all(&to_eval));
} else {
assert!(false, "Failed to parse function bodies.");
}
}
#[test]
#[should_panic]
fn test_recursive_panic() {
let tests = parse(&
"(define (factorial a)
(if (eq? a 0)
1
(* a (factorial (- a 1)))))
(factorial 10)");
if let Ok(to_eval) = tests {
assert_eq!(Ok(ValueType::IntType(29)), eval_all(&to_eval));
} else {
assert!(false, "Failed to parse function bodies.");
}
}