mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 07:49:17 +03:00
Merge branch 'main' of github.com:lukewilliamboswell/roc into rust-docs
This commit is contained in:
commit
a6a2b59a79
1
.github/workflows/markdown_link_check.yml
vendored
1
.github/workflows/markdown_link_check.yml
vendored
@ -18,7 +18,6 @@ jobs:
|
||||
with:
|
||||
use-quiet-mode: 'yes'
|
||||
use-verbose-mode: 'yes'
|
||||
check-modified-files-only: 'yes'
|
||||
base-branch: 'main'
|
||||
check-modified-files-only: 'yes'
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
20
TUTORIAL.md
20
TUTORIAL.md
@ -570,6 +570,26 @@ We refer to whatever comes before a `->` in a `when` expression as a *pattern* -
|
||||
patterns in branching conditionals like `when` is known as [pattern matching](https://en.wikipedia.org/wiki/Pattern_matching). You may hear people say things like "let's pattern match on `Custom` here" as a way to
|
||||
suggest making a `when` branch that begins with something like `Custom description ->`.
|
||||
|
||||
### Pattern Matching on Lists
|
||||
You can also pattern match on lists, like so:
|
||||
|
||||
when myList is
|
||||
[] -> 0 # the list is empty
|
||||
[Foo, ..] -> 1 # it starts with a Foo tag
|
||||
[_, ..] -> 2 # it contains at least one element, which we ignore
|
||||
[Foo, Bar, ..] -> 3 # it starts with a Foo tag followed by a Bar tag
|
||||
[Foo, Bar, Baz] -> 4 # it contains exactly three elements: Foo, Bar, and Baz
|
||||
[Foo, a, ..] -> 5 # it starts with a Foo tag followed by something we name `a`
|
||||
[Ok a, ..] -> 6 # it starts with an Ok tag containing a payload named `a`
|
||||
[.., Foo] -> 7 # it ends with a Foo tag
|
||||
[A, B, .., C, D] -> 8 # it has certain elements at the beginning and and
|
||||
|
||||
This can be both more concise and more efficient (at runtime) than calling [`List.get`](https://www.roc-lang.org/builtins/List#get)
|
||||
multiple times, since each call to `get` requires a separate conditional to handle the different
|
||||
`Result`s they return.
|
||||
|
||||
> **Note:** Each list pattern can only have one `..`, which is known as the "rest pattern" because it's where the _rest_ of the list goes.
|
||||
|
||||
## Booleans
|
||||
|
||||
In many programming languages, `true` and `false` are special language keywords that refer to
|
||||
|
@ -14,10 +14,8 @@ pub fn space0_around_ee<'a, P, S, E>(
|
||||
indent_after_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, Loc<S>, E>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
P: Parser<'a, Loc<S>, E>,
|
||||
P: 'a,
|
||||
S: 'a + Spaceable<'a>,
|
||||
P: 'a + Parser<'a, Loc<S>, E>,
|
||||
E: 'a + SpaceProblem,
|
||||
{
|
||||
parser::map_with_arena(
|
||||
@ -29,16 +27,32 @@ where
|
||||
)
|
||||
}
|
||||
|
||||
pub fn space0_around_e_no_after_indent_check<'a, P, S, E>(
|
||||
parser: P,
|
||||
indent_before_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, Loc<S>, E>
|
||||
where
|
||||
S: 'a + Spaceable<'a>,
|
||||
P: 'a + Parser<'a, Loc<S>, E>,
|
||||
E: 'a + SpaceProblem,
|
||||
{
|
||||
parser::map_with_arena(
|
||||
and(
|
||||
space0_e(indent_before_problem),
|
||||
and(parser, space0_no_after_indent_check()),
|
||||
),
|
||||
spaces_around_help,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn space0_before_optional_after<'a, P, S, E>(
|
||||
parser: P,
|
||||
indent_before_problem: fn(Position) -> E,
|
||||
indent_after_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, Loc<S>, E>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
P: Parser<'a, Loc<S>, E>,
|
||||
P: 'a,
|
||||
S: 'a + Spaceable<'a>,
|
||||
P: 'a + Parser<'a, Loc<S>, E>,
|
||||
E: 'a + SpaceProblem,
|
||||
{
|
||||
parser::map_with_arena(
|
||||
@ -64,8 +78,7 @@ fn spaces_around_help<'a, S>(
|
||||
),
|
||||
) -> Loc<S>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
S: 'a + Spaceable<'a>,
|
||||
{
|
||||
let (spaces_before, (loc_val, spaces_after)) = tuples;
|
||||
|
||||
@ -97,10 +110,8 @@ pub fn space0_before_e<'a, P, S, E>(
|
||||
indent_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, Loc<S>, E>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
P: Parser<'a, Loc<S>, E>,
|
||||
P: 'a,
|
||||
S: 'a + Spaceable<'a>,
|
||||
P: 'a + Parser<'a, Loc<S>, E>,
|
||||
E: 'a + SpaceProblem,
|
||||
{
|
||||
parser::map_with_arena(
|
||||
@ -122,10 +133,8 @@ pub fn space0_after_e<'a, P, S, E>(
|
||||
indent_problem: fn(Position) -> E,
|
||||
) -> impl Parser<'a, Loc<S>, E>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
P: Parser<'a, Loc<S>, E>,
|
||||
P: 'a,
|
||||
S: 'a + Spaceable<'a>,
|
||||
P: 'a + Parser<'a, Loc<S>, E>,
|
||||
E: 'a + SpaceProblem,
|
||||
{
|
||||
parser::map_with_arena(
|
||||
@ -200,6 +209,38 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn space0_no_after_indent_check<'a, E>() -> impl Parser<'a, &'a [CommentOrNewline<'a>], E>
|
||||
where
|
||||
E: 'a + SpaceProblem,
|
||||
{
|
||||
move |arena, state: State<'a>, _min_indent: u32| match fast_eat_spaces(&state) {
|
||||
FastSpaceState::HasTab(position) => Err((
|
||||
MadeProgress,
|
||||
E::space_problem(BadInputError::HasTab, position),
|
||||
state,
|
||||
)),
|
||||
FastSpaceState::Good {
|
||||
newlines,
|
||||
consumed,
|
||||
column: _,
|
||||
} => {
|
||||
if consumed == 0 {
|
||||
Ok((NoProgress, &[] as &[_], state))
|
||||
} else {
|
||||
let comments_and_newlines = Vec::with_capacity_in(newlines, arena);
|
||||
let spaces = eat_spaces(state, comments_and_newlines);
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
spaces.comments_and_newlines.into_bump_slice(),
|
||||
spaces.state,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum FastSpaceState {
|
||||
Good {
|
||||
newlines: usize,
|
||||
|
@ -3,15 +3,16 @@ use crate::ast::{
|
||||
Pattern, Spaceable, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
};
|
||||
use crate::blankspace::{
|
||||
space0_after_e, space0_around_ee, space0_before_e, space0_before_optional_after, space0_e,
|
||||
space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e,
|
||||
space0_before_optional_after, space0_e,
|
||||
};
|
||||
use crate::ident::{lowercase_ident, parse_ident, Ident};
|
||||
use crate::keyword;
|
||||
use crate::parser::{
|
||||
self, backtrackable, increment_min_indent, optional, parse_word1, reset_min_indent, sep_by1,
|
||||
sep_by1_e, set_min_indent, specialize, specialize_ref, then, trailing_sep_by0, word1, word2,
|
||||
EClosure, EExpect, EExpr, EIf, EInParens, EList, ENumber, EPattern, ERecord, EString, EType,
|
||||
EWhen, Either, ParseResult, Parser,
|
||||
self, backtrackable, increment_min_indent, line_min_indent, optional, reset_min_indent,
|
||||
sep_by1, sep_by1_e, set_min_indent, specialize, specialize_ref, then, trailing_sep_by0, word1,
|
||||
word1_indent, word2, EClosure, EExpect, EExpr, EIf, EInParens, EList, ENumber, EPattern,
|
||||
ERecord, EString, EType, EWhen, Either, ParseResult, Parser,
|
||||
};
|
||||
use crate::pattern::{loc_closure_param, loc_has_parser};
|
||||
use crate::state::State;
|
||||
@ -39,10 +40,7 @@ pub fn test_parse_expr<'a>(
|
||||
arena: &'a bumpalo::Bump,
|
||||
state: State<'a>,
|
||||
) -> Result<Loc<Expr<'a>>, EExpr<'a>> {
|
||||
let parser = skip_second!(
|
||||
space0_before_e(parse_loc_expr, EExpr::IndentStart,),
|
||||
expr_end()
|
||||
);
|
||||
let parser = skip_second!(space0_before_e(loc_expr(), EExpr::IndentStart,), expr_end());
|
||||
|
||||
match parser.parse(arena, state, min_indent) {
|
||||
Ok((_, expression, _)) => Ok(expression),
|
||||
@ -76,7 +74,9 @@ impl Default for ExprParseOptions {
|
||||
|
||||
pub fn expr_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
||||
move |arena, state: State<'a>, min_indent: u32| {
|
||||
parse_loc_expr(arena, state, min_indent).map(|(a, b, c)| (a, b.value, c))
|
||||
loc_expr()
|
||||
.parse(arena, state, min_indent)
|
||||
.map(|(a, b, c)| (a, b.value, c))
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ fn loc_expr_in_parens_help_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EInParen
|
||||
between!(
|
||||
word1(b'(', EInParens::Open),
|
||||
space0_around_ee(
|
||||
specialize_ref(EInParens::Expr, parse_loc_expr),
|
||||
specialize_ref(EInParens::Expr, loc_expr()),
|
||||
EInParens::IndentOpen,
|
||||
EInParens::IndentEnd,
|
||||
),
|
||||
@ -182,12 +182,9 @@ fn record_field_access<'a>() -> impl Parser<'a, &'a str, EExpr<'a>> {
|
||||
|
||||
/// In some contexts we want to parse the `_` as an expression, so it can then be turned into a
|
||||
/// pattern later
|
||||
fn parse_loc_term_or_underscore_or_conditional<'a>(
|
||||
min_indent: u32,
|
||||
fn loc_term_or_underscore_or_conditional<'a>(
|
||||
options: ExprParseOptions,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
one_of!(
|
||||
loc_expr_in_parens_etc_help(),
|
||||
loc!(specialize(EExpr::If, if_expr_help(options))),
|
||||
@ -204,17 +201,13 @@ fn parse_loc_term_or_underscore_or_conditional<'a>(
|
||||
ident_to_expr
|
||||
)),
|
||||
)
|
||||
.parse(arena, state, min_indent)
|
||||
}
|
||||
|
||||
/// In some contexts we want to parse the `_` as an expression, so it can then be turned into a
|
||||
/// pattern later
|
||||
fn parse_loc_term_or_underscore<'a>(
|
||||
min_indent: u32,
|
||||
fn loc_term_or_underscore<'a>(
|
||||
options: ExprParseOptions,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
one_of!(
|
||||
loc_expr_in_parens_etc_help(),
|
||||
loc!(specialize(EExpr::Str, string_literal_help())),
|
||||
@ -229,15 +222,9 @@ fn parse_loc_term_or_underscore<'a>(
|
||||
ident_to_expr
|
||||
)),
|
||||
)
|
||||
.parse(arena, state, min_indent)
|
||||
}
|
||||
|
||||
fn parse_loc_term<'a>(
|
||||
min_indent: u32,
|
||||
options: ExprParseOptions,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
fn loc_term<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
one_of!(
|
||||
loc_expr_in_parens_etc_help(),
|
||||
loc!(specialize(EExpr::Str, string_literal_help())),
|
||||
@ -251,7 +238,6 @@ fn parse_loc_term<'a>(
|
||||
ident_to_expr
|
||||
)),
|
||||
)
|
||||
.parse(arena, state, min_indent)
|
||||
}
|
||||
|
||||
fn underscore_expression<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
||||
@ -280,10 +266,7 @@ fn loc_possibly_negative_or_negated_term<'a>(
|
||||
let initial = state.clone();
|
||||
|
||||
let (_, (loc_op, loc_expr), state) =
|
||||
and!(loc!(unary_negate()), |a, s, m| parse_loc_term(
|
||||
m, options, a, s
|
||||
))
|
||||
.parse(arena, state, min_indent)?;
|
||||
and!(loc!(unary_negate()), loc_term(options)).parse(arena, state, min_indent)?;
|
||||
|
||||
let loc_expr = numeric_negate_expression(arena, initial, loc_op, loc_expr, &[]);
|
||||
|
||||
@ -292,16 +275,12 @@ fn loc_possibly_negative_or_negated_term<'a>(
|
||||
// this will parse negative numbers, which the unary negate thing up top doesn't (for now)
|
||||
loc!(specialize(EExpr::Number, number_literal_help())),
|
||||
loc!(map_with_arena!(
|
||||
and!(loc!(word1(b'!', EExpr::Start)), |a, s, m| {
|
||||
parse_loc_term(m, options, a, s)
|
||||
}),
|
||||
and!(loc!(word1(b'!', EExpr::Start)), loc_term(options)),
|
||||
|arena: &'a Bump, (loc_op, loc_expr): (Loc<_>, _)| {
|
||||
Expr::UnaryOp(arena.alloc(loc_expr), Loc::at(loc_op.region, UnaryOp::Not))
|
||||
}
|
||||
)),
|
||||
|arena, state, min_indent: u32| {
|
||||
parse_loc_term_or_underscore_or_conditional(min_indent, options, arena, state)
|
||||
}
|
||||
loc_term_or_underscore_or_conditional(options)
|
||||
]
|
||||
}
|
||||
|
||||
@ -332,53 +311,41 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_expr_start<'a>(
|
||||
min_indent: u32,
|
||||
options: ExprParseOptions,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
fn expr_start<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
one_of![
|
||||
loc!(specialize(EExpr::If, if_expr_help(options))),
|
||||
loc!(specialize(EExpr::When, when::expr_help(options))),
|
||||
loc!(specialize(EExpr::Expect, expect_help(options))),
|
||||
loc!(specialize(EExpr::Closure, closure_help(options))),
|
||||
loc!(move |a, s, m| parse_expr_operator_chain(m, options, a, s)),
|
||||
loc!(expr_operator_chain(options)),
|
||||
fail_expr_start_e()
|
||||
]
|
||||
.trace("expr_start")
|
||||
.parse(arena, state, min_indent)
|
||||
}
|
||||
|
||||
fn parse_expr_operator_chain<'a>(
|
||||
min_indent: u32,
|
||||
options: ExprParseOptions,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
let min_indent = state.check_indent(min_indent, EExpr::IndentStart)?;
|
||||
fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
||||
line_min_indent(move |arena, state: State<'a>, min_indent: u32| {
|
||||
let (_, expr, state) =
|
||||
loc_possibly_negative_or_negated_term(options).parse(arena, state, min_indent)?;
|
||||
|
||||
let (_, expr, state) =
|
||||
loc_possibly_negative_or_negated_term(options).parse(arena, state, min_indent)?;
|
||||
let initial_state = state.clone();
|
||||
let end = state.pos();
|
||||
|
||||
let initial = state.clone();
|
||||
let end = state.pos();
|
||||
match space0_e(EExpr::IndentEnd).parse(arena, state, min_indent) {
|
||||
Err((_, _, state)) => Ok((MadeProgress, expr.value, state)),
|
||||
Ok((_, spaces_before_op, state)) => {
|
||||
let expr_state = ExprState {
|
||||
operators: Vec::new_in(arena),
|
||||
arguments: Vec::new_in(arena),
|
||||
expr,
|
||||
spaces_after: spaces_before_op,
|
||||
end,
|
||||
};
|
||||
|
||||
match space0_e(EExpr::IndentEnd).parse(arena, state, min_indent) {
|
||||
Err((_, _, state)) => Ok((MadeProgress, expr.value, state)),
|
||||
Ok((_, spaces_before_op, state)) => {
|
||||
let expr_state = ExprState {
|
||||
operators: Vec::new_in(arena),
|
||||
arguments: Vec::new_in(arena),
|
||||
expr,
|
||||
spaces_after: spaces_before_op,
|
||||
initial,
|
||||
end,
|
||||
};
|
||||
|
||||
parse_expr_end(min_indent, options, expr_state, arena, state)
|
||||
parse_expr_end(min_indent, options, expr_state, arena, state, initial_state)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -387,7 +354,6 @@ struct ExprState<'a> {
|
||||
arguments: Vec<'a, &'a Loc<Expr<'a>>>,
|
||||
expr: Loc<Expr<'a>>,
|
||||
spaces_after: &'a [CommentOrNewline<'a>],
|
||||
initial: State<'a>,
|
||||
end: Position,
|
||||
}
|
||||
|
||||
@ -477,23 +443,17 @@ impl<'a> ExprState<'a> {
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
fn parse_expr_final<'a>(
|
||||
expr_state: ExprState<'a>,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
fn parse_expr_final<'a>(expr_state: ExprState<'a>, arena: &'a Bump) -> Expr<'a> {
|
||||
let right_arg = to_call(arena, expr_state.arguments, expr_state.expr);
|
||||
|
||||
let expr = if expr_state.operators.is_empty() {
|
||||
if expr_state.operators.is_empty() {
|
||||
right_arg.value
|
||||
} else {
|
||||
Expr::BinOps(
|
||||
expr_state.operators.into_bump_slice(),
|
||||
arena.alloc(right_arg),
|
||||
)
|
||||
};
|
||||
|
||||
Ok((MadeProgress, expr, state))
|
||||
}
|
||||
}
|
||||
|
||||
fn to_call<'a>(
|
||||
@ -632,7 +592,7 @@ pub fn parse_single_def<'a>(
|
||||
}
|
||||
Ok((_, expect_flavor, state)) => {
|
||||
let parse_def_expr =
|
||||
space0_before_e(increment_min_indent(parse_loc_expr), EExpr::IndentEnd);
|
||||
space0_before_e(increment_min_indent(loc_expr()), EExpr::IndentEnd);
|
||||
|
||||
let (_, loc_def_expr, state) =
|
||||
parse_def_expr.parse(arena, state, min_indent)?;
|
||||
@ -726,7 +686,7 @@ pub fn parse_single_def<'a>(
|
||||
match operator().parse(arena, state, min_indent) {
|
||||
Ok((_, BinOp::Assignment, state)) => {
|
||||
let parse_def_expr =
|
||||
space0_before_e(increment_min_indent(parse_loc_expr), EExpr::IndentEnd);
|
||||
space0_before_e(increment_min_indent(loc_expr()), EExpr::IndentEnd);
|
||||
|
||||
let (_, loc_def_expr, state) =
|
||||
parse_def_expr.parse(arena, state, min_indent)?;
|
||||
@ -745,8 +705,11 @@ pub fn parse_single_def<'a>(
|
||||
))
|
||||
}
|
||||
Ok((_, BinOp::IsAliasType, state)) => {
|
||||
let (_, ann_type, state) = alias_signature_with_space_before(min_indent + 1)
|
||||
.parse(arena, state, min_indent)?;
|
||||
// the increment_min_indent here is probably _wrong_, since alias_signature_with_space_before does
|
||||
// that internally.
|
||||
// TODO: re-evaluate this
|
||||
let parser = increment_min_indent(alias_signature_with_space_before());
|
||||
let (_, ann_type, state) = parser.parse(arena, state, min_indent)?;
|
||||
let region = Region::span_across(&loc_pattern.region, &ann_type.region);
|
||||
|
||||
match &loc_pattern.value {
|
||||
@ -1066,7 +1029,7 @@ fn parse_defs_expr<'a>(
|
||||
Err(bad) => Err(bad),
|
||||
Ok((_, def_state, state)) => {
|
||||
// this is no def, because there is no `=` or `:`; parse as an expr
|
||||
let parse_final_expr = space0_before_e(parse_loc_expr, EExpr::IndentEnd);
|
||||
let parse_final_expr = space0_before_e(loc_expr(), EExpr::IndentEnd);
|
||||
|
||||
match parse_final_expr.parse(arena, state, min_indent) {
|
||||
Err((_, fail, state)) => {
|
||||
@ -1087,19 +1050,12 @@ fn parse_defs_expr<'a>(
|
||||
}
|
||||
}
|
||||
}
|
||||
fn alias_signature_with_space_before<'a>(
|
||||
min_indent: u32,
|
||||
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EExpr<'a>> {
|
||||
move |arena, state, _min_indent| {
|
||||
specialize(
|
||||
EExpr::Type,
|
||||
space0_before_e(
|
||||
set_min_indent(min_indent + 1, type_annotation::located(false)),
|
||||
EType::TIndentStart,
|
||||
),
|
||||
)
|
||||
.parse(arena, state, min_indent + 1)
|
||||
}
|
||||
|
||||
fn alias_signature_with_space_before<'a>() -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EExpr<'a>> {
|
||||
increment_min_indent(specialize(
|
||||
EExpr::Type,
|
||||
space0_before_e(type_annotation::located(false), EType::TIndentStart),
|
||||
))
|
||||
}
|
||||
|
||||
fn opaque_signature_with_space_before<'a>(
|
||||
@ -1160,8 +1116,8 @@ fn finish_parsing_alias_or_opaque<'a>(
|
||||
|
||||
match kind {
|
||||
AliasOrOpaque::Alias => {
|
||||
let (_, signature, state) = alias_signature_with_space_before(indented_more)
|
||||
.parse(arena, state, min_indent)?;
|
||||
let (_, signature, state) =
|
||||
alias_signature_with_space_before().parse(arena, state, min_indent)?;
|
||||
|
||||
let def_region = Region::span_across(&expr.region, &signature.region);
|
||||
|
||||
@ -1263,11 +1219,10 @@ mod ability {
|
||||
};
|
||||
|
||||
/// Parses a single ability demand line; see `parse_demand`.
|
||||
fn parse_demand_help<'a>(
|
||||
start_column: u32,
|
||||
) -> impl Parser<'a, AbilityMember<'a>, EAbility<'a>> {
|
||||
fn parse_demand_help<'a>() -> impl Parser<'a, AbilityMember<'a>, EAbility<'a>> {
|
||||
map!(
|
||||
and!(
|
||||
// Require the type to be more indented than the name
|
||||
absolute_indented_seq!(
|
||||
specialize(|_, pos| EAbility::DemandName(pos), loc!(lowercase_ident())),
|
||||
skip_first!(
|
||||
and!(
|
||||
@ -1275,11 +1230,7 @@ mod ability {
|
||||
space0_e(EAbility::DemandName),
|
||||
word1(b':', EAbility::DemandColon)
|
||||
),
|
||||
specialize(
|
||||
EAbility::Type,
|
||||
// Require the type to be more indented than the name
|
||||
set_min_indent(start_column + 1, type_annotation::located(true))
|
||||
)
|
||||
specialize(EAbility::Type, type_annotation::located(true))
|
||||
)
|
||||
),
|
||||
|(name, typ): (Loc<&'a str>, Loc<TypeAnnotation<'a>>)| {
|
||||
@ -1352,7 +1303,7 @@ mod ability {
|
||||
_ => {
|
||||
let indent_column = state.column();
|
||||
|
||||
let parser = parse_demand_help(indent_column);
|
||||
let parser = parse_demand_help();
|
||||
|
||||
match parser.parse(arena, state, min_indent) {
|
||||
Err((MadeProgress, fail, state)) => {
|
||||
@ -1445,6 +1396,7 @@ fn parse_expr_operator<'a>(
|
||||
loc_op: Loc<BinOp>,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
initial_state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
let (_, spaces_after_operator, state) =
|
||||
space0_e(EExpr::IndentEnd).parse(arena, state, min_indent)?;
|
||||
@ -1459,18 +1411,18 @@ fn parse_expr_operator<'a>(
|
||||
BinOp::Minus if expr_state.end != op_start && op_end == new_start => {
|
||||
// negative terms
|
||||
|
||||
let (_, negated_expr, state) = parse_loc_term(min_indent, options, arena, state)?;
|
||||
let (_, negated_expr, state) = loc_term(options).parse(arena, state, min_indent)?;
|
||||
let new_end = state.pos();
|
||||
|
||||
let arg = numeric_negate_expression(
|
||||
arena,
|
||||
expr_state.initial,
|
||||
initial_state,
|
||||
loc_op,
|
||||
negated_expr,
|
||||
expr_state.spaces_after,
|
||||
);
|
||||
|
||||
expr_state.initial = state.clone();
|
||||
let initial_state = state.clone();
|
||||
|
||||
let (spaces, state) = match space0_e(EExpr::IndentEnd).parse(arena, state, min_indent) {
|
||||
Err((_, _, state)) => (&[] as &[_], state),
|
||||
@ -1481,7 +1433,7 @@ fn parse_expr_operator<'a>(
|
||||
expr_state.spaces_after = spaces;
|
||||
expr_state.end = new_end;
|
||||
|
||||
parse_expr_end(min_indent, options, expr_state, arena, state)
|
||||
parse_expr_end(min_indent, options, expr_state, arena, state, initial_state)
|
||||
}
|
||||
BinOp::Assignment => {
|
||||
let expr_region = expr_state.expr.region;
|
||||
@ -1494,7 +1446,7 @@ fn parse_expr_operator<'a>(
|
||||
let (value_def, def_region, state) = {
|
||||
match expr_to_pattern_help(arena, &call.value) {
|
||||
Ok(good) => {
|
||||
let (_, mut body, state) = parse_loc_expr(arena, state, indented_more)?;
|
||||
let (_, mut body, state) = loc_expr().parse(arena, state, indented_more)?;
|
||||
|
||||
// put the spaces from after the operator in front of the call
|
||||
if !spaces_after_operator.is_empty() {
|
||||
@ -1539,7 +1491,8 @@ fn parse_expr_operator<'a>(
|
||||
let (loc_pattern, loc_body, state) = {
|
||||
match expr_to_pattern_help(arena, &call.value) {
|
||||
Ok(good) => {
|
||||
let (_, mut ann_type, state) = parse_loc_expr(arena, state, indented_more)?;
|
||||
let (_, mut ann_type, state) =
|
||||
loc_expr().parse(arena, state, indented_more)?;
|
||||
|
||||
// put the spaces from after the operator in front of the call
|
||||
if !spaces_after_operator.is_empty() {
|
||||
@ -1559,7 +1512,7 @@ fn parse_expr_operator<'a>(
|
||||
}
|
||||
};
|
||||
|
||||
let parse_cont = space0_before_e(parse_loc_expr, EExpr::IndentEnd);
|
||||
let parse_cont = space0_before_e(loc_expr(), EExpr::IndentEnd);
|
||||
|
||||
let (_, loc_cont, state) = parse_cont.parse(arena, state, min_indent)?;
|
||||
|
||||
@ -1590,7 +1543,7 @@ fn parse_expr_operator<'a>(
|
||||
Ok((_, mut new_expr, state)) => {
|
||||
let new_end = state.pos();
|
||||
|
||||
expr_state.initial = state.clone();
|
||||
let initial_state = state.clone();
|
||||
|
||||
// put the spaces from after the operator in front of the new_expr
|
||||
if !spaces_after_operator.is_empty() {
|
||||
@ -1610,7 +1563,8 @@ fn parse_expr_operator<'a>(
|
||||
expr_state.end = new_end;
|
||||
expr_state.spaces_after = &[];
|
||||
|
||||
parse_expr_final(expr_state, arena, state)
|
||||
let expr = parse_expr_final(expr_state, arena);
|
||||
Ok((MadeProgress, expr, state))
|
||||
}
|
||||
Ok((_, spaces, state)) => {
|
||||
let args = std::mem::replace(&mut expr_state.arguments, Vec::new_in(arena));
|
||||
@ -1623,7 +1577,7 @@ fn parse_expr_operator<'a>(
|
||||
expr_state.spaces_after = spaces;
|
||||
|
||||
// TODO new start?
|
||||
parse_expr_end(min_indent, options, expr_state, arena, state)
|
||||
parse_expr_end(min_indent, options, expr_state, arena, state, initial_state)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1640,10 +1594,11 @@ fn parse_expr_end<'a>(
|
||||
mut expr_state: ExprState<'a>,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
initial_state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
let parser = skip_first!(
|
||||
crate::blankspace::check_indent(EExpr::IndentEnd),
|
||||
move |a, s, m| parse_loc_term_or_underscore(m, options, a, s)
|
||||
loc_term_or_underscore(options)
|
||||
);
|
||||
|
||||
match parser.parse(arena, state.clone(), min_indent) {
|
||||
@ -1715,7 +1670,7 @@ fn parse_expr_end<'a>(
|
||||
|
||||
expr_state.spaces_after = &[];
|
||||
}
|
||||
expr_state.initial = state.clone();
|
||||
let initial_state = state.clone();
|
||||
|
||||
match space0_e(EExpr::IndentEnd).parse(arena, state, min_indent) {
|
||||
Err((_, _, state)) => {
|
||||
@ -1723,14 +1678,15 @@ fn parse_expr_end<'a>(
|
||||
expr_state.end = new_end;
|
||||
expr_state.spaces_after = &[];
|
||||
|
||||
parse_expr_final(expr_state, arena, state)
|
||||
let expr = parse_expr_final(expr_state, arena);
|
||||
Ok((MadeProgress, expr, state))
|
||||
}
|
||||
Ok((_, new_spaces, state)) => {
|
||||
expr_state.arguments.push(arena.alloc(arg));
|
||||
expr_state.end = new_end;
|
||||
expr_state.spaces_after = new_spaces;
|
||||
|
||||
parse_expr_end(min_indent, options, expr_state, arena, state)
|
||||
parse_expr_end(min_indent, options, expr_state, arena, state, initial_state)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1741,8 +1697,16 @@ fn parse_expr_end<'a>(
|
||||
Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)),
|
||||
Ok((_, loc_op, state)) => {
|
||||
expr_state.consume_spaces(arena);
|
||||
expr_state.initial = before_op;
|
||||
parse_expr_operator(min_indent, options, expr_state, loc_op, arena, state)
|
||||
let initial_state = before_op;
|
||||
parse_expr_operator(
|
||||
min_indent,
|
||||
options,
|
||||
expr_state,
|
||||
loc_op,
|
||||
arena,
|
||||
state,
|
||||
initial_state,
|
||||
)
|
||||
}
|
||||
Err((NoProgress, _, mut state)) => {
|
||||
// try multi-backpassing
|
||||
@ -1778,14 +1742,14 @@ fn parse_expr_end<'a>(
|
||||
Err((_, fail, state)) => Err((MadeProgress, fail, state)),
|
||||
Ok((_, _, state)) => {
|
||||
let parse_body = space0_before_e(
|
||||
increment_min_indent(parse_loc_expr),
|
||||
increment_min_indent(loc_expr()),
|
||||
EExpr::IndentEnd,
|
||||
);
|
||||
|
||||
let (_, loc_body, state) =
|
||||
parse_body.parse(arena, state, min_indent)?;
|
||||
|
||||
let parse_cont = space0_before_e(parse_loc_expr, EExpr::IndentEnd);
|
||||
let parse_cont = space0_before_e(loc_expr(), EExpr::IndentEnd);
|
||||
|
||||
let (_, loc_cont, state) =
|
||||
parse_cont.parse(arena, state, min_indent)?;
|
||||
@ -1802,10 +1766,10 @@ fn parse_expr_end<'a>(
|
||||
} else if options.check_for_arrow && state.bytes().starts_with(b"->") {
|
||||
Err((MadeProgress, EExpr::BadOperator("->", state.pos()), state))
|
||||
} else {
|
||||
// roll back space parsing
|
||||
let state = expr_state.initial.clone();
|
||||
let expr = parse_expr_final(expr_state, arena);
|
||||
|
||||
parse_expr_final(expr_state, arena, state)
|
||||
// roll back space parsing
|
||||
Ok((MadeProgress, expr, initial_state))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1813,43 +1777,18 @@ fn parse_expr_end<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_loc_expr<'a>(
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
min_indent: u32,
|
||||
) -> ParseResult<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
parse_loc_expr_with_options(
|
||||
min_indent,
|
||||
ExprParseOptions {
|
||||
accept_multi_backpassing: true,
|
||||
..Default::default()
|
||||
},
|
||||
arena,
|
||||
state,
|
||||
)
|
||||
pub fn loc_expr<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
expr_start(ExprParseOptions {
|
||||
accept_multi_backpassing: true,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn loc_expr_no_multi_backpassing<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
move |arena, state, min_indent| {
|
||||
parse_loc_expr_with_options(
|
||||
min_indent,
|
||||
ExprParseOptions {
|
||||
accept_multi_backpassing: false,
|
||||
..Default::default()
|
||||
},
|
||||
arena,
|
||||
state,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_loc_expr_with_options<'a>(
|
||||
min_indent: u32,
|
||||
options: ExprParseOptions,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
parse_expr_start(min_indent, options, arena, state)
|
||||
expr_start(ExprParseOptions {
|
||||
accept_multi_backpassing: false,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
||||
/// If the given Expr would parse the same way as a valid Pattern, convert it.
|
||||
@ -2026,64 +1965,43 @@ pub fn toplevel_defs<'a>() -> impl Parser<'a, Defs<'a>, EExpr<'a>> {
|
||||
// PARSER HELPERS
|
||||
|
||||
fn closure_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EClosure<'a>> {
|
||||
move |arena, state, min_indent| parse_closure_help(arena, state, min_indent, options)
|
||||
}
|
||||
|
||||
fn parse_closure_help<'a>(
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
_min_indent: u32,
|
||||
options: ExprParseOptions,
|
||||
) -> ParseResult<'a, Expr<'a>, EClosure<'a>> {
|
||||
let start_indent = state.line_indent();
|
||||
|
||||
let min_indent = start_indent;
|
||||
|
||||
// All closures start with a '\' - e.g. (\x -> x + 1)
|
||||
let (_, (), state) = parse_word1(state, min_indent, b'\\', EClosure::Start)?;
|
||||
|
||||
// After the first token, all other tokens must be indented past the start of the line
|
||||
let min_indent = min_indent + 1;
|
||||
|
||||
// Once we see the '\', we're committed to parsing this as a closure.
|
||||
// It may turn out to be malformed, but it is definitely a closure.
|
||||
|
||||
// Parse the params
|
||||
// Params are comma-separated
|
||||
let (_, params, state) = sep_by1_e(
|
||||
word1(b',', EClosure::Comma),
|
||||
space0_around_ee(
|
||||
specialize(EClosure::Pattern, loc_closure_param()),
|
||||
EClosure::IndentArg,
|
||||
EClosure::IndentArrow,
|
||||
// closure_help_help(options)
|
||||
map_with_arena!(
|
||||
// After the first token, all other tokens must be indented past the start of the line
|
||||
indented_seq!(
|
||||
// All closures start with a '\' - e.g. (\x -> x + 1)
|
||||
word1_indent(b'\\', EClosure::Start),
|
||||
// Once we see the '\', we're committed to parsing this as a closure.
|
||||
// It may turn out to be malformed, but it is definitely a closure.
|
||||
and!(
|
||||
// Parse the params
|
||||
// Params are comma-separated
|
||||
sep_by1_e(
|
||||
word1(b',', EClosure::Comma),
|
||||
space0_around_ee(
|
||||
specialize(EClosure::Pattern, loc_closure_param()),
|
||||
EClosure::IndentArg,
|
||||
EClosure::IndentArrow,
|
||||
),
|
||||
EClosure::Arg,
|
||||
),
|
||||
skip_first!(
|
||||
// Parse the -> which separates params from body
|
||||
word2(b'-', b'>', EClosure::Arrow),
|
||||
// Parse the body
|
||||
space0_before_e(
|
||||
specialize_ref(EClosure::Body, expr_start(options)),
|
||||
EClosure::IndentBody
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
EClosure::Arg,
|
||||
|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))
|
||||
}
|
||||
)
|
||||
.parse(arena, state, min_indent)
|
||||
.map_err(|(_p, e, s)| (MadeProgress, e, s))?;
|
||||
|
||||
let (_, loc_body, state) = skip_first!(
|
||||
// Parse the -> which separates params from body
|
||||
word2(b'-', b'>', EClosure::Arrow),
|
||||
// Parse the body
|
||||
space0_before_e(
|
||||
specialize_ref(EClosure::Body, move |arena, state, min_indent| {
|
||||
parse_loc_expr_with_options(min_indent, options, arena, state)
|
||||
}),
|
||||
EClosure::IndentBody
|
||||
)
|
||||
)
|
||||
.parse(arena, state, min_indent)
|
||||
.map_err(|(_p, e, s)| (MadeProgress, e, s))?;
|
||||
|
||||
let params: Vec<'a, Loc<Pattern<'a>>> = params;
|
||||
let params: &'a [Loc<Pattern<'a>>] = params.into_bump_slice();
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Expr::Closure(params, arena.alloc(loc_body)),
|
||||
state,
|
||||
))
|
||||
}
|
||||
|
||||
mod when {
|
||||
@ -2092,55 +2010,28 @@ mod when {
|
||||
|
||||
/// Parser for when expressions.
|
||||
pub fn expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EWhen<'a>> {
|
||||
then(
|
||||
map_with_arena!(
|
||||
and!(
|
||||
when_with_indent(),
|
||||
skip_second!(
|
||||
space0_around_ee(
|
||||
specialize_ref(EWhen::Condition, move |arena, state, min_indent| {
|
||||
parse_loc_expr_with_options(min_indent, options, arena, state)
|
||||
}),
|
||||
indented_seq!(
|
||||
parser::keyword_e(keyword::WHEN, EWhen::When),
|
||||
space0_around_e_no_after_indent_check(
|
||||
specialize_ref(EWhen::Condition, expr_start(options)),
|
||||
EWhen::IndentCondition,
|
||||
EWhen::IndentIs,
|
||||
),
|
||||
parser::keyword_e(keyword::IS, EWhen::Is)
|
||||
)
|
||||
),
|
||||
// Note that we allow the `is` to be at any indent level, since this doesn't introduce any
|
||||
// ambiguity. The formatter will fix it up.
|
||||
//
|
||||
// We require that branches are indented relative to the line containing the `is`.
|
||||
indented_seq!(
|
||||
parser::keyword_e(keyword::IS, EWhen::Is),
|
||||
branches(options)
|
||||
)
|
||||
),
|
||||
move |arena, state, _progress, (case_indent, loc_condition), min_indent| {
|
||||
if case_indent < min_indent {
|
||||
return Err((
|
||||
MadeProgress,
|
||||
// TODO maybe pass case_indent here?
|
||||
EWhen::PatternAlignment(5, state.pos()),
|
||||
state,
|
||||
));
|
||||
}
|
||||
|
||||
// Everything in the branches must be indented at least as much as the case itself.
|
||||
let min_indent = case_indent;
|
||||
|
||||
let (_p1, branches, state) = branches(options)
|
||||
.parse(arena, state, min_indent)
|
||||
.map_err(|(_p, e, s)| (MadeProgress, e, s))?;
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Expr::When(arena.alloc(loc_condition), branches.into_bump_slice()),
|
||||
state,
|
||||
))
|
||||
},
|
||||
move |arena: &'a Bump, (loc_condition, branches): (Loc<Expr<'a>>, Vec<'a, &'a WhenBranch<'a>>)| {
|
||||
Expr::When(arena.alloc(loc_condition), branches.into_bump_slice())
|
||||
}
|
||||
)
|
||||
.trace("when")
|
||||
}
|
||||
|
||||
/// Parsing when with indentation.
|
||||
fn when_with_indent<'a>() -> impl Parser<'a, u32, EWhen<'a>> {
|
||||
move |arena, state: State<'a>, _min_indent: u32| {
|
||||
let min_indent = state.line_indent() + 1;
|
||||
parser::keyword_e(keyword::WHEN, EWhen::When)
|
||||
.parse(arena, state, min_indent)
|
||||
.map(|(progress, (), state)| (progress, min_indent, state))
|
||||
}
|
||||
}
|
||||
|
||||
fn branches<'a>(
|
||||
@ -2175,11 +2066,7 @@ mod when {
|
||||
and!(
|
||||
then(
|
||||
branch_alternatives(options, Some(pattern_indent_level)),
|
||||
move |_arena,
|
||||
state,
|
||||
_,
|
||||
((indent_column, loc_patterns), loc_guard),
|
||||
_min_indent| {
|
||||
move |_arena, state, _, ((indent_column, loc_patterns), loc_guard)| {
|
||||
if pattern_indent_level == indent_column {
|
||||
Ok((MadeProgress, (loc_patterns, loc_guard), state))
|
||||
} else {
|
||||
@ -2243,9 +2130,10 @@ mod when {
|
||||
parser::keyword_e(keyword::IF, EWhen::IfToken),
|
||||
// TODO we should require space before the expression but not after
|
||||
space0_around_ee(
|
||||
specialize_ref(EWhen::IfGuard, move |arena, state, min_indent| {
|
||||
parse_loc_expr_with_options(min_indent + 1, options, arena, state)
|
||||
}),
|
||||
specialize_ref(
|
||||
EWhen::IfGuard,
|
||||
increment_min_indent(expr_start(options))
|
||||
),
|
||||
EWhen::IndentIfGuard,
|
||||
EWhen::IndentArrow,
|
||||
)
|
||||
@ -2351,7 +2239,7 @@ mod when {
|
||||
skip_first!(
|
||||
word2(b'-', b'>', EWhen::Arrow),
|
||||
space0_before_e(
|
||||
specialize_ref(EWhen::Branch, parse_loc_expr),
|
||||
specialize_ref(EWhen::Branch, loc_expr()),
|
||||
EWhen::IndentBranch,
|
||||
)
|
||||
)
|
||||
@ -2361,38 +2249,24 @@ mod when {
|
||||
}
|
||||
|
||||
fn if_branch<'a>() -> impl Parser<'a, (Loc<Expr<'a>>, Loc<Expr<'a>>), EIf<'a>> {
|
||||
move |arena, state, min_indent| {
|
||||
// NOTE: only parse spaces before the expression
|
||||
let (_, cond, state) = space0_around_ee(
|
||||
specialize_ref(EIf::Condition, move |arena, state, min_indent| {
|
||||
parse_loc_expr(arena, state, min_indent)
|
||||
}),
|
||||
EIf::IndentCondition,
|
||||
EIf::IndentThenToken,
|
||||
)
|
||||
.parse(arena, state, min_indent)
|
||||
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
|
||||
|
||||
let (_, _, state) = parser::keyword_e(keyword::THEN, EIf::Then)
|
||||
.parse(arena, state, min_indent)
|
||||
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
|
||||
|
||||
let (_, then_branch, state) = space0_around_ee(
|
||||
specialize_ref(EIf::ThenBranch, move |arena, state, min_indent| {
|
||||
parse_loc_expr(arena, state, min_indent)
|
||||
}),
|
||||
EIf::IndentThenBranch,
|
||||
EIf::IndentElseToken,
|
||||
)
|
||||
.parse(arena, state, min_indent)
|
||||
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
|
||||
|
||||
let (_, _, state) = parser::keyword_e(keyword::ELSE, EIf::Else)
|
||||
.parse(arena, state, min_indent)
|
||||
.map_err(|(_, f, s)| (MadeProgress, f, s))?;
|
||||
|
||||
Ok((MadeProgress, (cond, then_branch), state))
|
||||
}
|
||||
skip_second!(
|
||||
and!(
|
||||
skip_second!(
|
||||
space0_around_ee(
|
||||
specialize_ref(EIf::Condition, loc_expr()),
|
||||
EIf::IndentCondition,
|
||||
EIf::IndentThenToken,
|
||||
),
|
||||
parser::keyword_e(keyword::THEN, EIf::Then)
|
||||
),
|
||||
space0_around_ee(
|
||||
specialize_ref(EIf::ThenBranch, loc_expr()),
|
||||
EIf::IndentThenBranch,
|
||||
EIf::IndentElseToken,
|
||||
)
|
||||
),
|
||||
parser::keyword_e(keyword::ELSE, EIf::Else)
|
||||
)
|
||||
}
|
||||
|
||||
fn expect_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpect<'a>> {
|
||||
@ -2403,9 +2277,10 @@ fn expect_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpe
|
||||
parser::keyword_e(keyword::EXPECT, EExpect::Expect).parse(arena, state, min_indent)?;
|
||||
|
||||
let (_, condition, state) = space0_before_e(
|
||||
specialize_ref(EExpect::Condition, move |arena, state, _m| {
|
||||
parse_loc_expr_with_options(start_column + 1, options, arena, state)
|
||||
}),
|
||||
specialize_ref(
|
||||
EExpect::Condition,
|
||||
set_min_indent(start_column + 1, expr_start(options)),
|
||||
),
|
||||
EExpect::IndentCondition,
|
||||
)
|
||||
.parse(arena, state, start_column + 1)
|
||||
@ -2413,7 +2288,7 @@ fn expect_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EExpe
|
||||
|
||||
let parse_cont = specialize_ref(
|
||||
EExpect::Continuation,
|
||||
space0_before_e(parse_loc_expr, EExpr::IndentEnd),
|
||||
space0_before_e(loc_expr(), EExpr::IndentEnd),
|
||||
);
|
||||
|
||||
let (_, loc_cont, state) = parse_cont.parse(arena, state, min_indent)?;
|
||||
@ -2456,9 +2331,7 @@ fn if_expr_help<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a>, EIf<
|
||||
};
|
||||
|
||||
let (_, else_branch, state) = space0_before_e(
|
||||
specialize_ref(EIf::ElseBranch, move |arena, state, min_indent| {
|
||||
parse_loc_expr_with_options(min_indent, options, arena, state)
|
||||
}),
|
||||
specialize_ref(EIf::ElseBranch, expr_start(options)),
|
||||
EIf::IndentElseBranch,
|
||||
)
|
||||
.parse(arena, state_final_else, min_indent)
|
||||
@ -2544,8 +2417,8 @@ fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
|
||||
}
|
||||
|
||||
fn list_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EList<'a>> {
|
||||
move |arena, state, min_indent| {
|
||||
let (_, elements, state) = collection_trailing_sep_e!(
|
||||
map_with_arena!(
|
||||
collection_trailing_sep_e!(
|
||||
word1(b'[', EList::Open),
|
||||
specialize_ref(EList::Expr, loc_expr_no_multi_backpassing()),
|
||||
word1(b',', EList::End),
|
||||
@ -2553,14 +2426,13 @@ fn list_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EList<'a>> {
|
||||
EList::Open,
|
||||
EList::IndentEnd,
|
||||
Expr::SpaceBefore
|
||||
)
|
||||
.parse(arena, state, min_indent)?;
|
||||
|
||||
let elements = elements.ptrify_items(arena);
|
||||
let expr = Expr::List(elements);
|
||||
|
||||
Ok((MadeProgress, expr, state))
|
||||
}
|
||||
),
|
||||
|arena, elements: Collection<'a, _>| {
|
||||
let elements = elements.ptrify_items(arena);
|
||||
Expr::List(elements)
|
||||
}
|
||||
)
|
||||
.trace("list_literal")
|
||||
}
|
||||
|
||||
pub fn record_value_field<'a>() -> impl Parser<'a, AssignedField<'a, Expr<'a>>, ERecord<'a>> {
|
||||
@ -2675,8 +2547,12 @@ fn record_help<'a>() -> impl Parser<
|
||||
|
||||
fn record_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
||||
then(
|
||||
loc!(specialize(EExpr::Record, record_help())),
|
||||
move |arena, state, _, loc_record, min_indent| {
|
||||
and!(
|
||||
loc!(specialize(EExpr::Record, record_help())),
|
||||
// there can be field access, e.g. `{ x : 4 }.x`
|
||||
optional(record_field_access_chain())
|
||||
),
|
||||
move |arena, state, _, (loc_record, accesses)| {
|
||||
let (opt_update, loc_assigned_fields_with_comments) = loc_record.value;
|
||||
|
||||
// This is a record literal, not a destructure.
|
||||
@ -2696,10 +2572,6 @@ fn record_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
||||
)),
|
||||
};
|
||||
|
||||
// there can be field access, e.g. `{ x : 4 }.x`
|
||||
let (_, accesses, state) =
|
||||
optional(record_field_access_chain()).parse(arena, state, min_indent)?;
|
||||
|
||||
if let Some(fields) = accesses {
|
||||
for field in fields {
|
||||
// Wrap the previous answer in the new one, so we end up
|
||||
|
@ -470,7 +470,6 @@ pub enum EWhen<'a> {
|
||||
Condition(&'a EExpr<'a>, Position),
|
||||
Branch(&'a EExpr<'a>, Position),
|
||||
|
||||
IndentIs(Position),
|
||||
IndentCondition(Position),
|
||||
IndentPattern(Position),
|
||||
IndentArrow(Position),
|
||||
@ -869,13 +868,13 @@ where
|
||||
P1: Parser<'a, Before, E>,
|
||||
After: 'a,
|
||||
E: 'a,
|
||||
F: Fn(&'a Bump, State<'a>, Progress, Before, u32) -> ParseResult<'a, After, E>,
|
||||
F: Fn(&'a Bump, State<'a>, Progress, Before) -> ParseResult<'a, After, E>,
|
||||
{
|
||||
move |arena, state, min_indent| {
|
||||
parser
|
||||
.parse(arena, state, min_indent)
|
||||
.and_then(|(progress, output, next_state)| {
|
||||
transform(arena, next_state, progress, output, min_indent)
|
||||
transform(arena, next_state, progress, output)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1376,6 +1375,64 @@ macro_rules! and {
|
||||
};
|
||||
}
|
||||
|
||||
/// Similar to `and`, but we modify the min_indent of the second parser to be
|
||||
/// 1 greater than the line_indent() at the start of the first parser.
|
||||
#[macro_export]
|
||||
macro_rules! indented_seq {
|
||||
($p1:expr, $p2:expr) => {
|
||||
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, _min_indent: u32| {
|
||||
let start_indent = state.line_indent();
|
||||
|
||||
// TODO: we should account for min_indent here, but this doesn't currently work
|
||||
// because min_indent is sometimes larger than it really should be, which is in turn
|
||||
// due to uses of `increment_indent`.
|
||||
//
|
||||
// let p1_indent = std::cmp::max(start_indent, min_indent);
|
||||
|
||||
let p1_indent = start_indent;
|
||||
let p2_indent = p1_indent + 1;
|
||||
|
||||
// We have to clone this because if the first parser passes and then
|
||||
// the second one fails, we need to revert back to the original state.
|
||||
let original_state = state.clone();
|
||||
|
||||
match $p1.parse(arena, state, p1_indent) {
|
||||
Ok((p1, (), state)) => match $p2.parse(arena, state, p2_indent) {
|
||||
Ok((p2, out2, state)) => Ok((p1.or(p2), out2, state)),
|
||||
Err((p2, fail, _)) => Err((p1.or(p2), fail, original_state)),
|
||||
},
|
||||
Err((progress, fail, state)) => Err((progress, fail, state)),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Similar to `and`, but we modify the min_indent of the second parser to be
|
||||
/// 1 greater than the column() at the start of the first parser.
|
||||
#[macro_export]
|
||||
macro_rules! absolute_indented_seq {
|
||||
($p1:expr, $p2:expr) => {
|
||||
move |arena: &'a bumpalo::Bump, state: $crate::state::State<'a>, _min_indent: u32| {
|
||||
let start_indent = state.column();
|
||||
|
||||
let p1_indent = start_indent;
|
||||
let p2_indent = p1_indent + 1;
|
||||
|
||||
// We have to clone this because if the first parser passes and then
|
||||
// the second one fails, we need to revert back to the original state.
|
||||
let original_state = state.clone();
|
||||
|
||||
match $p1.parse(arena, state, p1_indent) {
|
||||
Ok((p1, out1, state)) => match $p2.parse(arena, state, p2_indent) {
|
||||
Ok((p2, out2, state)) => Ok((p1.or(p2), (out1, out2), state)),
|
||||
Err((p2, fail, _)) => Err((p1.or(p2), fail, original_state)),
|
||||
},
|
||||
Err((progress, fail, state)) => Err((progress, fail, state)),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! one_of {
|
||||
($p1:expr, $p2:expr) => {
|
||||
@ -1450,6 +1507,16 @@ where
|
||||
move |arena, state, min_indent| parser.parse(arena, state, min_indent + 1)
|
||||
}
|
||||
|
||||
pub fn line_min_indent<'a, P, T, X: 'a>(parser: P) -> impl Parser<'a, T, X>
|
||||
where
|
||||
P: Parser<'a, T, X>,
|
||||
{
|
||||
move |arena, state: State<'a>, min_indent| {
|
||||
let min_indent = std::cmp::max(state.line_indent(), min_indent);
|
||||
parser.parse(arena, state, min_indent)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn absolute_column_min_indent<'a, P, T, X: 'a>(parser: P) -> impl Parser<'a, T, X>
|
||||
where
|
||||
P: Parser<'a, T, X>,
|
||||
@ -1517,28 +1584,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_word1<'a, ToError, E>(
|
||||
state: State<'a>,
|
||||
min_indent: u32,
|
||||
word: u8,
|
||||
to_error: ToError,
|
||||
) -> ParseResult<'a, (), E>
|
||||
pub fn word1_indent<'a, ToError, E>(word: u8, to_error: ToError) -> impl Parser<'a, (), E>
|
||||
where
|
||||
ToError: Fn(Position) -> E,
|
||||
E: 'a,
|
||||
{
|
||||
debug_assert_ne!(word, b'\n');
|
||||
|
||||
if min_indent > state.column() {
|
||||
return Err((NoProgress, to_error(state.pos()), state));
|
||||
}
|
||||
|
||||
match state.bytes().first() {
|
||||
Some(x) if *x == word => {
|
||||
let state = state.advance(1);
|
||||
Ok((MadeProgress, (), state))
|
||||
move |_arena: &'a Bump, state: State<'a>, min_indent: u32| {
|
||||
if min_indent > state.column() {
|
||||
return Err((NoProgress, to_error(state.pos()), state));
|
||||
}
|
||||
|
||||
match state.bytes().first() {
|
||||
Some(x) if *x == word => {
|
||||
let state = state.advance(1);
|
||||
Ok((MadeProgress, (), state))
|
||||
}
|
||||
_ => Err((NoProgress, to_error(state.pos()), state)),
|
||||
}
|
||||
_ => Err((NoProgress, to_error(state.pos()), state)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ fn loc_tag_pattern_arg<'a>(
|
||||
pub fn loc_has_parser<'a>() -> impl Parser<'a, Loc<Has<'a>>, EPattern<'a>> {
|
||||
then(
|
||||
loc_tag_pattern_arg(false),
|
||||
|_arena, state, progress, pattern, _min_indent| {
|
||||
|_arena, state, progress, pattern| {
|
||||
if matches!(pattern.value, Pattern::Identifier("has")) {
|
||||
Ok((progress, Loc::at(pattern.region, Has::Has), state))
|
||||
} else {
|
||||
@ -228,7 +228,7 @@ fn list_element_pattern<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
|
||||
fn three_list_rest_pattern_error<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PList<'a>> {
|
||||
then(
|
||||
loc!(word3(b'.', b'.', b'.', PList::Rest)),
|
||||
|_arena, state, _progress, word, _min_indent| {
|
||||
|_arena, state, _progress, word| {
|
||||
Err((MadeProgress, PList::Rest(word.region.start()), state))
|
||||
},
|
||||
)
|
||||
|
@ -99,7 +99,7 @@ fn parse_type_alias_after_as<'a>() -> impl Parser<'a, TypeHeader<'a>, EType<'a>>
|
||||
space0_before_e(term(false), EType::TAsIndentStart),
|
||||
// TODO: introduce a better combinator for this.
|
||||
// `check_type_alias` doesn't need to modify the state or progress, but it needs to access `state.pos()`
|
||||
|arena, state, progress, output, _min_indent| {
|
||||
|arena, state, progress, output| {
|
||||
let res = check_type_alias(arena, output);
|
||||
|
||||
match res {
|
||||
|
@ -1 +1 @@
|
||||
Expr(Closure(Arg(@1), @1), @0)
|
||||
Expr(Closure(Arg(@1), @0), @0)
|
@ -1 +1 @@
|
||||
Expr(Closure(IndentBody(@5), @3), @0)
|
||||
Expr(Closure(IndentBody(@5), @0), @0)
|
@ -1 +1 @@
|
||||
Expr(When(Arrow(@24), @24), @0)
|
||||
Expr(When(Arrow(@24), @0), @0)
|
@ -1 +1 @@
|
||||
Expr(When(Arrow(@26), @20), @0)
|
||||
Expr(When(Arrow(@26), @0), @0)
|
@ -6,6 +6,7 @@ use roc_collections::MutSet;
|
||||
use roc_mono::ir::OptLevel;
|
||||
use roc_parse::ast::{Expr, Pattern, TypeDef, TypeHeader, ValueDef};
|
||||
use roc_parse::expr::{parse_single_def, ExprParseOptions, SingleDef};
|
||||
use roc_parse::parser::Parser;
|
||||
use roc_parse::parser::{EClosure, EExpr, EPattern};
|
||||
use roc_parse::parser::{EWhen, Either};
|
||||
use roc_parse::state::State;
|
||||
@ -317,7 +318,7 @@ fn parse_src<'a>(arena: &'a Bump, line: &'a str) -> ParseOutcome<'a> {
|
||||
_ => {
|
||||
let src_bytes = line.as_bytes();
|
||||
|
||||
match roc_parse::expr::parse_loc_expr(arena, State::new(src_bytes), 0) {
|
||||
match roc_parse::expr::loc_expr().parse(arena, State::new(src_bytes), 0) {
|
||||
Ok((_, loc_expr, _)) => ParseOutcome::Expr(loc_expr.value),
|
||||
// Special case some syntax errors to allow for multi-line inputs
|
||||
Err((_, EExpr::Closure(EClosure::Body(_, _), _), _))
|
||||
|
@ -1384,7 +1384,7 @@ fn to_when_report<'a>(
|
||||
EWhen::IfToken(_pos) => unreachable!("the if-token is optional"),
|
||||
EWhen::When(_pos) => unreachable!("another branch would be taken"),
|
||||
|
||||
EWhen::Is(pos) | EWhen::IndentIs(pos) => to_unfinished_when_report(
|
||||
EWhen::Is(pos) => to_unfinished_when_report(
|
||||
alloc,
|
||||
lines,
|
||||
filename,
|
||||
|
@ -4851,6 +4851,7 @@ mod test_reporting {
|
||||
|
||||
I am partway through parsing a `when` expression, but got stuck here:
|
||||
|
||||
4│ when Just 4 is
|
||||
5│ Just when ->
|
||||
^
|
||||
|
||||
@ -4886,6 +4887,8 @@ mod test_reporting {
|
||||
|
||||
I was partway through parsing a `when` expression, but I got stuck here:
|
||||
|
||||
4│ when 5 is
|
||||
5│ 1 -> 2
|
||||
6│ _
|
||||
^
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user