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

View file

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

View file

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

View file

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