mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
Fix paren parsing bug, thread errors through eval
This commit is contained in:
parent
07a05b90fc
commit
fde3cedfef
5 changed files with 143 additions and 102 deletions
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
129
src/eval.rs
129
src/eval.rs
|
@ -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 {
|
||||||
|
|
64
src/parse.rs
64
src/parse.rs
|
@ -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,36 +374,27 @@ 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(
|
||||||
|
// Arguments are whitespace-separated.
|
||||||
// function_arg(min_indent).map(|val| vec![
|
sep_by1(
|
||||||
// Located::new(val.value, Region {
|
function_arg(min_indent),
|
||||||
// start_line: 0,
|
// Only consume these spaces if there's another argument after them.
|
||||||
// start_col: 0,
|
// Otherwise we consume too much and mess up indentation checking!
|
||||||
|
attempt(
|
||||||
// end_line: 0,
|
indented_whitespaces1(min_indent)
|
||||||
// end_col: 0,
|
.skip(
|
||||||
// }) ])
|
// Any of these indicates we've hit the end of the argument list.
|
||||||
// Arguments are whitespace-separated.
|
not_followed_by(
|
||||||
sep_by1(
|
choice((
|
||||||
function_arg(min_indent),
|
string(")"),
|
||||||
// Only consume these spaces if there's another argument after them.
|
operator().with(value("")),
|
||||||
// Otherwise we consume too much and mess up indentation checking!
|
string("then"),
|
||||||
attempt(
|
string("else"),
|
||||||
indented_whitespaces1(min_indent)
|
string("when"),
|
||||||
.skip(
|
))
|
||||||
// Any of these indicates we've hit the end of the argument list.
|
)
|
||||||
not_followed_by(
|
|
||||||
choice((
|
|
||||||
string(")"),
|
|
||||||
operator().with(value("")),
|
|
||||||
string("then"),
|
|
||||||
string("else"),
|
|
||||||
string("when"),
|
|
||||||
))
|
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))]);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue