mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Fix parenthetical defs
This commit is contained in:
parent
9a564df7bb
commit
f4f83d70ee
3 changed files with 129 additions and 30 deletions
|
@ -994,7 +994,7 @@ fn add_idents_from_pattern<'a>(
|
|||
&QualifiedIdentifier(_name) => {
|
||||
panic!("TODO implement QualifiedIdentifier pattern.");
|
||||
}
|
||||
&Apply(_) => {
|
||||
&Apply(_, _) => {
|
||||
panic!("TODO implement Apply pattern.");
|
||||
// &AppliedVariant(_, ref opt_loc_args) => match opt_loc_args {
|
||||
// &None => (),
|
||||
|
@ -1033,7 +1033,7 @@ fn remove_idents(pattern: &ast::Pattern, idents: &mut ImMap<Ident, (Symbol, Regi
|
|||
QualifiedIdentifier(_name) => {
|
||||
panic!("TODO implement QualifiedIdentifier pattern in remove_idents.");
|
||||
}
|
||||
Apply(_) => {
|
||||
Apply(_, _) => {
|
||||
panic!("TODO implement Apply pattern in remove_idents.");
|
||||
// AppliedVariant(_, Some(loc_args)) => {
|
||||
// for loc_arg in loc_args {
|
||||
|
|
|
@ -106,7 +106,7 @@ pub enum Pattern<'a> {
|
|||
|
||||
// Variant, optionally qualified
|
||||
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
|
||||
/// around the destructured names, e.g. { x ### x does stuff ###, y }
|
||||
/// 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('\\');
|
||||
|
||||
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(' ');
|
||||
}
|
||||
|
@ -432,7 +432,7 @@ pub fn format_def<'a>(arena: &'a Bump, def: &'a Def<'a>, indent: u16) -> String<
|
|||
match def {
|
||||
Def::AnnotationOnly(_region) => panic!("TODO have format_def support AnnotationOnly"),
|
||||
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(&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
|
||||
}
|
||||
|
||||
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::*;
|
||||
|
||||
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);
|
||||
}
|
||||
Apply((loc_pattern, loc_arg_patterns)) => {
|
||||
buf.push_str(&format_pattern(arena, loc_pattern.value, indent));
|
||||
Apply(loc_pattern, loc_arg_patterns) => {
|
||||
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_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) => {
|
||||
|
@ -479,7 +492,7 @@ fn format_pattern<'a>(arena: &'a Bump, pattern: &'a Pattern<'a>, indent: u16) ->
|
|||
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(" }");
|
||||
|
@ -494,10 +507,10 @@ fn format_pattern<'a>(arena: &'a Bump, pattern: &'a Pattern<'a>, indent: u16) ->
|
|||
// Space
|
||||
SpaceBefore(sub_pattern, spaces) => {
|
||||
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) => {
|
||||
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));
|
||||
}
|
||||
|
||||
|
|
106
src/parse/mod.rs
106
src/parse/mod.rs
|
@ -31,8 +31,8 @@ use parse::blankspace::{
|
|||
use parse::ident::{ident, Ident, MaybeQualified};
|
||||
use parse::number_literal::number_literal;
|
||||
use parse::parser::{
|
||||
and, attempt, between, char, either, loc, map, map_with_arena, not_followed_by, one_of10,
|
||||
one_of2, one_of4, one_of5, one_or_more, optional, sep_by0, skip_first, skip_second, string,
|
||||
and, attempt, between, char, either, loc, map, map_with_arena, not_followed_by, one_of2,
|
||||
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,
|
||||
State,
|
||||
};
|
||||
|
@ -69,8 +69,7 @@ fn loc_parse_expr_body_without_operators<'a>(
|
|||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Expr<'a>>> {
|
||||
one_of10(
|
||||
loc_parenthetical_def(min_indent),
|
||||
one_of9(
|
||||
loc_parenthetical_expr(min_indent),
|
||||
loc(string_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>>> {
|
||||
map_with_arena(
|
||||
then(
|
||||
loc(and(
|
||||
between(
|
||||
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 ')'
|
||||
// e.g. ((foo bar) baz)
|
||||
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,
|
||||
// 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
|
||||
either(
|
||||
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
|
||||
// in one region, so that (for example) the region for Apply includes its args.
|
||||
let (loc_expr, opt_extras) = loc_expr_with_extras.value;
|
||||
|
||||
match opt_extras {
|
||||
Some(Either::First(loc_args)) => Located {
|
||||
Some(Either::First(loc_args)) => Ok((
|
||||
Located {
|
||||
region: loc_expr_with_extras.region,
|
||||
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
|
||||
Some(Either::Second(fields)) => Located {
|
||||
Some(Either::Second(Either::First(fields))) => Ok((
|
||||
Located {
|
||||
region: loc_expr_with_extras.region,
|
||||
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:
|
||||
///
|
||||
/// (UserId userId) = ...
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue