infra for record patterns

This commit is contained in:
Folkert 2021-02-20 01:08:25 +01:00
parent 70b5e18c21
commit e007430584
3 changed files with 143 additions and 11 deletions

View file

@ -2,18 +2,18 @@ use crate::ast::{
AssignedField, Attempting, CommentOrNewline, Def, Expr, Pattern, Spaceable, TypeAnnotation,
};
use crate::blankspace::{
line_comment, space0, space0_after, space0_around, space0_before, space1, space1_around,
space1_before, spaces_exactly,
line_comment, space0, space0_after, space0_around, space0_around_e, space0_before,
space0_before_e, space0_e, space1, space1_around, space1_before, spaces_exactly,
};
use crate::expr::loc_pattern;
use crate::ident::{global_tag_or_ident, ident, lowercase_ident, Ident};
use crate::keyword;
use crate::number_literal::number_literal;
use crate::parser::Progress::{self, *};
use crate::parser::{
self, allocated, and_then_with_indent_level, ascii_char, ascii_string, attempt, backtrackable,
fail, map, newline_char, not, not_followed_by, optional, peek_utf8_char_e, sep_by1, specialize,
then, unexpected, unexpected_eof, word1, EPattern, Either, ParseResult, Parser, State,
SyntaxError,
self, allocated, fail, map, newline_char, not, not_followed_by, optional, peek_utf8_char_e,
sep_by1, specialize, specialize_ref, then, unexpected, unexpected_eof, word1, BadInputError,
EPattern, Either, PRecord, ParseResult, Parser, State, SyntaxError,
};
use crate::type_annotation;
use bumpalo::collections::string::String;
@ -99,3 +99,117 @@ fn lowercase_ident_pattern<'a>(
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
Ok((progress, buf.into_bump_str(), state))
}
pub fn record_pattern<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, SyntaxError<'a>> {
specialize(
|e, r, c| SyntaxError::Pattern(EPattern::Record(e, r, c)),
record_pattern_help(min_indent),
)
}
#[inline(always)]
fn record_pattern_help<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, PRecord<'a>> {
move |arena, state| {
let (_, (fields, final_comments), state) = collection_trailing_sep_e!(
// word1_check_indent!(b'{', PRecord::Open, min_indent, PRecord::IndentOpen),
word1(b'{', PRecord::Open),
loc!(record_pattern_field(min_indent)),
word1(b',', PRecord::End),
// word1_check_indent!(b'}', PRecord::End, min_indent, PRecord::IndentEnd),
word1(b'}', PRecord::End),
min_indent,
PRecord::Open,
PRecord::Space,
PRecord::IndentEnd
)
.parse(arena, state)?;
// TODO
let _unused = final_comments;
let result = Pattern::RecordDestructure(fields.into_bump_slice());
Ok((MadeProgress, result, state))
}
}
fn record_pattern_field<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, PRecord<'a>> {
use crate::parser::Either::*;
move |arena, state: State<'a>| {
// You must have a field name, e.g. "email"
// using the initial row/col is important for error reporting
let row = state.line;
let col = state.column;
let (progress, loc_label, state) = loc!(specialize(
move |_, _, _| PRecord::Field(row, col),
lowercase_ident()
))
.parse(arena, state)?;
debug_assert_eq!(progress, MadeProgress);
let (_, spaces, state) =
space0_e(min_indent, PRecord::Space, PRecord::IndentEnd).parse(arena, state)?;
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
// (This is true in both literals and types.)
let (_, opt_loc_val, state) = optional(either!(
word1(b':', PRecord::Colon),
word1(b'?', PRecord::Optional)
))
.parse(arena, state)?;
match opt_loc_val {
Some(First(_)) => {
let val_parser = specialize_ref(PRecord::Syntax, loc_pattern(min_indent));
let (_, loc_val, state) =
space0_before_e(val_parser, min_indent, PRecord::Space, PRecord::IndentColon)
.parse(arena, state)?;
let Located { value, region } = loc_val;
Ok((
MadeProgress,
Pattern::RequiredField(
loc_label.value,
arena.alloc(arena.alloc(value).with_spaces_before(spaces, region)),
),
state,
))
}
Some(Second(_)) => {
let val_parser =
specialize_ref(PRecord::Syntax, loc!(crate::expr::expr(min_indent)));
let (_, loc_val, state) =
space0_before_e(val_parser, min_indent, PRecord::Space, PRecord::IndentColon)
.parse(arena, state)?;
let Located { value, region } = loc_val;
Ok((
MadeProgress,
Pattern::OptionalField(
loc_label.value,
arena.alloc(arena.alloc(value).with_spaces_before(spaces, region)),
),
state,
))
}
// If no value was provided, record it as a Var.
// Canonicalize will know what to do with a Var later.
None => {
let value = if !spaces.is_empty() {
let Located { value, .. } = loc_label;
Pattern::SpaceAfter(arena.alloc(Pattern::Identifier(value)), spaces)
} else {
let Located { value, .. } = loc_label;
Pattern::Identifier(value)
};
Ok((MadeProgress, value, state))
}
}
}
}