Functions can take mulitple args.

This commit is contained in:
Richard Feldman 2019-05-25 22:39:43 -04:00
parent 298716a925
commit 1819b9955f
4 changed files with 86 additions and 52 deletions

View file

@ -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()))
} }
}, },

View file

@ -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 */),
} }

View file

@ -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)
} }
) )
} }

View file

@ -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(