more backtracking

This commit is contained in:
Folkert 2021-02-02 18:47:58 +01:00
parent d284d3bb72
commit e643d1ea3c
7 changed files with 101 additions and 72 deletions

View file

@ -51,6 +51,9 @@ mod gen_num {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
app "test" provides [ main ] to "./platform"
main =
i : I64 i : I64
i = 64 i = 64
@ -446,24 +449,6 @@ mod gen_num {
-1, -1,
i64 i64
); );
assert_evals_to!(
indoc!(
r#"
limitedNegate = \num ->
if num == 1 then
-1
else if num == -1 then
1
else
num
limitedNegate 1
"#
),
-1,
i64
);
} }
#[test] #[test]

View file

@ -3396,9 +3396,7 @@ fn parse<'a>(arena: &'a Bump, header: ModuleHeader<'a>) -> Result<Msg<'a>, Loadi
), ),
_ => panic!( _ => panic!(
"Parser Error\nmodule: {:?}\nattempting: {:?}\n(line, col): {:?}\n", "Parser Error\nmodule: {:?}\nattempting: {:?}\n(line, col): {:?}\n",
header.module_id, header.module_id, fail.attempting, &state
fail.attempting,
(state.line, state.column)
), ),
} }
} }

View file

@ -174,8 +174,9 @@ fn loc_parse_expr_body_without_operators<'a>(
pub fn unary_op<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> { pub fn unary_op<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
one_of!( one_of!(
map_with_arena!( map_with_arena!(
// must backtrack to distinguish `!x` from `!= y`
and!( and!(
loc!(ascii_char(b'!')), loc!(backtrackable(ascii_char(b'!'))),
loc!(move |arena, state| parse_expr(min_indent, arena, state)) loc!(move |arena, state| parse_expr(min_indent, arena, state))
), ),
|arena: &'a Bump, (loc_op, loc_expr): (Located<()>, Located<Expr<'a>>)| { |arena: &'a Bump, (loc_op, loc_expr): (Located<()>, Located<Expr<'a>>)| {
@ -208,9 +209,7 @@ fn parse_expr<'a>(min_indent: u16, arena: &'a Bump, state: State<'a>) -> ParseRe
optional(and!( optional(and!(
// and!(space0(min_indent), loc!(binop())), // and!(space0(min_indent), loc!(binop())),
move |arena, state| { move |arena, state| {
dbg!("... -----> starting here");
let (_, spaces, state) = space0(min_indent).parse(arena, state)?; let (_, spaces, state) = space0(min_indent).parse(arena, state)?;
dbg!("got here");
let (_, op, state) = loc!(binop()).parse(arena, state)?; let (_, op, state) = loc!(binop()).parse(arena, state)?;
Ok((MadeProgress, (spaces, op), state)) Ok((MadeProgress, (spaces, op), state))
@ -225,7 +224,7 @@ fn parse_expr<'a>(min_indent: u16, arena: &'a Bump, state: State<'a>) -> ParseRe
) )
)) ))
), ),
|arena, (loc_expr1, opt_operator)| match dbg!(opt_operator) { |arena, (loc_expr1, opt_operator)| match opt_operator {
Some(((spaces_before_op, loc_op), loc_expr2)) => { Some(((spaces_before_op, loc_op), loc_expr2)) => {
let loc_expr1 = if spaces_before_op.is_empty() { let loc_expr1 = if spaces_before_op.is_empty() {
loc_expr1 loc_expr1
@ -583,10 +582,16 @@ fn spaces_then_comment_or_newline<'a>() -> impl Parser<'a, Option<&'a str>> {
type Body<'a> = (Located<Pattern<'a>>, Located<Expr<'a>>); type Body<'a> = (Located<Pattern<'a>>, Located<Expr<'a>>);
fn body<'a>(min_indent: u16) -> impl Parser<'a, Body<'a>> { fn body<'a>(min_indent: u16) -> impl Parser<'a, Body<'a>> {
dbg!("we get here?");
let indented_more = min_indent + 1; let indented_more = min_indent + 1;
let result = and!( let result = and!(
pattern(min_indent), // this backtrackable is required for the case
//
// i = 64
//
// i
//
// so the final i is not considered the start of a def
backtrackable(pattern(min_indent)),
skip_first!( skip_first!(
equals_for_def(), equals_for_def(),
// Spaces after the '=' (at a normal indentation level) and then the expr. // Spaces after the '=' (at a normal indentation level) and then the expr.
@ -1570,14 +1575,11 @@ fn unary_negate_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Exp
then( then(
// Spaces, then '-', then *not* more spaces. // Spaces, then '-', then *not* more spaces.
not_followed_by( not_followed_by(
and!( either!(
backtrackable(space1(min_indent)), // Try to parse a number literal *before* trying to parse unary negate,
either!( // because otherwise (foo -1) will parse as (foo (Num.neg 1))
// Try to parse a number literal *before* trying to parse unary negate, loc!(number_literal()),
// because otherwise (foo -1) will parse as (foo (Num.neg 1)) loc!(ascii_char(b'-'))
loc!(number_literal()),
loc!(ascii_char(b'-'))
)
), ),
one_of!( one_of!(
ascii_char(b' '), ascii_char(b' '),
@ -1586,7 +1588,7 @@ fn unary_negate_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Exp
ascii_char(b'>') ascii_char(b'>')
), ),
), ),
move |arena, state, progress, (spaces, num_or_minus_char)| { move |arena, state, progress, num_or_minus_char| {
debug_assert_eq!(progress, MadeProgress); debug_assert_eq!(progress, MadeProgress);
match num_or_minus_char { match num_or_minus_char {
@ -1616,12 +1618,7 @@ fn unary_negate_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Exp
value, value,
}; };
// spaces can be empy if it's all space characters (no newlines or comments). let value = loc_expr.value;
let value = if spaces.is_empty() {
loc_expr.value
} else {
Expr::SpaceBefore(arena.alloc(loc_expr.value), spaces)
};
Ok(( Ok((
MadeProgress, MadeProgress,
@ -1638,13 +1635,27 @@ fn unary_negate_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Exp
} }
fn loc_function_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Expr<'a>>>> { fn loc_function_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Expr<'a>>>> {
move |a, s| { one_or_more!(move |arena: &'a Bump, s| {
dbg!((one_or_more!(one_of!( map!(
unary_negate_function_arg(min_indent), and!(
space1_before(loc_function_arg(min_indent), min_indent) backtrackable(space1(min_indent)),
))) one_of!(
.parse(a, s)) unary_negate_function_arg(min_indent),
} loc_function_arg(min_indent)
)
),
|(spaces, loc_expr): (&'a [_], Located<Expr<'a>>)| {
if spaces.is_empty() {
loc_expr
} else {
arena
.alloc(loc_expr.value)
.with_spaces_before(spaces, loc_expr.region)
}
}
)
.parse(arena, s)
})
} }
/// When we parse an ident like `foo ` it could be any of these: /// When we parse an ident like `foo ` it could be any of these:
@ -1674,8 +1685,6 @@ fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
move |arena, state, progress, (loc_ident, opt_extras)| { move |arena, state, progress, (loc_ident, opt_extras)| {
debug_assert_eq!(progress, MadeProgress); debug_assert_eq!(progress, MadeProgress);
dbg!("parsed the ident", &opt_extras);
// This appears to be a var, keyword, or function application. // This appears to be a var, keyword, or function application.
match opt_extras { match opt_extras {
(Some(loc_args), Some((_spaces_before_equals, Either::First(_equals_indent)))) => { (Some(loc_args), Some((_spaces_before_equals, Either::First(_equals_indent)))) => {

View file

@ -298,7 +298,7 @@ pub fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>> {
pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located<Def<'a>>>> { pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located<Def<'a>>>> {
move |a: &'a Bump, s: State<'a>| { move |a: &'a Bump, s: State<'a>| {
// this parses just the defs // this parses just the defs
let defs = zero_or_more!(space0_around(loc(debug!(def(0))), 0)); let defs = zero_or_more!(space0_around(loc(def(0)), 0));
// let result = skip_second!(defs, end_of_file()).parse(a, s); // let result = skip_second!(defs, end_of_file()).parse(a, s);
let result = defs.parse(a, s); let result = defs.parse(a, s);
@ -322,8 +322,8 @@ fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>> {
map!( map!(
and!( and!(
and!( and!(
skip_second!(debug!(backtrackable(space1(1))), ascii_string("provides")), skip_second!(backtrackable(space1(1)), ascii_string("provides")),
debug!(space1(1)) space1(1)
), ),
and!( and!(
collection!( collection!(

View file

@ -792,7 +792,7 @@ where
} }
} }
} }
Err((element_progress, fail, new_state)) => match dbg!(element_progress) { Err((element_progress, fail, new_state)) => match element_progress {
MadeProgress => { MadeProgress => {
return Err(( return Err((
MadeProgress, MadeProgress,
@ -1058,16 +1058,12 @@ macro_rules! collection {
// We could change the AST to add extra storage specifically to // We could change the AST to add extra storage specifically to
// support empty literals containing newlines or comments, but this // support empty literals containing newlines or comments, but this
// does not seem worth even the tiniest regression in compiler performance. // does not seem worth even the tiniest regression in compiler performance.
{ zero_or_more!($crate::parser::ascii_char(b' ')),
dbg!("we are here? ");
|a, s| dbg!(zero_or_more!($crate::parser::ascii_char(b' ')).parse(a, s))
},
skip_second!( skip_second!(
move |a, s| dbg!($crate::parser::sep_by0( $crate::parser::sep_by0(
$delimiter, $delimiter,
$crate::blankspace::space0_around($elem, $min_indent) $crate::blankspace::space0_around($elem, $min_indent)
) ),
.parse(a, s)),
$closing_brace $closing_brace
) )
) )
@ -1260,8 +1256,6 @@ macro_rules! one_or_more {
move |arena, state: State<'a>| { move |arena, state: State<'a>| {
use bumpalo::collections::Vec; use bumpalo::collections::Vec;
dbg!(state.bytes);
match $parser.parse(arena, state) { match $parser.parse(arena, state) {
Ok((_, first_output, next_state)) => { Ok((_, first_output, next_state)) => {
let mut state = next_state; let mut state = next_state;
@ -1282,8 +1276,7 @@ macro_rules! one_or_more {
} }
} }
Err((progress, _, new_state)) => { Err((progress, _, new_state)) => {
dbg!(new_state.bytes); debug_assert_eq!(progress, NoProgress, "{:?}", &new_state);
debug_assert_eq!(progress, NoProgress);
Err($crate::parser::unexpected_eof( Err($crate::parser::unexpected_eof(
0, 0,
new_state.attempting, new_state.attempting,

View file

@ -61,14 +61,13 @@ pub fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>>
loc!(parse_type_variable) loc!(parse_type_variable)
), ),
|a, s| { |a, s| {
dbg!("term state", &s);
optional( optional(
// Inline type annotation, e.g. [ Nil, Cons a (List a) ] as List a // Inline type annotation, e.g. [ Nil, Cons a (List a) ] as List a
and!( and!(
space1(min_indent), space1(min_indent),
skip_first!( skip_first!(
crate::parser::keyword(keyword::AS, min_indent), crate::parser::keyword(keyword::AS, min_indent),
|a, s| dbg!(space1_before(term(min_indent), min_indent).parse(a, s)) space1_before(term(min_indent), min_indent)
) )
), ),
) )
@ -125,7 +124,7 @@ fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotatio
} }
fn loc_applied_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<TypeAnnotation<'a>>>> { fn loc_applied_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<TypeAnnotation<'a>>>> {
zero_or_more!(move |arena, state| dbg!(loc_applied_arg(min_indent).parse(arena, state))) zero_or_more!(loc_applied_arg(min_indent))
} }
#[inline(always)] #[inline(always)]
@ -220,7 +219,6 @@ fn applied_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>> {
} }
fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> { fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
dbg!("parsing type ig");
use crate::blankspace::space0; use crate::blankspace::space0;
move |arena, state: State<'a>| { move |arena, state: State<'a>| {
let (p1, first, state) = space0_before(term(min_indent), min_indent).parse(arena, state)?; let (p1, first, state) = space0_before(term(min_indent), min_indent).parse(arena, state)?;

View file

@ -1803,7 +1803,7 @@ mod test_parse {
indoc!( indoc!(
r#" r#"
foo : Foo.Bar.Baz x y as Blah a b foo : Foo.Bar.Baz x y as Blah a b
42 42
"# "#
), ),
@ -1839,7 +1839,7 @@ mod test_parse {
indoc!( indoc!(
r#" r#"
Blah a b : Foo.Bar.Baz x y Blah a b : Foo.Bar.Baz x y
42 42
"# "#
), ),
@ -2773,6 +2773,52 @@ mod test_parse {
assert_eq!(Ok(expected), actual); assert_eq!(Ok(expected), actual);
} }
#[test]
fn module_def_newline() {
use roc_parse::ast::Def::*;
let arena = Bump::new();
let src = indoc!(
r#"
main =
i = 64
i
"#
);
let actual = module_defs()
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
.map(|tuple| tuple.1);
assert!(actual.is_ok());
}
#[test]
fn nested_def_annotation() {
use roc_parse::ast::Def::*;
let arena = Bump::new();
let src = indoc!(
r#"
main =
wrappedNotEq : a, a -> Bool
wrappedNotEq = \num1, num2 ->
num1 != num2
wrappedNotEq 2 3
"#
);
let actual = module_defs()
.parse(&arena, State::new(src.as_bytes(), Attempting::Module))
.map(|tuple| tuple.1);
assert!(actual.is_ok());
}
#[test] #[test]
fn newline_after_equals() { fn newline_after_equals() {
// Regression test for https://github.com/rtfeldman/roc/issues/51 // Regression test for https://github.com/rtfeldman/roc/issues/51