mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 16:44:33 +00:00
Merge pull request #1077 from rtfeldman/expression-move
New parser for defs in expressions
This commit is contained in:
commit
d7fc35d9a1
9 changed files with 714 additions and 76 deletions
|
@ -704,21 +704,24 @@ mod test_can {
|
|||
_ -> g (x - 1)
|
||||
|
||||
# use parens to force the ordering!
|
||||
(h = \x ->
|
||||
when x is
|
||||
0 -> 0
|
||||
_ -> g (x - 1)
|
||||
(
|
||||
h = \x ->
|
||||
when x is
|
||||
0 -> 0
|
||||
_ -> g (x - 1)
|
||||
|
||||
(p = \x ->
|
||||
when x is
|
||||
0 -> 0
|
||||
1 -> g (x - 1)
|
||||
_ -> p (x - 1)
|
||||
(
|
||||
p = \x ->
|
||||
when x is
|
||||
0 -> 0
|
||||
1 -> g (x - 1)
|
||||
_ -> p (x - 1)
|
||||
|
||||
|
||||
# variables must be (indirectly) referenced in the body for analysis to work
|
||||
{ x: p, y: h }
|
||||
))
|
||||
# variables must be (indirectly) referenced in the body for analysis to work
|
||||
{ x: p, y: h }
|
||||
)
|
||||
)
|
||||
"#
|
||||
);
|
||||
let arena = Bump::new();
|
||||
|
|
|
@ -296,28 +296,13 @@ fn expr_in_parens_then_arguments<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn in_parens_region_fix<'a>(min_indent: u16) -> impl Parser<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||
// we get the region of the expression inside the parens, but current tests want us to
|
||||
// include the parentheses in the region
|
||||
// map!(
|
||||
// loc!(loc_expr_in_parens_etc_help(min_indent)),
|
||||
// |loc_loc_expr: Located<Located<Expr<'a>>>| {
|
||||
// let value = loc_loc_expr.value.value;
|
||||
// let region = loc_loc_expr.region;
|
||||
//
|
||||
// Located::at(region, value)
|
||||
// }
|
||||
// )
|
||||
loc_expr_in_parens_etc_help(min_indent)
|
||||
}
|
||||
|
||||
fn parse_loc_term<'a>(
|
||||
min_indent: u16,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||
one_of!(
|
||||
in_parens_region_fix(min_indent),
|
||||
loc_expr_in_parens_etc_help(min_indent),
|
||||
loc!(specialize(EExpr::Str, string_literal_help())),
|
||||
loc!(specialize(EExpr::Number, number_literal_help())),
|
||||
loc!(specialize(EExpr::Lambda, closure_help(min_indent))),
|
||||
|
@ -334,7 +319,7 @@ fn parse_loc_term_better<'a>(
|
|||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||
one_of!(
|
||||
in_parens_region_fix(min_indent),
|
||||
loc_expr_in_parens_etc_help(min_indent),
|
||||
loc!(specialize(EExpr::Str, string_literal_help())),
|
||||
loc!(specialize(EExpr::Number, positive_number_literal_help())),
|
||||
loc!(specialize(EExpr::Lambda, closure_help(min_indent))),
|
||||
|
@ -366,6 +351,8 @@ 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| parse_loc_term(
|
||||
min_indent, a, s
|
||||
|
@ -379,7 +366,7 @@ fn loc_possibly_negative_or_negated_term<'a>(
|
|||
)),
|
||||
|arena, state| {
|
||||
// TODO use parse_loc_term_better
|
||||
parse_loc_term(min_indent, arena, state)
|
||||
parse_loc_term_better(min_indent, arena, state)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -435,6 +422,7 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> {
|
|||
|
||||
fn parse_expr_start<'a>(
|
||||
min_indent: u16,
|
||||
start: Position,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||
|
@ -442,7 +430,7 @@ fn parse_expr_start<'a>(
|
|||
loc!(specialize(EExpr::If, if_expr_help(min_indent))),
|
||||
loc!(specialize(EExpr::When, when::expr_help(min_indent))),
|
||||
loc!(specialize(EExpr::Lambda, closure_help(min_indent))),
|
||||
loc!(|a, s| parse_expr_operator_chain(min_indent, a, s)),
|
||||
loc!(|a, s| parse_expr_operator_chain(min_indent, start, a, s)),
|
||||
fail_expr_start_e()
|
||||
]
|
||||
.parse(arena, state)
|
||||
|
@ -450,6 +438,7 @@ fn parse_expr_start<'a>(
|
|||
|
||||
fn parse_expr_operator_chain<'a>(
|
||||
min_indent: u16,
|
||||
start: Position,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
|
@ -470,7 +459,7 @@ fn parse_expr_operator_chain<'a>(
|
|||
end,
|
||||
};
|
||||
|
||||
parse_expr_end(min_indent, expr_state, arena, state)
|
||||
parse_expr_end(min_indent, start, expr_state, arena, state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -608,8 +597,317 @@ fn numeric_negate_expression<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn append_body_definition<'a>(
|
||||
arena: &'a Bump,
|
||||
defs: &mut Vec<'a, &'a Located<Def<'a>>>,
|
||||
spaces: &'a [CommentOrNewline<'a>],
|
||||
loc_pattern: Located<Pattern<'a>>,
|
||||
loc_def_body: Located<Expr<'a>>,
|
||||
) {
|
||||
let region = Region::span_across(&loc_pattern.region, &loc_def_body.region);
|
||||
|
||||
if spaces.len() <= 1 {
|
||||
let last = defs.pop();
|
||||
match last {
|
||||
Some(Located {
|
||||
value: Def::Annotation(ann_pattern, ann_type),
|
||||
..
|
||||
}) => {
|
||||
return append_body_definition_help(
|
||||
arena,
|
||||
defs,
|
||||
region,
|
||||
&[],
|
||||
spaces,
|
||||
loc_pattern,
|
||||
loc_def_body,
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
);
|
||||
}
|
||||
Some(Located {
|
||||
value: Def::SpaceBefore(Def::Annotation(ann_pattern, ann_type), before_ann_spaces),
|
||||
..
|
||||
}) => {
|
||||
return append_body_definition_help(
|
||||
arena,
|
||||
defs,
|
||||
region,
|
||||
before_ann_spaces,
|
||||
spaces,
|
||||
loc_pattern,
|
||||
loc_def_body,
|
||||
ann_pattern,
|
||||
ann_type,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
defs.extend(last);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the previous and current def can't be joined up
|
||||
let mut loc_def = Located::at(
|
||||
region,
|
||||
Def::Body(arena.alloc(loc_pattern), &*arena.alloc(loc_def_body)),
|
||||
);
|
||||
|
||||
if !spaces.is_empty() {
|
||||
loc_def = arena
|
||||
.alloc(loc_def.value)
|
||||
.with_spaces_before(spaces, loc_def.region);
|
||||
}
|
||||
|
||||
defs.push(arena.alloc(loc_def));
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn append_body_definition_help<'a>(
|
||||
arena: &'a Bump,
|
||||
defs: &mut Vec<'a, &'a Located<Def<'a>>>,
|
||||
region: Region,
|
||||
before_ann_spaces: &'a [CommentOrNewline<'a>],
|
||||
before_body_spaces: &'a [CommentOrNewline<'a>],
|
||||
loc_pattern_body: Located<Pattern<'a>>,
|
||||
loc_def_body: Located<Expr<'a>>,
|
||||
loc_pattern_ann: &'a Located<Pattern<'a>>,
|
||||
loc_ann: &'a Located<TypeAnnotation<'a>>,
|
||||
) {
|
||||
let comment = match before_body_spaces.get(0) {
|
||||
Some(CommentOrNewline::LineComment(s)) => Some(*s),
|
||||
Some(CommentOrNewline::DocComment(s)) => Some(*s),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let mut loc_def = Located::at(
|
||||
region,
|
||||
Def::AnnotatedBody {
|
||||
ann_pattern: loc_pattern_ann,
|
||||
ann_type: loc_ann,
|
||||
comment,
|
||||
body_pattern: arena.alloc(loc_pattern_body),
|
||||
body_expr: &*arena.alloc(loc_def_body),
|
||||
},
|
||||
);
|
||||
|
||||
if !before_ann_spaces.is_empty() {
|
||||
loc_def = arena
|
||||
.alloc(loc_def.value)
|
||||
.with_spaces_before(before_ann_spaces, loc_def.region);
|
||||
}
|
||||
|
||||
defs.push(arena.alloc(loc_def));
|
||||
}
|
||||
|
||||
fn append_annotation_definition<'a>(
|
||||
arena: &'a Bump,
|
||||
defs: &mut Vec<'a, &'a Located<Def<'a>>>,
|
||||
spaces: &'a [CommentOrNewline<'a>],
|
||||
loc_pattern: Located<Pattern<'a>>,
|
||||
loc_ann: Located<TypeAnnotation<'a>>,
|
||||
) {
|
||||
let region = Region::span_across(&loc_pattern.region, &loc_ann.region);
|
||||
|
||||
// the previous and current def can't be joined up
|
||||
match &loc_pattern.value {
|
||||
Pattern::Apply(
|
||||
Located {
|
||||
value: Pattern::GlobalTag(name),
|
||||
..
|
||||
},
|
||||
alias_arguments,
|
||||
) => append_alias_definition(
|
||||
arena,
|
||||
defs,
|
||||
region,
|
||||
spaces,
|
||||
Located::at(loc_pattern.region, name),
|
||||
alias_arguments,
|
||||
loc_ann,
|
||||
),
|
||||
Pattern::GlobalTag(name) => append_alias_definition(
|
||||
arena,
|
||||
defs,
|
||||
region,
|
||||
spaces,
|
||||
Located::at(loc_pattern.region, name),
|
||||
&[],
|
||||
loc_ann,
|
||||
),
|
||||
_ => {
|
||||
let mut loc_def = Located::at(region, Def::Annotation(loc_pattern, loc_ann));
|
||||
if !spaces.is_empty() {
|
||||
loc_def = arena
|
||||
.alloc(loc_def.value)
|
||||
.with_spaces_before(spaces, loc_def.region);
|
||||
}
|
||||
|
||||
defs.push(arena.alloc(loc_def));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn append_alias_definition<'a>(
|
||||
arena: &'a Bump,
|
||||
defs: &mut Vec<'a, &'a Located<Def<'a>>>,
|
||||
region: Region,
|
||||
spaces: &'a [CommentOrNewline<'a>],
|
||||
name: Located<&'a str>,
|
||||
pattern_arguments: &'a [Located<Pattern<'a>>],
|
||||
loc_ann: Located<TypeAnnotation<'a>>,
|
||||
) {
|
||||
let def = Def::Alias {
|
||||
name,
|
||||
vars: pattern_arguments,
|
||||
ann: loc_ann,
|
||||
};
|
||||
let mut loc_def = Located::at(region, def);
|
||||
|
||||
if !spaces.is_empty() {
|
||||
loc_def = arena
|
||||
.alloc(loc_def.value)
|
||||
.with_spaces_before(spaces, loc_def.region);
|
||||
}
|
||||
|
||||
defs.push(arena.alloc(loc_def));
|
||||
}
|
||||
|
||||
struct DefState<'a> {
|
||||
defs: Vec<'a, &'a Located<Def<'a>>>,
|
||||
spaces_after: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
fn parse_defs_end<'a>(
|
||||
start: Position,
|
||||
mut def_state: DefState<'a>,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
let min_indent = start.col;
|
||||
let initial = state;
|
||||
|
||||
let state = match space0_e(min_indent, EExpr::Space, EExpr::IndentStart).parse(arena, state) {
|
||||
Err((MadeProgress, _, s)) => {
|
||||
return Err((
|
||||
MadeProgress,
|
||||
EExpr::DefMissingFinalExpr(s.line, s.column),
|
||||
s,
|
||||
));
|
||||
}
|
||||
Ok((_, spaces, state)) => {
|
||||
def_state.spaces_after = spaces;
|
||||
state
|
||||
}
|
||||
Err((NoProgress, _, state)) => state,
|
||||
};
|
||||
|
||||
match space0_after_e(
|
||||
crate::pattern::loc_pattern_help(min_indent),
|
||||
min_indent,
|
||||
EPattern::Space,
|
||||
EPattern::IndentEnd,
|
||||
)
|
||||
.parse(arena, state)
|
||||
{
|
||||
Err((_, _, _)) => {
|
||||
// a hacky way to get expression-based error messages. TODO fix this
|
||||
let state = initial;
|
||||
|
||||
let parse_final_expr = space0_before_e(
|
||||
move |a, s| parse_expr_start(min_indent, start, a, s),
|
||||
min_indent,
|
||||
EExpr::Space,
|
||||
EExpr::IndentStart,
|
||||
);
|
||||
|
||||
match parse_final_expr.parse(arena, state) {
|
||||
Err((_, fail, state)) => {
|
||||
return Err((
|
||||
MadeProgress,
|
||||
EExpr::DefMissingFinalExpr2(arena.alloc(fail), state.line, state.column),
|
||||
state,
|
||||
));
|
||||
}
|
||||
Ok((_, loc_ret, state)) => {
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
Expr::Defs(def_state.defs.into_bump_slice(), arena.alloc(loc_ret)),
|
||||
state,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((_, loc_pattern, state)) => {
|
||||
match operator().parse(arena, state) {
|
||||
Ok((_, BinOp::Assignment, state)) => {
|
||||
let parse_def_expr = space0_before_e(
|
||||
move |a, s| parse_expr_help(min_indent + 1, a, s),
|
||||
min_indent,
|
||||
EExpr::Space,
|
||||
EExpr::IndentEnd,
|
||||
);
|
||||
|
||||
let (_, loc_def_expr, state) = parse_def_expr.parse(arena, state)?;
|
||||
|
||||
append_body_definition(
|
||||
arena,
|
||||
&mut def_state.defs,
|
||||
def_state.spaces_after,
|
||||
loc_pattern,
|
||||
loc_def_expr,
|
||||
);
|
||||
|
||||
parse_defs_end(start, def_state, arena, state)
|
||||
}
|
||||
Ok((_, BinOp::HasType, state)) => {
|
||||
let (_, ann_type, state) = specialize(
|
||||
EExpr::Type,
|
||||
space0_before_e(
|
||||
type_annotation::located_help(min_indent + 1),
|
||||
min_indent + 1,
|
||||
Type::TSpace,
|
||||
Type::TIndentStart,
|
||||
),
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
append_annotation_definition(
|
||||
arena,
|
||||
&mut def_state.defs,
|
||||
def_state.spaces_after,
|
||||
loc_pattern,
|
||||
ann_type,
|
||||
);
|
||||
|
||||
parse_defs_end(start, def_state, arena, state)
|
||||
}
|
||||
_ => {
|
||||
// this is no def, because there is no `=`, `:` or `<-`; parse as an expr
|
||||
let state = initial;
|
||||
let parse_final_expr = space0_before_e(
|
||||
move |a, s| parse_expr_help(min_indent, a, s),
|
||||
min_indent,
|
||||
EExpr::Space,
|
||||
EExpr::IndentEnd,
|
||||
);
|
||||
|
||||
let (_, loc_ret, state) = parse_final_expr.parse(arena, state)?;
|
||||
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
Expr::Defs(def_state.defs.into_bump_slice(), arena.alloc(loc_ret)),
|
||||
state,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_expr_operator<'a>(
|
||||
min_indent: u16,
|
||||
start: Position,
|
||||
mut expr_state: ExprState<'a>,
|
||||
loc_op: Located<BinOp>,
|
||||
arena: &'a Bump,
|
||||
|
@ -651,7 +949,7 @@ fn parse_expr_operator<'a>(
|
|||
expr_state.spaces_after = spaces;
|
||||
expr_state.end = new_end;
|
||||
|
||||
parse_expr_end(min_indent, expr_state, arena, state)
|
||||
parse_expr_end(min_indent, start, expr_state, arena, state)
|
||||
}
|
||||
BinOp::Assignment => {
|
||||
if !expr_state.operators.is_empty() {
|
||||
|
@ -674,7 +972,138 @@ fn parse_expr_operator<'a>(
|
|||
|
||||
Err((MadeProgress, fail, state))
|
||||
} else {
|
||||
todo!()
|
||||
expr_state.consume_spaces(arena);
|
||||
let expr_region = expr_state.expr.region;
|
||||
|
||||
let indented_more = start.col + 1;
|
||||
|
||||
let call = to_call(
|
||||
arena,
|
||||
expr_state.arguments,
|
||||
expr_state.expr,
|
||||
spaces_after_operator,
|
||||
);
|
||||
|
||||
let (loc_def, state) = {
|
||||
match expr_to_pattern_help(arena, &call.value) {
|
||||
Ok(good) => {
|
||||
let (_, mut ann_type, state) =
|
||||
parse_expr_help(indented_more, arena, state)?;
|
||||
|
||||
// put the spaces from after the operator in front of the call
|
||||
if !spaces_after_operator.is_empty() {
|
||||
ann_type = arena
|
||||
.alloc(ann_type.value)
|
||||
.with_spaces_before(spaces_after_operator, ann_type.region);
|
||||
}
|
||||
|
||||
let alias_region = Region::span_across(&call.region, &ann_type.region);
|
||||
|
||||
let alias = Def::Body(
|
||||
arena.alloc(Located::at(expr_region, good)),
|
||||
arena.alloc(ann_type),
|
||||
);
|
||||
|
||||
(&*arena.alloc(Located::at(alias_region, alias)), state)
|
||||
}
|
||||
Err(_) => {
|
||||
// this `=` likely occured inline; treat it as an invalid operator
|
||||
let fail = EExpr::BadOperator(
|
||||
arena.alloc([b'=']),
|
||||
loc_op.region.start_line,
|
||||
loc_op.region.start_col,
|
||||
);
|
||||
|
||||
return Err((MadeProgress, fail, state));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let def_state = DefState {
|
||||
defs: bumpalo::vec![in arena; loc_def],
|
||||
spaces_after: &[],
|
||||
};
|
||||
|
||||
parse_defs_end(start, def_state, arena, state)
|
||||
}
|
||||
}
|
||||
BinOp::Backpassing => {
|
||||
if !expr_state.operators.is_empty() {
|
||||
// this `=` likely occured inline; treat it as an invalid operator
|
||||
let fail = EExpr::BadOperator(
|
||||
arena.alloc([b'<', b'-']),
|
||||
loc_op.region.start_line,
|
||||
loc_op.region.start_col,
|
||||
);
|
||||
|
||||
Err((MadeProgress, fail, state))
|
||||
} else if !expr_state.arguments.is_empty() {
|
||||
let region = Region::across_all(expr_state.arguments.iter().map(|v| &v.region));
|
||||
|
||||
let fail = EExpr::ElmStyleFunction(
|
||||
region,
|
||||
loc_op.region.start_line,
|
||||
loc_op.region.start_col,
|
||||
);
|
||||
|
||||
Err((MadeProgress, fail, state))
|
||||
} else {
|
||||
expr_state.consume_spaces(arena);
|
||||
let expr_region = expr_state.expr.region;
|
||||
|
||||
let indented_more = start.col + 1;
|
||||
|
||||
let call = to_call(
|
||||
arena,
|
||||
expr_state.arguments,
|
||||
expr_state.expr,
|
||||
spaces_after_operator,
|
||||
);
|
||||
|
||||
let (loc_pattern, loc_body, state) = {
|
||||
match expr_to_pattern_help(arena, &call.value) {
|
||||
Ok(good) => {
|
||||
let (_, mut ann_type, state) =
|
||||
parse_expr_help(indented_more, arena, state)?;
|
||||
|
||||
// put the spaces from after the operator in front of the call
|
||||
if !spaces_after_operator.is_empty() {
|
||||
ann_type = arena
|
||||
.alloc(ann_type.value)
|
||||
.with_spaces_before(spaces_after_operator, ann_type.region);
|
||||
}
|
||||
|
||||
(Located::at(expr_region, good), ann_type, state)
|
||||
}
|
||||
Err(_) => {
|
||||
// this `=` likely occured inline; treat it as an invalid operator
|
||||
let fail = EExpr::BadOperator(
|
||||
arena.alloc([b'=']),
|
||||
loc_op.region.start_line,
|
||||
loc_op.region.start_col,
|
||||
);
|
||||
|
||||
return Err((MadeProgress, fail, state));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let parse_cont = space0_before_e(
|
||||
move |a, s| parse_expr_help(min_indent, a, s),
|
||||
min_indent,
|
||||
EExpr::Space,
|
||||
EExpr::IndentEnd,
|
||||
);
|
||||
|
||||
let (_, loc_cont, state) = parse_cont.parse(arena, state)?;
|
||||
|
||||
let ret = Expr::Backpassing(
|
||||
arena.alloc([loc_pattern]),
|
||||
arena.alloc(loc_body),
|
||||
arena.alloc(loc_cont),
|
||||
);
|
||||
|
||||
Ok((MadeProgress, ret, state))
|
||||
}
|
||||
}
|
||||
BinOp::HasType => {
|
||||
|
@ -682,7 +1111,7 @@ fn parse_expr_operator<'a>(
|
|||
|
||||
let expr_region = expr_state.expr.region;
|
||||
|
||||
let indented_more = min_indent + 1;
|
||||
let indented_more = start.col + 1;
|
||||
|
||||
expr_state.consume_spaces(arena);
|
||||
|
||||
|
@ -735,7 +1164,7 @@ fn parse_expr_operator<'a>(
|
|||
|
||||
match expr_to_pattern_help(arena, &call.value) {
|
||||
Ok(good) => {
|
||||
let (_, ann_type, state) = specialize(
|
||||
let (_, mut ann_type, state) = specialize(
|
||||
EExpr::Type,
|
||||
space0_before_e(
|
||||
type_annotation::located_help(indented_more),
|
||||
|
@ -746,6 +1175,13 @@ fn parse_expr_operator<'a>(
|
|||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
// put the spaces from after the operator in front of the call
|
||||
if !spaces_after_operator.is_empty() {
|
||||
ann_type = arena
|
||||
.alloc(ann_type.value)
|
||||
.with_spaces_before(spaces_after_operator, ann_type.region);
|
||||
}
|
||||
|
||||
let alias_region = Region::span_across(&call.region, &ann_type.region);
|
||||
|
||||
let alias = Def::Annotation(Located::at(expr_region, good), ann_type);
|
||||
|
@ -766,20 +1202,12 @@ fn parse_expr_operator<'a>(
|
|||
}
|
||||
};
|
||||
|
||||
let parse_final_expr = space0_before_e(
|
||||
move |a, s| parse_expr_help(min_indent, a, s),
|
||||
min_indent,
|
||||
EExpr::Space,
|
||||
EExpr::IndentEnd,
|
||||
);
|
||||
let def_state = DefState {
|
||||
defs: bumpalo::vec![in arena; loc_def],
|
||||
spaces_after: &[],
|
||||
};
|
||||
|
||||
let (_, loc_ret, state) = parse_final_expr.parse(arena, state)?;
|
||||
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
Expr::Defs(&*arena.alloc([loc_def]), arena.alloc(loc_ret)),
|
||||
state,
|
||||
));
|
||||
parse_defs_end(start, def_state, arena, state)
|
||||
}
|
||||
_ => match loc_possibly_negative_or_negated_term(min_indent).parse(arena, state) {
|
||||
Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)),
|
||||
|
@ -818,7 +1246,8 @@ fn parse_expr_operator<'a>(
|
|||
expr_state.end = new_end;
|
||||
expr_state.spaces_after = spaces;
|
||||
|
||||
parse_expr_end(min_indent, expr_state, arena, state)
|
||||
// TODO new start?
|
||||
parse_expr_end(min_indent, start, expr_state, arena, state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -831,6 +1260,7 @@ fn parse_expr_operator<'a>(
|
|||
|
||||
fn parse_expr_end<'a>(
|
||||
min_indent: u16,
|
||||
start: Position,
|
||||
mut expr_state: ExprState<'a>,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
|
@ -868,7 +1298,7 @@ fn parse_expr_end<'a>(
|
|||
expr_state.end = new_end;
|
||||
expr_state.spaces_after = new_spaces;
|
||||
|
||||
parse_expr_end(min_indent, expr_state, arena, state)
|
||||
parse_expr_end(min_indent, start, expr_state, arena, state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -880,7 +1310,7 @@ fn parse_expr_end<'a>(
|
|||
Ok((_, loc_op, state)) => {
|
||||
expr_state.consume_spaces(arena);
|
||||
expr_state.initial = before_op;
|
||||
parse_expr_operator(min_indent, expr_state, loc_op, arena, state)
|
||||
parse_expr_operator(min_indent, start, expr_state, loc_op, arena, state)
|
||||
}
|
||||
Err((NoProgress, _, _)) => {
|
||||
// roll back space parsing
|
||||
|
@ -922,7 +1352,8 @@ fn parse_expr_help<'a>(
|
|||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||
parse_expr_start(min_indent, arena, state)
|
||||
let start = state.get_position();
|
||||
parse_expr_start(min_indent, start, arena, state)
|
||||
}
|
||||
|
||||
/// If the given Expr would parse the same way as a valid Pattern, convert it.
|
||||
|
|
|
@ -18,7 +18,7 @@ pub enum Ident<'a> {
|
|||
module_name: &'a str,
|
||||
parts: &'a [&'a str],
|
||||
},
|
||||
/// .foo
|
||||
/// .foo { foo: 42 }
|
||||
AccessorFunction(&'a str),
|
||||
/// .Foo or foo. or something like foo.Bar
|
||||
Malformed(&'a str, BadIdent),
|
||||
|
|
|
@ -254,7 +254,6 @@ fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
|
|||
if state.has_reached_end() {
|
||||
Ok((NoProgress, (), state))
|
||||
} else {
|
||||
dbg!(state);
|
||||
Err((
|
||||
NoProgress,
|
||||
SyntaxError::NotEndOfFile(state.line, state.column),
|
||||
|
|
|
@ -388,6 +388,7 @@ pub enum EExpr<'a> {
|
|||
BadOperator(&'a [u8], Row, Col),
|
||||
|
||||
DefMissingFinalExpr(Row, Col),
|
||||
DefMissingFinalExpr2(&'a EExpr<'a>, Row, Col),
|
||||
Type(Type<'a>, Row, Col),
|
||||
Pattern(&'a EPattern<'a>, Row, Col),
|
||||
IndentDefBody(Row, Col),
|
||||
|
|
|
@ -1770,6 +1770,120 @@ mod test_parse {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_backpassing() {
|
||||
let arena = Bump::new();
|
||||
let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
||||
let identifier_x = Located::new(1, 1, 0, 1, Identifier("x"));
|
||||
let identifier_y = Located::new(1, 1, 7, 8, Identifier("y"));
|
||||
|
||||
let var_x = Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
};
|
||||
|
||||
let var_y = Var {
|
||||
module_name: "",
|
||||
ident: "y",
|
||||
};
|
||||
let loc_var_y = arena.alloc(Located::new(1, 1, 12, 13, var_y));
|
||||
|
||||
let closure = ParensAround(arena.alloc(Closure(arena.alloc([identifier_y]), loc_var_y)));
|
||||
let loc_closure = Located::new(1, 1, 5, 14, closure);
|
||||
|
||||
let ret = Expr::SpaceBefore(arena.alloc(var_x), newlines.into_bump_slice());
|
||||
let loc_ret = Located::new(3, 3, 0, 1, ret);
|
||||
|
||||
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
||||
let expected = Expr::SpaceBefore(
|
||||
arena.alloc(Expr::Backpassing(
|
||||
arena.alloc([identifier_x]),
|
||||
arena.alloc(loc_closure),
|
||||
arena.alloc(loc_ret),
|
||||
)),
|
||||
reset_indentation.into_bump_slice(),
|
||||
);
|
||||
|
||||
assert_parses_to(
|
||||
indoc!(
|
||||
r#"# leading comment
|
||||
x <- (\y -> y)
|
||||
|
||||
x
|
||||
"#
|
||||
),
|
||||
expected,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_backpassing() {
|
||||
let arena = Bump::new();
|
||||
|
||||
let inner_backpassing = {
|
||||
let identifier_z = Located::new(2, 2, 0, 1, Identifier("z"));
|
||||
|
||||
let empty_record = Record {
|
||||
update: None,
|
||||
fields: &[],
|
||||
final_comments: &[],
|
||||
};
|
||||
let loc_empty_record = Located::new(2, 2, 5, 7, empty_record);
|
||||
|
||||
let var_x = Var {
|
||||
module_name: "",
|
||||
ident: "x",
|
||||
};
|
||||
|
||||
let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
||||
let ret = Expr::SpaceBefore(arena.alloc(var_x), newlines.into_bump_slice());
|
||||
let loc_ret = Located::new(4, 4, 0, 1, ret);
|
||||
|
||||
Expr::SpaceBefore(
|
||||
arena.alloc(Expr::Backpassing(
|
||||
arena.alloc([identifier_z]),
|
||||
arena.alloc(loc_empty_record),
|
||||
arena.alloc(loc_ret),
|
||||
)),
|
||||
arena.alloc([Newline]),
|
||||
)
|
||||
};
|
||||
|
||||
let identifier_x = Located::new(1, 1, 0, 1, Identifier("x"));
|
||||
let identifier_y = Located::new(1, 1, 7, 8, Identifier("y"));
|
||||
|
||||
let var_y = Var {
|
||||
module_name: "",
|
||||
ident: "y",
|
||||
};
|
||||
let loc_var_y = arena.alloc(Located::new(1, 1, 12, 13, var_y));
|
||||
|
||||
let closure = ParensAround(arena.alloc(Closure(arena.alloc([identifier_y]), loc_var_y)));
|
||||
let loc_closure = Located::new(1, 1, 5, 14, closure);
|
||||
|
||||
let reset_indentation = bumpalo::vec![in &arena; LineComment(" leading comment")];
|
||||
let expected = Expr::SpaceBefore(
|
||||
arena.alloc(Expr::Backpassing(
|
||||
arena.alloc([identifier_x]),
|
||||
arena.alloc(loc_closure),
|
||||
arena.alloc(Located::new(2, 4, 0, 1, inner_backpassing)),
|
||||
)),
|
||||
reset_indentation.into_bump_slice(),
|
||||
);
|
||||
|
||||
assert_parses_to(
|
||||
indoc!(
|
||||
r#"# leading comment
|
||||
x <- (\y -> y)
|
||||
z <- {}
|
||||
|
||||
x
|
||||
"#
|
||||
),
|
||||
expected,
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn type_signature_def() {
|
||||
// let arena = Bump::new();
|
||||
|
@ -2857,6 +2971,7 @@ mod test_parse {
|
|||
.parse(&arena, State::new(src.as_bytes()))
|
||||
.map(|tuple| tuple.0);
|
||||
|
||||
|
||||
assert!(actual.is_ok());
|
||||
}
|
||||
|
||||
|
|
|
@ -174,9 +174,11 @@ fn to_syntax_report<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
enum Context {
|
||||
InNode(Node, Row, Col, Box<Context>),
|
||||
InDef(Row, Col),
|
||||
InDefFinalExpr(Row, Col),
|
||||
}
|
||||
|
||||
enum Node {
|
||||
|
@ -340,6 +342,35 @@ fn to_expr_report<'a>(
|
|||
}
|
||||
|
||||
EExpr::Start(row, col) | EExpr::IndentStart(row, col) => {
|
||||
let (title, expecting) = match &context {
|
||||
Context::InNode { .. } | Context::InDef { .. } => (
|
||||
"MISSING EXPRESSION",
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("I was expecting to see an expression like "),
|
||||
alloc.parser_suggestion("42"),
|
||||
alloc.reflow(" or "),
|
||||
alloc.parser_suggestion("\"hello\""),
|
||||
alloc.text("."),
|
||||
]),
|
||||
),
|
||||
Context::InDefFinalExpr { .. } => (
|
||||
"MISSING FINAL EXPRESSION",
|
||||
alloc.stack(vec![
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("This definition is missing a final expression."),
|
||||
alloc.reflow(" A nested definition must be followed by"),
|
||||
alloc.reflow(" either another definition, or an expression"),
|
||||
]),
|
||||
alloc.vcat(vec![
|
||||
alloc.text("x = 4").indent(4),
|
||||
alloc.text("y = 2").indent(4),
|
||||
alloc.text(""),
|
||||
alloc.text("x + y").indent(4),
|
||||
]),
|
||||
]),
|
||||
),
|
||||
};
|
||||
|
||||
let (context_row, context_col, a_thing) = match context {
|
||||
Context::InNode(node, r, c, _) => match node {
|
||||
Node::WhenCondition | Node::WhenBranch | Node::WhenIfGuard => (
|
||||
|
@ -366,6 +397,9 @@ fn to_expr_report<'a>(
|
|||
Node::InsideParens => (r, c, alloc.text("some parentheses")),
|
||||
},
|
||||
Context::InDef(r, c) => (r, c, alloc.text("a definition")),
|
||||
Context::InDefFinalExpr(r, c) => {
|
||||
(r, c, alloc.text("a definition's final expression"))
|
||||
}
|
||||
};
|
||||
|
||||
let surroundings = Region::from_rows_cols(context_row, context_col, *row, *col);
|
||||
|
@ -378,19 +412,13 @@ fn to_expr_report<'a>(
|
|||
alloc.reflow(", but I got stuck here:"),
|
||||
]),
|
||||
alloc.region_with_subregion(surroundings, region),
|
||||
alloc.concat(vec![
|
||||
alloc.reflow("I was expecting to see an expression like "),
|
||||
alloc.parser_suggestion("42"),
|
||||
alloc.reflow(" or "),
|
||||
alloc.parser_suggestion("\"hello\""),
|
||||
alloc.text("."),
|
||||
]),
|
||||
expecting,
|
||||
]);
|
||||
|
||||
Report {
|
||||
filename,
|
||||
doc,
|
||||
title: "MISSING EXPRESSION".to_string(),
|
||||
title: title.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -421,6 +449,15 @@ fn to_expr_report<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
EExpr::DefMissingFinalExpr2(expr, row, col) => to_expr_report(
|
||||
alloc,
|
||||
filename,
|
||||
Context::InDefFinalExpr(start_row, start_col),
|
||||
expr,
|
||||
*row,
|
||||
*col,
|
||||
),
|
||||
|
||||
EExpr::Colon(row, col) => {
|
||||
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
|
||||
let region = Region::from_row_col(*row, *col);
|
||||
|
|
|
@ -4680,18 +4680,19 @@ mod test_reporting {
|
|||
indoc!(
|
||||
r#"
|
||||
── MISSING FINAL EXPRESSION ────────────────────────────────────────────────────
|
||||
|
||||
I am partway through parsing a definition, but I got stuck here:
|
||||
|
||||
|
||||
I am partway through parsing a definition's final expression, but I
|
||||
got stuck here:
|
||||
|
||||
1│ f : Foo.foo
|
||||
^
|
||||
|
||||
|
||||
This definition is missing a final expression. A nested definition
|
||||
must be followed by either another definition, or an expression
|
||||
|
||||
|
||||
x = 4
|
||||
y = 2
|
||||
|
||||
|
||||
x + y
|
||||
"#
|
||||
),
|
||||
|
@ -5765,14 +5766,21 @@ mod test_reporting {
|
|||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── MISSING EXPRESSION ──────────────────────────────────────────────────────────
|
||||
|
||||
I am partway through parsing a definition, but I got stuck here:
|
||||
|
||||
── MISSING FINAL EXPRESSION ────────────────────────────────────────────────────
|
||||
|
||||
I am partway through parsing a definition's final expression, but I
|
||||
got stuck here:
|
||||
|
||||
1│ main = 5 -> 3
|
||||
^
|
||||
|
||||
I was expecting to see an expression like 42 or "hello".
|
||||
|
||||
This definition is missing a final expression. A nested definition
|
||||
must be followed by either another definition, or an expression
|
||||
|
||||
x = 4
|
||||
y = 2
|
||||
|
||||
x + y
|
||||
"#
|
||||
),
|
||||
)
|
||||
|
@ -5936,4 +5944,48 @@ mod test_reporting {
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn foobar() {
|
||||
report_problem_as(
|
||||
indoc!(
|
||||
r#"
|
||||
g = \x ->
|
||||
when x is
|
||||
0 -> 0
|
||||
_ -> g (x - 1)
|
||||
|
||||
# use parens to force the ordering!
|
||||
(h = \x ->
|
||||
when x is
|
||||
0 -> 0
|
||||
_ -> g (x - 1)
|
||||
|
||||
(p = \x ->
|
||||
when x is
|
||||
0 -> 0
|
||||
1 -> g (x - 1)
|
||||
_ -> p (x - 1)
|
||||
|
||||
|
||||
# variables must be (indirectly) referenced in the body for analysis to work
|
||||
{ x: p, y: h }
|
||||
))
|
||||
"#
|
||||
),
|
||||
indoc!(
|
||||
r#"
|
||||
── TOO MANY ARGS ───────────────────────────────────────────────────────────────
|
||||
|
||||
This value is not a function, but it was given 2 arguments:
|
||||
|
||||
3│ !foo 1 2
|
||||
^^^^
|
||||
|
||||
Are there any missing commas? Or missing parentheses?
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
2
examples/.gitignore
vendored
2
examples/.gitignore
vendored
|
@ -6,10 +6,10 @@ roc_app.ll
|
|||
roc_app.bc
|
||||
benchmarks/nqueens
|
||||
benchmarks/deriv
|
||||
benchmarks/closure
|
||||
benchmarks/cfold
|
||||
benchmarks/rbtree-insert
|
||||
benchmarks/rbtree-del
|
||||
benchmarks/closure
|
||||
benchmarks/test-astar
|
||||
benchmarks/test-base64
|
||||
effect-example
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue