Skip to content

Commit

Permalink
Merge pull request #7518 from gamebox/new-lambda-syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
gamebox authored Jan 15, 2025
2 parents ad645cd + 8e1e152 commit b84f0d1
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 19 deletions.
36 changes: 18 additions & 18 deletions crates/compiler/can/tests/test_can.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ mod test_can {
let src = indoc!(
r"
f : Num.Int * -> Num.Int *
f = \ a -> a
f = | a| a
f
"
Expand All @@ -300,7 +300,7 @@ mod test_can {
let src = indoc!(
r"
f : Num.Int * -> Num.Int * # comment
f = \ a -> a
f = | a| a
f
"
Expand All @@ -316,7 +316,7 @@ mod test_can {
let src = indoc!(
r"
f : Num.Int * -> Num.Int *
g = \ a -> a
g = | a| a
g
"
Expand Down Expand Up @@ -344,7 +344,7 @@ mod test_can {
let src = indoc!(
r"
f : Num.Int * -> Num.Int * # comment
g = \ a -> a
g = | a| a
g
"
Expand Down Expand Up @@ -373,7 +373,7 @@ mod test_can {
r"
f : Num.Int * -> Num.Int *
f = \ a -> a
f = | a| a
f 42
"
Expand All @@ -390,7 +390,7 @@ mod test_can {
r"
f : Num.Int * -> Num.Int *
# comment
f = \ a -> a
f = | a| a
f 42
"
Expand Down Expand Up @@ -677,8 +677,8 @@ mod test_can {
fn record_builder_desugar() {
let src = indoc!(
r#"
map2 = \a, b, combine -> combine a b
double = \n -> n * 2
map2 = |a, b, combine| combine a b
double = |n| n * 2
c = 3
Expand Down Expand Up @@ -1241,20 +1241,20 @@ mod test_can {
fn recognize_tail_calls() {
let src = indoc!(
r"
g = \x ->
g = |x|
when x is
0 -> 0
_ -> g (x - 1)
# use parens to force the ordering!
(
h = \x ->
h = |x|
when x is
0 -> 0
_ -> g (x - 1)
(
p = \x ->
p = |x|
when x is
0 -> 0
1 -> g (x - 1)
Expand Down Expand Up @@ -1342,7 +1342,7 @@ mod test_can {
fn when_tail_call() {
let src = indoc!(
r"
g = \x ->
g = |x|
when x is
0 -> 0
_ -> g (x + 1)
Expand All @@ -1364,7 +1364,7 @@ mod test_can {
fn immediate_tail_call() {
let src = indoc!(
r"
f = \x -> f x
f = |x| f x
f 0
"
Expand All @@ -1385,7 +1385,7 @@ mod test_can {
fn when_condition_is_no_tail_call() {
let src = indoc!(
r"
q = \x ->
q = |x|
when q x is
_ -> 0
Expand All @@ -1406,12 +1406,12 @@ mod test_can {
fn good_mutual_recursion() {
let src = indoc!(
r"
q = \x ->
q = |x|
when x is
0 -> 0
_ -> p (x - 1)
p = \x ->
p = |x|
when x is
0 -> 0
_ -> q (x - 1)
Expand All @@ -1437,7 +1437,7 @@ mod test_can {
fn valid_self_recursion() {
let src = indoc!(
r"
boom = \_ -> boom {}
boom = |_| boom {}
boom
"
Expand Down Expand Up @@ -1548,7 +1548,7 @@ mod test_can {
r"
fallbackZ = 3
fn = \{ x, y, z ? fallbackZ } ->
fn = |{ x, y, z ? fallbackZ }|
{ x, y, z }
fn { x: 0, y: 1 }
Expand Down
60 changes: 60 additions & 0 deletions crates/compiler/parse/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2335,6 +2335,53 @@ pub fn parse_top_level_defs<'a>(
// PARSER HELPERS

fn closure_help<'a>(check_for_arrow: CheckForArrow) -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
one_of!(
closure_new_syntax_help(),
closure_old_syntax_help(check_for_arrow),
)
}

fn closure_new_syntax_help<'a>() -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let parser = map_with_arena(
indented_seq_skip_first(
error_on_pizza(byte_indent(b'|', EClosure::Bar), EClosure::Start),
and(
sep_by1_e(
byte_indent(b',', EClosure::Comma),
space0_around_ee(
specialize_err(EClosure::Pattern, closure_param()),
EClosure::IndentArg,
EClosure::IndentArrow,
),
EClosure::Arg,
),
skip_first(
// Parse the -> which separates params from body
byte(b'|', EClosure::Bar),
// Parse the body
block(
CheckForArrow(false),
true,
EClosure::IndentBody,
EClosure::Body,
),
),
),
),
|arena: &'a Bump, (params, body)| {
let params: Vec<'a, Loc<Pattern<'a>>> = params;
let params: &'a [Loc<Pattern<'a>>] = params.into_bump_slice();
Expr::Closure(params, arena.alloc(body))
},
);
parser.parse(arena, state, min_indent)
}
}

fn closure_old_syntax_help<'a>(
check_for_arrow: CheckForArrow,
) -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
// closure_help_help(options)
map_with_arena(
// After the first token, all other tokens must be indented past the start of the line
Expand Down Expand Up @@ -2371,6 +2418,19 @@ fn closure_help<'a>(check_for_arrow: CheckForArrow) -> impl Parser<'a, Expr<'a>,
)
}

fn error_on_pizza<'a, T, E: 'a>(
p: impl Parser<'a, T, E>,
f: impl Fn(Position) -> E,
) -> impl Parser<'a, T, E> {
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
if state.bytes().starts_with(b"|>") || state.bytes().starts_with(b"||") {
Err((NoProgress, f(state.pos())))
} else {
p.parse(arena, state, min_indent)
}
}
}

mod when {
use parser::indented_seq_skip_first;

Expand Down
1 change: 1 addition & 0 deletions crates/compiler/parse/src/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,7 @@ impl<'a> Normalize<'a> for EClosure<'a> {
EClosure::IndentArrow(_) => EClosure::IndentArrow(Position::zero()),
EClosure::IndentBody(_) => EClosure::IndentBody(Position::zero()),
EClosure::IndentArg(_) => EClosure::IndentArg(Position::zero()),
EClosure::Bar(_) => EClosure::Bar(Position::zero()),
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion crates/compiler/parse/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -747,6 +747,7 @@ impl<'a> EInParens<'a> {

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EClosure<'a> {
Bar(Position),
Space(BadInputError, Position),
Start(Position),
Arrow(Position),
Expand All @@ -768,7 +769,8 @@ impl<'a> EClosure<'a> {
EClosure::Body(expr, _) => expr.get_region(),

// Cases with Position values
EClosure::Space(_, p)
EClosure::Bar(p)
| EClosure::Space(_, p)
| EClosure::Start(p)
| EClosure::Arrow(p)
| EClosure::Comma(p)
Expand Down
104 changes: 104 additions & 0 deletions crates/compiler/test_syntax/tests/test_fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1776,6 +1776,110 @@ mod test_fmt {
);
}

#[test]
fn lambda_returns_record_new_syntax() {
expr_formats_to(
indoc!(
r"
to_record = |_| {
x: 1,
y: 2,
z: 3,
}
to_record
"
),
indoc!(
r"
to_record = \_ -> {
x: 1,
y: 2,
z: 3,
}
to_record
"
),
);

expr_formats_to(
indoc!(
r"
func = |_|
{ x: 1, y: 2, z: 3 }
func
"
),
indoc!(
r"
func = \_ ->
{ x: 1, y: 2, z: 3 }
func
"
),
);

expr_formats_to(
indoc!(
r"
to_record = |_|
val = 0
{
x: 1,
y: 2,
z: 3,
}
to_record
"
),
indoc!(
r"
to_record = \_ ->
val = 0
{
x: 1,
y: 2,
z: 3,
}
to_record
"
),
);

expr_formats_to(
indoc!(
r"
to_record = |_|
{
x: 1,
y: 2,
z: 3,
}
to_record
"
),
indoc!(
r"
to_record = \_ -> {
x: 1,
y: 2,
z: 3,
}
to_record
"
),
);
}

#[test]
fn lambda_returns_list() {
expr_formats_same(indoc!(
Expand Down
20 changes: 20 additions & 0 deletions crates/reporting/src/error/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,26 @@ fn to_lambda_report<'a>(
let severity = Severity::RuntimeError;

match *parse_problem {
EClosure::Bar(pos) => {
let region = LineColumnRegion::from_pos(lines.convert_pos(pos));

let doc = alloc.stack([
alloc.reflow(r"I was trying to parse the arguments list for a function, but I got stuck here:"),
alloc.region(region, severity),
alloc.concat([
alloc.reflow("I was expecting to find a "),
alloc.parser_suggestion("|"),
alloc.reflow(" next."),
]),
]);

Report {
filename,
doc,
title: "MALFORMED ARGS LIST".to_string(),
severity,
}
}
EClosure::Arrow(pos) => match what_is_next(alloc.src_lines, lines.convert_pos(pos)) {
Next::Token("=>") => {
let surroundings = Region::new(start, pos);
Expand Down

0 comments on commit b84f0d1

Please sign in to comment.