roc/src/eval.rs
2019-06-12 21:25:15 -04:00

330 lines
12 KiB
Rust

use expr;
use expr::{Expr, Operator, Pattern, Problem};
use expr::Expr::*;
use expr::Problem::*;
use expr::Pattern::*;
use expr::Operator::*;
use std::rc::Rc;
use im_rc::hashmap::HashMap;
use self::Evaluated::*;
use smallvec::SmallVec;
pub fn eval(expr: Expr) -> Evaluated {
scoped_eval(expr, &HashMap::new())
}
#[inline(always)]
pub fn from_evaluated(Evaluated(expr): Evaluated) -> Expr {
expr
}
/// Wrapper indicating the expression has been evaluated
pub enum Evaluated { Evaluated(Expr) }
type Scope = HashMap<String, Rc<Evaluated>>;
#[inline(always)]
fn problem(prob: Problem) -> Evaluated {
Evaluated(Error(prob))
}
pub fn scoped_eval(expr: Expr, vars: &Scope) -> Evaluated {
match expr {
// Primitives need no further evaluation
Error(_) | Int(_) | Str(_) | Frac(_, _) | Char(_) | Bool(_) | Closure(_, _) | Expr::EmptyRecord => Evaluated(expr),
// Resolve variable names
Var(name) => match vars.get(&name) {
Some(resolved) => {
let Evaluated(ref evaluated_expr) = **resolved;
// TODO is there any way to avoid this clone? (Do we care,
// once this is a canonical expression with Rc instead of Box?
Evaluated(evaluated_expr.clone())
},
None => problem(UnrecognizedVarName(name))
}
Let(Identifier(name), definition, in_expr) => {
if vars.contains_key(&name) {
problem(ReassignedVarName(name))
} else {
// Create a new scope containing the new declaration.
let mut new_vars = vars.clone();
let evaluated_defn = scoped_eval(*definition, vars);
new_vars.insert(name, Rc::new(evaluated_defn));
// Evaluate in_expr with that new scope's variables.
scoped_eval(*in_expr, &new_vars)
}
},
Let(Variant(name, patterns), definition, in_expr) => {
panic!("Pattern matching on variants is not yet supported!");
},
Let(Underscore, definition, in_expr) => {
// Faithfully eval this, but discard its result.
scoped_eval(*definition, &vars);
// Actually use this part.
scoped_eval(*in_expr, vars)
},
Let(Pattern::EmptyRecord, definition, in_expr) => {
// Faithfully eval this, but discard its result.
scoped_eval(*definition, &vars);
// Actually use this part.
scoped_eval(*in_expr, vars)
},
Func(name, args) => {
let func_expr = match vars.get(&name) {
Some(resolved) => {
let Evaluated(ref evaluated_expr) = **resolved;
// TODO is there any way to avoid this clone? (Do we care,
// once this is a canonical expression with Rc instead of Box?
Evaluated(evaluated_expr.clone())
},
None => problem(UnrecognizedVarName(name))
};
eval_apply(func_expr, args, vars)
},
ApplyVariant(_, _) => Evaluated(expr), // This is all we do - for now...
Apply(func_expr, args) => {
eval_apply(scoped_eval(*func_expr, vars), args, vars)
},
Case(condition, branches) => {
eval_case(scoped_eval(*condition, vars), branches, vars)
},
Operator(left_arg, op, right_arg) => {
eval_operator(
&scoped_eval(*left_arg, vars),
op,
&scoped_eval(*right_arg, vars)
)
},
If(condition, if_true, if_false) => {
match scoped_eval(*condition, vars) {
Evaluated(Bool(true)) => scoped_eval(*if_true, vars),
Evaluated(Bool(false)) => scoped_eval(*if_false, vars),
_ => problem(TypeMismatch("non-Bool used in `if` condition".to_string()))
}
}
}
}
#[inline(always)]
fn eval_apply(expr: Evaluated, args: Vec<Expr>, vars: &Scope) -> Evaluated {
match expr {
Evaluated(Closure(arg_patterns, body)) => {
let evaluated_args =
args.into_iter()
.map(|arg| scoped_eval(arg, vars))
.collect();
match eval_closure(evaluated_args, arg_patterns, vars) {
Ok(new_vars) => scoped_eval(*body, &new_vars),
Err(prob) => problem(prob)
}
},
_ => problem(TypeMismatch("Tried to call a non-function.".to_string()))
}
}
#[inline(always)]
fn eval_closure(args: Vec<Evaluated>, arg_patterns: SmallVec<[Pattern; 4]>, vars: &Scope)
-> Result<Scope, expr::Problem>
{
if arg_patterns.len() == args.len() {
// Create a new scope for the function to use.
let mut new_vars = vars.clone();
for ( arg, pattern ) in args.into_iter().zip(arg_patterns) {
pattern_match(arg, pattern, &mut new_vars)?;
}
Ok(new_vars)
} else {
Err(WrongArity(arg_patterns.len() as u32, args.len() as u32))
}
}
#[inline(always)]
fn eval_operator(Evaluated(left_expr): &Evaluated, op: Operator, Evaluated(right_expr): &Evaluated) -> Evaluated {
// TODO in the future, replace these with named function calls to stdlib
match (left_expr, op, right_expr) {
// Error
(Error(prob), _, _) => problem(prob.clone()),
(_, _, Error(prob)) => problem(prob.clone()),
// Equals
// All functions are defined as equal
(Closure(_, _), Equals, Closure(_, _)) => Evaluated(Bool(true)),
(Bool(left), Equals, Bool(right)) => Evaluated(Bool(left == right)),
(Int(left), Equals, Int(right)) => Evaluated(Bool(left == right)),
(Str(left), Equals, Str(right)) => Evaluated(Bool(left == right)),
(Char(left), Equals, Char(right)) => Evaluated(Bool(left == right)),
(Frac(_, _), Equals, Frac(_, _)) => panic!("Don't know how to == on Fracs yet"),
(_, Equals, _) => problem(TypeMismatch("tried to use == on two values with incompatible types".to_string())),
// Plus
(Int(left_num), Plus, Int(right_num)) => Evaluated(Int(left_num + right_num)),
(Frac(_, _), Plus, Frac(_, _)) => panic!("Don't know how to add fracs yet"),
(Int(_), Plus, Frac(_, _)) => panic!("Tried to add Int and Frac"),
(Frac(_, _), Plus, Int(_)) => panic!("Tried to add Frac and Int"),
(_, Plus, _) => panic!("Tried to add non-numbers"),
// Star
(Int(left_num), Star, Int(right_num)) => Evaluated(Int(left_num * right_num)),
(Frac(_, _), Star, Frac(_, _)) => panic!("Don't know how to multiply fracs yet"),
(Int(_), Star, Frac(_, _)) => panic!("Tried to multiply Int and Frac"),
(Frac(_, _), Star, Int(_)) => panic!("Tried to multiply Frac and Int"),
(_, Star, _) => panic!("Tried to multiply non-numbers"),
// Minus
(Int(left_num), Minus, Int(right_num)) => Evaluated(Int(left_num - right_num)),
(Frac(_, _), Minus, Frac(_, _)) => panic!("Don't know how to subtract fracs yet"),
(Int(_), Minus, Frac(_, _)) => panic!("Tried to subtract Frac from Int"),
(Frac(_, _), Minus, Int(_)) => panic!("Tried to subtract Int from Frac"),
(_, Minus, _) => panic!("Tried to subtract non-numbers"),
// Slash
(Int(left_num), Slash, Int(right_num)) => Evaluated(Int(left_num / right_num)),
(Frac(_, _), Slash, Frac(_, _)) => panic!("Don't know how to divide fracs yet"),
(Int(_), Slash, Frac(_, _)) => panic!("Tried to divide Int by Frac"),
(Frac(_, _), Slash, Int(_)) => panic!("Tried to divide Frac by Int"),
(_, Slash, _) => panic!("Tried to divide non-numbers"),
// DoubleSlash
(Int(left_num), DoubleSlash, Int(right_num)) => Evaluated(Int(left_num / right_num)),
(Frac(_, _), DoubleSlash, Frac(_, _)) => panic!("Tried to do integer division on fracs"),
(Int(_), DoubleSlash, Frac(_, _)) => panic!("Tried to integer-divide Int by Frac"),
(Frac(_, _), DoubleSlash, Int(_)) => panic!("Tried to integer-divide Frac by Int"),
(_, DoubleSlash, _) => panic!("Tried to integer-divide non-numbers"),
}
}
#[inline(always)]
fn eval_case (condition: Evaluated, branches: SmallVec<[(Pattern, Box<Expr>); 4]>, vars: &Scope) -> Evaluated {
let Evaluated(ref evaluated_expr) = condition;
for (pattern, definition) in branches {
let mut branch_vars = vars.clone();
// TODO is there any way to avoid this clone? (Do we care,
// once this is a canonical expression with Rc instead of Box?
let cloned_expr = Evaluated(evaluated_expr.clone());
if pattern_match(cloned_expr, pattern, &mut branch_vars).is_ok() {
return scoped_eval(*definition, &branch_vars);
}
}
problem(NoBranchesMatched)
}
fn pattern_match(evaluated: Evaluated, pattern: Pattern, vars: &mut Scope) -> Result<(), expr::Problem> {
match pattern {
Identifier(name) => {
vars.insert(name, Rc::new(evaluated));
Ok(())
},
Underscore => {
// Underscore matches anything, and records no new vars.
Ok(())
},
Pattern::EmptyRecord => {
match evaluated {
Evaluated(Expr::EmptyRecord) => Ok(()),
Evaluated(expr) => Err(TypeMismatch(
format!("Wanted a `{}`, but was given `{}`.", "{}", expr)
))
}
},
Variant(pattern_variant_name, opt_pattern_contents) => {
match evaluated {
Evaluated(ApplyVariant(applied_variant_name, opt_applied_contents)) => {
if *pattern_variant_name != applied_variant_name {
return Err(TypeMismatch(
format!("Wanted a `{}` variant, but was given a `{}` variant.",
pattern_variant_name,
applied_variant_name
)
)
);
}
match (opt_pattern_contents, opt_applied_contents) {
( Some(pattern_contents), Some(applied_contents) ) => {
if pattern_contents.len() == applied_contents.len() {
// Recursively pattern match
for ( pattern_val, applied_val ) in pattern_contents.into_iter().zip(applied_contents) {
let evaluated_applied_val =
// We know this was already evaluated
// because we are matching on Evaluated(ApplyVariant)
Evaluated(applied_val);
pattern_match(evaluated_applied_val, pattern_val, vars)?;
}
Ok(())
} else {
Err(WrongArity(
pattern_contents.len() as u32,
applied_contents.len() as u32
)
)
}
},
( None, None ) => {
// It's the variant we expected, but it has no values in it,
// so we don't insert anything into vars.
Ok(())
},
( None, Some(contents) ) => {
// It's the variant we expected, but the arity is wrong.
Err(WrongArity(contents.len() as u32, 0))
},
( Some(patterns), None ) => {
// It's the variant we expected, but the arity is wrong.
Err(WrongArity(0, patterns.len() as u32))
},
}
},
_ => {
Err(TypeMismatch(format!("Wanted to destructure a `{}` variant, but was given a non-variant.", pattern_variant_name)))
}
}
}
}
}