mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +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) => {
|
&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 {
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
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::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) = ...
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue