mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 21:39:07 +00:00
Make unit assignment optional for suffixed defs
This commit is contained in:
parent
237bd942ed
commit
42fdcb7ff1
15 changed files with 208 additions and 7 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2531,6 +2531,7 @@ version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bumpalo",
|
"bumpalo",
|
||||||
"roc_collections",
|
"roc_collections",
|
||||||
|
"roc_error_macros",
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
|
|
|
@ -1093,6 +1093,8 @@ fn desugar_pattern<'a>(
|
||||||
SpaceAfter(sub_pattern, _spaces) => {
|
SpaceAfter(sub_pattern, _spaces) => {
|
||||||
desugar_pattern(arena, *sub_pattern, src, line_info, module_path)
|
desugar_pattern(arena, *sub_pattern, src, line_info, module_path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stmt(_) => unreachable!("should have been handled in the parser"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::num::{
|
||||||
ParsedNumResult,
|
ParsedNumResult,
|
||||||
};
|
};
|
||||||
use crate::scope::{PendingAbilitiesInScope, Scope};
|
use crate::scope::{PendingAbilitiesInScope, Scope};
|
||||||
|
use roc_error_macros::internal_error;
|
||||||
use roc_exhaustive::ListArity;
|
use roc_exhaustive::ListArity;
|
||||||
use roc_module::ident::{Ident, Lowercase, TagName};
|
use roc_module::ident::{Ident, Lowercase, TagName};
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
|
@ -882,6 +883,8 @@ pub fn canonicalize_pattern<'a>(
|
||||||
let problem = MalformedPatternProblem::QualifiedIdentifier;
|
let problem = MalformedPatternProblem::QualifiedIdentifier;
|
||||||
malformed_pattern(env, problem, region)
|
malformed_pattern(env, problem, region)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stmt(_) => internal_error!("should have been handled in the parser"),
|
||||||
};
|
};
|
||||||
|
|
||||||
Loc {
|
Loc {
|
||||||
|
|
|
@ -12,5 +12,6 @@ roc_collections = { path = "../collections" }
|
||||||
roc_module = { path = "../module" }
|
roc_module = { path = "../module" }
|
||||||
roc_parse = { path = "../parse" }
|
roc_parse = { path = "../parse" }
|
||||||
roc_region = { path = "../region" }
|
roc_region = { path = "../region" }
|
||||||
|
roc_error_macros = { path = "../../error_macros" }
|
||||||
|
|
||||||
bumpalo.workspace = true
|
bumpalo.workspace = true
|
||||||
|
|
|
@ -357,9 +357,19 @@ pub fn fmt_defs(buf: &mut Buf, defs: &Defs, indent: u16) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_body<'a>(buf: &mut Buf, pattern: &'a Pattern<'a>, body: &'a Expr<'a>, indent: u16) {
|
pub fn fmt_body<'a>(buf: &mut Buf, pattern: &'a Pattern<'a>, body: &'a Expr<'a>, indent: u16) {
|
||||||
|
// Check if this is an assignment into the unit/empty record, format as a Statement
|
||||||
|
let is_statement = if let Pattern::RecordDestructure(collection) = pattern {
|
||||||
|
collection.is_empty()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't format the `{} =` for defs with this pattern
|
||||||
|
if !is_statement {
|
||||||
pattern.format_with_options(buf, Parens::InApply, Newlines::No, indent);
|
pattern.format_with_options(buf, Parens::InApply, Newlines::No, indent);
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str(" =");
|
buf.push_str(" =");
|
||||||
|
}
|
||||||
|
|
||||||
if body.is_multiline() {
|
if body.is_multiline() {
|
||||||
match body {
|
match body {
|
||||||
|
|
|
@ -2,6 +2,7 @@ use crate::annotation::{Formattable, Newlines, Parens};
|
||||||
use crate::expr::{fmt_str_literal, format_sq_literal};
|
use crate::expr::{fmt_str_literal, format_sq_literal};
|
||||||
use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT};
|
use crate::spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT};
|
||||||
use crate::Buf;
|
use crate::Buf;
|
||||||
|
use roc_error_macros::internal_error;
|
||||||
use roc_parse::ast::{Base, CommentOrNewline, Pattern, PatternAs};
|
use roc_parse::ast::{Base, CommentOrNewline, Pattern, PatternAs};
|
||||||
|
|
||||||
pub fn fmt_pattern<'a>(buf: &mut Buf, pattern: &'a Pattern<'a>, indent: u16, parens: Parens) {
|
pub fn fmt_pattern<'a>(buf: &mut Buf, pattern: &'a Pattern<'a>, indent: u16, parens: Parens) {
|
||||||
|
@ -81,6 +82,7 @@ impl<'a> Formattable for Pattern<'a> {
|
||||||
Pattern::Tuple(patterns) | Pattern::List(patterns) => {
|
Pattern::Tuple(patterns) | Pattern::List(patterns) => {
|
||||||
patterns.iter().any(|p| p.is_multiline())
|
patterns.iter().any(|p| p.is_multiline())
|
||||||
}
|
}
|
||||||
|
Pattern::Stmt(_) => true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,6 +281,10 @@ impl<'a> Formattable for Pattern<'a> {
|
||||||
|
|
||||||
buf.push_str(ident);
|
buf.push_str(ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Statement e.g. Suffixed with optional `{}=`
|
||||||
|
// e.g. `Stdout.line! "Hello World"`
|
||||||
|
Stmt(_) => internal_error!("should be desugared in parser into alternate pattern"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -837,6 +837,7 @@ impl<'a> RemoveSpaces<'a> for Pattern<'a> {
|
||||||
opt_pattern_as
|
opt_pattern_as
|
||||||
.map(|(_, pattern_as)| ([].as_ref(), pattern_as.remove_spaces(arena))),
|
.map(|(_, pattern_as)| ([].as_ref(), pattern_as.remove_spaces(arena))),
|
||||||
),
|
),
|
||||||
|
Pattern::Stmt(a) => Pattern::Stmt(a),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -872,6 +872,10 @@ impl<'a> PatternAs<'a> {
|
||||||
pub enum Pattern<'a> {
|
pub enum Pattern<'a> {
|
||||||
// Identifier
|
// Identifier
|
||||||
Identifier(&'a str),
|
Identifier(&'a str),
|
||||||
|
QualifiedIdentifier {
|
||||||
|
module_name: &'a str,
|
||||||
|
ident: &'a str,
|
||||||
|
},
|
||||||
|
|
||||||
Tag(&'a str),
|
Tag(&'a str),
|
||||||
|
|
||||||
|
@ -923,10 +927,9 @@ pub enum Pattern<'a> {
|
||||||
// Malformed
|
// Malformed
|
||||||
Malformed(&'a str),
|
Malformed(&'a str),
|
||||||
MalformedIdent(&'a str, crate::ident::BadIdent),
|
MalformedIdent(&'a str, crate::ident::BadIdent),
|
||||||
QualifiedIdentifier {
|
|
||||||
module_name: &'a str,
|
// Statement e.g. `Stdout.line! "Hello"`
|
||||||
ident: &'a str,
|
Stmt(&'a str),
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
@ -1126,6 +1129,8 @@ impl<'a> Pattern<'a> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stmt(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1773,6 +1778,8 @@ impl<'a> Malformed for Pattern<'a> {
|
||||||
Malformed(_) |
|
Malformed(_) |
|
||||||
MalformedIdent(_, _) |
|
MalformedIdent(_, _) |
|
||||||
QualifiedIdentifier { .. } => true,
|
QualifiedIdentifier { .. } => true,
|
||||||
|
|
||||||
|
Stmt(_) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -626,6 +626,40 @@ pub fn parse_single_def<'a>(
|
||||||
// a hacky way to get expression-based error messages. TODO fix this
|
// a hacky way to get expression-based error messages. TODO fix this
|
||||||
Ok((NoProgress, None, initial))
|
Ok((NoProgress, None, initial))
|
||||||
}
|
}
|
||||||
|
// Check if we have a Statement with Suffixed first,
|
||||||
|
// re-parse the state as an expression
|
||||||
|
// and then use a `{}=` pattern for the ValueDef::Body.
|
||||||
|
Ok((
|
||||||
|
MadeProgress,
|
||||||
|
Loc {
|
||||||
|
region,
|
||||||
|
value: Pattern::Stmt(_),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
_,
|
||||||
|
)) => {
|
||||||
|
let parse_def_expr =
|
||||||
|
space0_before_e(increment_min_indent(expr_start(options)), EExpr::IndentEnd);
|
||||||
|
|
||||||
|
let (_, loc_def_expr, updated_state) =
|
||||||
|
parse_def_expr.parse(arena, state, min_indent)?;
|
||||||
|
|
||||||
|
let loc_pattern = Loc::at(region, Pattern::RecordDestructure(Collection::empty()));
|
||||||
|
|
||||||
|
let value_def = ValueDef::Body(arena.alloc(loc_pattern), &*arena.alloc(loc_def_expr));
|
||||||
|
|
||||||
|
let region = Region::span_across(&loc_pattern.region, &loc_def_expr.region);
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
MadeProgress,
|
||||||
|
Some(SingleDef {
|
||||||
|
type_or_value: Either::Second(value_def),
|
||||||
|
region,
|
||||||
|
spaces_before: spaces_before_current,
|
||||||
|
}),
|
||||||
|
updated_state,
|
||||||
|
))
|
||||||
|
}
|
||||||
Ok((_, loc_pattern, state)) => {
|
Ok((_, loc_pattern, state)) => {
|
||||||
// First let's check whether this is an ability definition.
|
// First let's check whether this is an ability definition.
|
||||||
let opt_tag_and_args: Option<(&str, Region, &[Loc<Pattern>])> = match loc_pattern.value
|
let opt_tag_and_args: Option<(&str, Region, &[Loc<Pattern>])> = match loc_pattern.value
|
||||||
|
|
|
@ -12,6 +12,7 @@ use crate::string_literal::StrLikeLiteral;
|
||||||
use bumpalo::collections::string::String;
|
use bumpalo::collections::string::String;
|
||||||
use bumpalo::collections::Vec;
|
use bumpalo::collections::Vec;
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
use roc_error_macros::internal_error;
|
||||||
use roc_region::all::{Loc, Region};
|
use roc_region::all::{Loc, Region};
|
||||||
|
|
||||||
/// Different patterns are supported in different circumstances.
|
/// Different patterns are supported in different circumstances.
|
||||||
|
@ -49,6 +50,11 @@ pub fn loc_pattern_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>>
|
||||||
|
|
||||||
let pattern_state = state.clone();
|
let pattern_state = state.clone();
|
||||||
|
|
||||||
|
// Return early with the suffixed statement
|
||||||
|
if let Pattern::Stmt(_) = pattern.value {
|
||||||
|
return Ok((MadeProgress, pattern, pattern_state));
|
||||||
|
}
|
||||||
|
|
||||||
let (pattern_spaces, state) =
|
let (pattern_spaces, state) =
|
||||||
match space0_e(EPattern::AsKeyword).parse(arena, state, min_indent) {
|
match space0_e(EPattern::AsKeyword).parse(arena, state, min_indent) {
|
||||||
Err(_) => return Ok((MadeProgress, pattern, pattern_state)),
|
Err(_) => return Ok((MadeProgress, pattern, pattern_state)),
|
||||||
|
@ -385,6 +391,39 @@ fn loc_ident_pattern_help<'a>(
|
||||||
Ok((MadeProgress, loc_pat, state))
|
Ok((MadeProgress, loc_pat, state))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Parse a statement that begins with a suffixed identifier, e.g. `Stdout.line! "Hello"`
|
||||||
|
Ident::Access {
|
||||||
|
module_name,
|
||||||
|
parts,
|
||||||
|
suffixed,
|
||||||
|
..
|
||||||
|
} if suffixed => {
|
||||||
|
if module_name.is_empty() && parts.len() == 1 {
|
||||||
|
if let Accessor::RecordField(var) = &parts[0] {
|
||||||
|
return Ok((
|
||||||
|
MadeProgress,
|
||||||
|
Loc {
|
||||||
|
region: loc_ident.region,
|
||||||
|
value: Pattern::Stmt(var),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
internal_error!("unexpected suffixed TupleIndex");
|
||||||
|
}
|
||||||
|
} else if let Accessor::RecordField(var) = &parts[0] {
|
||||||
|
return Ok((
|
||||||
|
MadeProgress,
|
||||||
|
Loc {
|
||||||
|
region: loc_ident.region,
|
||||||
|
value: Pattern::Stmt(arena.alloc(format!("{}.{}", module_name, var))),
|
||||||
|
},
|
||||||
|
state,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
internal_error!("unexpected suffixed TupleIndex");
|
||||||
|
}
|
||||||
|
}
|
||||||
Ident::Access {
|
Ident::Access {
|
||||||
module_name, parts, ..
|
module_name, parts, ..
|
||||||
} => {
|
} => {
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
A.x! "Foo" []
|
||||||
|
B.y! "Bar" \a -> x + y
|
|
@ -0,0 +1,91 @@
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
Index(2147483648),
|
||||||
|
Index(2147483649),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-19,
|
||||||
|
@20-45,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
Slice(start = 0, length = 1),
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice(start = 0, length = 0),
|
||||||
|
Slice(start = 1, length = 0),
|
||||||
|
],
|
||||||
|
spaces: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Body(
|
||||||
|
@0-4 RecordDestructure(
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
@0-19 Apply(
|
||||||
|
@0-4 Suffixed(
|
||||||
|
Var {
|
||||||
|
module_name: "A",
|
||||||
|
ident: "x",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
[
|
||||||
|
@8-13 Str(
|
||||||
|
PlainLine(
|
||||||
|
"Foo",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
@17-19 List(
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Body(
|
||||||
|
@20-24 RecordDestructure(
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
@20-45 Apply(
|
||||||
|
@20-24 Suffixed(
|
||||||
|
Var {
|
||||||
|
module_name: "B",
|
||||||
|
ident: "y",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
[
|
||||||
|
@27-32 Str(
|
||||||
|
PlainLine(
|
||||||
|
"Bar",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
@34-45 Closure(
|
||||||
|
[
|
||||||
|
@35-36 Identifier(
|
||||||
|
"a",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@40-45 BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@40-41 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "x",
|
||||||
|
},
|
||||||
|
@42-43 Plus,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@44-45 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "y",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
A.x! "Foo" []
|
||||||
|
B.y! "Bar" \a -> x + y
|
|
@ -442,6 +442,7 @@ mod test_snapshots {
|
||||||
pass/sub_var_with_spaces.expr,
|
pass/sub_var_with_spaces.expr,
|
||||||
pass/sub_with_spaces.expr,
|
pass/sub_with_spaces.expr,
|
||||||
pass/suffixed.expr,
|
pass/suffixed.expr,
|
||||||
|
pass/suffixed_def_optional_bang.moduledefs,
|
||||||
pass/suffixed_nested.expr,
|
pass/suffixed_nested.expr,
|
||||||
pass/tag_pattern.expr,
|
pass/tag_pattern.expr,
|
||||||
pass/ten_times_eleven.expr,
|
pass/ten_times_eleven.expr,
|
||||||
|
|
|
@ -777,6 +777,7 @@ impl IterTokens for Loc<Pattern<'_>> {
|
||||||
}
|
}
|
||||||
Pattern::QualifiedIdentifier { .. } => onetoken(Token::Variable, region, arena),
|
Pattern::QualifiedIdentifier { .. } => onetoken(Token::Variable, region, arena),
|
||||||
Pattern::Malformed(_) | Pattern::MalformedIdent(_, _) => bumpvec![in arena;],
|
Pattern::Malformed(_) | Pattern::MalformedIdent(_, _) => bumpvec![in arena;],
|
||||||
|
Pattern::Stmt(_) => onetoken(Token::Operator, region, arena),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue