mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 15:51:12 +00:00
Functions can take mulitple args.
This commit is contained in:
parent
298716a925
commit
1819b9955f
4 changed files with 86 additions and 52 deletions
41
src/eval.rs
41
src/eval.rs
|
@ -27,6 +27,11 @@ pub fn scoped_eval(expr: Expr, vars: &HashMap<String, Rc<Expr>>) -> Expr {
|
|||
} else {
|
||||
// Create a new scope containing the new declaration.
|
||||
let mut new_vars = (*vars).clone();
|
||||
// TODO traverse definition searching for Closure exprs. If we
|
||||
// find any, traverse their bodies and resolve as many Var
|
||||
// and Func exprs as we can using current vars map. (If we
|
||||
// don't find the value in the vars map, np - keep going.)
|
||||
// In this way, we have inlined "closing over" those vars!
|
||||
new_vars.insert(name, Rc::new(scoped_eval(*definition, vars)));
|
||||
|
||||
// Evaluate in_expr with that new scope's variables.
|
||||
|
@ -38,25 +43,43 @@ pub fn scoped_eval(expr: Expr, vars: &HashMap<String, Rc<Expr>>) -> Expr {
|
|||
// Faithfully eval this, but discard its result.
|
||||
scoped_eval(*definition, vars);
|
||||
|
||||
// Actually use this.
|
||||
// Actually use this part.
|
||||
scoped_eval(*in_expr, vars)
|
||||
},
|
||||
|
||||
Func(name, arg) => {
|
||||
Func(name, args) => {
|
||||
let func_expr = match vars.get(&name) {
|
||||
Some(resolved) => (*Rc::clone(resolved)).clone(),
|
||||
None => Error(UnrecognizedVarName(name))
|
||||
};
|
||||
|
||||
scoped_eval(Apply(Box::new((func_expr, *arg))), vars)
|
||||
scoped_eval(Apply(Box::new(func_expr), args), vars)
|
||||
},
|
||||
|
||||
Apply(boxed_tuple) => {
|
||||
match *boxed_tuple {
|
||||
// (Closure(args, definition), arg) => {
|
||||
// panic!("TODO apply arg to closure");
|
||||
// },
|
||||
_ => { panic!("Type mismatch: trying to call a non-function!"); }
|
||||
Apply(func_expr, args) => {
|
||||
match *func_expr.clone() {
|
||||
Closure(arg_patterns, body) => {
|
||||
if arg_patterns.len() == args.len() {
|
||||
// Create a new scope for the function to use.
|
||||
let mut new_vars = (*vars).clone();
|
||||
|
||||
for index in 0..arg_patterns.len() {
|
||||
match arg_patterns.get(index).unwrap() {
|
||||
Underscore => (),
|
||||
Identifier(name) => {
|
||||
let new_val = scoped_eval((*args.get(index).unwrap()).clone(), vars);
|
||||
|
||||
new_vars.insert(name.clone(), Rc::new(new_val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scoped_eval(*body, &new_vars)
|
||||
} else {
|
||||
Error(WrongArity(arg_patterns.len() as u32, args.len() as u32))
|
||||
}
|
||||
},
|
||||
_ => Error(TypeMismatch("Tried to call a non-function.".to_string()))
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -12,8 +12,8 @@ pub enum Expr {
|
|||
Let(Pattern, Box<Expr>, Box<Expr>),
|
||||
|
||||
// Functions
|
||||
Func(String, Box<Expr>),
|
||||
Apply(Box<(Expr, Expr)>),
|
||||
Func(String, Vec<Expr>),
|
||||
Apply(Box<Expr>, Vec<Expr>),
|
||||
Operator(Box<Expr>, Operator, Box<Expr>),
|
||||
Closure(Vec<Pattern>, Box<Expr>),
|
||||
|
||||
|
@ -26,6 +26,7 @@ pub enum Problem {
|
|||
UnrecognizedVarName(String),
|
||||
TypeMismatch(String),
|
||||
ReassignedVarName(String),
|
||||
WrongArity(u32 /* Expected */, u32 /* Provided */),
|
||||
}
|
||||
|
||||
|
||||
|
|
56
src/parse.rs
56
src/parse.rs
|
@ -5,7 +5,7 @@ use std::char;
|
|||
use parse_state::{IndentablePosition};
|
||||
|
||||
use combine::parser::char::{char, string, spaces, digit, hex_digit, HexDigit, alpha_num};
|
||||
use combine::parser::repeat::{many, count_min_max};
|
||||
use combine::parser::repeat::{many, count_min_max, sep_by1};
|
||||
use combine::parser::item::{any, satisfy_map, value, position};
|
||||
use combine::parser::combinator::{look_ahead, not_followed_by};
|
||||
use combine::{attempt, choice, eof, many1, parser, Parser, optional, between, unexpected_any, unexpected};
|
||||
|
@ -179,25 +179,39 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
|||
indented_whitespaces(min_indent).with(expr_body(min_indent)).skip(indented_whitespaces(min_indent))
|
||||
).and(
|
||||
// Parenthetical expressions can optionally be followed by
|
||||
// whitespace and an expr, meaning this is function application!
|
||||
optional(
|
||||
attempt(
|
||||
// whitespace and one or more comma-separated expressions,
|
||||
// meaning this is function application!
|
||||
optional(attempt(function_application(min_indent)))
|
||||
).map(|(expr, opt_args): (Expr, Option<Vec<Expr>>)|
|
||||
match opt_args {
|
||||
None => expr,
|
||||
Some(args) => Expr::Apply(Box::new(expr), args)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn function_application<I>(min_indent: i32) -> impl Parser<Input = I, Output = Vec<Expr>>
|
||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||
{
|
||||
indented_whitespaces1(min_indent)
|
||||
// TODO figure out how to refactor out comma_separated()
|
||||
// which takes a Parser<T> and returns a Parser<Optional<T>>
|
||||
.with(
|
||||
sep_by1(
|
||||
attempt(
|
||||
// Keywords like "then" and "else" are not function application!
|
||||
.skip(not_followed_by(choice((string("then"), string("else")))))
|
||||
not_followed_by(choice((string("then"), string("else"))))
|
||||
.with(expr_body(min_indent))
|
||||
.skip(indented_whitespaces(min_indent))
|
||||
),
|
||||
char(',')
|
||||
.skip(indented_whitespaces(min_indent))
|
||||
)
|
||||
)
|
||||
).map(|(expr1, opt_expr2)|
|
||||
match opt_expr2 {
|
||||
None => expr1,
|
||||
Some(expr2) => {
|
||||
Expr::Apply(Box::new((expr1, expr2)))
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
pub fn operator<I>() -> impl Parser<Input = I, Output = Operator>
|
||||
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||
|
@ -250,17 +264,13 @@ pub fn func_or_var<I>(min_indent: i32) -> impl Parser<Input = I, Output = Expr>
|
|||
where I: Stream<Item = char, Position = IndentablePosition>,
|
||||
I::Error: ParseError<I::Item, I::Range, I::Position>
|
||||
{
|
||||
ident()
|
||||
.and(optional(
|
||||
attempt(
|
||||
indented_whitespaces1(min_indent)
|
||||
.skip(not_followed_by(choice((string("then"), string("else"), string("=")))))
|
||||
.with(expr_body(min_indent))
|
||||
)
|
||||
)).map(|(name, opt_arg)|
|
||||
match opt_arg {
|
||||
Some(arg) => Expr::Func(name, Box::new(arg)),
|
||||
ident().and(optional(attempt(function_application(min_indent))))
|
||||
.map(|(name, opt_args): (String, Option<Vec<Expr>>)|
|
||||
// Use optional(sep_by1()) over sep_by() to avoid
|
||||
// allocating a Vec in the common case where this is a var
|
||||
match opt_args {
|
||||
None => Expr::Var(name),
|
||||
Some(args) => Expr::Func(name, args)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ mod tests {
|
|||
|
||||
fn expect_parsed_str<'a>(expected_str: &'a str, actual_str: &'a str) {
|
||||
assert_eq!(
|
||||
Ok((String(expected_str.to_string()), "")),
|
||||
Ok((Str(expected_str.to_string()), "")),
|
||||
parse_standalone(actual_str)
|
||||
);
|
||||
}
|
||||
|
@ -363,7 +363,7 @@ mod tests {
|
|||
|
||||
fn expect_parsed_apply<'a>(parse_str: &'a str, expr1: Expr, expr2: Expr) {
|
||||
assert_eq!(
|
||||
Ok((Apply(Box::new((expr1, expr2))), "")),
|
||||
Ok((Apply(Box::new(expr1), vec![expr2]), "")),
|
||||
parse_standalone(parse_str)
|
||||
);
|
||||
}
|
||||
|
@ -385,14 +385,14 @@ mod tests {
|
|||
|
||||
expect_parsed_apply(
|
||||
"(x 5) y",
|
||||
Func("x".to_string(), Box::new(Int(5))),
|
||||
Func("x".to_string(), vec![Int(5)]),
|
||||
Var("y".to_string())
|
||||
);
|
||||
|
||||
expect_parsed_apply(
|
||||
"(x 5) (y 6)",
|
||||
Func("x".to_string(), Box::new(Int(5))),
|
||||
Func("y".to_string(), Box::new(Int(6))),
|
||||
Func("x".to_string(), vec![Int(5)]),
|
||||
Func("y".to_string(), vec![Int(6)]),
|
||||
);
|
||||
|
||||
expect_parsed_apply(
|
||||
|
@ -418,7 +418,7 @@ mod tests {
|
|||
|
||||
fn expect_parsed_func<'a>(parse_str: &'a str, func_str: &'a str, expr: Expr) {
|
||||
assert_eq!(
|
||||
Ok((Func(func_str.to_string(), Box::new(expr)), "")),
|
||||
Ok((Func(func_str.to_string(), vec![expr]), "")),
|
||||
parse_standalone(parse_str)
|
||||
);
|
||||
}
|
||||
|
@ -442,7 +442,7 @@ mod tests {
|
|||
fn parse_func() {
|
||||
expect_parsed_func("f 1", "f", Int(1));
|
||||
expect_parsed_func("foo bar", "foo", Var("bar".to_string()));
|
||||
expect_parsed_func("foo \"hi\"", "foo", String("hi".to_string()));
|
||||
expect_parsed_func("foo \"hi\"", "foo", Str("hi".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -461,7 +461,7 @@ mod tests {
|
|||
expect_parsed_str("abc", "((\"abc\"))");
|
||||
expect_parsed_func("(f 1)", "f", Int(1));
|
||||
expect_parsed_func("(foo bar)", "foo", Var("bar".to_string()));
|
||||
expect_parsed_func("( foo \"hi\" )", "foo", String("hi".to_string()));
|
||||
expect_parsed_func("( foo \"hi\" )", "foo", Str("hi".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -510,18 +510,18 @@ mod tests {
|
|||
fn parse_complex_expressions() {
|
||||
expect_parsed_apply(
|
||||
"(x 5) (y + (f 6))",
|
||||
Func("x".to_string(), Box::new(Int(5))),
|
||||
Func("x".to_string(), vec![Int(5)]),
|
||||
Operator(
|
||||
Box::new(Var("y".to_string())),
|
||||
Plus,
|
||||
Box::new(Func("f".to_string(), Box::new(Int(6)))),
|
||||
Box::new(Func("f".to_string(), vec![Int(6)])),
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
parse_standalone("(x 5)"),
|
||||
Ok((
|
||||
Func("x".to_string(), Box::new(Int(5))),
|
||||
Func("x".to_string(), vec![Int(5)]),
|
||||
"")
|
||||
)
|
||||
);
|
||||
|
@ -582,7 +582,7 @@ mod tests {
|
|||
parse_standalone("(x 5) + 123"),
|
||||
Ok((
|
||||
Operator(
|
||||
Box::new(Func("x".to_string(), Box::new(Int(5)))),
|
||||
Box::new(Func("x".to_string(), vec![Int(5)])),
|
||||
Plus,
|
||||
Box::new(Int(123))
|
||||
),
|
||||
|
@ -594,7 +594,7 @@ mod tests {
|
|||
parse_standalone("(x 5) + (2 * y)"),
|
||||
Ok((
|
||||
Operator(
|
||||
Box::new(Func("x".to_string(), Box::new(Int(5)))),
|
||||
Box::new(Func("x".to_string(), vec![Int(5)])),
|
||||
Plus,
|
||||
Box::new(
|
||||
Operator(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue