Use commas between function arguments

This commit is contained in:
Richard Feldman 2019-12-18 21:45:00 -05:00
parent a0bf4d829d
commit d0fa8bf857
10 changed files with 123 additions and 61 deletions

View file

@ -28,5 +28,5 @@ eq : val, val -> Bool
## ##
## This is the same as the #=/= operator. ## This is the same as the #=/= operator.
notEq : val, val -> Bool notEq : val, val -> Bool
notEq = \left right -> notEq = \left, right ->
not (equal left right) not (equal left right)

View file

@ -109,7 +109,7 @@ round = \num ->
## >>> Float.pi ## >>> Float.pi
## >>> |> Float.div 2.0 ## >>> |> Float.div 2.0
#div : Float, Float -> Result Float DivByZero #div : Float, Float -> Result Float DivByZero
div = \numerator denominator -> div = \numerator, denominator ->
case numerator when case numerator when
0.0 -> 0.0 # TODO return Result! 0.0 -> 0.0 # TODO return Result!
_ -> denominator _ -> denominator

View file

@ -102,12 +102,24 @@ pub fn fmt_expr<'a>(
indent indent
}; };
for loc_pattern in loc_patterns.iter() { let mut any_args_printed = false;
fmt_pattern(buf, &loc_pattern.value, indent, true);
if !arguments_are_multiline { for loc_pattern in loc_patterns.iter() {
buf.push(' '); if any_args_printed {
buf.push(',');
if !arguments_are_multiline {
buf.push(' ');
}
} else {
any_args_printed = true;
} }
fmt_pattern(buf, &loc_pattern.value, indent, true);
}
if !arguments_are_multiline {
buf.push(' ');
} }
buf.push_str("->"); buf.push_str("->");

View file

@ -13,14 +13,13 @@ pub mod type_annotation;
use crate::operator::{BinOp, CalledVia, UnaryOp}; use crate::operator::{BinOp, CalledVia, UnaryOp};
use crate::parse::ast::{AssignedField, Attempting, Def, Expr, MaybeQualified, Pattern, Spaceable}; use crate::parse::ast::{AssignedField, Attempting, Def, Expr, MaybeQualified, Pattern, Spaceable};
use crate::parse::blankspace::{ use crate::parse::blankspace::{
space0, space0_after, space0_around, space0_before, space1, space1_after, space1_around, space0, space0_after, space0_around, space0_before, space1, space1_around, space1_before,
space1_before,
}; };
use crate::parse::ident::{global_tag_or_ident, ident, lowercase_ident, Ident}; use crate::parse::ident::{global_tag_or_ident, ident, lowercase_ident, Ident};
use crate::parse::number_literal::number_literal; use crate::parse::number_literal::number_literal;
use crate::parse::parser::{ use crate::parse::parser::{
allocated, char, not, not_followed_by, optional, string, then, unexpected, unexpected_eof, allocated, char, not, not_followed_by, optional, sep_by1, string, then, unexpected,
Either, Fail, FailReason, ParseResult, Parser, State, unexpected_eof, Either, Fail, FailReason, ParseResult, Parser, State,
}; };
use crate::region::{Located, Region}; use crate::region::{Located, Region};
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
@ -409,7 +408,6 @@ fn equals_for_def<'a>() -> impl Parser<'a, ()> {
/// A definition, consisting of one of these: /// A definition, consisting of one of these:
/// ///
/// * A custom type definition using (`:=`)
/// * A type alias using `:` /// * A type alias using `:`
/// * A pattern followed by '=' and then an expression /// * A pattern followed by '=' and then an expression
/// * A type annotation /// * A type annotation
@ -653,13 +651,11 @@ fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
// Parse the params // Parse the params
attempt!( attempt!(
Attempting::ClosureParams, Attempting::ClosureParams,
// Note: because this is parse1_after, you *must* have // Params are comma-separated
// a space before the "->" in a closure declaration. sep_by1(
// char(','),
// We could make this significantly more complicated in space0_around(loc_closure_param(min_indent), min_indent)
// order to support e.g. (\x-> 5) with no space before )
// the "->" but that does not seem worthwhile.
one_or_more!(space1_after(loc_closure_param(min_indent), min_indent))
), ),
skip_first!( skip_first!(
// Parse the -> which separates params from body // Parse the -> which separates params from body

View file

@ -462,6 +462,60 @@ where
} }
} }
/// Parse one or more values separated by a delimiter (e.g. a comma) whose
/// values are discarded
pub fn sep_by1<'a, P, D, Val>(delimiter: D, parser: P) -> impl Parser<'a, Vec<'a, Val>>
where
D: Parser<'a, ()>,
P: Parser<'a, Val>,
{
move |arena, state: State<'a>| {
let original_attempting = state.attempting;
match parser.parse(arena, state) {
Ok((first_output, next_state)) => {
let mut state = next_state;
let mut buf = Vec::with_capacity_in(1, arena);
buf.push(first_output);
loop {
match delimiter.parse(arena, state) {
Ok(((), next_state)) => {
// If the delimiter passed, check the element parser.
match parser.parse(arena, next_state) {
Ok((next_output, next_state)) => {
state = next_state;
buf.push(next_output);
}
Err((fail, state)) => {
// If the delimiter parsed, but the following
// element did not, that's a fatal error.
return Err((
Fail {
attempting: original_attempting,
..fail
},
state,
));
}
}
}
Err((_, old_state)) => return Ok((buf, old_state)),
}
}
}
Err((fail, new_state)) => Err((
Fail {
attempting: original_attempting,
..fail
},
new_state,
)),
}
}
}
pub fn satisfies<'a, P, A, F>(parser: P, predicate: F) -> impl Parser<'a, A> pub fn satisfies<'a, P, A, F>(parser: P, predicate: F) -> impl Parser<'a, A>
where where
P: Parser<'a, A>, P: Parser<'a, A>,

View file

@ -205,7 +205,7 @@ mod test_canonicalize {
// This function will get passed in as a pointer. // This function will get passed in as a pointer.
let src = indoc!( let src = indoc!(
r#" r#"
apply = \f x -> f x apply = \f, x -> f x
identity = \a -> a identity = \a -> a

View file

@ -134,7 +134,7 @@ mod test_format {
fn func_def() { fn func_def() {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
f = \x y -> f = \x, y ->
x x
f 4 f 4
@ -147,7 +147,7 @@ mod test_format {
expr_formats_to( expr_formats_to(
indoc!( indoc!(
r#" r#"
f = \x y -> f = \x, y ->
y = 4 y = 4
z = 8 z = 8
x x
@ -156,7 +156,7 @@ mod test_format {
), ),
indoc!( indoc!(
r#" r#"
f = \x y -> f = \x, y ->
y = 4 y = 4
z = 8 z = 8
@ -169,7 +169,7 @@ mod test_format {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
f = \x y -> f = \x, y ->
a = 3 a = 3
b = 6 b = 6
@ -289,7 +289,7 @@ mod test_format {
fn multi_arg_closure() { fn multi_arg_closure() {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
\a b c -> a b c \a, b, c -> a b c
"# "#
)); ));
} }
@ -467,7 +467,7 @@ mod test_format {
// fn record_field_destructuring() { // fn record_field_destructuring() {
// expr_formats_same(indoc!( // expr_formats_same(indoc!(
// r#" // r#"
// case foo of // case foo when
// { x: 5 } -> 42 // { x: 5 } -> 42
// "# // "#
// )); // ));
@ -519,7 +519,7 @@ mod test_format {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
identity = \a identity = \a,
b b
-> a -> a
@ -529,8 +529,8 @@ mod test_format {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
identity = \a identity = \a,
b b,
# it's c!! # it's c!!
c c
-> a -> a

View file

@ -305,7 +305,7 @@ mod test_infer {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
\_ _ -> 42 \_, _ -> 42
"# "#
), ),
"*, * -> Int", "*, * -> Int",
@ -317,7 +317,7 @@ mod test_infer {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
\_ _ _ -> "test!" \_, _, _ -> "test!"
"# "#
), ),
"*, *, * -> Str", "*, *, * -> Str",
@ -373,7 +373,7 @@ mod test_infer {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
func = \_ _ -> 42 func = \_, _ -> 42
func func
"# "#
@ -387,7 +387,7 @@ mod test_infer {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
f = \_ _ _ -> "test!" f = \_, _, _ -> "test!"
f f
"# "#
@ -401,7 +401,7 @@ mod test_infer {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
a = \_ _ _ -> "test!" a = \_, _, _ -> "test!"
b = a b = a
@ -545,7 +545,7 @@ mod test_infer {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
always = \a b -> a always = \a, b -> a
1 |> always "foo" 1 |> always "foo"
"# "#
@ -610,7 +610,7 @@ mod test_infer {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
apply = \f x -> f x apply = \f, x -> f x
identity = \a -> a identity = \a -> a
apply identity 5 apply identity 5
@ -625,7 +625,7 @@ mod test_infer {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
\f x -> f x \f, x -> f x
"# "#
), ),
"(a -> b), a -> b", "(a -> b), a -> b",
@ -654,7 +654,7 @@ mod test_infer {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
\f -> (\a b -> f b a), \f -> (\a, b -> f b a),
"# "#
), ),
"(a, b -> c) -> (b, a -> c)", "(a, b -> c) -> (b, a -> c)",

View file

@ -989,13 +989,13 @@ mod test_parse {
fn two_arg_closure() { fn two_arg_closure() {
let arena = Bump::new(); let arena = Bump::new();
let arg1 = Located::new(0, 0, 1, 2, Identifier("a")); let arg1 = Located::new(0, 0, 1, 2, Identifier("a"));
let arg2 = Located::new(0, 0, 3, 4, Identifier("b")); let arg2 = Located::new(0, 0, 4, 5, Identifier("b"));
let patterns = bumpalo::vec![in &arena; arg1, arg2]; let patterns = bumpalo::vec![in &arena; arg1, arg2];
let expected = Closure( let expected = Closure(
arena.alloc(patterns), arena.alloc(patterns),
arena.alloc(Located::new(0, 0, 8, 10, Int("42"))), arena.alloc(Located::new(0, 0, 9, 11, Int("42"))),
); );
let actual = parse_with(&arena, "\\a b -> 42"); let actual = parse_with(&arena, "\\a, b -> 42");
assert_eq!(Ok(expected), actual); assert_eq!(Ok(expected), actual);
} }
@ -1004,14 +1004,14 @@ mod test_parse {
fn three_arg_closure() { fn three_arg_closure() {
let arena = Bump::new(); let arena = Bump::new();
let arg1 = Located::new(0, 0, 1, 2, Identifier("a")); let arg1 = Located::new(0, 0, 1, 2, Identifier("a"));
let arg2 = Located::new(0, 0, 3, 4, Identifier("b")); let arg2 = Located::new(0, 0, 4, 5, Identifier("b"));
let arg3 = Located::new(0, 0, 5, 6, Identifier("c")); let arg3 = Located::new(0, 0, 7, 8, Identifier("c"));
let patterns = bumpalo::vec![in &arena; arg1, arg2, arg3]; let patterns = bumpalo::vec![in &arena; arg1, arg2, arg3];
let expected = Closure( let expected = Closure(
arena.alloc(patterns), arena.alloc(patterns),
arena.alloc(Located::new(0, 0, 10, 12, Int("42"))), arena.alloc(Located::new(0, 0, 12, 14, Int("42"))),
); );
let actual = parse_with(&arena, "\\a b c -> 42"); let actual = parse_with(&arena, "\\a, b, c -> 42");
assert_eq!(Ok(expected), actual); assert_eq!(Ok(expected), actual);
} }
@ -1020,13 +1020,13 @@ mod test_parse {
fn closure_with_underscores() { fn closure_with_underscores() {
let arena = Bump::new(); let arena = Bump::new();
let underscore1 = Located::new(0, 0, 1, 2, Underscore); let underscore1 = Located::new(0, 0, 1, 2, Underscore);
let underscore2 = Located::new(0, 0, 3, 4, Underscore); let underscore2 = Located::new(0, 0, 4, 5, Underscore);
let patterns = bumpalo::vec![in &arena; underscore1, underscore2]; let patterns = bumpalo::vec![in &arena; underscore1, underscore2];
let expected = Closure( let expected = Closure(
arena.alloc(patterns), arena.alloc(patterns),
arena.alloc(Located::new(0, 0, 8, 10, Int("42"))), arena.alloc(Located::new(0, 0, 9, 11, Int("42"))),
); );
let actual = parse_with(&arena, "\\_ _ -> 42"); let actual = parse_with(&arena, "\\_, _ -> 42");
assert_eq!(Ok(expected), actual); assert_eq!(Ok(expected), actual);
} }
@ -1259,21 +1259,21 @@ mod test_parse {
let args = bumpalo::vec![in &arena; let args = bumpalo::vec![in &arena;
Located::new(1,1,7,8, Identifier("x")), Located::new(1,1,7,8, Identifier("x")),
Located::new(1,1,9,10, Underscore) Located::new(1,1,10,11, Underscore)
]; ];
let body = Located::new(1, 1, 14, 16, Int("42")); let body = Located::new(1, 1, 15, 17, Int("42"));
let closure = Expr::Closure(&args, &body); let closure = Expr::Closure(&args, &body);
let def = Def::Body( let def = Def::Body(
arena.alloc(Located::new(1, 1, 0, 3, Identifier("foo"))), arena.alloc(Located::new(1, 1, 0, 3, Identifier("foo"))),
arena.alloc(Located::new(1, 1, 6, 16, closure)), arena.alloc(Located::new(1, 1, 6, 17, closure)),
); );
let loc_def = &*arena.alloc(Located::new( let loc_def = &*arena.alloc(Located::new(
1, 1,
1, 1,
0, 0,
16, 17,
Def::SpaceBefore(arena.alloc(def), newline.into_bump_slice()), Def::SpaceBefore(arena.alloc(def), newline.into_bump_slice()),
)); ));
@ -1287,7 +1287,7 @@ mod test_parse {
indoc!( indoc!(
r#" r#"
foo : Int, Float -> Bool foo : Int, Float -> Bool
foo = \x _ -> 42 foo = \x, _ -> 42
42 42
"# "#

View file

@ -322,7 +322,7 @@ mod test_infer_uniq {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
\_ _ -> 42 \_, _ -> 42
"# "#
), ),
"Attr.Attr * (*, * -> Attr.Attr * Int)", "Attr.Attr * (*, * -> Attr.Attr * Int)",
@ -334,7 +334,7 @@ mod test_infer_uniq {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
\_ _ _ -> "test!" \_, _, _ -> "test!"
"# "#
), ),
"Attr.Attr * (*, *, * -> Attr.Attr * Str)", "Attr.Attr * (*, *, * -> Attr.Attr * Str)",
@ -390,7 +390,7 @@ mod test_infer_uniq {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
func = \_ _ -> 42 func = \_, _ -> 42
func func
"# "#
@ -404,7 +404,7 @@ mod test_infer_uniq {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
f = \_ _ _ -> "test!" f = \_, _, _ -> "test!"
f f
"# "#
@ -418,7 +418,7 @@ mod test_infer_uniq {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
a = \_ _ _ -> "test!" a = \_, _, _ -> "test!"
b = a b = a
@ -575,7 +575,7 @@ mod test_infer_uniq {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
always = \a b -> a always = \a, b -> a
1 |> always "foo" 1 |> always "foo"
"# "#
@ -640,7 +640,7 @@ mod test_infer_uniq {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
apply = \f x -> f x apply = \f, x -> f x
identity = \a -> a identity = \a -> a
apply identity 5 apply identity 5
@ -655,7 +655,7 @@ mod test_infer_uniq {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
\f x -> f x \f, x -> f x
"# "#
), ),
"Attr.Attr * (Attr.Attr * (a -> b), a -> b)", "Attr.Attr * (Attr.Attr * (a -> b), a -> b)",
@ -684,7 +684,7 @@ mod test_infer_uniq {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
\f -> (\a b -> f b a), \f -> (\a, b -> f b a),
"# "#
), ),
"Attr.Attr * (Attr.Attr * (a, b -> c) -> Attr.Attr * (b, a -> c))", "Attr.Attr * (Attr.Attr * (a, b -> c) -> Attr.Attr * (b, a -> c))",