mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
another attempt to tame this grammar
This commit is contained in:
parent
89f5e0465e
commit
9273c74c70
4 changed files with 242 additions and 179 deletions
|
@ -52,9 +52,27 @@ pub fn test_parse_expr<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum MultiBackpassing {
|
pub struct ExprParseOptions {
|
||||||
Allow,
|
/// Check for and accept multi-backpassing syntax
|
||||||
Disallow,
|
/// This is usually true, but false within list/record literals
|
||||||
|
/// because the comma separating backpassing arguments conflicts
|
||||||
|
/// with the comma separating literal elements
|
||||||
|
accept_multi_backpassing: bool,
|
||||||
|
|
||||||
|
/// Check for the `->` token, and raise an error if found
|
||||||
|
/// This is usually true, but false in if-guards
|
||||||
|
///
|
||||||
|
/// Just foo if foo == 2 -> ...
|
||||||
|
check_for_arrow: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ExprParseOptions {
|
||||||
|
fn default() -> Self {
|
||||||
|
ExprParseOptions {
|
||||||
|
accept_multi_backpassing: true,
|
||||||
|
check_for_arrow: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn expr_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
pub fn expr_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
||||||
|
@ -176,7 +194,7 @@ fn record_field_access<'a>() -> impl Parser<'a, &'a str, EExpr<'a>> {
|
||||||
|
|
||||||
fn parse_loc_term<'a>(
|
fn parse_loc_term<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||||
|
@ -184,10 +202,7 @@ fn parse_loc_term<'a>(
|
||||||
loc_expr_in_parens_etc_help(min_indent),
|
loc_expr_in_parens_etc_help(min_indent),
|
||||||
loc!(specialize(EExpr::Str, string_literal_help())),
|
loc!(specialize(EExpr::Str, string_literal_help())),
|
||||||
loc!(specialize(EExpr::Number, positive_number_literal_help())),
|
loc!(specialize(EExpr::Number, positive_number_literal_help())),
|
||||||
loc!(specialize(
|
loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))),
|
||||||
EExpr::Lambda,
|
|
||||||
closure_help(min_indent, multi_backpassing)
|
|
||||||
)),
|
|
||||||
loc!(record_literal_help(min_indent)),
|
loc!(record_literal_help(min_indent)),
|
||||||
loc!(specialize(EExpr::List, list_literal_help(min_indent))),
|
loc!(specialize(EExpr::List, list_literal_help(min_indent))),
|
||||||
loc!(map_with_arena!(
|
loc!(map_with_arena!(
|
||||||
|
@ -200,17 +215,14 @@ fn parse_loc_term<'a>(
|
||||||
|
|
||||||
fn loc_possibly_negative_or_negated_term<'a>(
|
fn loc_possibly_negative_or_negated_term<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
) -> impl Parser<'a, Located<Expr<'a>>, EExpr<'a>> {
|
) -> impl Parser<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||||
one_of![
|
one_of![
|
||||||
|arena, state: State<'a>| {
|
|arena, state: State<'a>| {
|
||||||
let initial = state;
|
let initial = state;
|
||||||
|
|
||||||
let (_, (loc_op, loc_expr), state) = and!(loc!(unary_negate()), |a, s| parse_loc_term(
|
let (_, (loc_op, loc_expr), state) = and!(loc!(unary_negate()), |a, s| parse_loc_term(
|
||||||
min_indent,
|
min_indent, options, a, s
|
||||||
multi_backpassing,
|
|
||||||
a,
|
|
||||||
s
|
|
||||||
))
|
))
|
||||||
.parse(arena, state)?;
|
.parse(arena, state)?;
|
||||||
|
|
||||||
|
@ -222,7 +234,7 @@ fn loc_possibly_negative_or_negated_term<'a>(
|
||||||
loc!(specialize(EExpr::Number, number_literal_help())),
|
loc!(specialize(EExpr::Number, number_literal_help())),
|
||||||
loc!(map_with_arena!(
|
loc!(map_with_arena!(
|
||||||
and!(loc!(word1(b'!', EExpr::Start)), |a, s| {
|
and!(loc!(word1(b'!', EExpr::Start)), |a, s| {
|
||||||
parse_loc_term(min_indent, multi_backpassing, a, s)
|
parse_loc_term(min_indent, options, a, s)
|
||||||
}),
|
}),
|
||||||
|arena: &'a Bump, (loc_op, loc_expr): (Located<_>, _)| {
|
|arena: &'a Bump, (loc_op, loc_expr): (Located<_>, _)| {
|
||||||
Expr::UnaryOp(
|
Expr::UnaryOp(
|
||||||
|
@ -233,7 +245,7 @@ fn loc_possibly_negative_or_negated_term<'a>(
|
||||||
)),
|
)),
|
||||||
|arena, state| {
|
|arena, state| {
|
||||||
// TODO use parse_loc_term_better
|
// TODO use parse_loc_term_better
|
||||||
parse_loc_term(min_indent, multi_backpassing, arena, state)
|
parse_loc_term(min_indent, options, arena, state)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -277,25 +289,19 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> {
|
||||||
|
|
||||||
fn parse_expr_start<'a>(
|
fn parse_expr_start<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
start: Position,
|
start: Position,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||||
one_of![
|
one_of![
|
||||||
loc!(specialize(
|
loc!(specialize(EExpr::If, if_expr_help(min_indent, options))),
|
||||||
EExpr::If,
|
|
||||||
if_expr_help(min_indent, multi_backpassing)
|
|
||||||
)),
|
|
||||||
loc!(specialize(
|
loc!(specialize(
|
||||||
EExpr::When,
|
EExpr::When,
|
||||||
when::expr_help(min_indent, multi_backpassing)
|
when::expr_help(min_indent, options)
|
||||||
)),
|
)),
|
||||||
loc!(specialize(
|
loc!(specialize(EExpr::Lambda, closure_help(min_indent, options))),
|
||||||
EExpr::Lambda,
|
loc!(move |a, s| parse_expr_operator_chain(min_indent, options, start, a, s)),
|
||||||
closure_help(min_indent, multi_backpassing)
|
|
||||||
)),
|
|
||||||
loc!(move |a, s| parse_expr_operator_chain(min_indent, multi_backpassing, start, a, s)),
|
|
||||||
fail_expr_start_e()
|
fail_expr_start_e()
|
||||||
]
|
]
|
||||||
.parse(arena, state)
|
.parse(arena, state)
|
||||||
|
@ -303,13 +309,13 @@ fn parse_expr_start<'a>(
|
||||||
|
|
||||||
fn parse_expr_operator_chain<'a>(
|
fn parse_expr_operator_chain<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
start: Position,
|
start: Position,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||||
let (_, expr, state) =
|
let (_, expr, state) =
|
||||||
loc_possibly_negative_or_negated_term(min_indent, multi_backpassing).parse(arena, state)?;
|
loc_possibly_negative_or_negated_term(min_indent, options).parse(arena, state)?;
|
||||||
|
|
||||||
let initial = state;
|
let initial = state;
|
||||||
let end = state.get_position();
|
let end = state.get_position();
|
||||||
|
@ -326,14 +332,7 @@ fn parse_expr_operator_chain<'a>(
|
||||||
end,
|
end,
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_expr_end(
|
parse_expr_end(min_indent, options, start, expr_state, arena, state)
|
||||||
min_indent,
|
|
||||||
multi_backpassing,
|
|
||||||
start,
|
|
||||||
expr_state,
|
|
||||||
arena,
|
|
||||||
state,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -705,7 +704,7 @@ struct DefState<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_defs_end<'a>(
|
fn parse_defs_end<'a>(
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
start: Position,
|
start: Position,
|
||||||
mut def_state: DefState<'a>,
|
mut def_state: DefState<'a>,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -760,7 +759,7 @@ fn parse_defs_end<'a>(
|
||||||
loc_def_expr,
|
loc_def_expr,
|
||||||
);
|
);
|
||||||
|
|
||||||
parse_defs_end(multi_backpassing, start, def_state, arena, state)
|
parse_defs_end(options, start, def_state, arena, state)
|
||||||
}
|
}
|
||||||
Ok((_, BinOp::HasType, state)) => {
|
Ok((_, BinOp::HasType, state)) => {
|
||||||
let (_, ann_type, state) = specialize(
|
let (_, ann_type, state) = specialize(
|
||||||
|
@ -782,7 +781,7 @@ fn parse_defs_end<'a>(
|
||||||
ann_type,
|
ann_type,
|
||||||
);
|
);
|
||||||
|
|
||||||
parse_defs_end(multi_backpassing, start, def_state, arena, state)
|
parse_defs_end(options, start, def_state, arena, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => Ok((MadeProgress, def_state, initial)),
|
_ => Ok((MadeProgress, def_state, initial)),
|
||||||
|
@ -791,7 +790,7 @@ fn parse_defs_end<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_defs_expr<'a>(
|
fn parse_defs_expr<'a>(
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
start: Position,
|
start: Position,
|
||||||
def_state: DefState<'a>,
|
def_state: DefState<'a>,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -799,7 +798,7 @@ fn parse_defs_expr<'a>(
|
||||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||||
let min_indent = start.col;
|
let min_indent = start.col;
|
||||||
|
|
||||||
match parse_defs_end(multi_backpassing, start, def_state, arena, state) {
|
match parse_defs_end(options, start, def_state, arena, state) {
|
||||||
Err(bad) => Err(bad),
|
Err(bad) => Err(bad),
|
||||||
Ok((_, def_state, state)) => {
|
Ok((_, def_state, state)) => {
|
||||||
// this is no def, because there is no `=` or `:`; parse as an expr
|
// this is no def, because there is no `=` or `:`; parse as an expr
|
||||||
|
@ -832,7 +831,7 @@ fn parse_defs_expr<'a>(
|
||||||
|
|
||||||
fn parse_expr_operator<'a>(
|
fn parse_expr_operator<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
start: Position,
|
start: Position,
|
||||||
mut expr_state: ExprState<'a>,
|
mut expr_state: ExprState<'a>,
|
||||||
loc_op: Located<BinOp>,
|
loc_op: Located<BinOp>,
|
||||||
|
@ -852,8 +851,7 @@ fn parse_expr_operator<'a>(
|
||||||
BinOp::Minus if expr_state.end != op_start && op_end == new_start => {
|
BinOp::Minus if expr_state.end != op_start && op_end == new_start => {
|
||||||
// negative terms
|
// negative terms
|
||||||
|
|
||||||
let (_, negated_expr, state) =
|
let (_, negated_expr, state) = parse_loc_term(min_indent, options, arena, state)?;
|
||||||
parse_loc_term(min_indent, multi_backpassing, arena, state)?;
|
|
||||||
let new_end = state.get_position();
|
let new_end = state.get_position();
|
||||||
|
|
||||||
let arg = numeric_negate_expression(
|
let arg = numeric_negate_expression(
|
||||||
|
@ -876,14 +874,7 @@ fn parse_expr_operator<'a>(
|
||||||
expr_state.spaces_after = spaces;
|
expr_state.spaces_after = spaces;
|
||||||
expr_state.end = new_end;
|
expr_state.end = new_end;
|
||||||
|
|
||||||
parse_expr_end(
|
parse_expr_end(min_indent, options, start, expr_state, arena, state)
|
||||||
min_indent,
|
|
||||||
multi_backpassing,
|
|
||||||
start,
|
|
||||||
expr_state,
|
|
||||||
arena,
|
|
||||||
state,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
BinOp::Assignment => {
|
BinOp::Assignment => {
|
||||||
let expr_region = expr_state.expr.region;
|
let expr_region = expr_state.expr.region;
|
||||||
|
@ -932,7 +923,7 @@ fn parse_expr_operator<'a>(
|
||||||
spaces_after: &[],
|
spaces_after: &[],
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_defs_expr(multi_backpassing, start, def_state, arena, state)
|
parse_defs_expr(options, start, def_state, arena, state)
|
||||||
}
|
}
|
||||||
BinOp::Backpassing => {
|
BinOp::Backpassing => {
|
||||||
let expr_region = expr_state.expr.region;
|
let expr_region = expr_state.expr.region;
|
||||||
|
@ -1079,11 +1070,9 @@ fn parse_expr_operator<'a>(
|
||||||
spaces_after: &[],
|
spaces_after: &[],
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_defs_expr(multi_backpassing, start, def_state, arena, state)
|
parse_defs_expr(options, start, def_state, arena, state)
|
||||||
}
|
}
|
||||||
_ => match loc_possibly_negative_or_negated_term(min_indent, multi_backpassing)
|
_ => match loc_possibly_negative_or_negated_term(min_indent, options).parse(arena, state) {
|
||||||
.parse(arena, state)
|
|
||||||
{
|
|
||||||
Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)),
|
Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)),
|
||||||
Ok((_, mut new_expr, state)) => {
|
Ok((_, mut new_expr, state)) => {
|
||||||
let new_end = state.get_position();
|
let new_end = state.get_position();
|
||||||
|
@ -1121,14 +1110,7 @@ fn parse_expr_operator<'a>(
|
||||||
expr_state.spaces_after = spaces;
|
expr_state.spaces_after = spaces;
|
||||||
|
|
||||||
// TODO new start?
|
// TODO new start?
|
||||||
parse_expr_end(
|
parse_expr_end(min_indent, options, start, expr_state, arena, state)
|
||||||
min_indent,
|
|
||||||
multi_backpassing,
|
|
||||||
start,
|
|
||||||
expr_state,
|
|
||||||
arena,
|
|
||||||
state,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1141,7 +1123,7 @@ fn parse_expr_operator<'a>(
|
||||||
|
|
||||||
fn parse_expr_end<'a>(
|
fn parse_expr_end<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
start: Position,
|
start: Position,
|
||||||
mut expr_state: ExprState<'a>,
|
mut expr_state: ExprState<'a>,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
|
@ -1149,7 +1131,7 @@ fn parse_expr_end<'a>(
|
||||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||||
let parser = skip_first!(
|
let parser = skip_first!(
|
||||||
crate::blankspace::check_indent(min_indent, EExpr::IndentEnd),
|
crate::blankspace::check_indent(min_indent, EExpr::IndentEnd),
|
||||||
move |a, s| parse_loc_term(min_indent, multi_backpassing, a, s)
|
move |a, s| parse_loc_term(min_indent, options, a, s)
|
||||||
);
|
);
|
||||||
|
|
||||||
match parser.parse(arena, state) {
|
match parser.parse(arena, state) {
|
||||||
|
@ -1180,14 +1162,7 @@ fn parse_expr_end<'a>(
|
||||||
expr_state.end = new_end;
|
expr_state.end = new_end;
|
||||||
expr_state.spaces_after = new_spaces;
|
expr_state.spaces_after = new_spaces;
|
||||||
|
|
||||||
parse_expr_end(
|
parse_expr_end(min_indent, options, start, expr_state, arena, state)
|
||||||
min_indent,
|
|
||||||
multi_backpassing,
|
|
||||||
start,
|
|
||||||
expr_state,
|
|
||||||
arena,
|
|
||||||
state,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1200,19 +1175,12 @@ fn parse_expr_end<'a>(
|
||||||
expr_state.consume_spaces(arena);
|
expr_state.consume_spaces(arena);
|
||||||
expr_state.initial = before_op;
|
expr_state.initial = before_op;
|
||||||
parse_expr_operator(
|
parse_expr_operator(
|
||||||
min_indent,
|
min_indent, options, start, expr_state, loc_op, arena, state,
|
||||||
multi_backpassing,
|
|
||||||
start,
|
|
||||||
expr_state,
|
|
||||||
loc_op,
|
|
||||||
arena,
|
|
||||||
state,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Err((NoProgress, _, mut state)) => {
|
Err((NoProgress, _, mut state)) => {
|
||||||
// try multi-backpassing
|
// try multi-backpassing
|
||||||
if multi_backpassing == MultiBackpassing::Allow && state.bytes.starts_with(b",")
|
if options.accept_multi_backpassing && state.bytes.starts_with(b",") {
|
||||||
{
|
|
||||||
state.bytes = &state.bytes[1..];
|
state.bytes = &state.bytes[1..];
|
||||||
state.column += 1;
|
state.column += 1;
|
||||||
|
|
||||||
|
@ -1273,6 +1241,12 @@ fn parse_expr_end<'a>(
|
||||||
Ok((MadeProgress, ret, state))
|
Ok((MadeProgress, ret, state))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if options.check_for_arrow && state.bytes.starts_with(b"->") {
|
||||||
|
Err((
|
||||||
|
MadeProgress,
|
||||||
|
EExpr::BadOperator(&[b'-', b'>'], state.line, state.column),
|
||||||
|
state,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
// roll back space parsing
|
// roll back space parsing
|
||||||
let state = expr_state.initial;
|
let state = expr_state.initial;
|
||||||
|
@ -1290,7 +1264,15 @@ fn parse_loc_expr<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||||
parse_loc_expr_with_options(min_indent, MultiBackpassing::Allow, arena, state)
|
parse_loc_expr_with_options(
|
||||||
|
min_indent,
|
||||||
|
ExprParseOptions {
|
||||||
|
accept_multi_backpassing: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
arena,
|
||||||
|
state,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_loc_expr_no_multi_backpassing<'a>(
|
pub fn parse_loc_expr_no_multi_backpassing<'a>(
|
||||||
|
@ -1298,17 +1280,25 @@ pub fn parse_loc_expr_no_multi_backpassing<'a>(
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||||
parse_loc_expr_with_options(min_indent, MultiBackpassing::Disallow, arena, state)
|
parse_loc_expr_with_options(
|
||||||
|
min_indent,
|
||||||
|
ExprParseOptions {
|
||||||
|
accept_multi_backpassing: false,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
arena,
|
||||||
|
state,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_loc_expr_with_options<'a>(
|
fn parse_loc_expr_with_options<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||||
let start = state.get_position();
|
let start = state.get_position();
|
||||||
parse_expr_start(min_indent, multi_backpassing, start, arena, state)
|
parse_expr_start(min_indent, options, start, arena, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If the given Expr would parse the same way as a valid Pattern, convert it.
|
/// If the given Expr would parse the same way as a valid Pattern, convert it.
|
||||||
|
@ -1460,8 +1450,13 @@ pub fn defs<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Def<'a>>>, E
|
||||||
space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state)?;
|
space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state)?;
|
||||||
|
|
||||||
let start = state.get_position();
|
let start = state.get_position();
|
||||||
let (_, def_state, state) =
|
|
||||||
parse_defs_end(MultiBackpassing::Disallow, start, def_state, arena, state)?;
|
let options = ExprParseOptions {
|
||||||
|
accept_multi_backpassing: false,
|
||||||
|
check_for_arrow: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (_, def_state, state) = parse_defs_end(options, start, def_state, arena, state)?;
|
||||||
|
|
||||||
let (_, final_space, state) =
|
let (_, final_space, state) =
|
||||||
space0_e(start.col, EExpr::Space, EExpr::IndentEnd).parse(arena, state)?;
|
space0_e(start.col, EExpr::Space, EExpr::IndentEnd).parse(arena, state)?;
|
||||||
|
@ -1499,7 +1494,7 @@ pub fn defs<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Def<'a>>>, E
|
||||||
|
|
||||||
fn closure_help<'a>(
|
fn closure_help<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
) -> impl Parser<'a, Expr<'a>, ELambda<'a>> {
|
) -> impl Parser<'a, Expr<'a>, ELambda<'a>> {
|
||||||
map_with_arena!(
|
map_with_arena!(
|
||||||
skip_first!(
|
skip_first!(
|
||||||
|
@ -1527,7 +1522,7 @@ fn closure_help<'a>(
|
||||||
// Parse the body
|
// Parse the body
|
||||||
space0_before_e(
|
space0_before_e(
|
||||||
specialize_ref(ELambda::Body, move |arena, state| {
|
specialize_ref(ELambda::Body, move |arena, state| {
|
||||||
parse_loc_expr_with_options(min_indent, multi_backpassing, arena, state)
|
parse_loc_expr_with_options(min_indent, options, arena, state)
|
||||||
}),
|
}),
|
||||||
min_indent,
|
min_indent,
|
||||||
ELambda::Space,
|
ELambda::Space,
|
||||||
|
@ -1552,7 +1547,7 @@ mod when {
|
||||||
/// Parser for when expressions.
|
/// Parser for when expressions.
|
||||||
pub fn expr_help<'a>(
|
pub fn expr_help<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
) -> impl Parser<'a, Expr<'a>, When<'a>> {
|
) -> impl Parser<'a, Expr<'a>, When<'a>> {
|
||||||
then(
|
then(
|
||||||
and!(
|
and!(
|
||||||
|
@ -1560,7 +1555,7 @@ mod when {
|
||||||
skip_second!(
|
skip_second!(
|
||||||
space0_around_ee(
|
space0_around_ee(
|
||||||
specialize_ref(When::Condition, move |arena, state| {
|
specialize_ref(When::Condition, move |arena, state| {
|
||||||
parse_loc_expr_with_options(min_indent, multi_backpassing, arena, state)
|
parse_loc_expr_with_options(min_indent, options, arena, state)
|
||||||
}),
|
}),
|
||||||
min_indent,
|
min_indent,
|
||||||
When::Space,
|
When::Space,
|
||||||
|
@ -1583,7 +1578,7 @@ mod when {
|
||||||
// Everything in the branches must be indented at least as much as the case itself.
|
// Everything in the branches must be indented at least as much as the case itself.
|
||||||
let min_indent = case_indent;
|
let min_indent = case_indent;
|
||||||
|
|
||||||
let (p1, branches, state) = branches(min_indent).parse(arena, state)?;
|
let (p1, branches, state) = branches(min_indent, options).parse(arena, state)?;
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
progress.or(p1),
|
progress.or(p1),
|
||||||
|
@ -1603,17 +1598,24 @@ mod when {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn branches<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, &'a WhenBranch<'a>>, When<'a>> {
|
fn branches<'a>(
|
||||||
move |arena, state| {
|
min_indent: u16,
|
||||||
|
options: ExprParseOptions,
|
||||||
|
) -> impl Parser<'a, Vec<'a, &'a WhenBranch<'a>>, When<'a>> {
|
||||||
|
move |arena, state: State<'a>| {
|
||||||
|
let when_indent = state.indent_col;
|
||||||
|
|
||||||
let mut branches: Vec<'a, &'a WhenBranch<'a>> = Vec::with_capacity_in(2, arena);
|
let mut branches: Vec<'a, &'a WhenBranch<'a>> = Vec::with_capacity_in(2, arena);
|
||||||
|
|
||||||
// 1. Parse the first branch and get its indentation level. (It must be >= min_indent.)
|
// 1. Parse the first branch and get its indentation level. (It must be >= min_indent.)
|
||||||
// 2. Parse the other branches. Their indentation levels must be == the first branch's.
|
// 2. Parse the other branches. Their indentation levels must be == the first branch's.
|
||||||
|
|
||||||
let (_, ((pattern_indent_level, loc_first_patterns), loc_first_guard), state) =
|
let (_, ((pattern_indent_level, loc_first_patterns), loc_first_guard), mut state) =
|
||||||
branch_alternatives(min_indent, None).parse(arena, state)?;
|
branch_alternatives(min_indent, options, None).parse(arena, state)?;
|
||||||
let original_indent = pattern_indent_level;
|
let original_indent = pattern_indent_level;
|
||||||
|
|
||||||
|
state.indent_col = pattern_indent_level;
|
||||||
|
|
||||||
// Parse the first "->" and the expression after it.
|
// Parse the first "->" and the expression after it.
|
||||||
let (_, loc_first_expr, mut state) =
|
let (_, loc_first_expr, mut state) =
|
||||||
branch_result(original_indent + 1).parse(arena, state)?;
|
branch_result(original_indent + 1).parse(arena, state)?;
|
||||||
|
@ -1628,7 +1630,7 @@ mod when {
|
||||||
let branch_parser = map!(
|
let branch_parser = map!(
|
||||||
and!(
|
and!(
|
||||||
then(
|
then(
|
||||||
branch_alternatives(min_indent, Some(pattern_indent_level)),
|
branch_alternatives(min_indent, options, Some(pattern_indent_level)),
|
||||||
move |_arena, state, _, ((indent_col, loc_patterns), loc_guard)| {
|
move |_arena, state, _, ((indent_col, loc_patterns), loc_guard)| {
|
||||||
if pattern_indent_level == indent_col {
|
if pattern_indent_level == indent_col {
|
||||||
Ok((MadeProgress, (loc_patterns, loc_guard), state))
|
Ok((MadeProgress, (loc_patterns, loc_guard), state))
|
||||||
|
@ -1672,13 +1674,21 @@ mod when {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok((MadeProgress, branches, state))
|
Ok((
|
||||||
|
MadeProgress,
|
||||||
|
branches,
|
||||||
|
State {
|
||||||
|
indent_col: when_indent,
|
||||||
|
..state
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parsing alternative patterns in when branches.
|
/// Parsing alternative patterns in when branches.
|
||||||
fn branch_alternatives<'a>(
|
fn branch_alternatives<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
|
options: ExprParseOptions,
|
||||||
pattern_indent_level: Option<u16>,
|
pattern_indent_level: Option<u16>,
|
||||||
) -> impl Parser<
|
) -> impl Parser<
|
||||||
'a,
|
'a,
|
||||||
|
@ -1688,6 +1698,10 @@ mod when {
|
||||||
),
|
),
|
||||||
When<'a>,
|
When<'a>,
|
||||||
> {
|
> {
|
||||||
|
let options = ExprParseOptions {
|
||||||
|
check_for_arrow: false,
|
||||||
|
..options
|
||||||
|
};
|
||||||
and!(
|
and!(
|
||||||
branch_alternatives_help(min_indent, pattern_indent_level),
|
branch_alternatives_help(min_indent, pattern_indent_level),
|
||||||
one_of![
|
one_of![
|
||||||
|
@ -1697,7 +1711,7 @@ mod when {
|
||||||
// TODO we should require space before the expression but not after
|
// TODO we should require space before the expression but not after
|
||||||
space0_around_ee(
|
space0_around_ee(
|
||||||
specialize_ref(When::IfGuard, move |arena, state| {
|
specialize_ref(When::IfGuard, move |arena, state| {
|
||||||
parse_loc_expr(min_indent + 1, arena, state)
|
parse_loc_expr_with_options(min_indent + 1, options, arena, state)
|
||||||
}),
|
}),
|
||||||
min_indent,
|
min_indent,
|
||||||
When::Space,
|
When::Space,
|
||||||
|
@ -1753,14 +1767,14 @@ mod when {
|
||||||
match space0_e(0, When::Space, When::IndentPattern).parse(arena, state) {
|
match space0_e(0, When::Space, When::IndentPattern).parse(arena, state) {
|
||||||
Err((MadeProgress, fail, _)) => Err((NoProgress, fail, initial)),
|
Err((MadeProgress, fail, _)) => Err((NoProgress, fail, initial)),
|
||||||
Err((NoProgress, fail, _)) => Err((NoProgress, fail, initial)),
|
Err((NoProgress, fail, _)) => Err((NoProgress, fail, initial)),
|
||||||
Ok((progress, spaces, state)) => {
|
Ok((_progress, spaces, state)) => {
|
||||||
match pattern_indent_level {
|
match pattern_indent_level {
|
||||||
Some(wanted) if state.column > wanted => {
|
Some(wanted) if state.column > wanted => {
|
||||||
// this branch is indented too much
|
// this branch is indented too much
|
||||||
Err((
|
Err((
|
||||||
progress,
|
NoProgress,
|
||||||
When::IndentPattern(state.line, state.column),
|
When::IndentPattern(state.line, state.column),
|
||||||
state,
|
initial,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Some(wanted) if state.column < wanted => {
|
Some(wanted) if state.column < wanted => {
|
||||||
|
@ -1871,7 +1885,7 @@ fn if_branch<'a>(
|
||||||
|
|
||||||
fn if_expr_help<'a>(
|
fn if_expr_help<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
multi_backpassing: MultiBackpassing,
|
options: ExprParseOptions,
|
||||||
) -> impl Parser<'a, Expr<'a>, If<'a>> {
|
) -> impl Parser<'a, Expr<'a>, If<'a>> {
|
||||||
move |arena: &'a Bump, state| {
|
move |arena: &'a Bump, state| {
|
||||||
let (_, _, state) = parser::keyword_e(keyword::IF, If::If).parse(arena, state)?;
|
let (_, _, state) = parser::keyword_e(keyword::IF, If::If).parse(arena, state)?;
|
||||||
|
@ -1903,7 +1917,7 @@ fn if_expr_help<'a>(
|
||||||
|
|
||||||
let (_, else_branch, state) = space0_before_e(
|
let (_, else_branch, state) = space0_before_e(
|
||||||
specialize_ref(If::ElseBranch, move |arena, state| {
|
specialize_ref(If::ElseBranch, move |arena, state| {
|
||||||
parse_loc_expr_with_options(min_indent, multi_backpassing, arena, state)
|
parse_loc_expr_with_options(min_indent, options, arena, state)
|
||||||
}),
|
}),
|
||||||
min_indent,
|
min_indent,
|
||||||
If::Space,
|
If::Space,
|
||||||
|
|
|
@ -2840,6 +2840,47 @@ mod test_parse {
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_in_parens_indented() {
|
||||||
|
let arena = Bump::new();
|
||||||
|
|
||||||
|
let branch1 = {
|
||||||
|
let newlines = &[Newline];
|
||||||
|
let pattern1 = Pattern::SpaceBefore(arena.alloc(Pattern::GlobalTag("Ok")), newlines);
|
||||||
|
let loc_pattern1 = Located::new(1, 1, 4, 6, pattern1);
|
||||||
|
let num_1 = Num("3");
|
||||||
|
let expr1 = Located::new(1, 1, 10, 11, num_1);
|
||||||
|
let loc_expr1 = expr1; // Located::new(1, 2, 9, 6, expr1);
|
||||||
|
&*arena.alloc(WhenBranch {
|
||||||
|
patterns: arena.alloc([loc_pattern1]),
|
||||||
|
value: loc_expr1,
|
||||||
|
guard: None,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
let branches = &[branch1];
|
||||||
|
let var = Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "x",
|
||||||
|
};
|
||||||
|
let loc_cond = Located::new(0, 0, 6, 7, var);
|
||||||
|
let when = Expr::When(arena.alloc(loc_cond), branches);
|
||||||
|
let spaced = Expr::SpaceAfter(&when, &[Newline]);
|
||||||
|
let expected = Expr::ParensAround(&spaced);
|
||||||
|
let actual = parse_expr_with(
|
||||||
|
&arena,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
(when x is
|
||||||
|
Ok -> 3
|
||||||
|
)
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(Ok(expected), actual);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn when_with_alternative_patterns() {
|
fn when_with_alternative_patterns() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
|
|
|
@ -272,22 +272,31 @@ fn to_expr_report<'a>(
|
||||||
])
|
])
|
||||||
.indent(4),
|
.indent(4),
|
||||||
])],
|
])],
|
||||||
b"->" => vec![alloc.stack(vec![
|
b"->" => match context {
|
||||||
alloc.concat(vec![
|
Context::InNode(Node::WhenBranch, _row, _col, _) => {
|
||||||
alloc.reflow("The arrow "),
|
return to_unexpected_arrow_report(
|
||||||
alloc.parser_suggestion("->"),
|
alloc, filename, *row, *col, start_row, start_col,
|
||||||
alloc.reflow(" is only used to define cases in a "),
|
);
|
||||||
alloc.keyword("when"),
|
}
|
||||||
alloc.reflow("."),
|
_ => {
|
||||||
]),
|
vec![alloc.stack(vec![
|
||||||
alloc
|
alloc.concat(vec![
|
||||||
.vcat(vec![
|
alloc.reflow("The arrow "),
|
||||||
alloc.text("when color is"),
|
alloc.parser_suggestion("->"),
|
||||||
alloc.text("Red -> \"stop!\"").indent(4),
|
alloc.reflow(" is only used to define cases in a "),
|
||||||
alloc.text("Green -> \"go!\"").indent(4),
|
alloc.keyword("when"),
|
||||||
])
|
alloc.reflow("."),
|
||||||
.indent(4),
|
]),
|
||||||
])],
|
alloc
|
||||||
|
.vcat(vec![
|
||||||
|
alloc.text("when color is"),
|
||||||
|
alloc.text("Red -> \"stop!\"").indent(4),
|
||||||
|
alloc.text("Green -> \"go!\"").indent(4),
|
||||||
|
])
|
||||||
|
.indent(4),
|
||||||
|
])]
|
||||||
|
}
|
||||||
|
},
|
||||||
b"!" => vec![
|
b"!" => vec![
|
||||||
alloc.reflow("The boolean negation operator "),
|
alloc.reflow("The boolean negation operator "),
|
||||||
alloc.parser_suggestion("!"),
|
alloc.parser_suggestion("!"),
|
||||||
|
@ -1411,31 +1420,7 @@ fn to_unfinished_when_report<'a>(
|
||||||
) -> Report<'a> {
|
) -> Report<'a> {
|
||||||
match what_is_next(alloc.src_lines, row, col) {
|
match what_is_next(alloc.src_lines, row, col) {
|
||||||
Next::Token("->") => {
|
Next::Token("->") => {
|
||||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
to_unexpected_arrow_report(alloc, filename, row, col, start_row, start_col)
|
||||||
let region = Region::from_rows_cols(row, col, row, col + 2);
|
|
||||||
|
|
||||||
let doc = alloc.stack(vec![
|
|
||||||
alloc.concat(vec![
|
|
||||||
alloc.reflow(r"I am parsing a "),
|
|
||||||
alloc.keyword("when"),
|
|
||||||
alloc.reflow(r" expression right now, but this arrow is confusing me:"),
|
|
||||||
]),
|
|
||||||
alloc.region_with_subregion(surroundings, region),
|
|
||||||
alloc.concat(vec![
|
|
||||||
alloc.reflow(r"It makes sense to see arrows around here, "),
|
|
||||||
alloc.reflow(r"so I suspect it is something earlier."),
|
|
||||||
alloc.reflow(
|
|
||||||
r"Maybe this pattern is indented a bit farther from the previous patterns?",
|
|
||||||
),
|
|
||||||
]),
|
|
||||||
note_for_when_error(alloc),
|
|
||||||
]);
|
|
||||||
|
|
||||||
Report {
|
|
||||||
filename,
|
|
||||||
doc,
|
|
||||||
title: "UNEXPECTED ARROW".to_string(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -1462,6 +1447,41 @@ fn to_unfinished_when_report<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_unexpected_arrow_report<'a>(
|
||||||
|
alloc: &'a RocDocAllocator<'a>,
|
||||||
|
filename: PathBuf,
|
||||||
|
row: Row,
|
||||||
|
col: Col,
|
||||||
|
start_row: Row,
|
||||||
|
start_col: Col,
|
||||||
|
) -> Report<'a> {
|
||||||
|
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||||
|
let region = Region::from_rows_cols(row, col, row, col + 2);
|
||||||
|
|
||||||
|
let doc = alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow(r"I am parsing a "),
|
||||||
|
alloc.keyword("when"),
|
||||||
|
alloc.reflow(r" expression right now, but this arrow is confusing me:"),
|
||||||
|
]),
|
||||||
|
alloc.region_with_subregion(surroundings, region),
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow(r"It makes sense to see arrows around here, "),
|
||||||
|
alloc.reflow(r"so I suspect it is something earlier."),
|
||||||
|
alloc.reflow(
|
||||||
|
r"Maybe this pattern is indented a bit farther from the previous patterns?",
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
note_for_when_error(alloc),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Report {
|
||||||
|
filename,
|
||||||
|
doc,
|
||||||
|
title: "UNEXPECTED ARROW".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn note_for_when_error<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocBuilder<'a> {
|
fn note_for_when_error<'a>(alloc: &'a RocDocAllocator<'a>) -> RocDocBuilder<'a> {
|
||||||
alloc.stack(vec![
|
alloc.stack(vec![
|
||||||
alloc.concat(vec![
|
alloc.concat(vec![
|
||||||
|
|
|
@ -5177,26 +5177,17 @@ mod test_reporting {
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
── UNFINISHED WHEN ─────────────────────────────────────────────────────────────
|
── SYNTAX PROBLEM ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
I was partway through parsing a `when` expression, but I got stuck here:
|
I got stuck here:
|
||||||
|
|
||||||
3│ _ -> 2
|
1│ when 4 is
|
||||||
^
|
2│ 5 -> 2
|
||||||
|
^
|
||||||
|
|
||||||
I was expecting to see a pattern next
|
Whatever I am running into is confusing me al lot! Normally I can give
|
||||||
|
fairly specific hints, but something is really tripping me up this
|
||||||
Note: Here is an example of a valid `when` expression for reference.
|
time.
|
||||||
|
|
||||||
when List.first plants is
|
|
||||||
Ok n ->
|
|
||||||
n
|
|
||||||
|
|
||||||
Err _ ->
|
|
||||||
200
|
|
||||||
|
|
||||||
Notice the indentation. All patterns are aligned, and each branch is
|
|
||||||
indented a bit more than the corresponding pattern. That is important!
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -5886,21 +5877,18 @@ mod test_reporting {
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
── MISSING FINAL EXPRESSION ────────────────────────────────────────────────────
|
── UNKNOWN OPERATOR ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
I am partway through parsing a definition's final expression, but I
|
This looks like an operator, but it's not one I recognize!
|
||||||
got stuck here:
|
|
||||||
|
|
||||||
1│ main = 5 -> 3
|
1│ main = 5 -> 3
|
||||||
^
|
^^
|
||||||
|
|
||||||
This definition is missing a final expression. A nested definition
|
The arrow -> is only used to define cases in a `when`.
|
||||||
must be followed by either another definition, or an expression
|
|
||||||
|
when color is
|
||||||
x = 4
|
Red -> "stop!"
|
||||||
y = 2
|
Green -> "go!"
|
||||||
|
|
||||||
x + y
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue