Reading from stdin works

This commit is contained in:
Richard Feldman 2019-06-13 21:32:07 -04:00
parent 1c3cf5f675
commit 140fa5ffa9
6 changed files with 196 additions and 220 deletions

View file

@ -2,9 +2,9 @@ extern crate roc;
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use roc::expr::Expr::*;
use roc::expr::Expr; use roc::expr::Expr;
use roc::eval::{eval, from_evaluated}; use roc::eval::{Evaluated, eval, call};
use roc::eval::Evaluated::*;
use roc::parse; use roc::parse;
use std::io; use std::io;
@ -16,49 +16,49 @@ fn main() -> std::io::Result<()> {
let expr = parse::parse_string(contents.as_str()).unwrap(); let expr = parse::parse_string(contents.as_str()).unwrap();
eval_task(expr) process_task(eval(expr))
} }
fn eval_task(expr: Expr) -> std::io::Result<()> { fn process_task(evaluated: Evaluated) -> std::io::Result<()> {
match from_evaluated(eval(expr)) { match evaluated {
Error(problem) => { EvalError(problem) => {
println!("\n\u{001B}[4mruntime error\u{001B}[24m\n\n{:?}\n", problem); println!("\n\u{001B}[4mruntime error\u{001B}[24m\n\n{:?}\n", problem);
Ok(()) Ok(())
}, },
ApplyVariant(name, Some(mut exprs)) => { ApplyVariant(name, Some(mut vals)) => {
match name.as_str() { match name.as_str() {
"Echo" => { "Echo" => {
let payload = exprs.pop().unwrap(); let payload = vals.pop().unwrap();
let next_expr = exprs.pop().unwrap(); let callback = vals.pop().unwrap();
println!("{}", payload); println!("{}", payload);
eval_task(Apply(Box::new(next_expr), vec![EmptyRecord])) process_task(call(callback, vec![Expr::EmptyRecord]))
}, },
"Read" => { "Read" => {
let callback = vals.pop().unwrap();
let mut input = String::new(); let mut input = String::new();
io::stdin().read_line(&mut input)?; io::stdin().read_line(&mut input)?;
println!("[debug] You said: {}", input); process_task(call(callback, vec![Expr::Str(input)]))
Ok(())
}, },
_ => { _ => {
display_expr(ApplyVariant(name, Some(exprs))); display_val(ApplyVariant(name, Some(vals)));
Ok(()) Ok(())
} }
} }
}, },
output => { output => {
display_expr(output); display_val(output);
Ok(()) Ok(())
} }
} }
} }
fn display_expr(expr: Expr) { fn display_val(evaluated: Evaluated) {
println!("\n\u{001B}[4mroc out\u{001B}[24m\n\n{}\n", expr); println!("\n\u{001B}[4mroc out\u{001B}[24m\n\n{}\n", evaluated);
} }

View file

@ -59,9 +59,7 @@ demo =
after readInput, (firstName) -> after readInput, (firstName) ->
after (echo "Enter last name"), ({}) -> after (echo "Enter last name"), ({}) ->
after readInput, (lastName) -> after readInput, (lastName) ->
fullName = "\(firstName) \(lastName)" echo "Your name is: \(lastName)"
echo "Your name is: \(fullName)"
demo demo

View file

@ -1,78 +1,95 @@
use expr; use expr::{Expr, Operator, Pattern, Ident};
use expr::{Expr, Operator, Pattern, Problem};
use expr::Expr::*;
use expr::Problem::*;
use expr::Pattern::*; use expr::Pattern::*;
use expr::Operator::*; use expr::Operator::*;
use std::rc::Rc; use std::rc::Rc;
use std::fmt;
use im_rc::hashmap::HashMap; use im_rc::hashmap::HashMap;
use self::Evaluated::*;
use smallvec::SmallVec; use smallvec::SmallVec;
use self::Evaluated::*;
use self::Problem::*;
pub fn eval(expr: Expr) -> Evaluated { pub fn eval(expr: Expr) -> Evaluated {
scoped_eval(expr, &HashMap::new()) scoped_eval(expr, &HashMap::new())
} }
#[inline(always)] #[derive(Clone, Debug, PartialEq)]
pub fn from_evaluated(Evaluated(expr): Evaluated) -> Expr { pub enum Evaluated {
expr // Literals
Int(i64),
Frac(i64, u64),
EmptyStr,
Str(String),
InterpolatedStr(Vec<(String, Ident)>, String),
Char(char),
Closure(SmallVec<[Pattern; 2]>, Box<Expr>, Scope),
// Sum Types
ApplyVariant(String, Option<Vec<Evaluated>>),
// Product Types
EmptyRecord,
// Errors
EvalError(Problem)
}
#[derive(Clone, Debug, PartialEq)]
pub enum Problem {
UnrecognizedVarName(String),
TypeMismatch(String),
ReassignedVarName(String),
WrongArity(u32 /* Expected */, u32 /* Provided */),
NotEqual, // Used when (for example) a string literal pattern match fails
NoBranchesMatched,
} }
/// Wrapper indicating the expression has been evaluated
pub enum Evaluated { Evaluated(Expr) }
type Scope = HashMap<String, Rc<Evaluated>>; 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 { pub fn scoped_eval(expr: Expr, vars: &Scope) -> Evaluated {
match expr { match expr {
// Primitives need no further evaluation Expr::Int(num) => Int(num),
Error(_) | Int(_) | EmptyStr | Str(_) | Frac(_, _) | Char(_) | Closure(_, _) | Expr::EmptyRecord => Evaluated(expr), Expr::EmptyStr => EmptyStr,
Expr::Str(string) => Str(string),
Expr::Frac(numerator, denominator) => Frac(numerator, denominator),
Expr::Char(ch) => Char(ch),
Expr::Closure(args, body) => Closure(args, body, vars.clone()),
Expr::EmptyRecord => EmptyRecord,
// Resolve variable names // Resolve variable names
Var(name) => match vars.get(&name) { Expr::Var(name) => match vars.get(&name) {
Some(resolved) => { Some(resolved) => (**resolved).clone(),
let Evaluated(ref evaluated_expr) = **resolved; None => EvalError(UnrecognizedVarName(name))
// 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))
}, },
InterpolatedStr(pairs, trailing_str) => { Expr::InterpolatedStr(pairs, trailing_str) => {
let mut output = String::new(); let mut output = String::new();
for (string, var_name) in pairs.into_iter() { for (string, var_name) in pairs.into_iter() {
match vars.get(&var_name) { match vars.get(&var_name) {
Some(resolved) => { Some(resolved) => {
match **resolved { match **resolved {
Evaluated(Str(ref var_string)) => { Str(ref var_string) => {
output.push_str(string.as_str()); output.push_str(string.as_str());
output.push_str(var_string.as_str()); output.push_str(var_string.as_str());
}, },
_ => { _ => {
return problem(TypeMismatch(var_name)); return EvalError(TypeMismatch(var_name));
} }
} }
}, },
None => { return problem(UnrecognizedVarName(var_name)); } None => { return EvalError(UnrecognizedVarName(var_name)); }
} }
} }
output.push_str(trailing_str.as_str()); output.push_str(trailing_str.as_str());
Evaluated(Str(output)) Str(output)
}, },
Assign(Identifier(name), definition, in_expr) => { Expr::Assign(Identifier(name), definition, in_expr) => {
if vars.contains_key(&name) { if vars.contains_key(&name) {
problem(ReassignedVarName(name)) EvalError(ReassignedVarName(name))
} else { } else {
// Create a new scope containing the new declaration. // Create a new scope containing the new declaration.
let mut new_vars = vars.clone(); let mut new_vars = vars.clone();
@ -85,19 +102,19 @@ pub fn scoped_eval(expr: Expr, vars: &Scope) -> Evaluated {
} }
}, },
Assign(Integer(_), _, _) => { Expr::Assign(Integer(_), _, _) => {
panic!("You cannot assign integers to other values!"); panic!("You cannot assign integers to other values!");
}, },
Assign(Fraction(_, _), _, _) => { Expr::Assign(Fraction(_, _), _, _) => {
panic!("You cannot assign fractions to other values!"); panic!("You cannot assign fractions to other values!");
}, },
Assign(Variant(_name, _patterns), _definition, _in_expr) => { Expr::Assign(Variant(_name, _patterns), _definition, _in_expr) => {
panic!("Pattern matching on variants is not yet supported!"); panic!("Pattern matching on variants is not yet supported!");
}, },
Assign(Underscore, definition, in_expr) => { Expr::Assign(Underscore, definition, in_expr) => {
// Faithfully eval this, but discard its result. // Faithfully eval this, but discard its result.
scoped_eval(*definition, &vars); scoped_eval(*definition, &vars);
@ -105,7 +122,7 @@ pub fn scoped_eval(expr: Expr, vars: &Scope) -> Evaluated {
scoped_eval(*in_expr, vars) scoped_eval(*in_expr, vars)
}, },
Assign(Pattern::EmptyRecord, definition, in_expr) => { Expr::Assign(Pattern::EmptyRecordLiteral, definition, in_expr) => {
// Faithfully eval this, but discard its result. // Faithfully eval this, but discard its result.
scoped_eval(*definition, &vars); scoped_eval(*definition, &vars);
@ -113,46 +130,33 @@ pub fn scoped_eval(expr: Expr, vars: &Scope) -> Evaluated {
scoped_eval(*in_expr, vars) scoped_eval(*in_expr, vars)
}, },
CallByName(name, args) => { Expr::CallByName(name, args) => {
let func_expr = match vars.get(&name) { let func_expr = match vars.get(&name) {
Some(resolved) => { Some(resolved) => (**resolved).clone(),
let Evaluated(ref evaluated_expr) = **resolved; None => EvalError(UnrecognizedVarName(name))
// 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) eval_apply(func_expr, args, vars)
}, },
ApplyVariant(_, None) => Evaluated(expr), // This is all we do - for now... Expr::ApplyVariant(name, None) => ApplyVariant(name, None),
ApplyVariant(name, Some(exprs)) => {
Evaluated(
ApplyVariant(
name,
Some(
exprs.into_iter().map(|arg| {
let Evaluated(final_expr) = scoped_eval(arg, vars);
final_expr Expr::ApplyVariant(name, Some(exprs)) => {
}).collect() ApplyVariant(
) name,
) Some(exprs.into_iter().map(|arg| scoped_eval(arg, vars)).collect())
) )
} }
Apply(func_expr, args) => { Expr::Apply(func_expr, args) => {
eval_apply(scoped_eval(*func_expr, vars), args, vars) eval_apply(scoped_eval(*func_expr, vars), args, vars)
}, },
Case(condition, branches) => { Expr::Case(condition, branches) => {
eval_case(scoped_eval(*condition, vars), branches, vars) eval_case(scoped_eval(*condition, vars), branches, vars)
}, },
Operator(left_arg, op, right_arg) => { Expr::Operator(left_arg, op, right_arg) => {
eval_operator( eval_operator(
&scoped_eval(*left_arg, vars), &scoped_eval(*left_arg, vars),
op, op,
@ -160,44 +164,50 @@ pub fn scoped_eval(expr: Expr, vars: &Scope) -> Evaluated {
) )
}, },
If(condition, if_true, if_false) => { Expr::If(condition, if_true, if_false) => {
match scoped_eval(*condition, vars) { match scoped_eval(*condition, vars) {
Evaluated(ApplyVariant(variant_name, None)) => { ApplyVariant(variant_name, None) => {
match variant_name.as_str() { match variant_name.as_str() {
"True" => scoped_eval(*if_true, vars), "True" => scoped_eval(*if_true, vars),
"False" => scoped_eval(*if_false, vars), "False" => scoped_eval(*if_false, vars),
_ => problem(TypeMismatch("non-Bool used in `if` condition".to_string())) _ => EvalError(TypeMismatch("non-Bool used in `if` condition".to_string()))
} }
}, },
_ => problem(TypeMismatch("non-Bool used in `if` condition".to_string())) _ => EvalError(TypeMismatch("non-Bool used in `if` condition".to_string()))
} }
} }
} }
} }
#[inline(always)]
pub fn call(evaluated: Evaluated, args: Vec<Expr>) -> Evaluated {
eval_apply(evaluated, args, &HashMap::new())
}
#[inline(always)] #[inline(always)]
fn eval_apply(evaluated: Evaluated, args: Vec<Expr>, vars: &Scope) -> Evaluated { fn eval_apply(evaluated: Evaluated, args: Vec<Expr>, vars: &Scope) -> Evaluated {
match evaluated { match evaluated {
Evaluated(Closure(arg_patterns, body)) => { Closure(arg_patterns, body, closure_vars) => {
let combined_vars = vars.clone().union(closure_vars);
let evaluated_args = let evaluated_args =
args.into_iter() args.into_iter()
.map(|arg| scoped_eval(arg, vars)) .map(|arg| scoped_eval(arg, &combined_vars))
.collect(); .collect();
match eval_closure(evaluated_args, arg_patterns, vars) { match eval_closure(evaluated_args, arg_patterns, &combined_vars) {
Ok(new_vars) => scoped_eval(*body, &new_vars), Ok(new_vars) => scoped_eval(*body, &new_vars),
Err(prob) => problem(prob) Err(prob) => EvalError(prob)
} }
}, },
Evaluated(expr) => { expr => {
problem(TypeMismatch(format!("Tried to call a non-function: {}", expr))) EvalError(TypeMismatch(format!("Tried to call a non-function: {}", expr)))
} }
} }
} }
#[inline(always)] #[inline(always)]
fn eval_closure(args: Vec<Evaluated>, arg_patterns: SmallVec<[Pattern; 2]>, vars: &Scope) fn eval_closure(args: Vec<Evaluated>, arg_patterns: SmallVec<[Pattern; 2]>, vars: &Scope)
-> Result<Scope, expr::Problem> -> Result<Scope, Problem>
{ {
if arg_patterns.len() == args.len() { if arg_patterns.len() == args.len() {
// Create a new scope for the function to use. // Create a new scope for the function to use.
@ -213,7 +223,7 @@ fn eval_closure(args: Vec<Evaluated>, arg_patterns: SmallVec<[Pattern; 2]>, vars
} }
} }
fn bool_variant(is_true: bool) -> Expr { fn bool_variant(is_true: bool) -> Evaluated {
if is_true { if is_true {
ApplyVariant("True".to_string(), None) ApplyVariant("True".to_string(), None)
} else { } else {
@ -221,11 +231,10 @@ fn bool_variant(is_true: bool) -> Expr {
} }
} }
fn eq(expr1: &Expr, expr2: &Expr) -> Expr { fn eq(evaluated1: &Evaluated, evaluated2: &Evaluated) -> Evaluated {
match (expr1, expr2) { match (evaluated1, evaluated2) {
// All functions are defined as equal // All functions are defined as equal
(Closure(_, _), Closure(_, _)) => bool_variant(true), (Closure(_, _, _), Closure(_, _, _)) => bool_variant(true),
(ApplyVariant(left, None), ApplyVariant(right, None)) => { (ApplyVariant(left, None), ApplyVariant(right, None)) => {
bool_variant(left == right) bool_variant(left == right)
@ -248,23 +257,19 @@ fn eq(expr1: &Expr, expr2: &Expr) -> Expr {
(Char(left), Char(right)) => bool_variant(left == right), (Char(left), Char(right)) => bool_variant(left == right),
(Frac(_, _), Frac(_, _)) => panic!("Don't know how to == on Fracs yet"), (Frac(_, _), Frac(_, _)) => panic!("Don't know how to == on Fracs yet"),
(_, _) => Error(TypeMismatch("tried to use == on two values with incompatible types".to_string())), (_, _) => EvalError(TypeMismatch("tried to use == on two values with incompatible types".to_string())),
} }
} }
#[inline(always)] #[inline(always)]
fn eval_operator(Evaluated(left_expr): &Evaluated, op: Operator, Evaluated(right_expr): &Evaluated) -> Evaluated { fn eval_operator(left_expr: &Evaluated, op: Operator, right_expr: &Evaluated) -> Evaluated {
// TODO in the future, replace these with named function calls to stdlib // TODO in the future, replace these with named function calls to stdlib
match (left_expr, op, right_expr) { match (left_expr, op, right_expr) {
// Error
(Error(prob), _, _) => problem(prob.clone()),
(_, _, Error(prob)) => problem(prob.clone()),
// Equals // Equals
(_, Equals, _) => Evaluated(eq(left_expr, right_expr)), (_, Equals, _) => eq(left_expr, right_expr),
// Plus // Plus
(Int(left_num), Plus, Int(right_num)) => Evaluated(Int(left_num + right_num)), (Int(left_num), Plus, Int(right_num)) => Int(left_num + right_num),
(Frac(_, _), Plus, Frac(_, _)) => panic!("Don't know how to add fracs yet"), (Frac(_, _), Plus, Frac(_, _)) => panic!("Don't know how to add fracs yet"),
(Int(_), Plus, Frac(_, _)) => panic!("Tried to add Int and Frac"), (Int(_), Plus, Frac(_, _)) => panic!("Tried to add Int and Frac"),
@ -274,7 +279,7 @@ fn eval_operator(Evaluated(left_expr): &Evaluated, op: Operator, Evaluated(right
(_, Plus, _) => panic!("Tried to add non-numbers"), (_, Plus, _) => panic!("Tried to add non-numbers"),
// Star // Star
(Int(left_num), Star, Int(right_num)) => Evaluated(Int(left_num * right_num)), (Int(left_num), Star, Int(right_num)) => Int(left_num * right_num),
(Frac(_, _), Star, Frac(_, _)) => panic!("Don't know how to multiply fracs yet"), (Frac(_, _), Star, Frac(_, _)) => panic!("Don't know how to multiply fracs yet"),
(Int(_), Star, Frac(_, _)) => panic!("Tried to multiply Int and Frac"), (Int(_), Star, Frac(_, _)) => panic!("Tried to multiply Int and Frac"),
@ -284,7 +289,7 @@ fn eval_operator(Evaluated(left_expr): &Evaluated, op: Operator, Evaluated(right
(_, Star, _) => panic!("Tried to multiply non-numbers"), (_, Star, _) => panic!("Tried to multiply non-numbers"),
// Minus // Minus
(Int(left_num), Minus, Int(right_num)) => Evaluated(Int(left_num - right_num)), (Int(left_num), Minus, Int(right_num)) => Int(left_num - right_num),
(Frac(_, _), Minus, Frac(_, _)) => panic!("Don't know how to subtract fracs yet"), (Frac(_, _), Minus, Frac(_, _)) => panic!("Don't know how to subtract fracs yet"),
(Int(_), Minus, Frac(_, _)) => panic!("Tried to subtract Frac from Int"), (Int(_), Minus, Frac(_, _)) => panic!("Tried to subtract Frac from Int"),
@ -294,7 +299,7 @@ fn eval_operator(Evaluated(left_expr): &Evaluated, op: Operator, Evaluated(right
(_, Minus, _) => panic!("Tried to subtract non-numbers"), (_, Minus, _) => panic!("Tried to subtract non-numbers"),
// Slash // Slash
(Int(left_num), Slash, Int(right_num)) => Evaluated(Int(left_num / right_num)), (Int(left_num), Slash, Int(right_num)) => Int(left_num / right_num),
(Frac(_, _), Slash, Frac(_, _)) => panic!("Don't know how to divide fracs yet"), (Frac(_, _), Slash, Frac(_, _)) => panic!("Don't know how to divide fracs yet"),
(Int(_), Slash, Frac(_, _)) => panic!("Tried to divide Int by Frac"), (Int(_), Slash, Frac(_, _)) => panic!("Tried to divide Int by Frac"),
@ -304,7 +309,7 @@ fn eval_operator(Evaluated(left_expr): &Evaluated, op: Operator, Evaluated(right
(_, Slash, _) => panic!("Tried to divide non-numbers"), (_, Slash, _) => panic!("Tried to divide non-numbers"),
// DoubleSlash // DoubleSlash
(Int(left_num), DoubleSlash, Int(right_num)) => Evaluated(Int(left_num / right_num)), (Int(left_num), DoubleSlash, Int(right_num)) => Int(left_num / right_num),
(Frac(_, _), DoubleSlash, Frac(_, _)) => panic!("Tried to do integer division on fracs"), (Frac(_, _), DoubleSlash, Frac(_, _)) => panic!("Tried to do integer division on fracs"),
(Int(_), DoubleSlash, Frac(_, _)) => panic!("Tried to integer-divide Int by Frac"), (Int(_), DoubleSlash, Frac(_, _)) => panic!("Tried to integer-divide Int by Frac"),
@ -316,25 +321,22 @@ fn eval_operator(Evaluated(left_expr): &Evaluated, op: Operator, Evaluated(right
} }
#[inline(always)] #[inline(always)]
fn eval_case (condition: Evaluated, branches: SmallVec<[(Pattern, Box<Expr>); 2]>, vars: &Scope) -> Evaluated { fn eval_case (evaluated: Evaluated, branches: SmallVec<[(Pattern, Box<Expr>); 2]>, vars: &Scope) -> Evaluated {
let Evaluated(ref evaluated_expr) = condition;
for (pattern, definition) in branches { for (pattern, definition) in branches {
let mut branch_vars = vars.clone(); let mut branch_vars = vars.clone();
// TODO is there any way to avoid this clone? (Do we care, // TODO can we avoid doing this clone somehow?
// once this is a canonical expression with Rc instead of Box? let clone = evaluated.clone();
let cloned_expr = Evaluated(evaluated_expr.clone());
if pattern_match(cloned_expr, pattern, &mut branch_vars).is_ok() { if pattern_match(clone, pattern, &mut branch_vars).is_ok() {
return scoped_eval(*definition, &branch_vars); return scoped_eval(*definition, &branch_vars);
} }
} }
problem(NoBranchesMatched) EvalError(NoBranchesMatched)
} }
fn pattern_match(evaluated: Evaluated, pattern: Pattern, vars: &mut Scope) -> Result<(), expr::Problem> { fn pattern_match(evaluated: Evaluated, pattern: Pattern, vars: &mut Scope) -> Result<(), Problem> {
match pattern { match pattern {
Identifier(name) => { Identifier(name) => {
vars.insert(name, Rc::new(evaluated)); vars.insert(name, Rc::new(evaluated));
@ -345,10 +347,10 @@ fn pattern_match(evaluated: Evaluated, pattern: Pattern, vars: &mut Scope) -> Re
// Underscore matches anything, and records no new vars. // Underscore matches anything, and records no new vars.
Ok(()) Ok(())
}, },
Pattern::EmptyRecord => { EmptyRecordLiteral => {
match evaluated { match evaluated {
Evaluated(Expr::EmptyRecord) => Ok(()), EmptyRecord => Ok(()),
Evaluated(expr) => Err(TypeMismatch( expr => Err(TypeMismatch(
format!("Wanted a `{}`, but was given `{}`.", "{}", expr) format!("Wanted a `{}`, but was given `{}`.", "{}", expr)
)) ))
} }
@ -356,7 +358,7 @@ fn pattern_match(evaluated: Evaluated, pattern: Pattern, vars: &mut Scope) -> Re
Integer(pattern_num) => { Integer(pattern_num) => {
match evaluated { match evaluated {
Evaluated(Expr::Int(evaluated_num)) => { Int(evaluated_num) => {
if pattern_num == evaluated_num { if pattern_num == evaluated_num {
Ok(()) Ok(())
} else { } else {
@ -364,7 +366,7 @@ fn pattern_match(evaluated: Evaluated, pattern: Pattern, vars: &mut Scope) -> Re
} }
}, },
Evaluated(expr) => Err(TypeMismatch( expr => Err(TypeMismatch(
format!("Wanted a `{}`, but was given `{}`.", "{}", expr) format!("Wanted a `{}`, but was given `{}`.", "{}", expr)
)) ))
} }
@ -372,11 +374,11 @@ fn pattern_match(evaluated: Evaluated, pattern: Pattern, vars: &mut Scope) -> Re
Fraction(_pattern_numerator, _pattern_denominator) => { Fraction(_pattern_numerator, _pattern_denominator) => {
match evaluated { match evaluated {
Evaluated(Expr::Frac(_evaluated_numerator, _evaluated_denominator)) => { Frac(_evaluated_numerator, _evaluated_denominator) => {
panic!("Can't handle pattern matching on fracs yet."); panic!("Can't handle pattern matching on fracs yet.");
}, },
Evaluated(expr) => Err(TypeMismatch( expr => Err(TypeMismatch(
format!("Wanted a `{}`, but was given `{}`.", "{}", expr) format!("Wanted a `{}`, but was given `{}`.", "{}", expr)
)) ))
} }
@ -384,7 +386,7 @@ fn pattern_match(evaluated: Evaluated, pattern: Pattern, vars: &mut Scope) -> Re
Variant(pattern_variant_name, opt_pattern_contents) => { Variant(pattern_variant_name, opt_pattern_contents) => {
match evaluated { match evaluated {
Evaluated(ApplyVariant(applied_variant_name, opt_applied_contents)) => { ApplyVariant(applied_variant_name, opt_applied_contents) => {
if *pattern_variant_name != applied_variant_name { if *pattern_variant_name != applied_variant_name {
return Err(TypeMismatch( return Err(TypeMismatch(
format!("Wanted a `{}` variant, but was given a `{}` variant.", format!("Wanted a `{}` variant, but was given a `{}` variant.",
@ -400,12 +402,7 @@ fn pattern_match(evaluated: Evaluated, pattern: Pattern, vars: &mut Scope) -> Re
if pattern_contents.len() == applied_contents.len() { if pattern_contents.len() == applied_contents.len() {
// Recursively pattern match // Recursively pattern match
for ( pattern_val, applied_val ) in pattern_contents.into_iter().zip(applied_contents) { for ( pattern_val, applied_val ) in pattern_contents.into_iter().zip(applied_contents) {
let evaluated_applied_val = pattern_match(applied_val, pattern_val, vars)?;
// 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(()) Ok(())
@ -440,3 +437,61 @@ fn pattern_match(evaluated: Evaluated, pattern: Pattern, vars: &mut Scope) -> Re
} }
} }
impl fmt::Display for Evaluated {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
// PRIMITIVES
Int(num) => write!(f, "{}", *num),
Frac(numerator, denominator) => {
if *denominator == 10 {
write!(f, "{}", (*numerator as f64 / 10.0))
} else {
write!(f, "{}/{}", numerator, denominator)
}
},
Str(string) => {
let escaped_str =
(*string)
.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\t", "\\t")
.replace("\n", "\\n")
.replace("\r", "\\r");
write!(f, "\"{}\"", escaped_str)
},
Char(ch) => write!(f, "'{}'", *ch),
Closure(args, _, _) => write!(f, "<{}-argument function>", args.len()),
ApplyVariant(name, opt_exprs) => {
match opt_exprs {
None => write!(f, "{}", name),
Some(exprs) => {
let contents =
exprs.into_iter()
.map(|expr| format!(" {}", expr))
.collect::<Vec<_>>()
.join(",");
write!(f, "{}{}", name, contents)
}
}
},
// ERRORS
EvalError(Problem::UnrecognizedVarName(name)) => write!(f, "NAMING ERROR: Unrecognized var name `{}`", name),
EvalError(Problem::NoBranchesMatched) => write!(f, "No branches matched in this case-expression"),
EvalError(Problem::TypeMismatch(info)) => write!(f, "TYPE ERROR: {}", info),
EvalError(Problem::ReassignedVarName(name)) => write!(f, "REASSIGNED CONSTANT: {}", name),
EvalError(Problem::WrongArity(expected_arity, provided_arity)) => {
if provided_arity > expected_arity {
write!(f, "TOO MANY ARGUMENTS: needed {} arguments, but got {}", expected_arity, provided_arity)
} else {
write!(f, "MISSING ARGUMENTS: needed {} arguments, but got {}", expected_arity, provided_arity)
}
}
// UNFORMATTED
_ => write!(f, "<partially evaluated expression>")
}
}
}

View file

@ -1,5 +1,3 @@
use std::fmt;
use self::Expr::*;
use smallvec::SmallVec; use smallvec::SmallVec;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
@ -30,80 +28,10 @@ pub enum Expr {
// Conditionals // Conditionals
If(Box<Expr>, Box<Expr>, Box<Expr>), If(Box<Expr>, Box<Expr>, Box<Expr>),
Case(Box<Expr>, SmallVec<[(Pattern, Box<Expr>); 2]>), Case(Box<Expr>, SmallVec<[(Pattern, Box<Expr>); 2]>),
// Error
Error(Problem),
} }
pub type Ident = String; pub type Ident = String;
impl fmt::Display for Expr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
// PRIMITIVES
Int(num) => write!(f, "{}", *num),
Frac(numerator, denominator) => {
if *denominator == 10 {
write!(f, "{}", (*numerator as f64 / 10.0))
} else {
write!(f, "{}/{}", numerator, denominator)
}
},
Str(string) => {
let escaped_str =
(*string)
.replace("\\", "\\\\")
.replace("\"", "\\\"")
.replace("\t", "\\t")
.replace("\n", "\\n")
.replace("\r", "\\r");
write!(f, "\"{}\"", escaped_str)
},
Char(ch) => write!(f, "'{}'", *ch),
Closure(args, _) => write!(f, "<{}-argument function>", args.len()),
ApplyVariant(name, opt_exprs) => {
match opt_exprs {
None => write!(f, "{}", name),
Some(exprs) => {
let contents =
exprs.into_iter()
.map(|expr| format!(" {}", expr))
.collect::<Vec<_>>()
.join(",");
write!(f, "{}{}", name, contents)
}
}
},
// ERRORS
Error(Problem::UnrecognizedVarName(name)) => write!(f, "NAMING ERROR: Unrecognized var name `{}`", name),
Error(Problem::TypeMismatch(info)) => write!(f, "TYPE ERROR: {}", info),
Error(Problem::ReassignedVarName(name)) => write!(f, "REASSIGNED CONSTANT: {}", name),
Error(Problem::WrongArity(expected_arity, provided_arity)) => {
if provided_arity > expected_arity {
write!(f, "TOO MANY ARGUMENTS: needed {} arguments, but got {}", expected_arity, provided_arity)
} else {
write!(f, "MISSING ARGUMENTS: needed {} arguments, but got {}", expected_arity, provided_arity)
}
}
// UNFORMATTED
_ => write!(f, "<partially evaluated expression>")
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Problem {
UnrecognizedVarName(String),
TypeMismatch(String),
ReassignedVarName(String),
WrongArity(u32 /* Expected */, u32 /* Provided */),
NotEqual, // Used when (for example) a string literal pattern match fails
NoBranchesMatched,
}
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Pattern { pub enum Pattern {
@ -111,7 +39,7 @@ pub enum Pattern {
Variant(String, Option<Vec<Pattern>>), Variant(String, Option<Vec<Pattern>>),
Integer(i64), Integer(i64),
Fraction(i64, u64), Fraction(i64, u64),
EmptyRecord, EmptyRecordLiteral,
Underscore Underscore
} }

View file

@ -414,7 +414,7 @@ parser! {
choice(( choice((
char('_').map(|_| Pattern::Underscore), char('_').map(|_| Pattern::Underscore),
string("{}").map(|_| Pattern::EmptyRecord), string("{}").map(|_| Pattern::EmptyRecordLiteral),
match_variant(min_indent), match_variant(min_indent),
number_pattern(), // This goes before ident() so number literals aren't mistaken for malformed idents. number_pattern(), // This goes before ident() so number literals aren't mistaken for malformed idents.
ident().map(|name| Pattern::Identifier(name)), ident().map(|name| Pattern::Identifier(name)),

View file

@ -5,22 +5,17 @@ extern crate roc;
#[cfg(test)] #[cfg(test)]
mod test_eval { mod test_eval {
use roc::expr::{Expr};
use roc::expr::Expr::*;
use roc::expr::Operator::*; use roc::expr::Operator::*;
use roc::expr::Pattern::*; use roc::expr::Pattern::*;
use roc::expr::Expr::*;
fn eval(expr: Expr) -> Expr { use roc::eval::eval;
roc::eval::from_evaluated( use roc::eval::Evaluated;
roc::eval::eval(expr)
)
}
#[test] #[test]
fn one_plus_one() { fn one_plus_one() {
assert_eq!( assert_eq!(
eval(Operator(Box::new(Int(1)), Plus, Box::new(Int(1)))), eval(Operator(Box::new(Int(1)), Plus, Box::new(Int(1)))),
Int(2) Evaluated::Int(2)
); );
} }
@ -42,7 +37,7 @@ mod test_eval {
)) ))
))))) )))))
), ),
Str("hi_one_two_three_string!".to_string()) Evaluated::Str("hi_one_two_three_string!".to_string())
); );
} }
@ -55,7 +50,7 @@ mod test_eval {
Box::new(Operator(Box::new(Int(4)), Plus, Box::new(Int(5)))) Box::new(Operator(Box::new(Int(4)), Plus, Box::new(Int(5))))
) )
), ),
Int(3) Evaluated::Int(3)
); );
assert_eq!( assert_eq!(
@ -65,7 +60,7 @@ mod test_eval {
Box::new(Operator(Box::new(Int(4)), Plus, Box::new(Int(5)))) Box::new(Operator(Box::new(Int(4)), Plus, Box::new(Int(5))))
) )
), ),
Int(9) Evaluated::Int(9)
); );
} }
} }