mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Reading from stdin works
This commit is contained in:
parent
1c3cf5f675
commit
140fa5ffa9
6 changed files with 196 additions and 220 deletions
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
281
src/eval.rs
281
src/eval.rs
|
@ -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>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
src/expr.rs
74
src/expr.rs
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)),
|
||||||
|
|
|
@ -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)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue