add AnnotatedBody in AST enum type

This commit is contained in:
Sébastien Besnier 2020-09-24 17:39:22 +02:00
parent b0c169bc5a
commit efe3497cc9
3 changed files with 100 additions and 31 deletions

View file

@ -277,6 +277,14 @@ pub enum Def<'a> {
// No need to track that relationship in any data structure. // No need to track that relationship in any data structure.
Body(&'a Loc<Pattern<'a>>, &'a Loc<Expr<'a>>), Body(&'a Loc<Pattern<'a>>, &'a Loc<Expr<'a>>),
AnnotatedBody {
ann_pattern: Loc<Pattern<'a>>,
ann_type: Loc<TypeAnnotation<'a>>,
comment: Option<&'a str>,
body_pattern: Loc<Pattern<'a>>,
body_expr: Loc<Expr<'a>>,
},
// Blank Space (e.g. comments, spaces, newlines) before or after a def. // Blank Space (e.g. comments, spaces, newlines) before or after a def.
// We preserve this for the formatter; canonicalization ignores it. // We preserve this for the formatter; canonicalization ignores it.
SpaceBefore(&'a Def<'a>, &'a [CommentOrNewline<'a>]), SpaceBefore(&'a Def<'a>, &'a [CommentOrNewline<'a>]),
@ -668,6 +676,7 @@ impl<'a> Spaceable<'a> for Def<'a> {
/// "currently attempting to parse a list." This helps error messages! /// "currently attempting to parse a list." This helps error messages!
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Attempting { pub enum Attempting {
LineComment,
List, List,
Keyword, Keyword,
StrLiteral, StrLiteral,

View file

@ -1,7 +1,8 @@
use crate::ast::CommentOrNewline::{self, *}; use crate::ast::CommentOrNewline::{self, *};
use crate::ast::Spaceable; use crate::ast::{Attempting, Spaceable};
use crate::parser::{ use crate::parser::{
self, and, peek_utf8_char, unexpected, unexpected_eof, FailReason, Parser, State, self, and, ascii_char, ascii_string, optional, parse_utf8, peek_utf8_char, then, unexpected,
unexpected_eof, FailReason, Parser, State,
}; };
use bumpalo::collections::string::String; use bumpalo::collections::string::String;
use bumpalo::collections::vec::Vec; use bumpalo::collections::vec::Vec;
@ -211,6 +212,33 @@ enum LineState {
DocComment, DocComment,
} }
pub fn line_comment<'a>() -> impl Parser<'a, &'a str> {
then(
and!(ascii_char('#'), optional(ascii_string("# "))),
|_arena: &'a Bump, state: State<'a>, (_, opt_doc)| {
if opt_doc != None {
return Err(unexpected(3, state, Attempting::LineComment));
}
let mut length = 0;
for &byte in state.bytes.iter() {
if byte != b'\n' {
length += 1;
} else {
break;
}
}
let comment = &state.bytes[..length];
let state = state.advance_without_indenting(length + 1)?;
match parse_utf8(comment) {
Ok(comment_str) => Ok((comment_str, state)),
Err(reason) => state.fail(reason),
}
},
)
}
#[inline(always)] #[inline(always)]
fn spaces<'a>( fn spaces<'a>(
require_at_least_one: bool, require_at_least_one: bool,

View file

@ -2,7 +2,8 @@ use crate::ast::{
AssignedField, Attempting, CommentOrNewline, Def, Expr, Pattern, Spaceable, TypeAnnotation, AssignedField, Attempting, CommentOrNewline, Def, Expr, Pattern, Spaceable, TypeAnnotation,
}; };
use crate::blankspace::{ use crate::blankspace::{
space0, space0_after, space0_around, space0_before, space1, space1_around, space1_before, line_comment, space0, space0_after, space0_around, space0_before, space1, space1_around,
space1_before,
}; };
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;
@ -497,35 +498,66 @@ pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> {
// Indented more beyond the original indent. // Indented more beyond the original indent.
let indented_more = min_indent + 1; let indented_more = min_indent + 1;
// Constant or annotation let pattern = space0_after(loc_closure_param(min_indent), min_indent);
map_with_arena!(
and!( let annotation = and!(
// A pattern followed by '=' or ':' pattern,
space0_after(loc_closure_param(min_indent), min_indent), skip_first!(
either!( ascii_char(b':'),
// Constant // Spaces after the ':' (at a normal indentation level) and then the type.
skip_first!( // The type itself must be indented more than the pattern and ':'
equals_for_def(), space0_before(type_annotation::located(indented_more), indented_more)
// 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)), let body = and!(
min_indent, pattern,
) skip_first!(
), equals_for_def(),
// Annotation // Spaces after the '=' (at a normal indentation level) and then the expr.
skip_first!( // The expr itself must be indented more than the pattern and '='
ascii_char(b':'), space0_before(
// Spaces after the ':' (at a normal indentation level) and then the type. loc!(move |arena, state| parse_expr(indented_more, arena, state)),
// The type itself must be indented more than the pattern and ':' min_indent,
space0_before(type_annotation::located(indented_more), indented_more)
)
) )
), )
|arena: &'a Bump, (loc_pattern, expr_or_ann)| match expr_or_ann { );
Either::First(loc_expr) => Def::Body(arena.alloc(loc_pattern), arena.alloc(loc_expr)),
Either::Second(loc_ann) => let comment_or_newline = map!(
annotation_or_alias(arena, &loc_pattern.value, loc_pattern.region, loc_ann), either!(ascii_char('\n'), line_comment()),
|either_comment_or_newline| match either_comment_or_newline {
Either::First(_) => None,
Either::Second(comment) => Some(comment),
}
);
let spaces_then_comment_or_newline: dyn Parser<'a, Option<&'a str>> =
skip_first!(zero_or_more!(ascii_char(' ')), comment_or_newline);
let annotated_body = and!(
annotation,
optional(and!(spaces_then_comment_or_newline, body))
);
map_with_arena!(
either!(annotated_body, body),
|arena: &'a Bump, ann_body_or_body| match ann_body_or_body {
Either::First((body_pattern, body_expr)) =>
Def::Body(arena.alloc(body_pattern), arena.alloc(body_expr)),
Either::Second(((ann_pattern, ann_type), None)) => annotation_or_alias(
arena,
&(ann_pattern as Located<Pattern>).value,
(ann_pattern as Located<Pattern>).region,
ann_type
),
Either::Second((
(ann_pattern, ann_type),
Some((opt_comment, (body_pattern, body_expr))),
)) => Def::AnnotatedBody {
ann_pattern: ann_pattern,
ann_type: ann_type,
comment: opt_comment,
body_pattern: body_pattern,
body_expr: body_expr,
},
} }
) )
} }