mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
more backtracking
This commit is contained in:
parent
d284d3bb72
commit
e643d1ea3c
7 changed files with 101 additions and 72 deletions
|
@ -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]
|
||||||
|
|
|
@ -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)
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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!(
|
|
||||||
backtrackable(space1(min_indent)),
|
|
||||||
either!(
|
either!(
|
||||||
// Try to parse a number literal *before* trying to parse unary negate,
|
// Try to parse a number literal *before* trying to parse unary negate,
|
||||||
// because otherwise (foo -1) will parse as (foo (Num.neg 1))
|
// because otherwise (foo -1) will parse as (foo (Num.neg 1))
|
||||||
loc!(number_literal()),
|
loc!(number_literal()),
|
||||||
loc!(ascii_char(b'-'))
|
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!(
|
||||||
|
and!(
|
||||||
|
backtrackable(space1(min_indent)),
|
||||||
|
one_of!(
|
||||||
unary_negate_function_arg(min_indent),
|
unary_negate_function_arg(min_indent),
|
||||||
space1_before(loc_function_arg(min_indent), min_indent)
|
loc_function_arg(min_indent)
|
||||||
)))
|
)
|
||||||
.parse(a, s))
|
),
|
||||||
|
|(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)))) => {
|
||||||
|
|
|
@ -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!(
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)?;
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue