mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
infra for record patterns
This commit is contained in:
parent
70b5e18c21
commit
e007430584
3 changed files with 143 additions and 11 deletions
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue