Fix parenthetical defs

This commit is contained in:
Richard Feldman 2019-10-03 21:00:38 +03:00
parent 9a564df7bb
commit f4f83d70ee
3 changed files with 129 additions and 30 deletions

View file

@ -994,7 +994,7 @@ fn add_idents_from_pattern<'a>(
&QualifiedIdentifier(_name) => { &QualifiedIdentifier(_name) => {
panic!("TODO implement QualifiedIdentifier pattern."); panic!("TODO implement QualifiedIdentifier pattern.");
} }
&Apply(_) => { &Apply(_, _) => {
panic!("TODO implement Apply pattern."); panic!("TODO implement Apply pattern.");
// &AppliedVariant(_, ref opt_loc_args) => match opt_loc_args { // &AppliedVariant(_, ref opt_loc_args) => match opt_loc_args {
// &None => (), // &None => (),
@ -1033,7 +1033,7 @@ fn remove_idents(pattern: &ast::Pattern, idents: &mut ImMap<Ident, (Symbol, Regi
QualifiedIdentifier(_name) => { QualifiedIdentifier(_name) => {
panic!("TODO implement QualifiedIdentifier pattern in remove_idents."); panic!("TODO implement QualifiedIdentifier pattern in remove_idents.");
} }
Apply(_) => { Apply(_, _) => {
panic!("TODO implement Apply pattern in remove_idents."); panic!("TODO implement Apply pattern in remove_idents.");
// AppliedVariant(_, Some(loc_args)) => { // AppliedVariant(_, Some(loc_args)) => {
// for loc_arg in loc_args { // for loc_arg in loc_args {

View file

@ -106,7 +106,7 @@ pub enum Pattern<'a> {
// Variant, optionally qualified // Variant, optionally qualified
Variant(&'a [&'a str], &'a str), Variant(&'a [&'a str], &'a str),
Apply(&'a (Loc<&'a Pattern<'a>>, [Loc<Pattern<'a>>])), Apply(&'a Loc<Pattern<'a>>, &'a [Loc<Pattern<'a>>]),
/// This is Loc<Pattern> rather than Loc<str> so we can record comments /// This is Loc<Pattern> rather than Loc<str> so we can record comments
/// around the destructured names, e.g. { x ### x does stuff ###, y } /// around the destructured names, e.g. { x ### x does stuff ###, y }
/// In practice, these patterns will always be Identifier /// In practice, these patterns will always be Identifier
@ -388,7 +388,7 @@ pub fn format<'a>(arena: &'a Bump, expr: &'a Expr<'a>, indent: u16) -> String<'a
buf.push('\\'); buf.push('\\');
for loc_pattern in loc_patterns { for loc_pattern in loc_patterns {
buf.push_str(&format_pattern(arena, &loc_pattern.value, indent)); buf.push_str(&format_pattern(arena, &loc_pattern.value, indent, true));
buf.push(' '); buf.push(' ');
} }
@ -432,7 +432,7 @@ pub fn format_def<'a>(arena: &'a Bump, def: &'a Def<'a>, indent: u16) -> String<
match def { match def {
Def::AnnotationOnly(_region) => panic!("TODO have format_def support AnnotationOnly"), Def::AnnotationOnly(_region) => panic!("TODO have format_def support AnnotationOnly"),
BodyOnly(loc_pattern, loc_expr) => { BodyOnly(loc_pattern, loc_expr) => {
buf.push_str(&format_pattern(arena, &loc_pattern.value, indent)); buf.push_str(&format_pattern(arena, &loc_pattern.value, indent, true));
buf.push_str(" = "); buf.push_str(" = ");
buf.push_str(&format(arena, &loc_expr.value, indent)); buf.push_str(&format(arena, &loc_expr.value, indent));
} }
@ -444,7 +444,12 @@ pub fn format_def<'a>(arena: &'a Bump, def: &'a Def<'a>, indent: u16) -> String<
buf buf
} }
fn format_pattern<'a>(arena: &'a Bump, pattern: &'a Pattern<'a>, indent: u16) -> String<'a> { fn format_pattern<'a>(
arena: &'a Bump,
pattern: &'a Pattern<'a>,
indent: u16,
apply_needs_parens: bool,
) -> String<'a> {
use self::Pattern::*; use self::Pattern::*;
let mut buf = String::new_in(arena); let mut buf = String::new_in(arena);
@ -459,12 +464,20 @@ fn format_pattern<'a>(arena: &'a Bump, pattern: &'a Pattern<'a>, indent: u16) ->
buf.push_str(name); buf.push_str(name);
} }
Apply((loc_pattern, loc_arg_patterns)) => { Apply(loc_pattern, loc_arg_patterns) => {
buf.push_str(&format_pattern(arena, loc_pattern.value, indent)); if apply_needs_parens {
buf.push('(');
}
for loc_arg in loc_arg_patterns { buf.push_str(&format_pattern(arena, &loc_pattern.value, indent, true));
for loc_arg in loc_arg_patterns.iter() {
buf.push(' '); buf.push(' ');
buf.push_str(&format_pattern(arena, &loc_arg.value, indent)); buf.push_str(&format_pattern(arena, &loc_arg.value, indent, true));
}
if apply_needs_parens {
buf.push(')');
} }
} }
RecordDestructure(loc_patterns) => { RecordDestructure(loc_patterns) => {
@ -479,7 +492,7 @@ fn format_pattern<'a>(arena: &'a Bump, pattern: &'a Pattern<'a>, indent: u16) ->
buf.push_str(", "); buf.push_str(", ");
} }
buf.push_str(&format_pattern(arena, &loc_pattern.value, indent)); buf.push_str(&format_pattern(arena, &loc_pattern.value, indent, true));
} }
buf.push_str(" }"); buf.push_str(" }");
@ -494,10 +507,10 @@ fn format_pattern<'a>(arena: &'a Bump, pattern: &'a Pattern<'a>, indent: u16) ->
// Space // Space
SpaceBefore(sub_pattern, spaces) => { SpaceBefore(sub_pattern, spaces) => {
buf.push_str(&format_spaces(arena, spaces.iter(), indent)); buf.push_str(&format_spaces(arena, spaces.iter(), indent));
buf.push_str(&format_pattern(arena, sub_pattern, indent)); buf.push_str(&format_pattern(arena, sub_pattern, indent, true));
} }
SpaceAfter(sub_pattern, spaces) => { SpaceAfter(sub_pattern, spaces) => {
buf.push_str(&format_pattern(arena, sub_pattern, indent)); buf.push_str(&format_pattern(arena, sub_pattern, indent, true));
buf.push_str(&format_spaces(arena, spaces.iter(), indent)); buf.push_str(&format_spaces(arena, spaces.iter(), indent));
} }

View file

@ -31,8 +31,8 @@ use parse::blankspace::{
use parse::ident::{ident, Ident, MaybeQualified}; use parse::ident::{ident, Ident, MaybeQualified};
use parse::number_literal::number_literal; use parse::number_literal::number_literal;
use parse::parser::{ use parse::parser::{
and, attempt, between, char, either, loc, map, map_with_arena, not_followed_by, one_of10, and, attempt, between, char, either, loc, map, map_with_arena, not_followed_by, one_of2,
one_of2, one_of4, one_of5, one_or_more, optional, sep_by0, skip_first, skip_second, string, one_of4, one_of5, one_of9, one_or_more, optional, sep_by0, skip_first, skip_second, string,
then, unexpected, unexpected_eof, zero_or_more, Either, Fail, FailReason, ParseResult, Parser, then, unexpected, unexpected_eof, zero_or_more, Either, Fail, FailReason, ParseResult, Parser,
State, State,
}; };
@ -69,8 +69,7 @@ fn loc_parse_expr_body_without_operators<'a>(
arena: &'a Bump, arena: &'a Bump,
state: State<'a>, state: State<'a>,
) -> ParseResult<'a, Located<Expr<'a>>> { ) -> ParseResult<'a, Located<Expr<'a>>> {
one_of10( one_of9(
loc_parenthetical_def(min_indent),
loc_parenthetical_expr(min_indent), loc_parenthetical_expr(min_indent),
loc(string_literal()), loc(string_literal()),
loc(number_literal()), loc(number_literal()),
@ -126,7 +125,7 @@ fn parse_expr<'a>(min_indent: u16, arena: &'a Bump, state: State<'a>) -> ParseRe
} }
pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located<Expr<'a>>> { pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located<Expr<'a>>> {
map_with_arena( then(
loc(and( loc(and(
between( between(
char('('), char('('),
@ -140,34 +139,121 @@ pub fn loc_parenthetical_expr<'a>(min_indent: u16) -> impl Parser<'a, Located<Ex
// There may optionally be function args after the ')' // There may optionally be function args after the ')'
// e.g. ((foo bar) baz) // e.g. ((foo bar) baz)
loc_function_args(min_indent), loc_function_args(min_indent),
// There may be a '.' for field access after it, e.g. `(foo).bar`, // If there aren't any args, there may be a '=' or ':' after it.
//
// (It's a syntax error to write e.g. `foo bar =` - so if there
// were any args, there is definitely no need to parse '=' or ':'!)
//
// Also, there may be a '.' for field access (e.g. `(foo).bar`),
// but we only want to look for that if there weren't any args, // but we only want to look for that if there weren't any args,
// as if there were any args they'd have consumed it anyway // as if there were any args they'd have consumed it anyway
// e.g. in `((foo bar) baz.blah)` the `.blah` will be consumed by the `baz` parser // e.g. in `((foo bar) baz.blah)` the `.blah` will be consumed by the `baz` parser
either(
one_or_more(skip_first(char('.'), unqualified_ident())), one_or_more(skip_first(char('.'), unqualified_ident())),
and(space0(min_indent), equals_with_indent()),
),
)), )),
)), )),
|arena, loc_expr_with_extras| { move |arena, state, loc_expr_with_extras| {
// We parse the parenthetical expression *and* the arguments after it // We parse the parenthetical expression *and* the arguments after it
// in one region, so that (for example) the region for Apply includes its args. // in one region, so that (for example) the region for Apply includes its args.
let (loc_expr, opt_extras) = loc_expr_with_extras.value; let (loc_expr, opt_extras) = loc_expr_with_extras.value;
match opt_extras { match opt_extras {
Some(Either::First(loc_args)) => Located { Some(Either::First(loc_args)) => Ok((
Located {
region: loc_expr_with_extras.region, region: loc_expr_with_extras.region,
value: Expr::Apply(arena.alloc((loc_expr, loc_args))), value: Expr::Apply(arena.alloc((loc_expr, loc_args))),
}, },
state,
)),
// '=' after optional spaces
Some(Either::Second(Either::Second((spaces_before_equals, equals_indent)))) => {
let region = loc_expr.region;
// Re-parse the Expr as a Pattern.
let pattern = match expr_to_pattern(arena, loc_expr.value) {
Ok(valid) => valid,
Err(fail) => return Err((fail, 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: region.clone(),
value,
};
// Continue parsing the expression as a Def.
let (spaces_after_equals, state) = space0(min_indent).parse(arena, state)?;
let (parsed_expr, state) =
parse_def_expr(min_indent, equals_indent, arena, state, loc_first_pattern)?;
let value = if spaces_after_equals.is_empty() {
parsed_expr
} else {
Expr::SpaceBefore(arena.alloc(parsed_expr), spaces_after_equals)
};
Ok((Located { value, region }, state))
}
// '.' and a record field immediately after ')', no optional spaces // '.' and a record field immediately after ')', no optional spaces
Some(Either::Second(fields)) => Located { Some(Either::Second(Either::First(fields))) => Ok((
Located {
region: loc_expr_with_extras.region, region: loc_expr_with_extras.region,
value: Expr::Field(arena.alloc(loc_expr), fields), value: Expr::Field(arena.alloc(loc_expr), fields),
}, },
None => loc_expr, state,
)),
None => Ok((loc_expr, state)),
} }
}, },
) )
} }
/// If the given Expr would parse the same way as a valid Pattern, convert it.
/// Example: (foo) could be either an Expr::Var("foo") or Pattern::Identifier("foo")
fn expr_to_pattern<'a>(arena: &'a Bump, expr: Expr<'a>) -> Result<Pattern<'a>, Fail> {
match expr {
Expr::Var(module_parts, value) => {
if module_parts.is_empty() {
Ok(Pattern::Identifier(value))
} else {
Ok(Pattern::QualifiedIdentifier(MaybeQualified {
module_parts,
value,
}))
}
}
Expr::Variant(module_parts, value) => Ok(Pattern::Variant(module_parts, value)),
Expr::Apply((loc_val, loc_args)) => {
let region = loc_val.region.clone();
let value = expr_to_pattern(arena, loc_val.value.clone())?;
let val_pattern = arena.alloc(Located { region, value });
let mut arg_patterns = Vec::with_capacity_in(loc_args.len(), arena);
for loc_arg in loc_args {
let region = loc_arg.region.clone();
let value = expr_to_pattern(arena, loc_arg.value.clone())?;
arg_patterns.push(Located { region, value });
}
let pattern = Pattern::Apply(val_pattern, arg_patterns.into_bump_slice());
Ok(pattern)
}
_ => panic!("TODO handle expr_to_pattern for {:?}", expr),
}
}
/// A def beginning with a parenthetical pattern, for example: /// A def beginning with a parenthetical pattern, for example:
/// ///
/// (UserId userId) = ... /// (UserId userId) = ...