Fix paren parsing bug, thread errors through eval

This commit is contained in:
Richard Feldman 2019-07-09 23:19:50 -04:00
parent 07a05b90fc
commit fde3cedfef
5 changed files with 143 additions and 102 deletions

View file

@ -57,6 +57,7 @@ fallback = \task onFailure =>
(\ioErr => fallback (prevOnFailure ioErr) onFailure) (\ioErr => fallback (prevOnFailure ioErr) onFailure)
(\str => fallback (cont str) onFailure) (\str => fallback (cont str) onFailure)
############################################################### ###############################################################
# In the future everything above here will be in a platform. # # In the future everything above here will be in a platform. #
############################################################### ###############################################################
@ -68,5 +69,4 @@ program =
after readInput \lastName => after readInput \lastName =>
echo "Your full name is: \(firstName) \(lastName)" echo "Your full name is: \(firstName) \(lastName)"
program program

View file

@ -33,8 +33,9 @@ fn main() -> std::io::Result<()> {
fn process_task(evaluated: Evaluated) -> std::io::Result<()> { fn process_task(evaluated: Evaluated) -> std::io::Result<()> {
match evaluated { match evaluated {
EvalError(problem) => { EvalError(region, 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{} at {}\n",
format!("{}", problem), format!("line {}, column {}", region.start_line, region.start_col));
Ok(()) Ok(())
}, },
@ -44,7 +45,13 @@ fn process_task(evaluated: Evaluated) -> std::io::Result<()> {
// Extract the string from the Echo variant. // Extract the string from the Echo variant.
let string_to_be_displayed = match vals.pop() { let string_to_be_displayed = match vals.pop() {
Some(Str(payload)) => payload, Some(Str(payload)) => payload,
Some(EvalError(err)) => { panic!("RUNTIME ERROR in Echo: {}", format!("{}", err)); }, Some(EvalError(region, err)) => {
panic!(
"RUNTIME ERROR in Echo: {} at {}",
format!("{}", err),
format!("line {}, column {}", region.start_line, region.start_col)
);
},
Some(val) => { panic!("TYPE MISMATCH in Echo: {}", format!("{}", val)); }, Some(val) => { panic!("TYPE MISMATCH in Echo: {}", format!("{}", val)); },
None => { panic!("TYPE MISMATCH in Echo: None"); } None => { panic!("TYPE MISMATCH in Echo: None"); }
}; };
@ -55,7 +62,13 @@ fn process_task(evaluated: Evaluated) -> std::io::Result<()> {
// Continue with the callback. // Continue with the callback.
let callback = vals.pop().unwrap(); let callback = vals.pop().unwrap();
process_task(call(callback, vec![with_zero_loc(Expr::EmptyRecord)])) process_task(
call(
Region { start_line: 0, start_col: 0, end_line: 0, end_col: 0 },
callback,
vec![with_zero_loc(Expr::EmptyRecord)]
)
)
}, },
"Read" => { "Read" => {
// Read a line from from stdin, since that's what Read does! // Read a line from from stdin, since that's what Read does!
@ -66,7 +79,13 @@ fn process_task(evaluated: Evaluated) -> std::io::Result<()> {
// Continue with the callback. // Continue with the callback.
let callback = vals.pop().unwrap(); let callback = vals.pop().unwrap();
process_task(call(callback, vec![with_zero_loc(Expr::Str(input.trim().to_string()))])) process_task(
call(
Region { start_line: 0, start_col: 0, end_line: 0, end_col: 0 },
callback,
vec![with_zero_loc(Expr::Str(input.trim().to_string()))]
)
)
}, },
"Success" => { "Success" => {
// We finished all our tasks. Great! No need to print anything. // We finished all our tasks. Great! No need to print anything.

View file

@ -36,14 +36,14 @@ pub enum Evaluated {
EmptyRecord, EmptyRecord,
// Errors // Errors
EvalError(Problem) EvalError(Region, Problem)
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub enum Problem { pub enum Problem {
UnrecognizedVarName(Region, String), UnrecognizedVarName(String),
TypeMismatch(String), TypeMismatch(String),
ReassignedVarName(Region, String), ReassignedVarName(String),
WrongArity(u32 /* Expected */, u32 /* Provided */), WrongArity(u32 /* Expected */, u32 /* Provided */),
NotEqual, // Used when (for example) a string literal pattern match fails NotEqual, // Used when (for example) a string literal pattern match fails
NoBranchesMatched, NoBranchesMatched,
@ -68,7 +68,7 @@ pub fn scoped_eval(expr: Located<Expr>, vars: &Scope) -> Evaluated {
// Resolve variable names // Resolve variable names
Expr::Var(name) => match vars.get(&name) { Expr::Var(name) => match vars.get(&name) {
Some(resolved) => (**resolved).clone(), Some(resolved) => (**resolved).clone(),
None => EvalError(UnrecognizedVarName(region, name)) None => EvalError(region, UnrecognizedVarName(name))
}, },
Expr::InterpolatedStr(pairs, trailing_str) => { Expr::InterpolatedStr(pairs, trailing_str) => {
@ -83,11 +83,11 @@ pub fn scoped_eval(expr: Located<Expr>, vars: &Scope) -> Evaluated {
output.push_str(var_string.as_str()); output.push_str(var_string.as_str());
}, },
_ => { _ => {
return EvalError(TypeMismatch(var_name.value)); return EvalError(region, TypeMismatch(var_name.value));
} }
} }
}, },
None => { return EvalError(UnrecognizedVarName(region, var_name.value)); } None => { return EvalError(region, UnrecognizedVarName(var_name.value)); }
} }
} }
@ -103,10 +103,10 @@ pub fn scoped_eval(expr: Located<Expr>, vars: &Scope) -> Evaluated {
Expr::CallByName(name, args) => { Expr::CallByName(name, args) => {
let func_expr = match vars.get(&name) { let func_expr = match vars.get(&name) {
Some(resolved) => (**resolved).clone(), Some(resolved) => (**resolved).clone(),
None => EvalError(UnrecognizedVarName(region, name)) None => EvalError(region, UnrecognizedVarName(name))
}; };
eval_apply(func_expr, args, vars) eval_apply(region, func_expr, args, vars)
}, },
Expr::ApplyVariant(name, None) => ApplyVariant(name, None), Expr::ApplyVariant(name, None) => ApplyVariant(name, None),
@ -119,15 +119,16 @@ pub fn scoped_eval(expr: Located<Expr>, vars: &Scope) -> Evaluated {
} }
Expr::Apply(func_expr, args) => { Expr::Apply(func_expr, args) => {
eval_apply(scoped_eval(*func_expr, vars), args, vars) eval_apply(region, scoped_eval(*func_expr, vars), args, vars)
}, },
Expr::Case(condition, branches) => { Expr::Case(condition, branches) => {
eval_case(scoped_eval(*condition, vars), branches, vars) eval_case(region, scoped_eval(*condition, vars), branches, vars)
}, },
Expr::Operator(left_arg, op, right_arg) => { Expr::Operator(left_arg, op, right_arg) => {
eval_operator( eval_operator(
region,
&scoped_eval(*left_arg, vars), &scoped_eval(*left_arg, vars),
op.value, op.value,
&scoped_eval(*right_arg, vars) &scoped_eval(*right_arg, vars)
@ -140,10 +141,10 @@ pub fn scoped_eval(expr: Located<Expr>, vars: &Scope) -> Evaluated {
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),
_ => EvalError(TypeMismatch("non-Bool used in `if` condition".to_string())) _ => EvalError(region, TypeMismatch("non-Bool used in `if` condition".to_string()))
} }
}, },
_ => EvalError(TypeMismatch("non-Bool used in `if` condition".to_string())) _ => EvalError(region, TypeMismatch("non-Bool used in `if` condition".to_string()))
} }
} }
} }
@ -157,7 +158,7 @@ fn eval_assign(pattern: Located<Pattern>, assigned_expr: Located<Expr>, returned
match pattern.value { match pattern.value {
Identifier(name) => { Identifier(name) => {
if vars.contains_key(&name) { if vars.contains_key(&name) {
EvalError(ReassignedVarName(pattern_region, name)) EvalError(pattern_region, 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();
@ -193,12 +194,12 @@ fn eval_assign(pattern: Located<Pattern>, assigned_expr: Located<Expr>, returned
} }
#[inline(always)] #[inline(always)]
pub fn call(evaluated: Evaluated, args: Vec<Located<Expr>>) -> Evaluated { pub fn call(region: Region, evaluated: Evaluated, args: Vec<Located<Expr>>) -> Evaluated {
eval_apply(evaluated, args, &HashMap::new()) eval_apply(region, evaluated, args, &HashMap::new())
} }
#[inline(always)] #[inline(always)]
fn eval_apply(evaluated: Evaluated, args: Vec<Located<Expr>>, vars: &Scope) -> Evaluated { fn eval_apply(region: Region, evaluated: Evaluated, args: Vec<Located<Expr>>, vars: &Scope) -> Evaluated {
use self::Evaluated::*; use self::Evaluated::*;
match evaluated { match evaluated {
@ -211,11 +212,11 @@ fn eval_apply(evaluated: Evaluated, args: Vec<Located<Expr>>, vars: &Scope) -> E
match eval_closure(evaluated_args, arg_patterns, &combined_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) => EvalError(prob) Err(prob) => EvalError(region, prob)
} }
}, },
val => { val => {
EvalError(TypeMismatch(format!("Tried to call a non-function: {}", val))) EvalError(region, TypeMismatch(format!("Tried to call a non-function: {}", val)))
} }
} }
} }
@ -246,7 +247,7 @@ fn bool_variant(is_true: bool) -> Evaluated {
} }
} }
fn eq(evaluated1: &Evaluated, evaluated2: &Evaluated) -> Evaluated { fn eq(region: Region, evaluated1: &Evaluated, evaluated2: &Evaluated) -> Evaluated {
use self::Evaluated::*; use self::Evaluated::*;
match (evaluated1, evaluated2) { match (evaluated1, evaluated2) {
@ -274,7 +275,7 @@ fn eq(evaluated1: &Evaluated, evaluated2: &Evaluated) -> Evaluated {
(Char(left), Char(right)) => bool_variant(left == right), (Char(left), Char(right)) => bool_variant(left == right),
(Frac(left), Frac(right)) => bool_variant(left == right), (Frac(left), Frac(right)) => bool_variant(left == right),
(_, _) => EvalError(TypeMismatch("tried to use == on two values with incompatible types".to_string())), (_, _) => EvalError(region, TypeMismatch("tried to use == on two values with incompatible types".to_string())),
} }
} }
@ -289,95 +290,95 @@ fn bool_from_variant_name(name: &str) -> Option<bool> {
} }
#[inline(always)] #[inline(always)]
fn eval_operator(left_expr: &Evaluated, op: Operator, right_expr: &Evaluated) -> Evaluated { fn eval_operator(region: Region, left_expr: &Evaluated, op: Operator, right_expr: &Evaluated) -> Evaluated {
use self::Evaluated::*; use self::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) {
// Equals // Equals
(_, Equals, _) => eq(left_expr, right_expr), (_, Equals, _) => eq(region, left_expr, right_expr),
// And // And
(ApplyVariant(left_name, None), And, ApplyVariant(right_name, None)) => { (ApplyVariant(left_name, None), And, ApplyVariant(right_name, None)) => {
match (bool_from_variant_name(left_name), bool_from_variant_name(right_name)) { match (bool_from_variant_name(left_name), bool_from_variant_name(right_name)) {
(Some(left_bool), Some(right_bool)) => bool_variant(left_bool && right_bool), (Some(left_bool), Some(right_bool)) => bool_variant(left_bool && right_bool),
_ => EvalError(TypeMismatch("tried to use && on non-bools".to_string())), _ => EvalError(region, TypeMismatch("tried to use && on non-bools".to_string())),
} }
} }
(_, And, _) => EvalError(TypeMismatch("tried to use && on non-bools".to_string())), (_, And, _) => EvalError(region, TypeMismatch("tried to use && on non-bools".to_string())),
// Or // Or
(ApplyVariant(left_name, None), Or, ApplyVariant(right_name, None)) => { (ApplyVariant(left_name, None), Or, ApplyVariant(right_name, None)) => {
match (bool_from_variant_name(left_name), bool_from_variant_name(right_name)) { match (bool_from_variant_name(left_name), bool_from_variant_name(right_name)) {
(Some(left_bool), Some(right_bool)) => bool_variant(left_bool || right_bool), (Some(left_bool), Some(right_bool)) => bool_variant(left_bool || right_bool),
_ => EvalError(TypeMismatch("tried to use && on non-bools".to_string())), _ => EvalError(region, TypeMismatch("tried to use && on non-bools".to_string())),
} }
} }
(_, Or, _) => EvalError(TypeMismatch("tried to use && on non-bools".to_string())), (_, Or, _) => EvalError(region, TypeMismatch("tried to use && on non-bools".to_string())),
// LessThan // LessThan
(Int(left_num), LessThan, Int(right_num)) => bool_variant(left_num < right_num), (Int(left_num), LessThan, Int(right_num)) => bool_variant(left_num < right_num),
(Frac(left_num), LessThan, Frac(right_num)) => bool_variant(left_num < right_num), (Frac(left_num), LessThan, Frac(right_num)) => bool_variant(left_num < right_num),
(Int(_), LessThan, Frac(_)) => EvalError(TypeMismatch("tried check Frac < Int. Explicitly convert them to the same type first!".to_string())), (Int(_), LessThan, Frac(_)) => EvalError(region, TypeMismatch("tried check Frac < Int. Explicitly convert them to the same type first!".to_string())),
(Frac(_), LessThan, Int(_)) => EvalError(TypeMismatch("tried check Int < Frac. Explicitly convert them to the same type first!".to_string())), (Frac(_), LessThan, Int(_)) => EvalError(region,TypeMismatch("tried check Int < Frac. Explicitly convert them to the same type first!".to_string())),
(_, LessThan, _) => EvalError(TypeMismatch("tried to check if one non-number < another non-number".to_string())), (_, LessThan, _) => EvalError(region, TypeMismatch("tried to check if one non-number < another non-number".to_string())),
// LessThanOrEq // LessThanOrEq
(Int(left_num), LessThanOrEq, Int(right_num)) => bool_variant(left_num <= right_num), (Int(left_num), LessThanOrEq, Int(right_num)) => bool_variant(left_num <= right_num),
(Frac(left_num), LessThanOrEq, Frac(right_num)) => bool_variant(left_num <= right_num), (Frac(left_num), LessThanOrEq, Frac(right_num)) => bool_variant(left_num <= right_num),
(Int(_), LessThanOrEq, Frac(_)) => EvalError(TypeMismatch("tried check Frac <= Int. Explicitly convert them to the same type first!".to_string())), (Int(_), LessThanOrEq, Frac(_)) => EvalError(region, TypeMismatch("tried check Frac <= Int. Explicitly convert them to the same type first!".to_string())),
(Frac(_), LessThanOrEq, Int(_)) => EvalError(TypeMismatch("tried check Int <= Frac. Explicitly convert them to the same type first!".to_string())), (Frac(_), LessThanOrEq, Int(_)) => EvalError(region, TypeMismatch("tried check Int <= Frac. Explicitly convert them to the same type first!".to_string())),
(_, LessThanOrEq, _) => EvalError(TypeMismatch("tried to check if one non-number <= another non-number".to_string())), (_, LessThanOrEq, _) => EvalError(region, TypeMismatch("tried to check if one non-number <= another non-number".to_string())),
// GreaterThan // GreaterThan
(Int(left_num), GreaterThan, Int(right_num)) => bool_variant(left_num > right_num), (Int(left_num), GreaterThan, Int(right_num)) => bool_variant(left_num > right_num),
(Frac(left_num), GreaterThan, Frac(right_num)) => bool_variant(left_num > right_num), (Frac(left_num), GreaterThan, Frac(right_num)) => bool_variant(left_num > right_num),
(Int(_), GreaterThan, Frac(_)) => EvalError(TypeMismatch("tried check Frac > Int. Explicitly convert them to the same type first!".to_string())), (Int(_), GreaterThan, Frac(_)) => EvalError(region, TypeMismatch("tried check Frac > Int. Explicitly convert them to the same type first!".to_string())),
(Frac(_), GreaterThan, Int(_)) => EvalError(TypeMismatch("tried check Int > Frac. Explicitly convert them to the same type first!".to_string())), (Frac(_), GreaterThan, Int(_)) => EvalError(region, TypeMismatch("tried check Int > Frac. Explicitly convert them to the same type first!".to_string())),
(_, GreaterThan, _) => EvalError(TypeMismatch("tried to check if one non-number > another non-number".to_string())), (_, GreaterThan, _) => EvalError(region, TypeMismatch("tried to check if one non-number > another non-number".to_string())),
// GreaterThanOrEq // GreaterThanOrEq
(Int(left_num), GreaterThanOrEq, Int(right_num)) => bool_variant(left_num >= right_num), (Int(left_num), GreaterThanOrEq, Int(right_num)) => bool_variant(left_num >= right_num),
(Frac(left_num), GreaterThanOrEq, Frac(right_num)) => bool_variant(left_num >= right_num), (Frac(left_num), GreaterThanOrEq, Frac(right_num)) => bool_variant(left_num >= right_num),
(Int(_), GreaterThanOrEq, Frac(_)) => EvalError(TypeMismatch("tried check Frac >= Int. Explicitly convert them to the same type first!".to_string())), (Int(_), GreaterThanOrEq, Frac(_)) => EvalError(region, TypeMismatch("tried check Frac >= Int. Explicitly convert them to the same type first!".to_string())),
(Frac(_), GreaterThanOrEq, Int(_)) => EvalError(TypeMismatch("tried check Int >= Frac. Explicitly convert them to the same type first!".to_string())), (Frac(_), GreaterThanOrEq, Int(_)) => EvalError(region, TypeMismatch("tried check Int >= Frac. Explicitly convert them to the same type first!".to_string())),
(_, GreaterThanOrEq, _) => EvalError(TypeMismatch("tried to check if one non-number >= another non-number".to_string())), (_, GreaterThanOrEq, _) => EvalError(region, TypeMismatch("tried to check if one non-number >= another non-number".to_string())),
// Plus // Plus
(Int(left_num), Plus, Int(right_num)) => Int(left_num.checked_add(*right_num).unwrap_or_else(|| panic!("Integer overflow on +"))), (Int(left_num), Plus, Int(right_num)) => Int(left_num.checked_add(*right_num).unwrap_or_else(|| panic!("Integer overflow on +"))),
(Frac(left_num), Plus, Frac(right_num)) => Frac(left_num + right_num), (Frac(left_num), Plus, Frac(right_num)) => Frac(left_num + right_num),
(Int(_), Plus, Frac(_)) => EvalError(TypeMismatch("tried to add Frac to Int. Explicitly convert them to the same type first!".to_string())), (Int(_), Plus, Frac(_)) => EvalError(region, TypeMismatch("tried to add Frac to Int. Explicitly convert them to the same type first!".to_string())),
(Frac(_), Plus, Int(_)) => EvalError(TypeMismatch("tried to add Int to Frac. Explicitly convert them to the same type first!".to_string())), (Frac(_), Plus, Int(_)) => EvalError(region, TypeMismatch("tried to add Int to Frac. Explicitly convert them to the same type first!".to_string())),
(_, Plus, _) => EvalError(TypeMismatch("tried to add non-numbers".to_string())), (_, Plus, _) => EvalError(region, TypeMismatch("tried to add non-numbers".to_string())),
// Star // Star
(Int(left_num), Star, Int(right_num)) => Int(left_num.checked_mul(*right_num).unwrap_or_else(|| panic!("Integer overflow on *"))), (Int(left_num), Star, Int(right_num)) => Int(left_num.checked_mul(*right_num).unwrap_or_else(|| panic!("Integer overflow on *"))),
(Frac(left_num), Star, Frac(right_num)) => Frac(left_num * right_num), (Frac(left_num), Star, Frac(right_num)) => Frac(left_num * right_num),
(Int(_), Star, Frac(_)) => EvalError(TypeMismatch("tried to multiply Int by Frac. Explicitly convert them to the same type first!".to_string())), (Int(_), Star, Frac(_)) => EvalError(region, TypeMismatch("tried to multiply Int by Frac. Explicitly convert them to the same type first!".to_string())),
(Frac(_), Star, Int(_)) => EvalError(TypeMismatch("tried to multiply Frac by Int. Explicitly convert them to the same type first!".to_string())), (Frac(_), Star, Int(_)) => EvalError(region, TypeMismatch("tried to multiply Frac by Int. Explicitly convert them to the same type first!".to_string())),
(_, Star, _) => EvalError(TypeMismatch("tried to multiply non-numbers".to_string())), (_, Star, _) => EvalError(region, TypeMismatch("tried to multiply non-numbers".to_string())),
// Minus // Minus
(Int(left_num), Minus, Int(right_num)) => Int(left_num.checked_sub(*right_num).unwrap_or_else(|| panic!("Integer underflow on -"))), (Int(left_num), Minus, Int(right_num)) => Int(left_num.checked_sub(*right_num).unwrap_or_else(|| panic!("Integer underflow on -"))),
(Frac(left_num), Minus, Frac(right_num)) => Frac(left_num - right_num), (Frac(left_num), Minus, Frac(right_num)) => Frac(left_num - right_num),
(Int(_), Minus, Frac(_)) => EvalError(TypeMismatch("tried to subtract Frac from Int. Explicitly convert them to the same type first!".to_string())), (Int(_), Minus, Frac(_)) => EvalError(region, TypeMismatch("tried to subtract Frac from Int. Explicitly convert them to the same type first!".to_string())),
(Frac(_), Minus, Int(_)) => EvalError(TypeMismatch("tried to subtract Int from Frac. Explicitly convert them to the same type first!".to_string())), (Frac(_), Minus, Int(_)) => EvalError(region, TypeMismatch("tried to subtract Int from Frac. Explicitly convert them to the same type first!".to_string())),
(_, Minus, _) => EvalError(TypeMismatch("tried to subtract non-numbers".to_string())), (_, Minus, _) => EvalError(region, TypeMismatch("tried to subtract non-numbers".to_string())),
// Caret // Caret
(Int(left_num), Caret, Int(right_num)) => Int(left_num.checked_pow(*right_num as u32 /* TODO panic if this cast fails */).unwrap_or_else(|| panic!("Integer underflow on ^"))), (Int(left_num), Caret, Int(right_num)) => Int(left_num.checked_pow(*right_num as u32 /* TODO panic if this cast fails */).unwrap_or_else(|| panic!("Integer underflow on ^"))),
(Frac(_), Caret, Frac(_)) => EvalError(TypeMismatch("tried to use ^ with a Frac, which is not yet supported on either side of the ^ operator.".to_string())), (Frac(_), Caret, Frac(_)) => EvalError(region, TypeMismatch("tried to use ^ with a Frac, which is not yet supported on either side of the ^ operator.".to_string())),
(_, Caret, _) => EvalError(TypeMismatch("tried to use ^ on non-numbers".to_string())), (_, Caret, _) => EvalError(region, TypeMismatch("tried to use ^ on non-numbers".to_string())),
// Slash // Slash
(Int(left_num), Slash, Int(right_num)) => Int(left_num.checked_div(*right_num).unwrap_or_else(|| panic!("Integer underflow on /"))), (Int(left_num), Slash, Int(right_num)) => Int(left_num.checked_div(*right_num).unwrap_or_else(|| panic!("Integer underflow on /"))),
@ -391,21 +392,21 @@ fn eval_operator(left_expr: &Evaluated, op: Operator, right_expr: &Evaluated) ->
} }
}, },
(Int(_), Slash, Frac(_)) => EvalError(TypeMismatch("tried to divide Int by Frac. Explicitly convert them to the same type first!".to_string())), (Int(_), Slash, Frac(_)) => EvalError(region, TypeMismatch("tried to divide Int by Frac. Explicitly convert them to the same type first!".to_string())),
(Frac(_), Slash, Int(_)) => EvalError(TypeMismatch("tried to divide Frac by Int. Explicitly convert them to the same type first!".to_string())), (Frac(_), Slash, Int(_)) => EvalError(region, TypeMismatch("tried to divide Frac by Int. Explicitly convert them to the same type first!".to_string())),
(_, Slash, _) => EvalError(TypeMismatch("tried to divide non-numbers".to_string())), (_, Slash, _) => EvalError(region, TypeMismatch("tried to divide non-numbers".to_string())),
// DoubleSlash // DoubleSlash
(Int(left_num), DoubleSlash, Int(right_num)) => Int(left_num / right_num), (Int(left_num), DoubleSlash, Int(right_num)) => Int(left_num / right_num),
(Frac(_), DoubleSlash, Frac(_)) => EvalError(TypeMismatch("tried to do integer division on two Frac values".to_string())), (Frac(_), DoubleSlash, Frac(_)) => EvalError(region, TypeMismatch("tried to do integer division on two Frac values".to_string())),
(Int(_), DoubleSlash, Frac(_)) => EvalError(TypeMismatch("tried to integer-divide Int by Frac".to_string())), (Int(_), DoubleSlash, Frac(_)) => EvalError(region,TypeMismatch("tried to integer-divide Int by Frac".to_string())),
(Frac(_), DoubleSlash, Int(_)) => EvalError(TypeMismatch("tried to integer-divide Frac by Int".to_string())), (Frac(_), DoubleSlash, Int(_)) => EvalError(region, TypeMismatch("tried to integer-divide Frac by Int".to_string())),
(_, DoubleSlash, _) => EvalError(TypeMismatch("tried to do integer division on two non-numbers".to_string())), (_, DoubleSlash, _) => EvalError(region, TypeMismatch("tried to do integer division on two non-numbers".to_string())),
// Percent // Percent
(Int(left_num), Percent, Int(right_num)) => Int(left_num % right_num), (Int(left_num), Percent, Int(right_num)) => Int(left_num % right_num),
@ -419,11 +420,11 @@ fn eval_operator(left_expr: &Evaluated, op: Operator, right_expr: &Evaluated) ->
} }
}, },
(Int(_), Percent, Frac(_)) => EvalError(TypeMismatch("tried to do Int % Frac. Explicitly convert them to the same type first!".to_string())), (Int(_), Percent, Frac(_)) => EvalError(region, TypeMismatch("tried to do Int % Frac. Explicitly convert them to the same type first!".to_string())),
(Frac(_), Percent, Int(_)) => EvalError(TypeMismatch("tried to do Frac % Int. Explicitly convert them to the same type first!".to_string())), (Frac(_), Percent, Int(_)) => EvalError(region, TypeMismatch("tried to do Frac % Int. Explicitly convert them to the same type first!".to_string())),
(_, Percent, _) => EvalError(TypeMismatch("tried to use % on non-numbers".to_string())), (_, Percent, _) => EvalError(region, TypeMismatch("tried to use % on non-numbers".to_string())),
// Pizza // Pizza
(_, Pizza, _) => { panic!("There was a |> operator that hadn't been removed prior to eval time. This should never happen!"); } (_, Pizza, _) => { panic!("There was a |> operator that hadn't been removed prior to eval time. This should never happen!"); }
@ -431,7 +432,7 @@ fn eval_operator(left_expr: &Evaluated, op: Operator, right_expr: &Evaluated) ->
} }
#[inline(always)] #[inline(always)]
fn eval_case(evaluated: Evaluated, branches: Vec<(Located<Pattern>, Box<Located<Expr>>)>, vars: &Scope) -> Evaluated { fn eval_case(region: Region, evaluated: Evaluated, branches: Vec<(Located<Pattern>, Box<Located<Expr>>)>, vars: &Scope) -> Evaluated {
use self::Evaluated::*; use self::Evaluated::*;
for (pattern, definition) in branches { for (pattern, definition) in branches {
@ -442,7 +443,7 @@ fn eval_case(evaluated: Evaluated, branches: Vec<(Located<Pattern>, Box<Located<
} }
} }
EvalError(NoBranchesMatched) EvalError(region, NoBranchesMatched)
} }
fn pattern_match(evaluated: &Evaluated, pattern: &Pattern, vars: &mut Scope) -> Result<(), Problem> { fn pattern_match(evaluated: &Evaluated, pattern: &Pattern, vars: &mut Scope) -> Result<(), Problem> {
@ -600,7 +601,7 @@ impl fmt::Display for Evaluated {
}, },
// ERRORS // ERRORS
EvalError(problem) => write!(f, "ERROR: {}", format!("{}", problem)), EvalError(region, problem) => write!(f, "ERROR: {} at {}", format!("{}", problem), format!("line {}, column {}", region.start_line, region.start_col)),
// UNFORMATTED // UNFORMATTED
_ => write!(f, "<partially evaluated expression>") _ => write!(f, "<partially evaluated expression>")
@ -611,10 +612,10 @@ impl fmt::Display for Evaluated {
impl fmt::Display for Problem { impl fmt::Display for Problem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
Problem::UnrecognizedVarName(region, name) => write!(f, "Unrecognized var name `{}` at {:?}", name, region), Problem::UnrecognizedVarName(name) => write!(f, "Unrecognized var name `{}`", name),
Problem::NoBranchesMatched => write!(f, "No branches matched in this case-expression"), Problem::NoBranchesMatched => write!(f, "No branches matched in this case-expression"),
Problem::TypeMismatch(info) => write!(f, "Type Mismatch - {}", info), Problem::TypeMismatch(info) => write!(f, "Type Mismatch - {}", info),
Problem::ReassignedVarName(region, name) => write!(f, "Reassigned constant - {} at {:?}", name, region), Problem::ReassignedVarName(name) => write!(f, "Reassigned constant - {}", name),
Problem::NotEqual => write!(f, "Pattern match on literal value failed; the branch wasn't equal."), Problem::NotEqual => write!(f, "Pattern match on literal value failed; the branch wasn't equal."),
Problem::WrongArity(expected_arity, provided_arity) => { Problem::WrongArity(expected_arity, provided_arity) => {
if provided_arity > expected_arity { if provided_arity > expected_arity {

View file

@ -204,7 +204,7 @@ parser! {
// 4. Parse variables but not functions. // 4. Parse variables but not functions.
choice(( choice((
closure(min_indent), closure(min_indent),
parenthetical_expr(min_indent), apply_with_parens(min_indent),
string("{}").with(value(Expr::EmptyRecord)), string("{}").with(value(Expr::EmptyRecord)),
string_literal(), string_literal(),
number_literal(), number_literal(),
@ -233,6 +233,7 @@ parser! {
located(choice(( located(choice((
function_arg_expr(min_indent), function_arg_expr(min_indent),
apply_with_parens(min_indent),
assignment(min_indent), assignment(min_indent),
apply_variant(min_indent), apply_variant(min_indent),
func_or_var(min_indent), func_or_var(min_indent),
@ -307,7 +308,7 @@ where I: Stream<Item = char, Position = IndentablePosition>,
) )
} }
pub fn parenthetical_expr<I>(min_indent: u32) -> impl Parser<Input = I, Output = Expr> pub fn apply_with_parens<I>(min_indent: u32) -> impl Parser<Input = I, Output = Expr>
where I: Stream<Item = char, Position = IndentablePosition>, where I: Stream<Item = char, Position = IndentablePosition>,
I::Error: ParseError<I::Item, I::Range, I::Position> I::Error: ParseError<I::Item, I::Range, I::Position>
{ {
@ -324,6 +325,7 @@ where I: Stream<Item = char, Position = IndentablePosition>,
) )
).map(|(located_expr, opt_args): (Located<Expr>, Option<Vec<Located<Expr>>>)| ).map(|(located_expr, opt_args): (Located<Expr>, Option<Vec<Located<Expr>>>)|
match opt_args { match opt_args {
// If there was nothing after the parens, that's okay; this is still parens, but not application.
None => located_expr.value, None => located_expr.value,
Some(args) => Expr::Apply(Box::new(located_expr), args) Some(args) => Expr::Apply(Box::new(located_expr), args)
} }
@ -337,13 +339,22 @@ fn function_arg<I>(min_indent: u32) -> impl Parser<Input = I, Output = Located<E
{ {
located( located(
choice(( choice((
// Don't use apply_with_parens here, because it will think anything following
// this parenthetical expr is an argument *to be passed to the parenthetical expr*.
between(char('('), char(')'),
indented_whitespaces(min_indent)
.with(expr_body(min_indent))
.skip(indented_whitespaces(min_indent))),
// Don't parse operators, because they have a higher // Don't parse operators, because they have a higher
// precedence than function application. If we see one, // precedence than function application. If we see one,
// we're done! // we're done!
function_arg_expr(min_indent), function_arg_expr(min_indent),
// Variants can't be applied in function args without parens; // Variants can't be applied in function args without parens;
// (foo Bar baz) will pass 2 arguments to foo, rather than parsing like (foo (Bar baz)) // (foo Bar baz) will pass 2 arguments to foo, rather than parsing like (foo (Bar baz))
attempt(variant_name()).map(|name| Expr::ApplyVariant(name, None)), attempt(variant_name()).map(|name| Expr::ApplyVariant(name, None)),
// Functions can't be called by name in function args without parens; // Functions can't be called by name in function args without parens;
// (foo bar baz) will pass 2 arguments to foo, rather than parsing like (foo (bar baz)) // (foo bar baz) will pass 2 arguments to foo, rather than parsing like (foo (bar baz))
attempt(ident()).map(|name| Expr::Var(name)), attempt(ident()).map(|name| Expr::Var(name)),
@ -363,15 +374,6 @@ where I: Stream<Item = char, Position = IndentablePosition>,
not_followed_by(choice((string("then"), string("else"), string("when")))) not_followed_by(choice((string("then"), string("else"), string("when"))))
) )
.with( .with(
// function_arg(min_indent).map(|val| vec![
// Located::new(val.value, Region {
// start_line: 0,
// start_col: 0,
// end_line: 0,
// end_col: 0,
// }) ])
// Arguments are whitespace-separated. // Arguments are whitespace-separated.
sep_by1( sep_by1(
function_arg(min_indent), function_arg(min_indent),

View file

@ -471,6 +471,13 @@ mod test_parse {
expect_parsed_var_error("2Foo2Furious"); expect_parsed_var_error("2Foo2Furious");
} }
#[test]
fn var_with_parens() {
assert_eq!(parse_without_loc("( x)"), Ok(( Var("x".to_string()), "" )));
assert_eq!(parse_without_loc("(x )"), Ok(( Var("x".to_string()), "" )));
assert_eq!(parse_without_loc("( x )"), Ok(( Var("x".to_string()), "" )));
}
// APPLY // APPLY
fn expect_parsed_apply<'a>(parse_str: &'a str, expr1: Expr, expr2: Expr) { fn expect_parsed_apply<'a>(parse_str: &'a str, expr1: Expr, expr2: Expr) {
@ -554,7 +561,6 @@ mod test_parse {
); );
} }
// FUNC // FUNC
fn expect_parsed_func<'a>(parse_str: &'a str, func_str: &'a str, args: Vec<Located<Expr>>) { fn expect_parsed_func<'a>(parse_str: &'a str, func_str: &'a str, args: Vec<Located<Expr>>) {
@ -593,6 +599,19 @@ mod test_parse {
expect_parsed_func("foo \"hi\" 1 blah", "foo", vec![loc(Str("hi".to_string())), loc(Int(1)), loc(Var("blah".to_string()))]); expect_parsed_func("foo \"hi\" 1 blah", "foo", vec![loc(Str("hi".to_string())), loc(Int(1)), loc(Var("blah".to_string()))]);
} }
#[test]
fn multi_arg_func_with_parens() {
expect_parsed_func("f (1) 23 456", "f", vec![loc(Int(1)), loc(Int(23)), loc(Int(456))]);
expect_parsed_func("foo bar ('z')", "foo", vec![loc(Var("bar".to_string())), loc(Char('z'))]);
expect_parsed_func("foo 1 (bar \"hi\") 2 (blah)", "foo", vec![
loc(Int(1)),
loc(CallByName("bar".to_string(), vec![loc(Str("hi".to_string()))])),
loc(Int(2)),
loc(Var("blah".to_string()))
]);
}
#[test] #[test]
fn multiline_func() { fn multiline_func() {
expect_parsed_func("f\n 1", "f", vec![loc(Int(1))]); expect_parsed_func("f\n 1", "f", vec![loc(Int(1))]);