This commit is contained in:
Folkert 2021-03-17 21:14:19 +01:00
parent 35e7f01362
commit 2fc9d20867
2 changed files with 1 additions and 557 deletions

View file

@ -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,

View file

@ -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),