mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-21 15:59:20 +03:00
fiddling with the when
parser
This commit is contained in:
parent
dd8bdcb806
commit
2a0c5c669b
@ -28,7 +28,7 @@ mod test_fmt {
|
|||||||
|
|
||||||
assert_eq!(buf, expected)
|
assert_eq!(buf, expected)
|
||||||
}
|
}
|
||||||
Err(error) => panic!("Unexpected parse failure when parsing this for formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", input, error)
|
Err(error) => panic!("Unexpected parse failure when parsing this for formatting:\n\n{}\n\nParse error was:\n\n{:?}\n\n", input, error)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1833,23 +1833,23 @@ mod test_fmt {
|
|||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
when b is
|
when b is
|
||||||
1 | 2 |
|
1 | 2 |
|
||||||
3
|
3
|
||||||
->
|
->
|
||||||
|
|
||||||
4
|
4
|
||||||
5 | 6 | 7 ->
|
5 | 6 | 7 ->
|
||||||
|
|
||||||
8
|
8
|
||||||
9
|
9
|
||||||
| 10 -> 11
|
| 10 -> 11
|
||||||
|
|
||||||
12 | 13 ->
|
12 | 13 ->
|
||||||
when c is
|
when c is
|
||||||
14 | 15 -> 16
|
14 | 15 -> 16
|
||||||
17
|
17
|
||||||
| 18 -> 19
|
| 18 -> 19
|
||||||
20 -> 21
|
20 -> 21
|
||||||
|
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -125,7 +125,7 @@ where
|
|||||||
E: 'a,
|
E: 'a,
|
||||||
{
|
{
|
||||||
move |_, state: State<'a>| {
|
move |_, state: State<'a>| {
|
||||||
if state.column > min_indent {
|
if state.column >= min_indent {
|
||||||
Ok((NoProgress, (), state))
|
Ok((NoProgress, (), state))
|
||||||
} else {
|
} else {
|
||||||
Err((NoProgress, indent_problem(state.line, state.column), state))
|
Err((NoProgress, indent_problem(state.line, state.column), state))
|
||||||
|
@ -16,16 +16,33 @@ use roc_region::all::{Located, Position, Region};
|
|||||||
|
|
||||||
use crate::parser::Progress::{self, *};
|
use crate::parser::Progress::{self, *};
|
||||||
|
|
||||||
|
fn expr_end<'a>() -> impl Parser<'a, (), EExpr<'a>> {
|
||||||
|
|_arena, state: State<'a>| {
|
||||||
|
if state.has_reached_end() {
|
||||||
|
Ok((NoProgress, (), state))
|
||||||
|
} else {
|
||||||
|
Err((
|
||||||
|
NoProgress,
|
||||||
|
EExpr::BadExprEnd(state.line, state.column),
|
||||||
|
state,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn test_parse_expr<'a>(
|
pub fn test_parse_expr<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
arena: &'a bumpalo::Bump,
|
arena: &'a bumpalo::Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> Result<Located<Expr<'a>>, EExpr<'a>> {
|
) -> Result<Located<Expr<'a>>, EExpr<'a>> {
|
||||||
let parser = space0_before_e(
|
let parser = skip_second!(
|
||||||
move |a, s| parse_loc_expr(min_indent, a, s),
|
space0_before_e(
|
||||||
min_indent,
|
move |a, s| parse_loc_expr(min_indent, a, s),
|
||||||
EExpr::Space,
|
min_indent,
|
||||||
EExpr::IndentStart,
|
EExpr::Space,
|
||||||
|
EExpr::IndentStart,
|
||||||
|
),
|
||||||
|
expr_end()
|
||||||
);
|
);
|
||||||
|
|
||||||
match parser.parse(arena, state) {
|
match parser.parse(arena, state) {
|
||||||
@ -1593,15 +1610,13 @@ mod when {
|
|||||||
// 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 (_, (loc_first_patterns, loc_first_guard), state) =
|
let (_, ((pattern_indent_level, loc_first_patterns), loc_first_guard), state) =
|
||||||
branch_alternatives(min_indent).parse(arena, state)?;
|
branch_alternatives(min_indent, None).parse(arena, state)?;
|
||||||
let loc_first_pattern = loc_first_patterns.first().unwrap();
|
let original_indent = pattern_indent_level;
|
||||||
let original_indent = loc_first_pattern.region.start_col;
|
|
||||||
let indented_more = original_indent + 1;
|
|
||||||
|
|
||||||
// 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(indented_more).parse(arena, state)?;
|
branch_result(original_indent + 1).parse(arena, state)?;
|
||||||
|
|
||||||
// Record this as the first branch, then optionally parse additional branches.
|
// Record this as the first branch, then optionally parse additional branches.
|
||||||
branches.push(arena.alloc(WhenBranch {
|
branches.push(arena.alloc(WhenBranch {
|
||||||
@ -1613,19 +1628,21 @@ mod when {
|
|||||||
let branch_parser = map!(
|
let branch_parser = map!(
|
||||||
and!(
|
and!(
|
||||||
then(
|
then(
|
||||||
branch_alternatives(min_indent),
|
branch_alternatives(min_indent, Some(pattern_indent_level)),
|
||||||
move |_arena, state, _, (loc_patterns, loc_guard)| {
|
move |_arena, state, _, ((indent_col, loc_patterns), loc_guard)| {
|
||||||
match alternatives_indented_correctly(&loc_patterns, original_indent) {
|
if pattern_indent_level == indent_col {
|
||||||
Ok(()) => Ok((MadeProgress, (loc_patterns, loc_guard), state)),
|
Ok((MadeProgress, (loc_patterns, loc_guard), state))
|
||||||
Err(indent) => Err((
|
} else {
|
||||||
|
let indent = pattern_indent_level - indent_col;
|
||||||
|
Err((
|
||||||
MadeProgress,
|
MadeProgress,
|
||||||
When::PatternAlignment(indent, state.line, state.column),
|
When::PatternAlignment(indent, state.line, state.column),
|
||||||
state,
|
state,
|
||||||
)),
|
))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
branch_result(indented_more)
|
branch_result(original_indent + 1)
|
||||||
),
|
),
|
||||||
|((patterns, guard), expr)| {
|
|((patterns, guard), expr)| {
|
||||||
let patterns: Vec<'a, _> = patterns;
|
let patterns: Vec<'a, _> = patterns;
|
||||||
@ -1662,33 +1679,17 @@ mod when {
|
|||||||
/// 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,
|
||||||
) -> impl Parser<'a, (Vec<'a, Located<Pattern<'a>>>, Option<Located<Expr<'a>>>), When<'a>> {
|
pattern_indent_level: Option<u16>,
|
||||||
|
) -> impl Parser<
|
||||||
|
'a,
|
||||||
|
(
|
||||||
|
(Col, Vec<'a, Located<Pattern<'a>>>),
|
||||||
|
Option<Located<Expr<'a>>>,
|
||||||
|
),
|
||||||
|
When<'a>,
|
||||||
|
> {
|
||||||
and!(
|
and!(
|
||||||
sep_by1(word1(b'|', When::Bar), |arena, state| {
|
branch_alternatives_help(min_indent, pattern_indent_level),
|
||||||
let (_, spaces, state) =
|
|
||||||
backtrackable(space0_e(min_indent, When::Space, When::IndentPattern))
|
|
||||||
.parse(arena, state)?;
|
|
||||||
|
|
||||||
let (_, loc_pattern, state) = space0_after_e(
|
|
||||||
specialize(When::Pattern, crate::pattern::loc_pattern_help(min_indent)),
|
|
||||||
min_indent,
|
|
||||||
When::Space,
|
|
||||||
When::IndentPattern,
|
|
||||||
)
|
|
||||||
.parse(arena, state)?;
|
|
||||||
|
|
||||||
Ok((
|
|
||||||
MadeProgress,
|
|
||||||
if spaces.is_empty() {
|
|
||||||
loc_pattern
|
|
||||||
} else {
|
|
||||||
arena
|
|
||||||
.alloc(loc_pattern.value)
|
|
||||||
.with_spaces_before(spaces, loc_pattern.region)
|
|
||||||
},
|
|
||||||
state,
|
|
||||||
))
|
|
||||||
}),
|
|
||||||
one_of![
|
one_of![
|
||||||
map!(
|
map!(
|
||||||
skip_first!(
|
skip_first!(
|
||||||
@ -1696,7 +1697,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, arena, state)
|
parse_loc_expr(min_indent + 1, arena, state)
|
||||||
}),
|
}),
|
||||||
min_indent,
|
min_indent,
|
||||||
When::Space,
|
When::Space,
|
||||||
@ -1711,22 +1712,103 @@ mod when {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if alternatives of a when branch are indented correctly.
|
fn branch_single_alternative<'a>(
|
||||||
fn alternatives_indented_correctly<'a>(
|
min_indent: u16,
|
||||||
loc_patterns: &'a Vec<'a, Located<Pattern<'a>>>,
|
) -> impl Parser<'a, Located<Pattern<'a>>, When<'a>> {
|
||||||
original_indent: u16,
|
move |arena, state| {
|
||||||
) -> Result<(), u16> {
|
let (_, spaces, state) =
|
||||||
let (first, rest) = loc_patterns.split_first().unwrap();
|
backtrackable(space0_e(min_indent, When::Space, When::IndentPattern))
|
||||||
let first_indented_correctly = first.region.start_col == original_indent;
|
.parse(arena, state)?;
|
||||||
if first_indented_correctly {
|
|
||||||
for when_pattern in rest.iter() {
|
let (_, loc_pattern, state) = space0_after_e(
|
||||||
if when_pattern.region.start_col < original_indent {
|
specialize(When::Pattern, crate::pattern::loc_pattern_help(min_indent)),
|
||||||
return Err(original_indent - when_pattern.region.start_col);
|
min_indent,
|
||||||
|
When::Space,
|
||||||
|
When::IndentPattern,
|
||||||
|
)
|
||||||
|
.parse(arena, state)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
MadeProgress,
|
||||||
|
if spaces.is_empty() {
|
||||||
|
loc_pattern
|
||||||
|
} else {
|
||||||
|
arena
|
||||||
|
.alloc(loc_pattern.value)
|
||||||
|
.with_spaces_before(spaces, loc_pattern.region)
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn branch_alternatives_help<'a>(
|
||||||
|
min_indent: u16,
|
||||||
|
pattern_indent_level: Option<u16>,
|
||||||
|
) -> impl Parser<'a, (Col, Vec<'a, Located<Pattern<'a>>>), When<'a>> {
|
||||||
|
move |arena, state: State<'a>| {
|
||||||
|
let initial = state;
|
||||||
|
|
||||||
|
// put no restrictions on the indent after the spaces; we'll check it manually
|
||||||
|
match space0_e(0, When::Space, When::IndentPattern).parse(arena, state) {
|
||||||
|
Err((MadeProgress, fail, _)) => Err((NoProgress, fail, initial)),
|
||||||
|
Err((NoProgress, fail, _)) => Err((NoProgress, fail, initial)),
|
||||||
|
Ok((_, spaces, state)) => {
|
||||||
|
match pattern_indent_level {
|
||||||
|
Some(wanted) if state.column > wanted => {
|
||||||
|
// this branch is indented too much
|
||||||
|
Err((
|
||||||
|
MadeProgress,
|
||||||
|
When::IndentPattern(state.line, state.column),
|
||||||
|
state,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Some(wanted) if state.column < wanted => {
|
||||||
|
let indent = wanted - state.column;
|
||||||
|
Err((
|
||||||
|
NoProgress,
|
||||||
|
When::PatternAlignment(indent, state.line, state.column),
|
||||||
|
initial,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let pattern_indent =
|
||||||
|
min_indent.max(pattern_indent_level.unwrap_or(min_indent));
|
||||||
|
// the region is not reliable for the indent col in the case of
|
||||||
|
// parentheses around patterns
|
||||||
|
let pattern_indent_col = state.column;
|
||||||
|
|
||||||
|
let parser = sep_by1(
|
||||||
|
word1(b'|', When::Bar),
|
||||||
|
branch_single_alternative(pattern_indent + 1),
|
||||||
|
);
|
||||||
|
|
||||||
|
match parser.parse(arena, state) {
|
||||||
|
Err((MadeProgress, fail, state)) => {
|
||||||
|
Err((MadeProgress, fail, state))
|
||||||
|
}
|
||||||
|
Err((NoProgress, fail, _)) => {
|
||||||
|
// roll back space parsing if the pattern made no progress
|
||||||
|
Err((NoProgress, fail, initial))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((_, mut loc_patterns, state)) => {
|
||||||
|
// tag spaces onto the first parsed pattern
|
||||||
|
if !spaces.is_empty() {
|
||||||
|
if let Some(first) = loc_patterns.get_mut(0) {
|
||||||
|
*first = arena
|
||||||
|
.alloc(first.value)
|
||||||
|
.with_spaces_before(spaces, first.region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((MadeProgress, (pattern_indent_col, loc_patterns), state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(original_indent - first.region.start_col)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,6 +379,7 @@ pub type Col = u16;
|
|||||||
pub enum EExpr<'a> {
|
pub enum EExpr<'a> {
|
||||||
Start(Row, Col),
|
Start(Row, Col),
|
||||||
End(Row, Col),
|
End(Row, Col),
|
||||||
|
BadExprEnd(Row, Col),
|
||||||
Space(BadInputError, Row, Col),
|
Space(BadInputError, Row, Col),
|
||||||
|
|
||||||
Dot(Row, Col),
|
Dot(Row, Col),
|
||||||
@ -926,8 +927,8 @@ where
|
|||||||
state = next_state;
|
state = next_state;
|
||||||
buf.push(next_output);
|
buf.push(next_output);
|
||||||
}
|
}
|
||||||
Err((element_progress, fail, state)) => {
|
Err((_, fail, state)) => {
|
||||||
return Err((element_progress, fail, state));
|
return Err((MadeProgress, fail, state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,8 +62,8 @@ pub fn loc_pattern_help<'a>(
|
|||||||
EPattern::Record,
|
EPattern::Record,
|
||||||
crate::pattern::record_pattern_help(min_indent)
|
crate::pattern::record_pattern_help(min_indent)
|
||||||
)),
|
)),
|
||||||
|
loc!(number_pattern_help()),
|
||||||
loc!(string_pattern_help()),
|
loc!(string_pattern_help()),
|
||||||
loc!(number_pattern_help())
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2550,6 +2550,201 @@ mod test_parse {
|
|||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_with_negative_numbers() {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let newlines = &[Newline];
|
||||||
|
let pattern1 = Pattern::SpaceBefore(arena.alloc(NumLiteral("1")), newlines);
|
||||||
|
let loc_pattern1 = Located::new(1, 1, 1, 2, pattern1);
|
||||||
|
let expr1 = Num("2");
|
||||||
|
let loc_expr1 = Located::new(1, 1, 6, 7, expr1);
|
||||||
|
let branch1 = &*arena.alloc(WhenBranch {
|
||||||
|
patterns: arena.alloc([loc_pattern1]),
|
||||||
|
value: loc_expr1,
|
||||||
|
guard: None,
|
||||||
|
});
|
||||||
|
let newlines = &[Newline];
|
||||||
|
let pattern2 = Pattern::SpaceBefore(arena.alloc(NumLiteral("-3")), newlines);
|
||||||
|
let loc_pattern2 = Located::new(2, 2, 1, 3, pattern2);
|
||||||
|
let expr2 = Num("4");
|
||||||
|
let loc_expr2 = Located::new(2, 2, 7, 8, expr2);
|
||||||
|
let branch2 = &*arena.alloc(WhenBranch {
|
||||||
|
patterns: arena.alloc([loc_pattern2]),
|
||||||
|
value: loc_expr2,
|
||||||
|
guard: None,
|
||||||
|
});
|
||||||
|
let branches = &[branch1, branch2];
|
||||||
|
let var = Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "x",
|
||||||
|
};
|
||||||
|
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||||
|
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||||
|
let actual = parse_expr_with(
|
||||||
|
&arena,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when x is
|
||||||
|
1 -> 2
|
||||||
|
-3 -> 4
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(Ok(expected), actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_with_function_application() {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let newlines = &[Newline];
|
||||||
|
let pattern1 = Pattern::SpaceBefore(arena.alloc(NumLiteral("1")), newlines);
|
||||||
|
let loc_pattern1 = Located::new(1, 1, 4, 5, pattern1);
|
||||||
|
let num_2 = Num("2");
|
||||||
|
let num_neg = Located::new(
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
9,
|
||||||
|
16,
|
||||||
|
Expr::Var {
|
||||||
|
module_name: "Num",
|
||||||
|
ident: "neg",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let expr0 = Located::new(2, 2, 5, 6, Expr::SpaceBefore(&num_2, &[Newline]));
|
||||||
|
let expr1 = Expr::Apply(
|
||||||
|
&num_neg,
|
||||||
|
&*arena.alloc([&*arena.alloc(expr0)]),
|
||||||
|
CalledVia::Space,
|
||||||
|
);
|
||||||
|
let loc_expr1 = Located::new(1, 2, 9, 6, expr1);
|
||||||
|
let branch1 = &*arena.alloc(WhenBranch {
|
||||||
|
patterns: arena.alloc([loc_pattern1]),
|
||||||
|
value: loc_expr1,
|
||||||
|
guard: None,
|
||||||
|
});
|
||||||
|
let newlines = &[Newline];
|
||||||
|
let pattern2 = Pattern::SpaceBefore(arena.alloc(Underscore("")), newlines);
|
||||||
|
let loc_pattern2 = Located::new(3, 3, 4, 5, pattern2);
|
||||||
|
let expr2 = Num("4");
|
||||||
|
let loc_expr2 = Located::new(3, 3, 9, 10, expr2);
|
||||||
|
let branch2 = &*arena.alloc(WhenBranch {
|
||||||
|
patterns: arena.alloc([loc_pattern2]),
|
||||||
|
value: loc_expr2,
|
||||||
|
guard: None,
|
||||||
|
});
|
||||||
|
let branches = &[branch1, branch2];
|
||||||
|
let var = Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "x",
|
||||||
|
};
|
||||||
|
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||||
|
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||||
|
let actual = parse_expr_with(
|
||||||
|
&arena,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when x is
|
||||||
|
1 -> Num.neg
|
||||||
|
2
|
||||||
|
_ -> 4
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(Ok(expected), actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_if_guard() {
|
||||||
|
let arena = Bump::new();
|
||||||
|
|
||||||
|
let branch1 = {
|
||||||
|
let newlines = &[Newline];
|
||||||
|
let pattern1 = Pattern::SpaceBefore(arena.alloc(Underscore("")), newlines);
|
||||||
|
let loc_pattern1 = Located::new(1, 1, 4, 5, pattern1);
|
||||||
|
let num_1 = Num("1");
|
||||||
|
let expr1 = Located::new(
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
Expr::SpaceBefore(arena.alloc(num_1), &[Newline]),
|
||||||
|
);
|
||||||
|
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 branch2 = {
|
||||||
|
let pattern1 = Pattern::SpaceBefore(arena.alloc(Underscore("")), &[Newline, Newline]);
|
||||||
|
let loc_pattern1 = Located::new(4, 4, 4, 5, pattern1);
|
||||||
|
let num_1 = Num("2");
|
||||||
|
let expr1 = Located::new(
|
||||||
|
5,
|
||||||
|
5,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
Expr::SpaceBefore(arena.alloc(num_1), &[Newline]),
|
||||||
|
);
|
||||||
|
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 branch3 = {
|
||||||
|
let pattern1 =
|
||||||
|
Pattern::SpaceBefore(arena.alloc(Pattern::GlobalTag("Ok")), &[Newline, Newline]);
|
||||||
|
let loc_pattern1 = Located::new(7, 7, 4, 6, pattern1);
|
||||||
|
let num_1 = Num("3");
|
||||||
|
let expr1 = Located::new(
|
||||||
|
8,
|
||||||
|
8,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
Expr::SpaceBefore(arena.alloc(num_1), &[Newline]),
|
||||||
|
);
|
||||||
|
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, branch2, branch3];
|
||||||
|
let var = Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "x",
|
||||||
|
};
|
||||||
|
let loc_cond = Located::new(0, 0, 5, 6, var);
|
||||||
|
let expected = Expr::When(arena.alloc(loc_cond), branches);
|
||||||
|
let actual = parse_expr_with(
|
||||||
|
&arena,
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when x is
|
||||||
|
_ ->
|
||||||
|
1
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
2
|
||||||
|
|
||||||
|
Ok ->
|
||||||
|
3
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(Ok(expected), actual);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn when_with_records() {
|
fn when_with_records() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
@ -2620,9 +2815,9 @@ mod test_parse {
|
|||||||
let pattern2_alt =
|
let pattern2_alt =
|
||||||
Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("bar"))), newlines);
|
Pattern::SpaceBefore(arena.alloc(StrLiteral(PlainLine("bar"))), newlines);
|
||||||
let loc_pattern2 = Located::new(2, 2, 1, 6, pattern2);
|
let loc_pattern2 = Located::new(2, 2, 1, 6, pattern2);
|
||||||
let loc_pattern2_alt = Located::new(3, 3, 1, 6, pattern2_alt);
|
let loc_pattern2_alt = Located::new(3, 3, 2, 7, pattern2_alt);
|
||||||
let expr2 = Num("2");
|
let expr2 = Num("2");
|
||||||
let loc_expr2 = Located::new(3, 3, 10, 11, expr2);
|
let loc_expr2 = Located::new(3, 3, 11, 12, expr2);
|
||||||
let branch2 = &*arena.alloc(WhenBranch {
|
let branch2 = &*arena.alloc(WhenBranch {
|
||||||
patterns: arena.alloc([loc_pattern2, loc_pattern2_alt]),
|
patterns: arena.alloc([loc_pattern2, loc_pattern2_alt]),
|
||||||
value: loc_expr2,
|
value: loc_expr2,
|
||||||
@ -2642,7 +2837,7 @@ mod test_parse {
|
|||||||
when x is
|
when x is
|
||||||
"blah" | "blop" -> 1
|
"blah" | "blop" -> 1
|
||||||
"foo" |
|
"foo" |
|
||||||
"bar" -> 2
|
"bar" -> 2
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -458,6 +458,27 @@ fn to_expr_report<'a>(
|
|||||||
*col,
|
*col,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
EExpr::BadExprEnd(row, col) => {
|
||||||
|
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
|
||||||
|
let region = Region::from_row_col(*row, *col);
|
||||||
|
|
||||||
|
let doc = alloc.stack(vec![
|
||||||
|
alloc.reflow(r"I got stuck here:"),
|
||||||
|
alloc.region_with_subregion(surroundings, region),
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow("Whatever I am running into is confusing me al lot! "),
|
||||||
|
alloc.reflow("Normally I can give fairly specific hints, "),
|
||||||
|
alloc.reflow("but something is really tripping me up this time."),
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Report {
|
||||||
|
filename,
|
||||||
|
doc,
|
||||||
|
title: "SYNTAX PROBLEM".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EExpr::Colon(row, col) => {
|
EExpr::Colon(row, col) => {
|
||||||
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
|
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
|
||||||
let region = Region::from_row_col(*row, *col);
|
let region = Region::from_row_col(*row, *col);
|
||||||
@ -982,7 +1003,7 @@ fn to_list_report<'a>(
|
|||||||
),
|
),
|
||||||
|
|
||||||
List::Open(row, col) | List::End(row, col) => {
|
List::Open(row, col) | List::End(row, col) => {
|
||||||
match dbg!(what_is_next(alloc.src_lines, row, col)) {
|
match what_is_next(alloc.src_lines, row, col) {
|
||||||
Next::Other(Some(',')) => {
|
Next::Other(Some(',')) => {
|
||||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||||
let region = Region::from_row_col(row, col);
|
let region = Region::from_row_col(row, col);
|
||||||
@ -1388,24 +1409,56 @@ fn to_unfinished_when_report<'a>(
|
|||||||
start_col: Col,
|
start_col: Col,
|
||||||
message: RocDocBuilder<'a>,
|
message: RocDocBuilder<'a>,
|
||||||
) -> Report<'a> {
|
) -> Report<'a> {
|
||||||
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
match what_is_next(alloc.src_lines, row, col) {
|
||||||
let region = Region::from_row_col(row, col);
|
Next::Token("->") => {
|
||||||
|
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![
|
let doc = alloc.stack(vec![
|
||||||
alloc.concat(vec![
|
alloc.concat(vec![
|
||||||
alloc.reflow(r"I was partway through parsing a "),
|
alloc.reflow(r"I am parsing a "),
|
||||||
alloc.keyword("when"),
|
alloc.keyword("when"),
|
||||||
alloc.reflow(r" expression, but I got stuck here:"),
|
alloc.reflow(r" expression right now, but this arrow is confusing me:"),
|
||||||
]),
|
]),
|
||||||
alloc.region_with_subregion(surroundings, region),
|
alloc.region_with_subregion(surroundings, region),
|
||||||
message,
|
alloc.concat(vec![
|
||||||
note_for_when_error(alloc),
|
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 {
|
Report {
|
||||||
filename,
|
filename,
|
||||||
doc,
|
doc,
|
||||||
title: "UNFINISHED WHEN".to_string(),
|
title: "UNEXPECTED ARROW".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
|
||||||
|
let region = Region::from_row_col(row, col);
|
||||||
|
|
||||||
|
let doc = alloc.stack(vec![
|
||||||
|
alloc.concat(vec![
|
||||||
|
alloc.reflow(r"I was partway through parsing a "),
|
||||||
|
alloc.keyword("when"),
|
||||||
|
alloc.reflow(r" expression, but I got stuck here:"),
|
||||||
|
]),
|
||||||
|
alloc.region_with_subregion(surroundings, region),
|
||||||
|
message,
|
||||||
|
note_for_when_error(alloc),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Report {
|
||||||
|
filename,
|
||||||
|
doc,
|
||||||
|
title: "UNFINISHED WHEN".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4964,7 +4964,6 @@ mod test_reporting {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_or_pattern() {
|
fn empty_or_pattern() {
|
||||||
// this should get better with time
|
|
||||||
report_problem_as(
|
report_problem_as(
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
@ -4978,29 +4977,16 @@ mod test_reporting {
|
|||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
── MISSING EXPRESSION ──────────────────────────────────────────────────────────
|
── UNFINISHED PATTERN ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
I am partway through parsing a definition, but I got stuck here:
|
I just started parsing a pattern, but I got stuck here:
|
||||||
|
|
||||||
1│ when Just 4 is
|
|
||||||
2│ Just 4 | ->
|
2│ Just 4 | ->
|
||||||
^
|
^
|
||||||
|
|
||||||
I was expecting to see an expression like 42 or "hello".
|
Note: I may be confused by indentation
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// ── UNFINISHED PATTERN ──────────────────────────────────────────────────────────
|
|
||||||
//
|
|
||||||
// I just started parsing a pattern, but I got stuck here:
|
|
||||||
//
|
|
||||||
// 2│ Just 4 | ->
|
|
||||||
// ^
|
|
||||||
//
|
|
||||||
// Note: I may be confused by indentation
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5132,29 +5118,120 @@ mod test_reporting {
|
|||||||
r#"
|
r#"
|
||||||
when 4 is
|
when 4 is
|
||||||
5 -> 2
|
5 -> 2
|
||||||
_ -> 2
|
2 -> 2
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── SYNTAX PROBLEM ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
I got stuck here:
|
||||||
|
|
||||||
|
1│ when 4 is
|
||||||
|
2│ 5 -> 2
|
||||||
|
^
|
||||||
|
|
||||||
|
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
|
||||||
|
time.
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
// TODO this formerly gave
|
||||||
|
//
|
||||||
|
// ── UNFINISHED WHEN ─────────────────────────────────────────────────────────────
|
||||||
|
//
|
||||||
|
// I was partway through parsing a `when` expression, but I got stuck here:
|
||||||
|
//
|
||||||
|
// 3│ _ -> 2
|
||||||
|
// ^
|
||||||
|
//
|
||||||
|
// I suspect this is a pattern that is not indented enough? (by 2 spaces)
|
||||||
|
//
|
||||||
|
// but that requires parsing the next pattern blindly, irrespective of indentation. Can
|
||||||
|
// we find an efficient solution that doesn't require parsing an extra pattern for
|
||||||
|
// every `when`, i.e. we want a good error message for the test case above, but for
|
||||||
|
// a valid `when`, we don't want to do extra work, e.g. here
|
||||||
|
//
|
||||||
|
// x
|
||||||
|
// when x is
|
||||||
|
// n -> n
|
||||||
|
//
|
||||||
|
// 4
|
||||||
|
//
|
||||||
|
// We don't want to parse the `4` and say it's an outdented pattern!
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_over_indented_underscore() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when 4 is
|
||||||
|
5 -> 2
|
||||||
|
_ -> 2
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
── UNFINISHED WHEN ─────────────────────────────────────────────────────────────
|
── UNFINISHED WHEN ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
I was partway through parsing a `when` expression, but I got stuck here:
|
I was partway through parsing a `when` expression, but I got stuck here:
|
||||||
|
|
||||||
3│ _ -> 2
|
3│ _ -> 2
|
||||||
^
|
^
|
||||||
|
|
||||||
I suspect this is a pattern that is not indented enough? (by 2 spaces)
|
I was expecting to see a pattern next
|
||||||
|
|
||||||
Note: Here is an example of a valid `when` expression for reference.
|
Note: Here is an example of a valid `when` expression for reference.
|
||||||
|
|
||||||
when List.first plants is
|
when List.first plants is
|
||||||
Ok n ->
|
Ok n ->
|
||||||
n
|
n
|
||||||
|
|
||||||
Err _ ->
|
Err _ ->
|
||||||
200
|
200
|
||||||
|
|
||||||
|
Notice the indentation. All patterns are aligned, and each branch is
|
||||||
|
indented a bit more than the corresponding pattern. That is important!
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn when_over_indented_int() {
|
||||||
|
report_problem_as(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when 4 is
|
||||||
|
5 -> Num.neg
|
||||||
|
2 -> 2
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
── UNEXPECTED ARROW ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
I am parsing a `when` expression right now, but this arrow is confusing
|
||||||
|
me:
|
||||||
|
|
||||||
|
3│ 2 -> 2
|
||||||
|
^^
|
||||||
|
|
||||||
|
It makes sense to see arrows around here, so I suspect it is something
|
||||||
|
earlier.Maybe this pattern is indented a bit farther from the previous
|
||||||
|
patterns?
|
||||||
|
|
||||||
|
Note: Here is an example of a valid `when` expression for reference.
|
||||||
|
|
||||||
|
when List.first plants is
|
||||||
|
Ok n ->
|
||||||
|
n
|
||||||
|
|
||||||
|
Err _ ->
|
||||||
|
200
|
||||||
|
|
||||||
Notice the indentation. All patterns are aligned, and each branch is
|
Notice the indentation. All patterns are aligned, and each branch is
|
||||||
indented a bit more than the corresponding pattern. That is important!
|
indented a bit more than the corresponding pattern. That is important!
|
||||||
"#
|
"#
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use roc_docs::{documentation_to_template_data, files_to_documentations, ModuleEntry};
|
use roc_docs::{documentation_to_template_data, files_to_documentations, ModuleEntry};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_docs {
|
mod test_docs {
|
||||||
|
Loading…
Reference in New Issue
Block a user