mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
other parsing adjustment!
This commit is contained in:
parent
bf68477c8a
commit
66392eeeaf
4 changed files with 152 additions and 9 deletions
|
@ -494,6 +494,50 @@ mod test_can {
|
||||||
|
|
||||||
assert_eq!(problems, Vec::new());
|
assert_eq!(problems, Vec::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn annotation_followed_with_unrelated_affectation() {
|
||||||
|
let src = indoc!(
|
||||||
|
r#"
|
||||||
|
F : Int
|
||||||
|
|
||||||
|
x = 1
|
||||||
|
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
let arena = Bump::new();
|
||||||
|
let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src);
|
||||||
|
|
||||||
|
assert_eq!(problems.len(), 1);
|
||||||
|
assert!(problems.iter().all(|problem| match problem {
|
||||||
|
Problem::UnusedDef(_, _) => true,
|
||||||
|
_ => false,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn two_annotations_followed_with_unrelated_affectation() {
|
||||||
|
let src = indoc!(
|
||||||
|
r#"
|
||||||
|
G : Int
|
||||||
|
|
||||||
|
F : Int
|
||||||
|
|
||||||
|
x = 1
|
||||||
|
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
let arena = Bump::new();
|
||||||
|
let CanExprOut { problems, .. } = can_expr_with(&arena, test_home(), src);
|
||||||
|
|
||||||
|
assert_eq!(problems.len(), 2);
|
||||||
|
assert!(problems.iter().all(|problem| match problem {
|
||||||
|
Problem::UnusedDef(_, _) => true,
|
||||||
|
_ => false,
|
||||||
|
}));
|
||||||
|
}
|
||||||
// LOCALS
|
// LOCALS
|
||||||
|
|
||||||
// TODO rewrite this test to check only for UnusedDef reports
|
// TODO rewrite this test to check only for UnusedDef reports
|
||||||
|
|
|
@ -239,6 +239,63 @@ pub fn line_comment<'a>() -> impl Parser<'a, &'a str> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn spaces_exactly<'a>(spaces_expected: u16) -> impl Parser<'a, ()> {
|
||||||
|
move |_arena: &'a Bump, state: State<'a>| {
|
||||||
|
if spaces_expected == 0 {
|
||||||
|
return Ok(((), state));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut state = state;
|
||||||
|
let mut spaces_seen: u16 = 0;
|
||||||
|
|
||||||
|
while !state.bytes.is_empty() {
|
||||||
|
match peek_utf8_char(&state) {
|
||||||
|
Ok((' ', _)) => {
|
||||||
|
spaces_seen += 1;
|
||||||
|
state = state.advance_spaces(1)?;
|
||||||
|
if spaces_seen == spaces_expected {
|
||||||
|
return Ok(((), state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(_) => {
|
||||||
|
return Err(unexpected(
|
||||||
|
spaces_seen.into(),
|
||||||
|
state.clone(),
|
||||||
|
state.attempting,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(FailReason::BadUtf8) => {
|
||||||
|
// If we hit an invalid UTF-8 character, bail out immediately.
|
||||||
|
return state.fail(FailReason::BadUtf8);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
if spaces_seen == 0 {
|
||||||
|
return Err(unexpected_eof(0, state.attempting, state));
|
||||||
|
} else {
|
||||||
|
return Err(unexpected(
|
||||||
|
spaces_seen.into(),
|
||||||
|
state.clone(),
|
||||||
|
state.attempting,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if spaces_seen == 0 {
|
||||||
|
return Err(unexpected_eof(0, state.attempting, state));
|
||||||
|
} else {
|
||||||
|
return Err(unexpected(
|
||||||
|
spaces_seen.into(),
|
||||||
|
state.clone(),
|
||||||
|
state.attempting,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn spaces<'a>(
|
fn spaces<'a>(
|
||||||
require_at_least_one: bool,
|
require_at_least_one: bool,
|
||||||
|
|
|
@ -3,15 +3,15 @@ use crate::ast::{
|
||||||
};
|
};
|
||||||
use crate::blankspace::{
|
use crate::blankspace::{
|
||||||
line_comment, space0, space0_after, space0_around, space0_before, space1, space1_around,
|
line_comment, space0, space0_after, space0_around, space0_before, space1, space1_around,
|
||||||
space1_before,
|
space1_before, spaces_exactly,
|
||||||
};
|
};
|
||||||
use crate::ident::{global_tag_or_ident, ident, lowercase_ident, Ident};
|
use crate::ident::{global_tag_or_ident, ident, lowercase_ident, Ident};
|
||||||
use crate::keyword;
|
use crate::keyword;
|
||||||
use crate::number_literal::number_literal;
|
use crate::number_literal::number_literal;
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
self, allocated, ascii_char, ascii_string, fail, newline_char, not, not_followed_by, optional,
|
self, allocated, and_then_with_indent_level, ascii_char, ascii_string, fail, map, newline_char,
|
||||||
sep_by1, then, unexpected, unexpected_eof, Either, Fail, FailReason, ParseResult, Parser,
|
not, not_followed_by, optional, sep_by1, then, unexpected, unexpected_eof, Either, Fail,
|
||||||
State,
|
FailReason, ParseResult, Parser, State,
|
||||||
};
|
};
|
||||||
use crate::type_annotation;
|
use crate::type_annotation;
|
||||||
use bumpalo::collections::string::String;
|
use bumpalo::collections::string::String;
|
||||||
|
@ -567,7 +567,7 @@ 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>> {
|
||||||
let indented_more = min_indent + 1;
|
let indented_more = min_indent + 1;
|
||||||
and!(
|
and!(
|
||||||
skip_first!(space0(min_indent), pattern(min_indent)),
|
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.
|
||||||
|
@ -580,6 +580,22 @@ fn body<'a>(min_indent: u16) -> impl Parser<'a, Body<'a>> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn body_at_indent<'a>(indent_level: u16) -> impl Parser<'a, Body<'a>> {
|
||||||
|
let indented_more = indent_level + 1;
|
||||||
|
and!(
|
||||||
|
skip_first!(spaces_exactly(indent_level), pattern(indent_level)),
|
||||||
|
skip_first!(
|
||||||
|
equals_for_def(),
|
||||||
|
// Spaces after the '=' (at a normal indentation level) and then the expr.
|
||||||
|
// The expr itself must be indented more than the pattern and '='
|
||||||
|
space0_before(
|
||||||
|
loc!(move |arena, state| parse_expr(indented_more, arena, state)),
|
||||||
|
indent_level,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
type AnnotationOrAnnotatedBody<'a> = (
|
type AnnotationOrAnnotatedBody<'a> = (
|
||||||
(Located<Pattern<'a>>, Located<TypeAnnotation<'a>>),
|
(Located<Pattern<'a>>, Located<TypeAnnotation<'a>>),
|
||||||
Option<(Option<&'a str>, Body<'a>)>,
|
Option<(Option<&'a str>, Body<'a>)>,
|
||||||
|
@ -588,7 +604,10 @@ type AnnotationOrAnnotatedBody<'a> = (
|
||||||
fn annotated_body<'a>(min_indent: u16) -> impl Parser<'a, AnnotationOrAnnotatedBody<'a>> {
|
fn annotated_body<'a>(min_indent: u16) -> impl Parser<'a, AnnotationOrAnnotatedBody<'a>> {
|
||||||
and!(
|
and!(
|
||||||
annotation(min_indent),
|
annotation(min_indent),
|
||||||
optional(and!(spaces_then_comment_or_newline(), body(min_indent)))
|
optional(and!(
|
||||||
|
spaces_then_comment_or_newline(),
|
||||||
|
body_at_indent(min_indent)
|
||||||
|
))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -813,11 +832,18 @@ fn parse_def_signature<'a>(
|
||||||
//
|
//
|
||||||
// It should be indented more than the original, and it will
|
// It should be indented more than the original, and it will
|
||||||
// end when outdented again.
|
// end when outdented again.
|
||||||
and!(
|
and_then_with_indent_level(
|
||||||
type_annotation::located(indented_more),
|
type_annotation::located(indented_more),
|
||||||
// The first annotation may be followed by a body
|
// The first annotation may be immediately (spaces_then_comment_or_newline())
|
||||||
|
// followed by a body at the exact same indent_level
|
||||||
// leading to an AnnotatedBody in this case
|
// leading to an AnnotatedBody in this case
|
||||||
optional(and!(spaces_then_comment_or_newline(), body(indented_more)))
|
|type_ann, indent_level| map(
|
||||||
|
optional(and!(
|
||||||
|
spaces_then_comment_or_newline(),
|
||||||
|
body_at_indent(indent_level)
|
||||||
|
)),
|
||||||
|
move |opt_body| (type_ann.clone(), opt_body)
|
||||||
|
)
|
||||||
),
|
),
|
||||||
and!(
|
and!(
|
||||||
// Optionally parse additional defs.
|
// Optionally parse additional defs.
|
||||||
|
|
|
@ -341,6 +341,22 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn and_then_with_indent_level<'a, P1, P2, F, Before, After>(
|
||||||
|
parser: P1,
|
||||||
|
transform: F,
|
||||||
|
) -> impl Parser<'a, After>
|
||||||
|
where
|
||||||
|
P1: Parser<'a, Before>,
|
||||||
|
P2: Parser<'a, After>,
|
||||||
|
F: Fn(Before, u16) -> P2,
|
||||||
|
{
|
||||||
|
move |arena, state| {
|
||||||
|
parser.parse(arena, state).and_then(|(output, next_state)| {
|
||||||
|
transform(output, next_state.indent_col).parse(arena, next_state)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn then<'a, P1, F, Before, After>(parser: P1, transform: F) -> impl Parser<'a, After>
|
pub fn then<'a, P1, F, Before, After>(parser: P1, transform: F) -> impl Parser<'a, After>
|
||||||
where
|
where
|
||||||
P1: Parser<'a, Before>,
|
P1: Parser<'a, Before>,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue