mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 07:41: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 {
|
} 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();
|
||||||
|
// 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)));
|
new_vars.insert(name, Rc::new(scoped_eval(*definition, vars)));
|
||||||
|
|
||||||
// Evaluate in_expr with that new scope's variables.
|
// 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.
|
// Faithfully eval this, but discard its result.
|
||||||
scoped_eval(*definition, vars);
|
scoped_eval(*definition, vars);
|
||||||
|
|
||||||
// Actually use this.
|
// Actually use this part.
|
||||||
scoped_eval(*in_expr, vars)
|
scoped_eval(*in_expr, vars)
|
||||||
},
|
},
|
||||||
|
|
||||||
Func(name, arg) => {
|
Func(name, args) => {
|
||||||
let func_expr = match vars.get(&name) {
|
let func_expr = match vars.get(&name) {
|
||||||
Some(resolved) => (*Rc::clone(resolved)).clone(),
|
Some(resolved) => (*Rc::clone(resolved)).clone(),
|
||||||
None => Error(UnrecognizedVarName(name))
|
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) => {
|
Apply(func_expr, args) => {
|
||||||
match *boxed_tuple {
|
match *func_expr.clone() {
|
||||||
// (Closure(args, definition), arg) => {
|
Closure(arg_patterns, body) => {
|
||||||
// panic!("TODO apply arg to closure");
|
if arg_patterns.len() == args.len() {
|
||||||
// },
|
// Create a new scope for the function to use.
|
||||||
_ => { panic!("Type mismatch: trying to call a non-function!"); }
|
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>),
|
Let(Pattern, Box<Expr>, Box<Expr>),
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
Func(String, Box<Expr>),
|
Func(String, Vec<Expr>),
|
||||||
Apply(Box<(Expr, Expr)>),
|
Apply(Box<Expr>, Vec<Expr>),
|
||||||
Operator(Box<Expr>, Operator, Box<Expr>),
|
Operator(Box<Expr>, Operator, Box<Expr>),
|
||||||
Closure(Vec<Pattern>, Box<Expr>),
|
Closure(Vec<Pattern>, Box<Expr>),
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ pub enum Problem {
|
||||||
UnrecognizedVarName(String),
|
UnrecognizedVarName(String),
|
||||||
TypeMismatch(String),
|
TypeMismatch(String),
|
||||||
ReassignedVarName(String),
|
ReassignedVarName(String),
|
||||||
|
WrongArity(u32 /* Expected */, u32 /* Provided */),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
64
src/parse.rs
64
src/parse.rs
|
@ -5,7 +5,7 @@ use std::char;
|
||||||
use parse_state::{IndentablePosition};
|
use parse_state::{IndentablePosition};
|
||||||
|
|
||||||
use combine::parser::char::{char, string, spaces, digit, hex_digit, HexDigit, alpha_num};
|
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::item::{any, satisfy_map, value, position};
|
||||||
use combine::parser::combinator::{look_ahead, not_followed_by};
|
use combine::parser::combinator::{look_ahead, not_followed_by};
|
||||||
use combine::{attempt, choice, eof, many1, parser, Parser, optional, between, unexpected_any, unexpected};
|
use combine::{attempt, choice, eof, many1, parser, Parser, optional, between, unexpected_any, unexpected};
|
||||||
|
@ -177,27 +177,41 @@ where I: Stream<Item = char, Position = IndentablePosition>,
|
||||||
{
|
{
|
||||||
between(char('('), char(')'),
|
between(char('('), char(')'),
|
||||||
indented_whitespaces(min_indent).with(expr_body(min_indent)).skip(indented_whitespaces(min_indent))
|
indented_whitespaces(min_indent).with(expr_body(min_indent)).skip(indented_whitespaces(min_indent))
|
||||||
).and(
|
).and(
|
||||||
// Parenthetical expressions can optionally be followed by
|
// Parenthetical expressions can optionally be followed by
|
||||||
// whitespace and an expr, meaning this is function application!
|
// whitespace and one or more comma-separated expressions,
|
||||||
optional(
|
// meaning this is function application!
|
||||||
attempt(
|
optional(attempt(function_application(min_indent)))
|
||||||
indented_whitespaces1(min_indent)
|
).map(|(expr, opt_args): (Expr, Option<Vec<Expr>>)|
|
||||||
// Keywords like "then" and "else" are not function application!
|
match opt_args {
|
||||||
.skip(not_followed_by(choice((string("then"), string("else")))))
|
None => expr,
|
||||||
.with(expr_body(min_indent))
|
Some(args) => Expr::Apply(Box::new(expr), args)
|
||||||
)
|
|
||||||
)
|
|
||||||
).map(|(expr1, opt_expr2)|
|
|
||||||
match opt_expr2 {
|
|
||||||
None => expr1,
|
|
||||||
Some(expr2) => {
|
|
||||||
Expr::Apply(Box::new((expr1, expr2)))
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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!
|
||||||
|
not_followed_by(choice((string("then"), string("else"))))
|
||||||
|
.with(expr_body(min_indent))
|
||||||
|
.skip(indented_whitespaces(min_indent))
|
||||||
|
),
|
||||||
|
char(',')
|
||||||
|
.skip(indented_whitespaces(min_indent))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn operator<I>() -> impl Parser<Input = I, Output = Operator>
|
pub fn operator<I>() -> impl Parser<Input = I, Output = Operator>
|
||||||
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>
|
||||||
|
@ -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>,
|
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>
|
||||||
{
|
{
|
||||||
ident()
|
ident().and(optional(attempt(function_application(min_indent))))
|
||||||
.and(optional(
|
.map(|(name, opt_args): (String, Option<Vec<Expr>>)|
|
||||||
attempt(
|
// Use optional(sep_by1()) over sep_by() to avoid
|
||||||
indented_whitespaces1(min_indent)
|
// allocating a Vec in the common case where this is a var
|
||||||
.skip(not_followed_by(choice((string("then"), string("else"), string("=")))))
|
match opt_args {
|
||||||
.with(expr_body(min_indent))
|
|
||||||
)
|
|
||||||
)).map(|(name, opt_arg)|
|
|
||||||
match opt_arg {
|
|
||||||
Some(arg) => Expr::Func(name, Box::new(arg)),
|
|
||||||
None => Expr::Var(name),
|
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) {
|
fn expect_parsed_str<'a>(expected_str: &'a str, actual_str: &'a str) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok((String(expected_str.to_string()), "")),
|
Ok((Str(expected_str.to_string()), "")),
|
||||||
parse_standalone(actual_str)
|
parse_standalone(actual_str)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -304,7 +304,7 @@ mod tests {
|
||||||
), ""))
|
), ""))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(parse_standalone("(1 + 2) * 3"),
|
assert_eq!(parse_standalone("(1 + 2) * 3"),
|
||||||
Ok((Operator(
|
Ok((Operator(
|
||||||
Box::new(Operator(Box::new(Int(1)), Plus, Box::new(Int(2)))),
|
Box::new(Operator(Box::new(Int(1)), Plus, Box::new(Int(2)))),
|
||||||
Star,
|
Star,
|
||||||
|
@ -363,7 +363,7 @@ mod tests {
|
||||||
|
|
||||||
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) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok((Apply(Box::new((expr1, expr2))), "")),
|
Ok((Apply(Box::new(expr1), vec![expr2]), "")),
|
||||||
parse_standalone(parse_str)
|
parse_standalone(parse_str)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -385,14 +385,14 @@ mod tests {
|
||||||
|
|
||||||
expect_parsed_apply(
|
expect_parsed_apply(
|
||||||
"(x 5) y",
|
"(x 5) y",
|
||||||
Func("x".to_string(), Box::new(Int(5))),
|
Func("x".to_string(), vec![Int(5)]),
|
||||||
Var("y".to_string())
|
Var("y".to_string())
|
||||||
);
|
);
|
||||||
|
|
||||||
expect_parsed_apply(
|
expect_parsed_apply(
|
||||||
"(x 5) (y 6)",
|
"(x 5) (y 6)",
|
||||||
Func("x".to_string(), Box::new(Int(5))),
|
Func("x".to_string(), vec![Int(5)]),
|
||||||
Func("y".to_string(), Box::new(Int(6))),
|
Func("y".to_string(), vec![Int(6)]),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect_parsed_apply(
|
expect_parsed_apply(
|
||||||
|
@ -418,7 +418,7 @@ mod tests {
|
||||||
|
|
||||||
fn expect_parsed_func<'a>(parse_str: &'a str, func_str: &'a str, expr: Expr) {
|
fn expect_parsed_func<'a>(parse_str: &'a str, func_str: &'a str, expr: Expr) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Ok((Func(func_str.to_string(), Box::new(expr)), "")),
|
Ok((Func(func_str.to_string(), vec![expr]), "")),
|
||||||
parse_standalone(parse_str)
|
parse_standalone(parse_str)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -442,7 +442,7 @@ mod tests {
|
||||||
fn parse_func() {
|
fn parse_func() {
|
||||||
expect_parsed_func("f 1", "f", Int(1));
|
expect_parsed_func("f 1", "f", Int(1));
|
||||||
expect_parsed_func("foo bar", "foo", Var("bar".to_string()));
|
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]
|
#[test]
|
||||||
|
@ -461,7 +461,7 @@ mod tests {
|
||||||
expect_parsed_str("abc", "((\"abc\"))");
|
expect_parsed_str("abc", "((\"abc\"))");
|
||||||
expect_parsed_func("(f 1)", "f", Int(1));
|
expect_parsed_func("(f 1)", "f", Int(1));
|
||||||
expect_parsed_func("(foo bar)", "foo", Var("bar".to_string()));
|
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]
|
#[test]
|
||||||
|
@ -510,18 +510,18 @@ mod tests {
|
||||||
fn parse_complex_expressions() {
|
fn parse_complex_expressions() {
|
||||||
expect_parsed_apply(
|
expect_parsed_apply(
|
||||||
"(x 5) (y + (f 6))",
|
"(x 5) (y + (f 6))",
|
||||||
Func("x".to_string(), Box::new(Int(5))),
|
Func("x".to_string(), vec![Int(5)]),
|
||||||
Operator(
|
Operator(
|
||||||
Box::new(Var("y".to_string())),
|
Box::new(Var("y".to_string())),
|
||||||
Plus,
|
Plus,
|
||||||
Box::new(Func("f".to_string(), Box::new(Int(6)))),
|
Box::new(Func("f".to_string(), vec![Int(6)])),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_standalone("(x 5)"),
|
parse_standalone("(x 5)"),
|
||||||
Ok((
|
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"),
|
parse_standalone("(x 5) + 123"),
|
||||||
Ok((
|
Ok((
|
||||||
Operator(
|
Operator(
|
||||||
Box::new(Func("x".to_string(), Box::new(Int(5)))),
|
Box::new(Func("x".to_string(), vec![Int(5)])),
|
||||||
Plus,
|
Plus,
|
||||||
Box::new(Int(123))
|
Box::new(Int(123))
|
||||||
),
|
),
|
||||||
|
@ -594,7 +594,7 @@ mod tests {
|
||||||
parse_standalone("(x 5) + (2 * y)"),
|
parse_standalone("(x 5) + (2 * y)"),
|
||||||
Ok((
|
Ok((
|
||||||
Operator(
|
Operator(
|
||||||
Box::new(Func("x".to_string(), Box::new(Int(5)))),
|
Box::new(Func("x".to_string(), vec![Int(5)])),
|
||||||
Plus,
|
Plus,
|
||||||
Box::new(
|
Box::new(
|
||||||
Operator(
|
Operator(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue