mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
cleanup
This commit is contained in:
parent
35e7f01362
commit
2fc9d20867
2 changed files with 1 additions and 557 deletions
|
@ -247,65 +247,6 @@ fn chomp_line_comment(buffer: &[u8]) -> Result<&str, Progress> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Advance the parser while also indenting as appropriate.
|
||||
/// This assumes we are only advancing with spaces, since they can indent.
|
||||
fn advance_spaces_e<TE, E>(
|
||||
state: State,
|
||||
spaces: usize,
|
||||
to_error: TE,
|
||||
) -> Result<State, (Progress, E, State)>
|
||||
where
|
||||
TE: Fn(Row, Col) -> E,
|
||||
{
|
||||
match (state.column as usize).checked_add(spaces) {
|
||||
Some(column_usize) => Ok(State {
|
||||
bytes: &state.bytes[spaces..],
|
||||
column: column_usize as u16,
|
||||
..state
|
||||
}),
|
||||
_ => Err((NoProgress, to_error(state.line, state.column), state)),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn spaces_exactly_e<'a>(spaces_expected: u16) -> impl Parser<'a, (), parser::EExpr<'a>> {
|
||||
use parser::EExpr;
|
||||
|
||||
move |_, state: State<'a>| {
|
||||
if spaces_expected == 0 {
|
||||
return Ok((NoProgress, (), state));
|
||||
}
|
||||
|
||||
let mut spaces_seen: u16 = 0;
|
||||
|
||||
for c in state.bytes {
|
||||
match c {
|
||||
b' ' => {
|
||||
spaces_seen += 1;
|
||||
if spaces_seen == spaces_expected {
|
||||
let state =
|
||||
advance_spaces_e(state, spaces_expected as usize, EExpr::IndentStart)?;
|
||||
return Ok((MadeProgress, (), state));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err((
|
||||
NoProgress,
|
||||
EExpr::IndentStart(state.line, state.column + spaces_seen),
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err((
|
||||
NoProgress,
|
||||
EExpr::IndentStart(state.line, state.column + spaces_seen),
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn spaces_help_help<'a, E>(
|
||||
min_indent: u16,
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use crate::ast::{AssignedField, CommentOrNewline, Def, Expr, Pattern, Spaceable, TypeAnnotation};
|
||||
use crate::blankspace::{
|
||||
space0_after_e, space0_around_ee, space0_before_e, space0_e, spaces_exactly_e,
|
||||
};
|
||||
use crate::blankspace::{space0_after_e, space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::ident::{lowercase_ident, parse_ident_help, Ident};
|
||||
use crate::keyword;
|
||||
use crate::parser::{
|
||||
|
@ -166,138 +164,6 @@ fn record_field_access<'a>() -> impl Parser<'a, &'a str, EExpr<'a>> {
|
|||
)
|
||||
}
|
||||
|
||||
type Extras<'a> = Located<(
|
||||
Located<Expr<'a>>,
|
||||
(
|
||||
Vec<'a, &'a str>,
|
||||
Option<Either<Vec<'a, Located<Expr<'a>>>, (&'a [CommentOrNewline<'a>], u16)>>,
|
||||
),
|
||||
)>;
|
||||
|
||||
fn helper_help<'a>(
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
loc_expr_with_extras: Extras<'a>,
|
||||
min_indent: u16,
|
||||
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||
// We parse the parenthetical expression *and* the arguments after it
|
||||
// in one region, so that (for example) the region for Apply includes its args.
|
||||
let (mut loc_expr, (accesses, opt_extras)) = loc_expr_with_extras.value;
|
||||
|
||||
let mut value = loc_expr.value;
|
||||
|
||||
for field in accesses {
|
||||
// Wrap the previous answer in the new one, so we end up
|
||||
// with a nested Expr. That way, `foo.bar.baz` gets represented
|
||||
// in the AST as if it had been written (foo.bar).baz all along.
|
||||
value = Expr::Access(arena.alloc(value), field);
|
||||
}
|
||||
|
||||
loc_expr = Located {
|
||||
region: loc_expr.region,
|
||||
value,
|
||||
};
|
||||
|
||||
match opt_extras {
|
||||
Some(Either::First(loc_args)) => Ok((
|
||||
MadeProgress,
|
||||
expr_in_parens_then_arguments(arena, loc_expr, loc_args, loc_expr_with_extras.region),
|
||||
state,
|
||||
)),
|
||||
Some(Either::Second((spaces_before_equals, equals_indent))) => {
|
||||
// '=' after optional spaces
|
||||
expr_in_parens_then_equals_help(
|
||||
min_indent,
|
||||
loc_expr,
|
||||
spaces_before_equals,
|
||||
equals_indent,
|
||||
loc_expr_with_extras.region.start_col,
|
||||
)
|
||||
.parse(arena, state)
|
||||
}
|
||||
None => Ok((MadeProgress, loc_expr, state)),
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_in_parens_then_equals_help<'a>(
|
||||
min_indent: u16,
|
||||
loc_expr: Located<Expr<'a>>,
|
||||
spaces_before_equals: &'a [CommentOrNewline],
|
||||
equals_indent: u16,
|
||||
def_start_col: u16,
|
||||
) -> impl Parser<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||
move |arena, state: State<'a>| {
|
||||
let region = loc_expr.region;
|
||||
|
||||
// Re-parse the Expr as a Pattern.
|
||||
let pattern = match expr_to_pattern_help(arena, &loc_expr.value) {
|
||||
Ok(valid) => valid,
|
||||
Err(_) => {
|
||||
return Err((
|
||||
MadeProgress,
|
||||
EExpr::MalformedPattern(state.line, state.column),
|
||||
state,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// Make sure we don't discard the spaces - might be comments in there!
|
||||
let value = if spaces_before_equals.is_empty() {
|
||||
pattern
|
||||
} else {
|
||||
Pattern::SpaceAfter(arena.alloc(pattern), spaces_before_equals)
|
||||
};
|
||||
|
||||
let loc_first_pattern = Located { region, value };
|
||||
|
||||
// Continue parsing the expression as a Def.
|
||||
let (_, spaces_after_equals, state) =
|
||||
space0_e(min_indent, EExpr::Space, EExpr::IndentDefBody).parse(arena, state)?;
|
||||
|
||||
// Use loc_expr_with_extras because we want to include the opening '(' char.
|
||||
let (_, parsed_expr, state) = parse_def_expr_help(
|
||||
min_indent,
|
||||
def_start_col,
|
||||
equals_indent,
|
||||
arena,
|
||||
state,
|
||||
loc_first_pattern,
|
||||
spaces_after_equals,
|
||||
)?;
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Located {
|
||||
value: parsed_expr,
|
||||
region,
|
||||
},
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_in_parens_then_arguments<'a>(
|
||||
arena: &'a Bump,
|
||||
loc_expr: Located<Expr<'a>>,
|
||||
loc_args: Vec<'a, Located<Expr<'a>>>,
|
||||
region: Region,
|
||||
) -> Located<Expr<'a>> {
|
||||
let mut allocated_args = Vec::with_capacity_in(loc_args.len(), arena);
|
||||
|
||||
for loc_arg in loc_args {
|
||||
allocated_args.push(&*arena.alloc(loc_arg));
|
||||
}
|
||||
|
||||
Located {
|
||||
region,
|
||||
value: Expr::Apply(
|
||||
arena.alloc(loc_expr),
|
||||
allocated_args.into_bump_slice(),
|
||||
CalledVia::Space,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_loc_term_better<'a>(
|
||||
min_indent: u16,
|
||||
arena: &'a Bump,
|
||||
|
@ -1478,32 +1344,6 @@ fn assigned_expr_field_to_pattern_help<'a>(
|
|||
})
|
||||
}
|
||||
|
||||
fn parse_defs_help<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Vec<'a, &'a Located<Def<'a>>>, EExpr<'a>> {
|
||||
let parse_def = move |arena, state| {
|
||||
let (_, (spaces, def), state) = and!(
|
||||
backtrackable(space0_e(min_indent, EExpr::Space, EExpr::IndentStart)),
|
||||
loc!(def_help(min_indent))
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
let result = if spaces.is_empty() {
|
||||
&*arena.alloc(def)
|
||||
} else {
|
||||
&*arena.alloc(
|
||||
arena
|
||||
.alloc(def.value)
|
||||
.with_spaces_before(spaces, def.region),
|
||||
)
|
||||
};
|
||||
|
||||
Ok((MadeProgress, result, state))
|
||||
};
|
||||
|
||||
zero_or_more!(parse_def)
|
||||
}
|
||||
|
||||
/// A definition, consisting of one of these:
|
||||
///
|
||||
/// * A type alias using `:`
|
||||
|
@ -1569,307 +1409,8 @@ pub fn def_help_help<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Def
|
|||
}
|
||||
}
|
||||
|
||||
fn def_help<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>, EExpr<'a>> {
|
||||
let indented_more = min_indent + 1;
|
||||
|
||||
enum DefKind {
|
||||
Colon,
|
||||
Equal,
|
||||
}
|
||||
|
||||
let def_colon_or_equals = one_of![
|
||||
map!(equals_with_indent_help(), |_| DefKind::Equal),
|
||||
map!(colon_with_indent(), |_| DefKind::Colon),
|
||||
];
|
||||
|
||||
then(
|
||||
// backtrackable because
|
||||
//
|
||||
// i = 0
|
||||
// i
|
||||
//
|
||||
// on the last line, we parse a pattern `i`, but it's not actually a def, so need to
|
||||
// backtrack
|
||||
and!(backtrackable(pattern_help(min_indent)), def_colon_or_equals),
|
||||
move |arena, state, _progress, (loc_pattern, def_kind)| match def_kind {
|
||||
DefKind::Colon => {
|
||||
// Spaces after the ':' (at a normal indentation level) and then the type.
|
||||
// The type itself must be indented more than the pattern and ':'
|
||||
let (_, ann_type, state) = specialize(
|
||||
EExpr::Type,
|
||||
space0_before_e(
|
||||
type_annotation::located_help(indented_more),
|
||||
min_indent,
|
||||
Type::TSpace,
|
||||
Type::TIndentStart,
|
||||
),
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
// see if there is a definition (assuming the preceding characters were a type
|
||||
// annotation
|
||||
// TODO parse all the spaces, and check if we moved more than >= 1 line down
|
||||
let (_, opt_rest, state) = optional(and!(
|
||||
spaces_till_end_of_line(),
|
||||
body_at_indent_help(min_indent)
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
|
||||
let def = match opt_rest {
|
||||
None => {
|
||||
annotation_or_alias(arena, &loc_pattern.value, loc_pattern.region, ann_type)
|
||||
}
|
||||
Some((opt_comment, (body_pattern, body_expr))) => Def::AnnotatedBody {
|
||||
ann_pattern: arena.alloc(loc_pattern),
|
||||
ann_type: arena.alloc(ann_type),
|
||||
comment: opt_comment,
|
||||
body_pattern: arena.alloc(body_pattern),
|
||||
body_expr: arena.alloc(body_expr),
|
||||
},
|
||||
};
|
||||
|
||||
Ok((MadeProgress, def, state))
|
||||
}
|
||||
DefKind::Equal => {
|
||||
// Spaces after the '=' (at a normal indentation level) and then the expr.
|
||||
// The expr itself must be indented more than the pattern and '='
|
||||
let (_, body_expr, state) = space0_before_e(
|
||||
move |arena, state| parse_expr_help(indented_more, arena, state),
|
||||
min_indent,
|
||||
EExpr::Space,
|
||||
EExpr::IndentStart,
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Def::Body(arena.alloc(loc_pattern), arena.alloc(body_expr)),
|
||||
state,
|
||||
))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// PARSER HELPERS
|
||||
|
||||
fn pattern_help<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>, EExpr<'a>> {
|
||||
specialize_ref(
|
||||
EExpr::Pattern,
|
||||
space0_after_e(
|
||||
loc_closure_param(min_indent),
|
||||
min_indent,
|
||||
EPattern::Space,
|
||||
EPattern::IndentStart,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn spaces_till_end_of_line<'a>() -> impl Parser<'a, Option<&'a str>, EExpr<'a>> {
|
||||
crate::blankspace::spaces_till_end_of_line(|r, c| {
|
||||
EExpr::Space(parser::BadInputError::HasTab, r, c)
|
||||
})
|
||||
}
|
||||
|
||||
type Body<'a> = (Located<Pattern<'a>>, Located<Expr<'a>>);
|
||||
|
||||
fn body_at_indent_help<'a>(indent_level: u16) -> impl Parser<'a, Body<'a>, EExpr<'a>> {
|
||||
let indented_more = indent_level + 1;
|
||||
and!(
|
||||
skip_first!(spaces_exactly_e(indent_level), pattern_help(indent_level)),
|
||||
skip_first!(
|
||||
equals_with_indent_help(),
|
||||
// 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_e(
|
||||
move |arena, state| parse_expr_help(indented_more, arena, state),
|
||||
indent_level,
|
||||
EExpr::Space,
|
||||
EExpr::IndentStart,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fn annotation_or_alias<'a>(
|
||||
arena: &'a Bump,
|
||||
pattern: &Pattern<'a>,
|
||||
pattern_region: Region,
|
||||
loc_ann: Located<TypeAnnotation<'a>>,
|
||||
) -> Def<'a> {
|
||||
use crate::ast::Pattern::*;
|
||||
|
||||
match pattern {
|
||||
// Type aliases initially parse as either global tags
|
||||
// or applied global tags, because they are always uppercase
|
||||
GlobalTag(name) => Def::Alias {
|
||||
name: Located {
|
||||
value: name,
|
||||
region: pattern_region,
|
||||
},
|
||||
vars: &[],
|
||||
ann: loc_ann,
|
||||
},
|
||||
Apply(
|
||||
Located {
|
||||
region: pattern_region,
|
||||
value: Pattern::GlobalTag(name),
|
||||
},
|
||||
loc_vars,
|
||||
) => Def::Alias {
|
||||
name: Located {
|
||||
value: name,
|
||||
region: *pattern_region,
|
||||
},
|
||||
vars: loc_vars,
|
||||
ann: loc_ann,
|
||||
},
|
||||
Apply(_, _) => {
|
||||
Def::NotYetImplemented("TODO gracefully handle invalid Apply in type annotation")
|
||||
}
|
||||
SpaceAfter(value, spaces_before) => Def::SpaceAfter(
|
||||
arena.alloc(annotation_or_alias(arena, value, pattern_region, loc_ann)),
|
||||
spaces_before,
|
||||
),
|
||||
SpaceBefore(value, spaces_before) => Def::SpaceBefore(
|
||||
arena.alloc(annotation_or_alias(arena, value, pattern_region, loc_ann)),
|
||||
spaces_before,
|
||||
),
|
||||
Nested(value) => annotation_or_alias(arena, value, pattern_region, loc_ann),
|
||||
|
||||
PrivateTag(_) => {
|
||||
Def::NotYetImplemented("TODO gracefully handle trying to use a private tag as an annotation.")
|
||||
}
|
||||
QualifiedIdentifier { .. } => {
|
||||
Def::NotYetImplemented("TODO gracefully handle trying to annotate a qualified identifier, e.g. `Foo.bar : ...`")
|
||||
}
|
||||
NumLiteral(_) | NonBase10Literal { .. } | FloatLiteral(_) | StrLiteral(_) => {
|
||||
Def::NotYetImplemented("TODO gracefully handle trying to annotate a litera")
|
||||
}
|
||||
Underscore(_) => {
|
||||
Def::NotYetImplemented("TODO gracefully handle trying to give a type annotation to an undrscore")
|
||||
}
|
||||
Malformed(_) => {
|
||||
Def::NotYetImplemented("TODO translate a malformed pattern into a malformed annotation")
|
||||
}
|
||||
MalformedIdent(_, _) => {
|
||||
Def::NotYetImplemented("TODO translate a malformed pattern into a malformed annotation")
|
||||
}
|
||||
Identifier(ident) => {
|
||||
// This is a regular Annotation
|
||||
Def::Annotation(
|
||||
Located {
|
||||
region: pattern_region,
|
||||
value: Pattern::Identifier(ident),
|
||||
},
|
||||
loc_ann,
|
||||
)
|
||||
}
|
||||
RecordDestructure(loc_patterns) => {
|
||||
// This is a record destructure Annotation
|
||||
Def::Annotation(
|
||||
Located {
|
||||
region: pattern_region,
|
||||
value: Pattern::RecordDestructure(loc_patterns),
|
||||
},
|
||||
loc_ann,
|
||||
)
|
||||
}
|
||||
RequiredField(_, _) | OptionalField(_, _) => {
|
||||
unreachable!("This should only be possible inside a record destruture.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_def_indent(
|
||||
min_indent: u16,
|
||||
def_start_column: u16,
|
||||
special_token_indent: u16,
|
||||
state: State,
|
||||
) -> Result<State, (Progress, EExpr, State)> {
|
||||
if def_start_column < min_indent || special_token_indent < def_start_column {
|
||||
Err((
|
||||
NoProgress,
|
||||
EExpr::IndentDefBody(state.line, state.column),
|
||||
state,
|
||||
))
|
||||
} else {
|
||||
Ok(state)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_def_expr_help<'a>(
|
||||
min_indent: u16,
|
||||
def_start_col: u16,
|
||||
equals_sign_indent: u16,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
loc_first_pattern: Located<Pattern<'a>>,
|
||||
spaces_after_equals: &'a [CommentOrNewline<'a>],
|
||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
let state = check_def_indent(min_indent, def_start_col, equals_sign_indent, state)?;
|
||||
|
||||
// Indented more beyond the original indent of the entire def-expr.
|
||||
let indented_more = def_start_col + 1;
|
||||
|
||||
then(
|
||||
and!(
|
||||
// Parse the body of the first def. It doesn't need any spaces
|
||||
// around it parsed, because both the subsquent defs and the
|
||||
// final body will have space1_before on them.
|
||||
//
|
||||
// It should be indented more than the original, and it will
|
||||
// end when outdented again.
|
||||
move |arena, state| parse_expr_help(indented_more, arena, state),
|
||||
and!(
|
||||
// Optionally parse additional defs.
|
||||
parse_defs_help(def_start_col),
|
||||
// Parse the final expression that will be returned.
|
||||
// It should be indented the same amount as the original.
|
||||
space0_before_e(
|
||||
move |arena, state: State<'a>| { parse_expr_help(def_start_col, arena, state) },
|
||||
def_start_col,
|
||||
EExpr::Space,
|
||||
EExpr::IndentStart,
|
||||
)
|
||||
)
|
||||
),
|
||||
move |arena, state, progress, (loc_first_body, (mut defs, loc_ret))| {
|
||||
let loc_first_body = if spaces_after_equals.is_empty() {
|
||||
loc_first_body
|
||||
} else {
|
||||
Located {
|
||||
value: Expr::SpaceBefore(
|
||||
arena.alloc(loc_first_body.value),
|
||||
spaces_after_equals,
|
||||
),
|
||||
region: loc_first_body.region,
|
||||
}
|
||||
};
|
||||
let def_region = Region::span_across(&loc_first_pattern.region, &loc_first_body.region);
|
||||
|
||||
let first_def: Def<'a> =
|
||||
// TODO is there some way to eliminate this .clone() here?
|
||||
Def::Body(arena.alloc(loc_first_pattern.clone()), arena.alloc(loc_first_body));
|
||||
|
||||
let loc_first_def = Located {
|
||||
value: first_def,
|
||||
region: def_region,
|
||||
};
|
||||
|
||||
// for formatting reasons, we must insert the first def first!
|
||||
defs.insert(0, &*arena.alloc(loc_first_def));
|
||||
|
||||
Ok((
|
||||
progress,
|
||||
Expr::Defs(defs.into_bump_slice(), arena.alloc(loc_ret)),
|
||||
state,
|
||||
))
|
||||
},
|
||||
)
|
||||
.parse(arena, state)
|
||||
}
|
||||
|
||||
fn closure_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, ELambda<'a>> {
|
||||
map_with_arena!(
|
||||
skip_first!(
|
||||
|
@ -2258,44 +1799,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn equals_with_indent_help<'a>() -> impl Parser<'a, u16, EExpr<'a>> {
|
||||
move |_arena, state: State<'a>| {
|
||||
let indent_col = state.indent_col;
|
||||
let good = state.bytes.starts_with(b"=") && !state.bytes.starts_with(b"==");
|
||||
|
||||
if good {
|
||||
match state.advance_without_indenting_e(1, EExpr::Space) {
|
||||
Err(bad) => Err(bad),
|
||||
Ok(good) => Ok((MadeProgress, indent_col, good)),
|
||||
}
|
||||
} else {
|
||||
let equals = EExpr::Equals(state.line, state.column);
|
||||
Err((NoProgress, equals, state))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn colon_with_indent<'a>() -> impl Parser<'a, u16, EExpr<'a>> {
|
||||
move |_arena, state: State<'a>| {
|
||||
let indent_col = state.indent_col;
|
||||
|
||||
if let Some(b':') = state.bytes.get(0) {
|
||||
if let Some(b':') = state.bytes.get(1) {
|
||||
let double = EExpr::DoubleColon(state.line, state.column);
|
||||
Err((NoProgress, double, state))
|
||||
} else {
|
||||
match state.advance_without_indenting_e(1, EExpr::Space) {
|
||||
Err(bad) => Err(bad),
|
||||
Ok(good) => Ok((MadeProgress, indent_col, good)),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let colon = EExpr::Colon(state.line, state.column);
|
||||
Err((NoProgress, colon, state))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
|
||||
match src {
|
||||
Ident::GlobalTag(string) => Expr::GlobalTag(string),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue