mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-04 20:28:02 +00:00
New Lambda Syntax with |...|
This adds parser support for the new lambda syntax. It does not remove the existing syntax, nor will the new syntax be retained in formatting. That will be done in a separate PR to keep the two respective PRs relatively small and easy to review.
This commit is contained in:
parent
4e66910ef8
commit
8e1e1520e3
6 changed files with 172 additions and 50 deletions
|
@ -284,7 +284,7 @@ mod test_can {
|
|||
let src = indoc!(
|
||||
r"
|
||||
f : Num.Int * -> Num.Int *
|
||||
f = \ a -> a
|
||||
f = | a| a
|
||||
|
||||
f
|
||||
"
|
||||
|
@ -300,7 +300,7 @@ mod test_can {
|
|||
let src = indoc!(
|
||||
r"
|
||||
f : Num.Int * -> Num.Int * # comment
|
||||
f = \ a -> a
|
||||
f = | a| a
|
||||
|
||||
f
|
||||
"
|
||||
|
@ -316,7 +316,7 @@ mod test_can {
|
|||
let src = indoc!(
|
||||
r"
|
||||
f : Num.Int * -> Num.Int *
|
||||
g = \ a -> a
|
||||
g = | a| a
|
||||
|
||||
g
|
||||
"
|
||||
|
@ -344,7 +344,7 @@ mod test_can {
|
|||
let src = indoc!(
|
||||
r"
|
||||
f : Num.Int * -> Num.Int * # comment
|
||||
g = \ a -> a
|
||||
g = | a| a
|
||||
|
||||
g
|
||||
"
|
||||
|
@ -373,7 +373,7 @@ mod test_can {
|
|||
r"
|
||||
f : Num.Int * -> Num.Int *
|
||||
|
||||
f = \ a -> a
|
||||
f = | a| a
|
||||
|
||||
f 42
|
||||
"
|
||||
|
@ -390,7 +390,7 @@ mod test_can {
|
|||
r"
|
||||
f : Num.Int * -> Num.Int *
|
||||
# comment
|
||||
f = \ a -> a
|
||||
f = | a| a
|
||||
|
||||
f 42
|
||||
"
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
"
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -1437,7 +1437,7 @@ mod test_can {
|
|||
fn valid_self_recursion() {
|
||||
let src = indoc!(
|
||||
r"
|
||||
boom = \_ -> boom {}
|
||||
boom = |_| boom {}
|
||||
|
||||
boom
|
||||
"
|
||||
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
@ -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)
|
||||
|
|
|
@ -1778,41 +1778,80 @@ mod test_fmt {
|
|||
|
||||
#[test]
|
||||
fn lambda_returns_record_new_syntax() {
|
||||
expr_formats_same(indoc!(
|
||||
r"
|
||||
to_record = |_| {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
}
|
||||
|
||||
to_record
|
||||
"
|
||||
));
|
||||
|
||||
expr_formats_same(indoc!(
|
||||
r"
|
||||
func = |_|
|
||||
{ x: 1, y: 2, z: 3 }
|
||||
|
||||
func
|
||||
"
|
||||
));
|
||||
|
||||
expr_formats_same(indoc!(
|
||||
r"
|
||||
to_record = |_|
|
||||
val = 0
|
||||
|
||||
{
|
||||
expr_formats_to(
|
||||
indoc!(
|
||||
r"
|
||||
to_record = |_| {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
}
|
||||
|
||||
to_record
|
||||
"
|
||||
));
|
||||
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!(
|
||||
|
@ -1829,7 +1868,7 @@ mod test_fmt {
|
|||
),
|
||||
indoc!(
|
||||
r"
|
||||
to_record = |_| {
|
||||
to_record = \_ -> {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue