mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
Merge pull request #7291 from joshuawarner32/reland-fuzzing-fixes-2
Reland fuzzing fixes from #7267
This commit is contained in:
commit
48657a57a8
347 changed files with 8206 additions and 1157 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2559,6 +2559,7 @@ dependencies = [
|
||||||
"roc_module",
|
"roc_module",
|
||||||
"roc_parse",
|
"roc_parse",
|
||||||
"roc_region",
|
"roc_region",
|
||||||
|
"soa",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -1104,10 +1104,21 @@ pub fn desugar_expr<'a>(
|
||||||
// Allow naked dbg, necessary for piping values into dbg with the `Pizza` binop
|
// Allow naked dbg, necessary for piping values into dbg with the `Pizza` binop
|
||||||
loc_expr
|
loc_expr
|
||||||
}
|
}
|
||||||
DbgStmt(condition, continuation) => {
|
DbgStmt {
|
||||||
|
first: condition,
|
||||||
|
extra_args,
|
||||||
|
continuation,
|
||||||
|
} => {
|
||||||
let desugared_condition = &*env.arena.alloc(desugar_expr(env, scope, condition));
|
let desugared_condition = &*env.arena.alloc(desugar_expr(env, scope, condition));
|
||||||
let desugared_continuation = &*env.arena.alloc(desugar_expr(env, scope, continuation));
|
let desugared_continuation = &*env.arena.alloc(desugar_expr(env, scope, continuation));
|
||||||
|
|
||||||
|
if let Some(last) = extra_args.last() {
|
||||||
|
let args_region = Region::span_across(&condition.region, &last.region);
|
||||||
|
env.problem(Problem::OverAppliedDbg {
|
||||||
|
region: args_region,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
env.arena.alloc(Loc {
|
env.arena.alloc(Loc {
|
||||||
value: *desugar_dbg_stmt(env, desugared_condition, desugared_continuation),
|
value: *desugar_dbg_stmt(env, desugared_condition, desugared_continuation),
|
||||||
region: loc_expr.region,
|
region: loc_expr.region,
|
||||||
|
|
|
@ -1245,7 +1245,7 @@ pub fn canonicalize_expr<'a>(
|
||||||
|
|
||||||
(loc_expr.value, output)
|
(loc_expr.value, output)
|
||||||
}
|
}
|
||||||
ast::Expr::DbgStmt(_, _) => {
|
ast::Expr::DbgStmt { .. } => {
|
||||||
internal_error!("DbgStmt should have been desugared by now")
|
internal_error!("DbgStmt should have been desugared by now")
|
||||||
}
|
}
|
||||||
ast::Expr::LowLevelDbg((source_location, source), message, continuation) => {
|
ast::Expr::LowLevelDbg((source_location, source), message, continuation) => {
|
||||||
|
@ -2546,7 +2546,7 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
|
||||||
| ast::Expr::Tag(_)
|
| ast::Expr::Tag(_)
|
||||||
| ast::Expr::OpaqueRef(_) => true,
|
| ast::Expr::OpaqueRef(_) => true,
|
||||||
// Newlines are disallowed inside interpolation, and these all require newlines
|
// Newlines are disallowed inside interpolation, and these all require newlines
|
||||||
ast::Expr::DbgStmt(_, _)
|
ast::Expr::DbgStmt { .. }
|
||||||
| ast::Expr::LowLevelDbg(_, _, _)
|
| ast::Expr::LowLevelDbg(_, _, _)
|
||||||
| ast::Expr::Return(_, _)
|
| ast::Expr::Return(_, _)
|
||||||
| ast::Expr::When(_, _)
|
| ast::Expr::When(_, _)
|
||||||
|
|
|
@ -15,3 +15,4 @@ roc_region.workspace = true
|
||||||
roc_error_macros.workspace = true
|
roc_error_macros.workspace = true
|
||||||
|
|
||||||
bumpalo.workspace = true
|
bumpalo.workspace = true
|
||||||
|
soa.workspace = true
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
collection::{fmt_collection, Braces},
|
collection::{fmt_collection, Braces},
|
||||||
|
expr::merge_spaces_conservative,
|
||||||
spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT},
|
spaces::{fmt_comments_only, fmt_spaces, NewlineAt, INDENT},
|
||||||
Buf,
|
Buf,
|
||||||
};
|
};
|
||||||
|
use bumpalo::{collections::Vec, Bump};
|
||||||
use roc_parse::ast::{
|
use roc_parse::ast::{
|
||||||
AbilityImpls, AssignedField, Collection, Expr, ExtractSpaces, FunctionArrow,
|
AbilityImpls, AssignedField, Collection, CommentOrNewline, Expr, ExtractSpaces, FunctionArrow,
|
||||||
ImplementsAbilities, ImplementsAbility, ImplementsClause, Tag, TypeAnnotation, TypeHeader,
|
ImplementsAbilities, ImplementsAbility, ImplementsClause, Spaceable, Spaces, SpacesAfter,
|
||||||
|
SpacesBefore, Tag, TypeAnnotation, TypeHeader,
|
||||||
};
|
};
|
||||||
use roc_parse::ident::UppercaseIdent;
|
use roc_parse::ident::UppercaseIdent;
|
||||||
use roc_region::all::Loc;
|
use roc_region::all::Loc;
|
||||||
|
@ -35,6 +38,7 @@ use roc_region::all::Loc;
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
pub enum Parens {
|
pub enum Parens {
|
||||||
NotNeeded,
|
NotNeeded,
|
||||||
|
InCollection,
|
||||||
InFunctionType,
|
InFunctionType,
|
||||||
InApply,
|
InApply,
|
||||||
InOperator,
|
InOperator,
|
||||||
|
@ -148,7 +152,10 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
Wildcard | Inferred | BoundVariable(_) | Malformed(_) => false,
|
TypeAnnotation::Wildcard
|
||||||
|
| TypeAnnotation::Inferred
|
||||||
|
| BoundVariable(_)
|
||||||
|
| Malformed(_) => false,
|
||||||
Function(args, _arrow, result) => {
|
Function(args, _arrow, result) => {
|
||||||
result.value.is_multiline()
|
result.value.is_multiline()
|
||||||
|| args.iter().any(|loc_arg| loc_arg.value.is_multiline())
|
|| args.iter().any(|loc_arg| loc_arg.value.is_multiline())
|
||||||
|
@ -165,8 +172,7 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|
||||||
Some(ann) if ann.value.is_multiline() => return true,
|
Some(ann) if ann.value.is_multiline() => return true,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
is_collection_multiline(fields)
|
||||||
fields.items.iter().any(|field| field.value.is_multiline())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Record { fields, ext } => {
|
Record { fields, ext } => {
|
||||||
|
@ -175,7 +181,7 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
fields.items.iter().any(|field| field.value.is_multiline())
|
is_collection_multiline(fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
TagUnion { tags, ext } => {
|
TagUnion { tags, ext } => {
|
||||||
|
@ -184,221 +190,333 @@ impl<'a> Formattable for TypeAnnotation<'a> {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
tags.iter().any(|tag| tag.value.is_multiline())
|
!tags.final_comments().is_empty() || tags.iter().any(|tag| tag.value.is_multiline())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
|
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
|
||||||
use roc_parse::ast::TypeAnnotation::*;
|
fmt_ty_ann(self, buf, indent, parens, newlines, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let self_is_multiline = self.is_multiline();
|
fn fmt_ty_ann(
|
||||||
|
me: &TypeAnnotation<'_>,
|
||||||
|
buf: &mut Buf<'_>,
|
||||||
|
indent: u16,
|
||||||
|
parens: Parens,
|
||||||
|
newlines: Newlines,
|
||||||
|
newline_at_top: bool,
|
||||||
|
) {
|
||||||
|
let me = ann_lift_spaces(buf.text.bump(), me);
|
||||||
|
|
||||||
match self {
|
let self_is_multiline = me.item.is_multiline();
|
||||||
Function(args, arrow, ret) => {
|
|
||||||
let needs_parens = parens != Parens::NotNeeded;
|
|
||||||
|
|
||||||
buf.indent(indent);
|
if !me.before.is_empty() {
|
||||||
|
buf.ensure_ends_with_newline();
|
||||||
|
fmt_comments_only(buf, me.before.iter(), NewlineAt::Bottom, indent);
|
||||||
|
}
|
||||||
|
if newline_at_top {
|
||||||
|
buf.ensure_ends_with_newline();
|
||||||
|
}
|
||||||
|
|
||||||
if needs_parens {
|
match &me.item {
|
||||||
buf.push('(')
|
TypeAnnotation::SpaceBefore(_ann, _spaces) | TypeAnnotation::SpaceAfter(_ann, _spaces) => {
|
||||||
}
|
unreachable!()
|
||||||
|
}
|
||||||
|
TypeAnnotation::Function(args, arrow, ret) => {
|
||||||
|
let needs_parens = parens != Parens::NotNeeded;
|
||||||
|
|
||||||
let mut it = args.iter().enumerate().peekable();
|
buf.indent(indent);
|
||||||
|
|
||||||
while let Some((index, argument)) = it.next() {
|
if needs_parens {
|
||||||
let is_first = index == 0;
|
buf.push('(')
|
||||||
let is_multiline = &argument.value.is_multiline();
|
|
||||||
|
|
||||||
if !is_first && !is_multiline && self_is_multiline {
|
|
||||||
buf.newline();
|
|
||||||
}
|
|
||||||
|
|
||||||
argument.value.format_with_options(
|
|
||||||
buf,
|
|
||||||
Parens::InFunctionType,
|
|
||||||
Newlines::Yes,
|
|
||||||
indent,
|
|
||||||
);
|
|
||||||
|
|
||||||
if it.peek().is_some() {
|
|
||||||
buf.push_str(",");
|
|
||||||
if !self_is_multiline {
|
|
||||||
buf.spaces(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self_is_multiline {
|
|
||||||
buf.newline();
|
|
||||||
buf.indent(indent);
|
|
||||||
} else {
|
|
||||||
buf.spaces(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
match arrow {
|
|
||||||
FunctionArrow::Pure => buf.push_str("->"),
|
|
||||||
FunctionArrow::Effectful => buf.push_str("=>"),
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.spaces(1);
|
|
||||||
|
|
||||||
ret.value
|
|
||||||
.format_with_options(buf, Parens::InFunctionType, Newlines::No, indent);
|
|
||||||
|
|
||||||
if needs_parens {
|
|
||||||
buf.push(')')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Apply(pkg, name, arguments) => {
|
|
||||||
buf.indent(indent);
|
|
||||||
let write_parens = parens == Parens::InApply && !arguments.is_empty();
|
|
||||||
|
|
||||||
if write_parens {
|
for (index, argument) in args.iter().enumerate() {
|
||||||
buf.push('(')
|
let is_first = index == 0;
|
||||||
}
|
|
||||||
|
|
||||||
if !pkg.is_empty() {
|
if !is_first {
|
||||||
buf.push_str(pkg);
|
buf.push_str(",");
|
||||||
buf.push('.');
|
if !self_is_multiline {
|
||||||
}
|
|
||||||
|
|
||||||
buf.push_str(name);
|
|
||||||
|
|
||||||
let needs_indent = except_last(arguments).any(|a| a.is_multiline())
|
|
||||||
|| arguments
|
|
||||||
.last()
|
|
||||||
.map(|a| {
|
|
||||||
a.is_multiline()
|
|
||||||
&& (!a.extract_spaces().before.is_empty()
|
|
||||||
|| !is_outdentable(&a.value))
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let arg_indent = if needs_indent {
|
|
||||||
indent + INDENT
|
|
||||||
} else {
|
|
||||||
indent
|
|
||||||
};
|
|
||||||
|
|
||||||
for arg in arguments.iter() {
|
|
||||||
if needs_indent {
|
|
||||||
let arg = arg.extract_spaces();
|
|
||||||
fmt_spaces(buf, arg.before.iter(), arg_indent);
|
|
||||||
buf.ensure_ends_with_newline();
|
|
||||||
arg.item.format_with_options(
|
|
||||||
buf,
|
|
||||||
Parens::InApply,
|
|
||||||
Newlines::Yes,
|
|
||||||
arg_indent,
|
|
||||||
);
|
|
||||||
fmt_spaces(buf, arg.after.iter(), arg_indent);
|
|
||||||
} else {
|
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if write_parens {
|
let newline_at_top = !is_first && self_is_multiline;
|
||||||
buf.push(')')
|
|
||||||
}
|
fmt_ty_ann(
|
||||||
|
&argument.value,
|
||||||
|
buf,
|
||||||
|
indent,
|
||||||
|
Parens::InFunctionType,
|
||||||
|
Newlines::Yes,
|
||||||
|
newline_at_top,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
BoundVariable(v) => {
|
|
||||||
|
if self_is_multiline {
|
||||||
|
buf.newline();
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str(v)
|
} else {
|
||||||
}
|
|
||||||
Wildcard => {
|
|
||||||
buf.indent(indent);
|
|
||||||
buf.push('*')
|
|
||||||
}
|
|
||||||
Inferred => {
|
|
||||||
buf.indent(indent);
|
|
||||||
buf.push('_')
|
|
||||||
}
|
|
||||||
|
|
||||||
TagUnion { tags, ext } => {
|
|
||||||
fmt_collection(buf, indent, Braces::Square, *tags, newlines);
|
|
||||||
|
|
||||||
if let Some(loc_ext_ann) = *ext {
|
|
||||||
loc_ext_ann.value.format(buf, indent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Tuple { elems: fields, ext } => {
|
|
||||||
fmt_collection(buf, indent, Braces::Round, *fields, newlines);
|
|
||||||
|
|
||||||
if let Some(loc_ext_ann) = *ext {
|
|
||||||
loc_ext_ann.value.format(buf, indent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Record { fields, ext } => {
|
|
||||||
fmt_collection(buf, indent, Braces::Curly, *fields, newlines);
|
|
||||||
|
|
||||||
if let Some(loc_ext_ann) = *ext {
|
|
||||||
loc_ext_ann.value.format(buf, indent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
As(lhs, _spaces, TypeHeader { name, vars }) => {
|
|
||||||
// TODO use _spaces?
|
|
||||||
lhs.value
|
|
||||||
.format_with_options(buf, Parens::InFunctionType, Newlines::No, indent);
|
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
buf.push_str("as");
|
|
||||||
buf.spaces(1);
|
|
||||||
buf.push_str(name.value);
|
|
||||||
for var in *vars {
|
|
||||||
buf.spaces(1);
|
|
||||||
var.value
|
|
||||||
.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Where(annot, implements_clauses) => {
|
match arrow {
|
||||||
annot.format_with_options(buf, parens, newlines, indent);
|
FunctionArrow::Pure => buf.push_str("->"),
|
||||||
if implements_clauses
|
FunctionArrow::Effectful => buf.push_str("=>"),
|
||||||
.iter()
|
}
|
||||||
.any(|implements| implements.is_multiline())
|
|
||||||
{
|
buf.spaces(1);
|
||||||
buf.newline();
|
|
||||||
buf.indent(indent);
|
ret.value
|
||||||
|
.format_with_options(buf, Parens::InFunctionType, Newlines::No, indent);
|
||||||
|
|
||||||
|
if needs_parens {
|
||||||
|
buf.push(')')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TypeAnnotation::Apply(pkg, name, arguments) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
let write_parens = parens == Parens::InApply && !arguments.is_empty();
|
||||||
|
|
||||||
|
if write_parens {
|
||||||
|
buf.push('(')
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pkg.is_empty() {
|
||||||
|
buf.push_str(pkg);
|
||||||
|
buf.push('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.push_str(name);
|
||||||
|
|
||||||
|
let needs_indent = except_last(arguments).any(|a| a.is_multiline())
|
||||||
|
|| arguments
|
||||||
|
.last()
|
||||||
|
.map(|a| {
|
||||||
|
a.is_multiline()
|
||||||
|
&& (!a.extract_spaces().before.is_empty()
|
||||||
|
|| !ty_is_outdentable(&a.value))
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let arg_indent = if needs_indent {
|
||||||
|
indent + INDENT
|
||||||
|
} else {
|
||||||
|
indent
|
||||||
|
};
|
||||||
|
|
||||||
|
for arg in arguments.iter() {
|
||||||
|
if needs_indent {
|
||||||
|
let arg = arg.extract_spaces();
|
||||||
|
fmt_spaces(buf, arg.before.iter(), arg_indent);
|
||||||
|
buf.ensure_ends_with_newline();
|
||||||
|
arg.item
|
||||||
|
.format_with_options(buf, Parens::InApply, Newlines::Yes, arg_indent);
|
||||||
|
fmt_spaces(buf, arg.after.iter(), arg_indent);
|
||||||
} else {
|
} else {
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
}
|
arg.format_with_options(buf, Parens::InApply, Newlines::No, arg_indent);
|
||||||
for (i, has) in implements_clauses.iter().enumerate() {
|
|
||||||
buf.push_str(if i == 0 {
|
|
||||||
roc_parse::keyword::WHERE
|
|
||||||
} else {
|
|
||||||
","
|
|
||||||
});
|
|
||||||
buf.spaces(1);
|
|
||||||
has.format_with_options(buf, parens, newlines, indent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SpaceBefore(ann, spaces) => {
|
if write_parens {
|
||||||
buf.ensure_ends_with_newline();
|
buf.push(')')
|
||||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
|
||||||
ann.format_with_options(buf, parens, newlines, indent)
|
|
||||||
}
|
}
|
||||||
SpaceAfter(ann, spaces) => {
|
}
|
||||||
ann.format_with_options(buf, parens, newlines, indent);
|
TypeAnnotation::BoundVariable(v) => {
|
||||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
buf.indent(indent);
|
||||||
|
buf.push_str(v)
|
||||||
|
}
|
||||||
|
TypeAnnotation::Wildcard => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push('*')
|
||||||
|
}
|
||||||
|
TypeAnnotation::Inferred => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push('_')
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeAnnotation::TagUnion { tags, ext } => {
|
||||||
|
fmt_collection(buf, indent, Braces::Square, *tags, newlines);
|
||||||
|
fmt_ext(ext, buf, indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeAnnotation::Tuple { elems: fields, ext } => {
|
||||||
|
fmt_ty_collection(buf, indent, Braces::Round, *fields, newlines);
|
||||||
|
fmt_ext(ext, buf, indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeAnnotation::Record { fields, ext } => {
|
||||||
|
fmt_collection(buf, indent, Braces::Curly, *fields, newlines);
|
||||||
|
fmt_ext(ext, buf, indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeAnnotation::As(lhs, _spaces, TypeHeader { name, vars }) => {
|
||||||
|
// TODO use _spaces?
|
||||||
|
lhs.value
|
||||||
|
.format_with_options(buf, Parens::InFunctionType, Newlines::No, indent);
|
||||||
|
buf.spaces(1);
|
||||||
|
buf.push_str("as");
|
||||||
|
buf.spaces(1);
|
||||||
|
buf.push_str(name.value);
|
||||||
|
for var in *vars {
|
||||||
|
buf.spaces(1);
|
||||||
|
var.value
|
||||||
|
.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
||||||
}
|
}
|
||||||
Malformed(raw) => {
|
}
|
||||||
|
|
||||||
|
TypeAnnotation::Where(annot, implements_clauses) => {
|
||||||
|
annot.format_with_options(buf, parens, newlines, indent);
|
||||||
|
if implements_clauses
|
||||||
|
.iter()
|
||||||
|
.any(|implements| implements.is_multiline())
|
||||||
|
{
|
||||||
|
buf.newline();
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str(raw)
|
} else {
|
||||||
|
buf.spaces(1);
|
||||||
}
|
}
|
||||||
|
for (i, has) in implements_clauses.iter().enumerate() {
|
||||||
|
buf.push_str(if i == 0 {
|
||||||
|
roc_parse::keyword::WHERE
|
||||||
|
} else {
|
||||||
|
","
|
||||||
|
});
|
||||||
|
buf.spaces(1);
|
||||||
|
has.format_with_options(buf, parens, newlines, indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TypeAnnotation::Malformed(raw) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str(raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !me.after.is_empty() {
|
||||||
|
fmt_comments_only(buf, me.after.iter(), NewlineAt::Bottom, indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lower<'a, 'b: 'a>(
|
||||||
|
arena: &'b Bump,
|
||||||
|
lifted: Spaces<'b, TypeAnnotation<'b>>,
|
||||||
|
) -> TypeAnnotation<'b> {
|
||||||
|
if lifted.before.is_empty() && lifted.after.is_empty() {
|
||||||
|
return lifted.item;
|
||||||
|
}
|
||||||
|
if lifted.before.is_empty() {
|
||||||
|
return TypeAnnotation::SpaceAfter(arena.alloc(lifted.item), lifted.after);
|
||||||
|
}
|
||||||
|
if lifted.after.is_empty() {
|
||||||
|
return TypeAnnotation::SpaceBefore(arena.alloc(lifted.item), lifted.before);
|
||||||
|
}
|
||||||
|
TypeAnnotation::SpaceBefore(
|
||||||
|
arena.alloc(TypeAnnotation::SpaceAfter(
|
||||||
|
arena.alloc(lifted.item),
|
||||||
|
lifted.after,
|
||||||
|
)),
|
||||||
|
lifted.before,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_ty_collection(
|
||||||
|
buf: &mut Buf<'_>,
|
||||||
|
indent: u16,
|
||||||
|
braces: Braces,
|
||||||
|
items: Collection<'_, Loc<TypeAnnotation<'_>>>,
|
||||||
|
newlines: Newlines,
|
||||||
|
) {
|
||||||
|
let arena = buf.text.bump();
|
||||||
|
let mut new_items: Vec<'_, NodeSpaces<'_, Node<'_>>> =
|
||||||
|
Vec::with_capacity_in(items.len(), arena);
|
||||||
|
|
||||||
|
let mut last_after: &[CommentOrNewline<'_>] = &[];
|
||||||
|
|
||||||
|
for (i, item) in items.items.iter().enumerate() {
|
||||||
|
let func_ty_risky = i > 0;
|
||||||
|
let lifted = ann_lift_to_node(
|
||||||
|
if func_ty_risky {
|
||||||
|
Parens::InCollection
|
||||||
|
} else {
|
||||||
|
Parens::NotNeeded
|
||||||
|
},
|
||||||
|
arena,
|
||||||
|
&item.value,
|
||||||
|
);
|
||||||
|
let before = merge_spaces_conservative(arena, last_after, lifted.before);
|
||||||
|
last_after = lifted.after;
|
||||||
|
new_items.push(NodeSpaces {
|
||||||
|
before,
|
||||||
|
item: lifted.item,
|
||||||
|
after: &[],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let final_comments = merge_spaces_conservative(arena, last_after, items.final_comments());
|
||||||
|
|
||||||
|
let new_items =
|
||||||
|
Collection::with_items_and_comments(arena, new_items.into_bump_slice(), final_comments);
|
||||||
|
|
||||||
|
fmt_collection(buf, indent, braces, new_items, newlines)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_ext(ext: &Option<&Loc<TypeAnnotation<'_>>>, buf: &mut Buf<'_>, indent: u16) {
|
||||||
|
if let Some(loc_ext_ann) = *ext {
|
||||||
|
let me = ann_lift_spaces(buf.text.bump(), &loc_ext_ann.value);
|
||||||
|
let parens_needed = !me.before.is_empty() || ext_needs_parens(me.item);
|
||||||
|
if parens_needed {
|
||||||
|
// We need to make sure to not have whitespace before the ext of a type,
|
||||||
|
// since that would make it parse as something else.
|
||||||
|
buf.push('(');
|
||||||
|
loc_ext_ann.value.format(buf, indent + INDENT);
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push(')');
|
||||||
|
} else {
|
||||||
|
loc_ext_ann.value.format(buf, indent + INDENT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_outdentable(ann: &TypeAnnotation) -> bool {
|
fn ext_needs_parens(item: TypeAnnotation<'_>) -> bool {
|
||||||
matches!(
|
match item {
|
||||||
ann.extract_spaces().item,
|
TypeAnnotation::Record { .. }
|
||||||
TypeAnnotation::Tuple { .. } | TypeAnnotation::Record { .. }
|
| TypeAnnotation::TagUnion { .. }
|
||||||
)
|
| TypeAnnotation::Tuple { .. }
|
||||||
|
| TypeAnnotation::BoundVariable(..)
|
||||||
|
| TypeAnnotation::Wildcard
|
||||||
|
| TypeAnnotation::Inferred => false,
|
||||||
|
TypeAnnotation::Apply(_module, _func, args) => !args.is_empty(),
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ty_is_outdentable(mut rhs: &TypeAnnotation) -> bool {
|
||||||
|
loop {
|
||||||
|
match rhs {
|
||||||
|
TypeAnnotation::SpaceBefore(sub_def, spaces) => {
|
||||||
|
let is_only_newlines = spaces.iter().all(|s| s.is_newline());
|
||||||
|
if !is_only_newlines || !sub_def.is_multiline() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rhs = sub_def;
|
||||||
|
}
|
||||||
|
TypeAnnotation::SpaceAfter(sub_def, _) => {
|
||||||
|
rhs = sub_def;
|
||||||
|
}
|
||||||
|
TypeAnnotation::Where(ann, _clauses) => {
|
||||||
|
if !ann.is_multiline() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rhs = &ann.value;
|
||||||
|
}
|
||||||
|
TypeAnnotation::Record { .. }
|
||||||
|
| TypeAnnotation::TagUnion { .. }
|
||||||
|
| TypeAnnotation::Tuple { .. } => return rhs.is_multiline(),
|
||||||
|
_ => return false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fields are subtly different on the type and term level:
|
/// Fields are subtly different on the type and term level:
|
||||||
|
@ -466,6 +584,7 @@ fn format_assigned_field_help<T>(
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.spaces(separator_spaces);
|
buf.spaces(separator_spaces);
|
||||||
|
buf.indent(indent);
|
||||||
buf.push(':');
|
buf.push(':');
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
ann.value.format(buf, indent);
|
ann.value.format(buf, indent);
|
||||||
|
@ -483,6 +602,7 @@ fn format_assigned_field_help<T>(
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.spaces(separator_spaces);
|
buf.spaces(separator_spaces);
|
||||||
|
buf.indent(indent);
|
||||||
buf.push('?');
|
buf.push('?');
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
ann.value.format(buf, indent);
|
ann.value.format(buf, indent);
|
||||||
|
@ -704,3 +824,279 @@ pub fn except_last<T>(items: &[T]) -> impl Iterator<Item = &T> {
|
||||||
items[..items.len() - 1].iter()
|
items[..items.len() - 1].iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ann_lift_spaces<'a, 'b: 'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
ann: &TypeAnnotation<'b>,
|
||||||
|
) -> Spaces<'a, TypeAnnotation<'a>> {
|
||||||
|
match ann {
|
||||||
|
TypeAnnotation::Apply(module, func, args) => {
|
||||||
|
if args.is_empty() {
|
||||||
|
return Spaces {
|
||||||
|
item: *ann,
|
||||||
|
before: &[],
|
||||||
|
after: &[],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let mut new_args = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
|
if !args.is_empty() {
|
||||||
|
for arg in args.iter().take(args.len() - 1) {
|
||||||
|
let lifted = ann_lift_spaces(arena, &arg.value);
|
||||||
|
new_args.push(Loc::at(arg.region, lower(arena, lifted)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let after = if let Some(last) = args.last() {
|
||||||
|
let lifted = ann_lift_spaces(arena, &last.value);
|
||||||
|
if lifted.before.is_empty() {
|
||||||
|
new_args.push(Loc::at(last.region, lifted.item));
|
||||||
|
} else {
|
||||||
|
new_args.push(Loc::at(
|
||||||
|
last.region,
|
||||||
|
TypeAnnotation::SpaceBefore(arena.alloc(lifted.item), lifted.before),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
lifted.after
|
||||||
|
} else {
|
||||||
|
&[]
|
||||||
|
};
|
||||||
|
|
||||||
|
Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: TypeAnnotation::Apply(module, func, new_args.into_bump_slice()),
|
||||||
|
after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TypeAnnotation::SpaceBefore(expr, spaces) => {
|
||||||
|
let mut inner = ann_lift_spaces(arena, expr);
|
||||||
|
inner.before = merge_spaces_conservative(arena, spaces, inner.before);
|
||||||
|
inner
|
||||||
|
}
|
||||||
|
TypeAnnotation::SpaceAfter(expr, spaces) => {
|
||||||
|
let mut inner = ann_lift_spaces(arena, expr);
|
||||||
|
inner.after = merge_spaces_conservative(arena, inner.after, spaces);
|
||||||
|
inner
|
||||||
|
}
|
||||||
|
_ => Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: *ann,
|
||||||
|
after: &[],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ann_lift_spaces_before<'a, 'b: 'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
ann: &TypeAnnotation<'b>,
|
||||||
|
) -> SpacesBefore<'a, TypeAnnotation<'a>> {
|
||||||
|
let lifted = ann_lift_spaces(arena, ann);
|
||||||
|
SpacesBefore {
|
||||||
|
before: lifted.before,
|
||||||
|
item: lifted.item.maybe_after(arena, lifted.after),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ann_lift_spaces_after<'a, 'b: 'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
ann: &TypeAnnotation<'b>,
|
||||||
|
) -> SpacesAfter<'a, TypeAnnotation<'a>> {
|
||||||
|
let lifted = ann_lift_spaces(arena, ann);
|
||||||
|
SpacesAfter {
|
||||||
|
item: lifted.item.maybe_before(arena, lifted.before),
|
||||||
|
after: lifted.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Sp<'a> = &'a [CommentOrNewline<'a>];
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
enum Node<'a> {
|
||||||
|
DelimitedSequence(Braces, &'a [(Sp<'a>, Node<'a>)], Sp<'a>),
|
||||||
|
TypeAnnotation(TypeAnnotation<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Formattable for Node<'a> {
|
||||||
|
fn is_multiline(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Node::DelimitedSequence(_braces, lefts, right) => {
|
||||||
|
if !right.is_empty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (sp, l) in *lefts {
|
||||||
|
if l.is_multiline() || !sp.is_empty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Node::TypeAnnotation(type_annotation) => type_annotation.is_multiline(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
|
||||||
|
match self {
|
||||||
|
Node::DelimitedSequence(braces, lefts, right) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push(braces.start());
|
||||||
|
|
||||||
|
for (sp, l) in *lefts {
|
||||||
|
if !sp.is_empty() {
|
||||||
|
fmt_spaces(buf, sp.iter(), indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
l.format_with_options(buf, parens, newlines, indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !right.is_empty() {
|
||||||
|
fmt_spaces(buf, right.iter(), indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push(braces.end());
|
||||||
|
}
|
||||||
|
Node::TypeAnnotation(type_annotation) => {
|
||||||
|
type_annotation.format_with_options(buf, parens, newlines, indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ann_lift_to_node<'a, 'b: 'a>(
|
||||||
|
parens: Parens,
|
||||||
|
arena: &'a Bump,
|
||||||
|
ann: &TypeAnnotation<'b>,
|
||||||
|
) -> Spaces<'a, Node<'a>> {
|
||||||
|
match ann {
|
||||||
|
TypeAnnotation::Apply(module, func, args) => {
|
||||||
|
if args.is_empty() {
|
||||||
|
return Spaces {
|
||||||
|
item: Node::TypeAnnotation(*ann),
|
||||||
|
before: &[],
|
||||||
|
after: &[],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let mut new_args = Vec::with_capacity_in(args.len(), arena);
|
||||||
|
|
||||||
|
if !args.is_empty() {
|
||||||
|
for arg in args.iter().take(args.len() - 1) {
|
||||||
|
let lifted = ann_lift_spaces(arena, &arg.value);
|
||||||
|
new_args.push(Loc::at(arg.region, lower(arena, lifted)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let after = if let Some(last) = args.last() {
|
||||||
|
let lifted = ann_lift_spaces(arena, &last.value);
|
||||||
|
if lifted.before.is_empty() {
|
||||||
|
new_args.push(Loc::at(last.region, lifted.item));
|
||||||
|
} else {
|
||||||
|
new_args.push(Loc::at(
|
||||||
|
last.region,
|
||||||
|
TypeAnnotation::SpaceBefore(arena.alloc(lifted.item), lifted.before),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
lifted.after
|
||||||
|
} else {
|
||||||
|
&[]
|
||||||
|
};
|
||||||
|
|
||||||
|
Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: Node::TypeAnnotation(TypeAnnotation::Apply(
|
||||||
|
module,
|
||||||
|
func,
|
||||||
|
new_args.into_bump_slice(),
|
||||||
|
)),
|
||||||
|
after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TypeAnnotation::SpaceBefore(expr, spaces) => {
|
||||||
|
let mut inner = ann_lift_to_node(parens, arena, expr);
|
||||||
|
inner.before = merge_spaces_conservative(arena, spaces, inner.before);
|
||||||
|
inner
|
||||||
|
}
|
||||||
|
TypeAnnotation::SpaceAfter(expr, spaces) => {
|
||||||
|
let mut inner = ann_lift_to_node(parens, arena, expr);
|
||||||
|
inner.after = merge_spaces_conservative(arena, inner.after, spaces);
|
||||||
|
inner
|
||||||
|
}
|
||||||
|
TypeAnnotation::Function(args, _purity, res) => {
|
||||||
|
let new_args = arena.alloc_slice_copy(args);
|
||||||
|
let before = if let Some(first) = new_args.first_mut() {
|
||||||
|
let lifted = ann_lift_spaces_before(arena, &first.value);
|
||||||
|
first.value = lifted.item;
|
||||||
|
lifted.before
|
||||||
|
} else {
|
||||||
|
&[]
|
||||||
|
};
|
||||||
|
let new_res = ann_lift_spaces_after(arena, &res.value);
|
||||||
|
let new_ann = TypeAnnotation::Function(
|
||||||
|
new_args,
|
||||||
|
FunctionArrow::Pure,
|
||||||
|
arena.alloc(Loc::at_zero(new_res.item)),
|
||||||
|
);
|
||||||
|
let inner = Spaces {
|
||||||
|
before,
|
||||||
|
item: Node::TypeAnnotation(new_ann),
|
||||||
|
after: new_res.after,
|
||||||
|
};
|
||||||
|
if parens == Parens::InCollection {
|
||||||
|
let node = Node::DelimitedSequence(
|
||||||
|
Braces::Round,
|
||||||
|
arena.alloc_slice_copy(&[(inner.before, inner.item)]),
|
||||||
|
inner.after,
|
||||||
|
);
|
||||||
|
|
||||||
|
Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: node,
|
||||||
|
after: &[],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: Node::TypeAnnotation(*ann),
|
||||||
|
after: &[],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub struct NodeSpaces<'a, T> {
|
||||||
|
pub before: &'a [CommentOrNewline<'a>],
|
||||||
|
pub item: T,
|
||||||
|
pub after: &'a [CommentOrNewline<'a>],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Copy> ExtractSpaces<'a> for NodeSpaces<'a, T> {
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn extract_spaces(&self) -> Spaces<'a, T> {
|
||||||
|
Spaces {
|
||||||
|
before: self.before,
|
||||||
|
item: self.item,
|
||||||
|
after: self.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, V: Formattable> Formattable for NodeSpaces<'a, V> {
|
||||||
|
fn is_multiline(&self) -> bool {
|
||||||
|
!self.before.is_empty() || !self.after.is_empty() || self.item.is_multiline()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_with_options(
|
||||||
|
&self,
|
||||||
|
buf: &mut Buf,
|
||||||
|
parens: crate::annotation::Parens,
|
||||||
|
newlines: Newlines,
|
||||||
|
indent: u16,
|
||||||
|
) {
|
||||||
|
fmt_spaces(buf, self.before.iter(), indent);
|
||||||
|
self.item.format_with_options(buf, parens, newlines, indent);
|
||||||
|
fmt_spaces(buf, self.after.iter(), indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
use roc_parse::ast::{Collection, CommentOrNewline, ExtractSpaces};
|
use roc_parse::{
|
||||||
|
ast::{Collection, CommentOrNewline, ExtractSpaces},
|
||||||
|
expr::merge_spaces,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
annotation::{is_collection_multiline, Formattable, Newlines},
|
annotation::{is_collection_multiline, Formattable, Newlines},
|
||||||
spaces::{fmt_comments_only, NewlineAt, INDENT},
|
spaces::{fmt_comments_only, NewlineAt, INDENT},
|
||||||
Buf,
|
Buf,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||||
pub enum Braces {
|
pub enum Braces {
|
||||||
Round,
|
Round,
|
||||||
|
@ -13,26 +15,35 @@ pub enum Braces {
|
||||||
Curly,
|
Curly,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable>(
|
impl Braces {
|
||||||
|
pub fn start(self) -> char {
|
||||||
|
match self {
|
||||||
|
Braces::Round => '(',
|
||||||
|
Braces::Curly => '{',
|
||||||
|
Braces::Square => '[',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end(self) -> char {
|
||||||
|
match self {
|
||||||
|
Braces::Round => ')',
|
||||||
|
Braces::Curly => '}',
|
||||||
|
Braces::Square => ']',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable + std::fmt::Debug>(
|
||||||
buf: &mut Buf<'buf>,
|
buf: &mut Buf<'buf>,
|
||||||
indent: u16,
|
indent: u16,
|
||||||
braces: Braces,
|
braces: Braces,
|
||||||
items: Collection<'a, T>,
|
items: Collection<'a, T>,
|
||||||
newline: Newlines,
|
newline: Newlines,
|
||||||
) where
|
) where
|
||||||
<T as ExtractSpaces<'a>>::Item: Formattable,
|
<T as ExtractSpaces<'a>>::Item: Formattable + std::fmt::Debug,
|
||||||
{
|
{
|
||||||
let start = match braces {
|
let start = braces.start();
|
||||||
Braces::Round => '(',
|
let end = braces.end();
|
||||||
Braces::Curly => '{',
|
|
||||||
Braces::Square => '[',
|
|
||||||
};
|
|
||||||
|
|
||||||
let end = match braces {
|
|
||||||
Braces::Round => ')',
|
|
||||||
Braces::Curly => '}',
|
|
||||||
Braces::Square => ']',
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_collection_multiline(&items) {
|
if is_collection_multiline(&items) {
|
||||||
let braces_indent = indent;
|
let braces_indent = indent;
|
||||||
|
@ -43,10 +54,21 @@ pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable>(
|
||||||
buf.indent(braces_indent);
|
buf.indent(braces_indent);
|
||||||
buf.push(start);
|
buf.push(start);
|
||||||
|
|
||||||
|
let mut last_after: &[CommentOrNewline<'_>] = &[];
|
||||||
|
|
||||||
for (index, item) in items.iter().enumerate() {
|
for (index, item) in items.iter().enumerate() {
|
||||||
let is_first_item = index == 0;
|
let is_first_item = index == 0;
|
||||||
let item = item.extract_spaces();
|
let item = item.extract_spaces();
|
||||||
let is_only_newlines = item.before.iter().all(|s| s.is_newline());
|
let is_only_newlines = item.before.iter().all(|s| s.is_newline());
|
||||||
|
let last_after_was_only_newlines = last_after.iter().all(|s| s.is_newline());
|
||||||
|
|
||||||
|
if !last_after.is_empty() {
|
||||||
|
if last_after.iter().any(|s| s.is_newline()) {
|
||||||
|
buf.newline();
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt_comments_only(buf, last_after.iter(), NewlineAt::None, item_indent);
|
||||||
|
}
|
||||||
|
|
||||||
if item.before.is_empty() || is_only_newlines {
|
if item.before.is_empty() || is_only_newlines {
|
||||||
buf.ensure_ends_with_newline();
|
buf.ensure_ends_with_newline();
|
||||||
|
@ -64,13 +86,14 @@ pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable>(
|
||||||
if item
|
if item
|
||||||
.before
|
.before
|
||||||
.starts_with(&[CommentOrNewline::Newline, CommentOrNewline::Newline])
|
.starts_with(&[CommentOrNewline::Newline, CommentOrNewline::Newline])
|
||||||
|
&& last_after_was_only_newlines
|
||||||
{
|
{
|
||||||
// If there's a comment, and it's not on the first item,
|
// If there's a comment, and it's not on the first item,
|
||||||
// and it's preceded by at least one blank line, maintain 1 blank line.
|
// and it's preceded by at least one blank line, maintain 1 blank line.
|
||||||
// (We already ensured that it ends in a newline, so this will turn that
|
// (We already ensured that it ends in a newline, so this will turn that
|
||||||
// into a blank line.)
|
// into a blank line.)
|
||||||
|
|
||||||
buf.newline();
|
buf.ensure_ends_with_blank_line();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,32 +114,30 @@ pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable>(
|
||||||
buf.indent(item_indent);
|
buf.indent(item_indent);
|
||||||
buf.push(',');
|
buf.push(',');
|
||||||
|
|
||||||
if !item.after.is_empty() {
|
last_after = item.after;
|
||||||
if item.after.iter().any(|s| s.is_newline()) {
|
}
|
||||||
buf.newline();
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt_comments_only(buf, item.after.iter(), NewlineAt::None, item_indent);
|
let final_comments = if !last_after.is_empty() {
|
||||||
|
if last_after.iter().any(|s| s.is_newline()) {
|
||||||
|
buf.newline();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if items.final_comments().iter().any(|s| s.is_newline()) {
|
merge_spaces(buf.text.bump(), last_after, items.final_comments())
|
||||||
buf.newline();
|
} else {
|
||||||
}
|
if items.final_comments().iter().any(|s| s.is_newline()) {
|
||||||
|
buf.newline();
|
||||||
|
}
|
||||||
|
|
||||||
if items
|
items.final_comments()
|
||||||
.final_comments()
|
};
|
||||||
.starts_with(&[CommentOrNewline::Newline, CommentOrNewline::Newline])
|
|
||||||
|
if has_comments(final_comments)
|
||||||
|
&& final_comments.starts_with(&[CommentOrNewline::Newline, CommentOrNewline::Newline])
|
||||||
{
|
{
|
||||||
buf.newline();
|
buf.ensure_ends_with_blank_line();
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt_comments_only(
|
fmt_comments_only(buf, final_comments.iter(), NewlineAt::None, item_indent);
|
||||||
buf,
|
|
||||||
items.final_comments().iter(),
|
|
||||||
NewlineAt::None,
|
|
||||||
item_indent,
|
|
||||||
);
|
|
||||||
|
|
||||||
buf.ensure_ends_with_newline();
|
buf.ensure_ends_with_newline();
|
||||||
buf.indent(braces_indent);
|
buf.indent(braces_indent);
|
||||||
|
@ -144,3 +165,13 @@ pub fn fmt_collection<'a, 'buf, T: ExtractSpaces<'a> + Formattable>(
|
||||||
|
|
||||||
buf.push(end);
|
buf.push(end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_comments(spaces: &[CommentOrNewline<'_>]) -> bool {
|
||||||
|
for space in spaces {
|
||||||
|
match space {
|
||||||
|
CommentOrNewline::Newline => {}
|
||||||
|
CommentOrNewline::LineComment(_) | CommentOrNewline::DocComment(_) => return true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +1,27 @@
|
||||||
use crate::annotation::{is_collection_multiline, Formattable, Newlines, Parens};
|
use crate::annotation::{
|
||||||
|
ann_lift_spaces, ann_lift_spaces_after, is_collection_multiline, ty_is_outdentable,
|
||||||
|
Formattable, Newlines, Parens,
|
||||||
|
};
|
||||||
use crate::collection::{fmt_collection, Braces};
|
use crate::collection::{fmt_collection, Braces};
|
||||||
use crate::expr::fmt_str_literal;
|
use crate::expr::{
|
||||||
use crate::pattern::fmt_pattern;
|
expr_lift_and_lower, expr_lift_spaces, expr_lift_spaces_after, expr_lift_spaces_before,
|
||||||
use crate::spaces::{fmt_default_newline, fmt_default_spaces, fmt_spaces, INDENT};
|
fmt_str_literal, is_str_multiline, sub_expr_requests_parens,
|
||||||
|
};
|
||||||
|
use crate::pattern::{fmt_pattern, pattern_lift_spaces};
|
||||||
|
use crate::pattern::{pattern_lift_spaces_before, starts_with_inline_comment};
|
||||||
|
use crate::spaces::{
|
||||||
|
fmt_comments_only, fmt_default_newline, fmt_default_spaces, fmt_spaces, NewlineAt, INDENT,
|
||||||
|
};
|
||||||
use crate::Buf;
|
use crate::Buf;
|
||||||
|
use bumpalo::Bump;
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
use roc_parse::ast::{
|
use roc_parse::ast::{
|
||||||
AbilityMember, Defs, Expr, ExtractSpaces, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
|
AbilityMember, CommentOrNewline, Defs, Expr, ExtractSpaces, ImportAlias, ImportAsKeyword,
|
||||||
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
|
ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport,
|
||||||
ModuleImportParams, Pattern, Spaces, StrLiteral, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
ModuleImport, ModuleImportParams, Pattern, Spaceable, Spaces, SpacesAfter, SpacesBefore,
|
||||||
|
StrLiteral, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||||
};
|
};
|
||||||
|
use roc_parse::expr::merge_spaces;
|
||||||
use roc_parse::header::Keyword;
|
use roc_parse::header::Keyword;
|
||||||
use roc_region::all::Loc;
|
use roc_region::all::Loc;
|
||||||
|
|
||||||
|
@ -28,18 +40,24 @@ impl<'a> Formattable for Defs<'a> {
|
||||||
indent: u16,
|
indent: u16,
|
||||||
) {
|
) {
|
||||||
let mut prev_spaces = true;
|
let mut prev_spaces = true;
|
||||||
|
let arena = buf.text.bump();
|
||||||
|
|
||||||
for (index, def) in self.defs().enumerate() {
|
for (index, def) in self.defs().enumerate() {
|
||||||
let spaces_before = &self.spaces[self.space_before[index].indices()];
|
let spaces_before = &self.spaces[self.space_before[index].indices()];
|
||||||
let spaces_after = &self.spaces[self.space_after[index].indices()];
|
let spaces_after = &self.spaces[self.space_after[index].indices()];
|
||||||
|
|
||||||
|
let def = def_lift_spaces(buf.text.bump(), def);
|
||||||
|
|
||||||
|
let spaces_before = merge_spaces(arena, spaces_before, def.before);
|
||||||
|
let spaces_after = merge_spaces(arena, def.after, spaces_after);
|
||||||
|
|
||||||
if prev_spaces {
|
if prev_spaces {
|
||||||
fmt_spaces(buf, spaces_before.iter(), indent);
|
fmt_spaces(buf, spaces_before.iter(), indent);
|
||||||
} else {
|
} else {
|
||||||
fmt_default_newline(buf, spaces_before, indent);
|
fmt_default_newline(buf, spaces_before, indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
match def {
|
match def.item {
|
||||||
Ok(type_def) => type_def.format(buf, indent),
|
Ok(type_def) => type_def.format(buf, indent),
|
||||||
Err(value_def) => value_def.format(buf, indent),
|
Err(value_def) => value_def.format(buf, indent),
|
||||||
}
|
}
|
||||||
|
@ -51,6 +69,339 @@ impl<'a> Formattable for Defs<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn def_lift_spaces<'a, 'b: 'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
def: Result<&'a TypeDef<'b>, &'a ValueDef<'b>>,
|
||||||
|
) -> Spaces<'a, Result<TypeDef<'a>, ValueDef<'a>>> {
|
||||||
|
match def {
|
||||||
|
Ok(td) => {
|
||||||
|
let td = tydef_lift_spaces(arena, *td);
|
||||||
|
Spaces {
|
||||||
|
before: td.before,
|
||||||
|
item: Ok(td.item),
|
||||||
|
after: td.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(vd) => {
|
||||||
|
let vd = valdef_lift_spaces(arena, *vd);
|
||||||
|
Spaces {
|
||||||
|
before: vd.before,
|
||||||
|
item: Err(vd.item),
|
||||||
|
after: vd.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lift_spaces_after<'a, 'b: 'a, T: 'b + ExtractSpaces<'a> + Spaceable<'a>>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
item: T,
|
||||||
|
) -> SpacesAfter<'a, <T as ExtractSpaces<'a>>::Item>
|
||||||
|
where
|
||||||
|
<T as ExtractSpaces<'a>>::Item: Spaceable<'a>,
|
||||||
|
{
|
||||||
|
let spaces = item.extract_spaces();
|
||||||
|
|
||||||
|
SpacesAfter {
|
||||||
|
item: spaces.item.maybe_before(arena, spaces.before),
|
||||||
|
after: spaces.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tydef_lift_spaces<'a, 'b: 'a>(arena: &'a Bump, def: TypeDef<'b>) -> Spaces<'a, TypeDef<'a>> {
|
||||||
|
match def {
|
||||||
|
TypeDef::Alias { header, ann } => {
|
||||||
|
let ann_lifted = ann_lift_spaces_after(arena, &ann.value);
|
||||||
|
|
||||||
|
Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: TypeDef::Alias {
|
||||||
|
header,
|
||||||
|
ann: Loc::at(ann.region, ann_lifted.item),
|
||||||
|
},
|
||||||
|
after: ann_lifted.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TypeDef::Opaque {
|
||||||
|
header,
|
||||||
|
typ,
|
||||||
|
derived,
|
||||||
|
} => {
|
||||||
|
if let Some(derived) = derived {
|
||||||
|
let derived_lifted = lift_spaces_after(arena, derived.value);
|
||||||
|
|
||||||
|
Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: TypeDef::Opaque {
|
||||||
|
header,
|
||||||
|
typ,
|
||||||
|
derived: Some(Loc::at(derived.region, derived_lifted.item)),
|
||||||
|
},
|
||||||
|
after: derived_lifted.after,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let typ_lifted = ann_lift_spaces_after(arena, &typ.value);
|
||||||
|
|
||||||
|
Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: TypeDef::Opaque {
|
||||||
|
header,
|
||||||
|
typ: Loc::at(typ.region, typ_lifted.item),
|
||||||
|
derived,
|
||||||
|
},
|
||||||
|
after: typ_lifted.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TypeDef::Ability {
|
||||||
|
header: _,
|
||||||
|
loc_implements: _,
|
||||||
|
members: _,
|
||||||
|
} => {
|
||||||
|
// TODO: if the fuzzer ever generates examples where it's important to lift spaces from the members,
|
||||||
|
// we'll need to implement this. I'm not sure that's possible, though.
|
||||||
|
Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: def,
|
||||||
|
after: &[],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn valdef_lift_spaces<'a, 'b: 'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
def: ValueDef<'b>,
|
||||||
|
) -> Spaces<'a, ValueDef<'a>> {
|
||||||
|
match def {
|
||||||
|
ValueDef::Annotation(pat, ann) => {
|
||||||
|
let pat_lifted = pattern_lift_spaces_before(arena, &pat.value);
|
||||||
|
let ann_lifted = ann_lift_spaces_after(arena, &ann.value);
|
||||||
|
|
||||||
|
Spaces {
|
||||||
|
before: pat_lifted.before,
|
||||||
|
item: ValueDef::Annotation(
|
||||||
|
Loc::at(pat.region, pat_lifted.item),
|
||||||
|
Loc::at(ann.region, ann_lifted.item),
|
||||||
|
),
|
||||||
|
after: ann_lifted.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueDef::Body(pat, expr) => {
|
||||||
|
let pat_lifted = pattern_lift_spaces_before(arena, &pat.value);
|
||||||
|
let expr_lifted = expr_lift_spaces_after(Parens::NotNeeded, arena, &expr.value);
|
||||||
|
|
||||||
|
Spaces {
|
||||||
|
before: pat_lifted.before,
|
||||||
|
item: ValueDef::Body(
|
||||||
|
arena.alloc(Loc::at(pat.region, pat_lifted.item)),
|
||||||
|
arena.alloc(Loc::at(expr.region, expr_lifted.item)),
|
||||||
|
),
|
||||||
|
after: expr_lifted.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueDef::AnnotatedBody {
|
||||||
|
ann_pattern,
|
||||||
|
ann_type,
|
||||||
|
lines_between,
|
||||||
|
body_pattern,
|
||||||
|
body_expr,
|
||||||
|
} => {
|
||||||
|
let ann_pattern_lifted = pattern_lift_spaces_before(arena, &ann_pattern.value);
|
||||||
|
let ann_type_lifted = ann_lift_spaces_after(arena, &ann_type.value);
|
||||||
|
let body_pattern_lifted = pattern_lift_spaces_before(arena, &body_pattern.value);
|
||||||
|
let body_expr_lifted =
|
||||||
|
expr_lift_spaces_after(Parens::NotNeeded, arena, &body_expr.value);
|
||||||
|
|
||||||
|
let lines_between = merge_spaces(
|
||||||
|
arena,
|
||||||
|
ann_type_lifted.after,
|
||||||
|
merge_spaces(arena, lines_between, body_pattern_lifted.before),
|
||||||
|
);
|
||||||
|
|
||||||
|
Spaces {
|
||||||
|
before: ann_pattern_lifted.before,
|
||||||
|
item: ValueDef::AnnotatedBody {
|
||||||
|
ann_pattern: arena.alloc(Loc::at(ann_pattern.region, ann_pattern_lifted.item)),
|
||||||
|
ann_type: arena.alloc(Loc::at(ann_type.region, ann_type_lifted.item)),
|
||||||
|
lines_between,
|
||||||
|
body_pattern: arena
|
||||||
|
.alloc(Loc::at(body_pattern.region, body_pattern_lifted.item)),
|
||||||
|
body_expr: arena.alloc(Loc::at(body_expr.region, body_expr_lifted.item)),
|
||||||
|
},
|
||||||
|
after: body_expr_lifted.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueDef::Dbg {
|
||||||
|
condition,
|
||||||
|
preceding_comment,
|
||||||
|
} => {
|
||||||
|
let condition_lifted =
|
||||||
|
expr_lift_spaces_after(Parens::NotNeeded, arena, &condition.value);
|
||||||
|
|
||||||
|
Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: ValueDef::Dbg {
|
||||||
|
condition: arena.alloc(Loc::at(condition.region, condition_lifted.item)),
|
||||||
|
preceding_comment,
|
||||||
|
},
|
||||||
|
after: condition_lifted.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueDef::Expect {
|
||||||
|
condition,
|
||||||
|
preceding_comment,
|
||||||
|
} => {
|
||||||
|
let condition_lifted =
|
||||||
|
expr_lift_spaces_after(Parens::NotNeeded, arena, &condition.value);
|
||||||
|
|
||||||
|
Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: ValueDef::Expect {
|
||||||
|
condition: arena.alloc(Loc::at(condition.region, condition_lifted.item)),
|
||||||
|
preceding_comment,
|
||||||
|
},
|
||||||
|
after: condition_lifted.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueDef::ModuleImport(module_import) => {
|
||||||
|
// Module imports begin with 'import', and end with either a ImportAlias or Collection
|
||||||
|
// No spaces in sight!
|
||||||
|
Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: ValueDef::ModuleImport(module_import),
|
||||||
|
after: &[],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueDef::IngestedFileImport(mut ingested_file_import) => {
|
||||||
|
// Ingested file imports begin with 'import', but can end with a TypeAnnotation, which can have spaces
|
||||||
|
let after = if let Some(ann) = &mut ingested_file_import.annotation {
|
||||||
|
let lifted = ann_lift_spaces_after(arena, &ann.annotation.value);
|
||||||
|
ann.annotation.value = lifted.item;
|
||||||
|
lifted.after
|
||||||
|
} else {
|
||||||
|
&[]
|
||||||
|
};
|
||||||
|
Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: ValueDef::IngestedFileImport(ingested_file_import),
|
||||||
|
after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueDef::Stmt(expr) => {
|
||||||
|
let expr_lifted = expr_lift_spaces(Parens::NotNeeded, arena, &expr.value);
|
||||||
|
Spaces {
|
||||||
|
before: expr_lifted.before,
|
||||||
|
item: ValueDef::Stmt(arena.alloc(Loc::at(expr.region, expr_lifted.item))),
|
||||||
|
after: expr_lifted.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueDef::StmtAfterExpr => Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: ValueDef::StmtAfterExpr,
|
||||||
|
after: &[],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn valdef_lift_spaces_before<'a, 'b: 'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
def: ValueDef<'b>,
|
||||||
|
) -> SpacesBefore<'a, ValueDef<'a>> {
|
||||||
|
match def {
|
||||||
|
ValueDef::Annotation(pat, ann) => {
|
||||||
|
let pat_lifted = pattern_lift_spaces_before(arena, &pat.value);
|
||||||
|
|
||||||
|
SpacesBefore {
|
||||||
|
before: pat_lifted.before,
|
||||||
|
item: ValueDef::Annotation(Loc::at(pat.region, pat_lifted.item), ann),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueDef::Body(pat, expr) => {
|
||||||
|
let pat_lifted = pattern_lift_spaces_before(arena, &pat.value);
|
||||||
|
|
||||||
|
SpacesBefore {
|
||||||
|
before: pat_lifted.before,
|
||||||
|
item: ValueDef::Body(arena.alloc(Loc::at(pat.region, pat_lifted.item)), expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueDef::AnnotatedBody {
|
||||||
|
ann_pattern,
|
||||||
|
ann_type,
|
||||||
|
lines_between,
|
||||||
|
body_pattern,
|
||||||
|
body_expr,
|
||||||
|
} => {
|
||||||
|
let ann_pattern_lifted = pattern_lift_spaces_before(arena, &ann_pattern.value);
|
||||||
|
let ann_type_lifted = ann_lift_spaces_after(arena, &ann_type.value);
|
||||||
|
let body_pattern_lifted = pattern_lift_spaces_before(arena, &body_pattern.value);
|
||||||
|
|
||||||
|
let lines_between = merge_spaces(
|
||||||
|
arena,
|
||||||
|
ann_type_lifted.after,
|
||||||
|
merge_spaces(arena, lines_between, body_pattern_lifted.before),
|
||||||
|
);
|
||||||
|
|
||||||
|
SpacesBefore {
|
||||||
|
before: ann_pattern_lifted.before,
|
||||||
|
item: ValueDef::AnnotatedBody {
|
||||||
|
ann_pattern: arena.alloc(Loc::at(ann_pattern.region, ann_pattern_lifted.item)),
|
||||||
|
ann_type: arena.alloc(Loc::at(ann_type.region, ann_type_lifted.item)),
|
||||||
|
lines_between,
|
||||||
|
body_pattern: arena
|
||||||
|
.alloc(Loc::at(body_pattern.region, body_pattern_lifted.item)),
|
||||||
|
body_expr,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueDef::Dbg {
|
||||||
|
condition,
|
||||||
|
preceding_comment,
|
||||||
|
} => SpacesBefore {
|
||||||
|
before: &[],
|
||||||
|
item: ValueDef::Dbg {
|
||||||
|
condition,
|
||||||
|
preceding_comment,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ValueDef::Expect {
|
||||||
|
condition,
|
||||||
|
preceding_comment,
|
||||||
|
} => SpacesBefore {
|
||||||
|
before: &[],
|
||||||
|
item: ValueDef::Expect {
|
||||||
|
condition,
|
||||||
|
preceding_comment,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ValueDef::ModuleImport(module_import) => {
|
||||||
|
// Module imports always start with 'import', no spaces
|
||||||
|
SpacesBefore {
|
||||||
|
before: &[],
|
||||||
|
item: ValueDef::ModuleImport(module_import),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueDef::IngestedFileImport(ingested_file_import) => {
|
||||||
|
// Similarly, ingested file imports always start with 'import', no spaces
|
||||||
|
SpacesBefore {
|
||||||
|
before: &[],
|
||||||
|
item: ValueDef::IngestedFileImport(ingested_file_import),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueDef::Stmt(expr) => {
|
||||||
|
let expr_lifted = expr_lift_spaces_before(Parens::NotNeeded, arena, &expr.value);
|
||||||
|
SpacesBefore {
|
||||||
|
before: expr_lifted.before,
|
||||||
|
item: ValueDef::Stmt(arena.alloc(Loc::at(expr.region, expr_lifted.item))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ValueDef::StmtAfterExpr => SpacesBefore {
|
||||||
|
before: &[],
|
||||||
|
item: ValueDef::StmtAfterExpr,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Formattable for TypeDef<'a> {
|
impl<'a> Formattable for TypeDef<'a> {
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
use roc_parse::ast::TypeDef::*;
|
use roc_parse::ast::TypeDef::*;
|
||||||
|
@ -66,34 +417,23 @@ impl<'a> Formattable for TypeDef<'a> {
|
||||||
use roc_parse::ast::TypeDef::*;
|
use roc_parse::ast::TypeDef::*;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
Alias {
|
Alias { header, ann } => {
|
||||||
header: TypeHeader { name, vars },
|
header.format(buf, indent);
|
||||||
ann,
|
|
||||||
} => {
|
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str(name.value);
|
|
||||||
|
|
||||||
for var in *vars {
|
|
||||||
buf.spaces(1);
|
|
||||||
|
|
||||||
let need_parens = matches!(var.value, Pattern::Apply(..));
|
|
||||||
|
|
||||||
if need_parens {
|
|
||||||
buf.push_str("(");
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
|
||||||
buf.indent(indent);
|
|
||||||
|
|
||||||
if need_parens {
|
|
||||||
buf.push_str(")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.push_str(" :");
|
buf.push_str(" :");
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
|
|
||||||
ann.format(buf, indent)
|
let ann = ann_lift_spaces(buf.text.bump(), &ann.value);
|
||||||
|
|
||||||
|
let inner_indent = if ty_is_outdentable(&ann.item) {
|
||||||
|
indent
|
||||||
|
} else {
|
||||||
|
indent + INDENT
|
||||||
|
};
|
||||||
|
fmt_comments_only(buf, ann.before.iter(), NewlineAt::Bottom, inner_indent);
|
||||||
|
ann.item.format(buf, inner_indent);
|
||||||
|
fmt_spaces(buf, ann.after.iter(), indent);
|
||||||
}
|
}
|
||||||
Opaque {
|
Opaque {
|
||||||
header,
|
header,
|
||||||
|
@ -127,17 +467,11 @@ impl<'a> Formattable for TypeDef<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ability {
|
Ability {
|
||||||
header: TypeHeader { name, vars },
|
header,
|
||||||
loc_implements: _,
|
loc_implements: _,
|
||||||
members,
|
members,
|
||||||
} => {
|
} => {
|
||||||
buf.indent(indent);
|
header.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
||||||
buf.push_str(name.value);
|
|
||||||
for var in *vars {
|
|
||||||
buf.spaces(1);
|
|
||||||
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
|
||||||
buf.indent(indent);
|
|
||||||
}
|
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
buf.push_str(roc_parse::keyword::IMPLEMENTS);
|
buf.push_str(roc_parse::keyword::IMPLEMENTS);
|
||||||
|
|
||||||
|
@ -180,10 +514,62 @@ impl<'a> Formattable for TypeHeader<'a> {
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str(self.name.value);
|
buf.push_str(self.name.value);
|
||||||
|
|
||||||
|
let vars_indent = if self.vars.iter().any(|v| v.is_multiline()) {
|
||||||
|
indent + INDENT
|
||||||
|
} else {
|
||||||
|
indent
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut last_after: &[CommentOrNewline<'_>] = &[];
|
||||||
|
let mut last_multiline = false;
|
||||||
|
|
||||||
for var in self.vars.iter() {
|
for var in self.vars.iter() {
|
||||||
buf.spaces(1);
|
let var = pattern_lift_spaces(buf.text.bump(), &var.value);
|
||||||
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
|
|
||||||
buf.indent(indent);
|
let before = if !last_after.is_empty() {
|
||||||
|
merge_spaces(buf.text.bump(), last_after, var.before)
|
||||||
|
} else {
|
||||||
|
var.before
|
||||||
|
};
|
||||||
|
|
||||||
|
if !before.is_empty() {
|
||||||
|
if !var.item.is_multiline() {
|
||||||
|
fmt_comments_only(buf, before.iter(), NewlineAt::Bottom, vars_indent)
|
||||||
|
} else {
|
||||||
|
fmt_spaces(buf, before.iter(), vars_indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.ensure_ends_with_whitespace();
|
||||||
|
|
||||||
|
last_after = var.after;
|
||||||
|
last_multiline = var.item.is_multiline();
|
||||||
|
|
||||||
|
let need_parens = matches!(var.item, Pattern::Apply(..));
|
||||||
|
|
||||||
|
if need_parens {
|
||||||
|
buf.push_str("(");
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt_pattern(buf, &var.item, vars_indent, Parens::NotNeeded);
|
||||||
|
|
||||||
|
buf.indent(vars_indent);
|
||||||
|
|
||||||
|
if need_parens {
|
||||||
|
buf.push_str(")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !last_after.is_empty() {
|
||||||
|
if starts_with_inline_comment(last_after.iter()) {
|
||||||
|
buf.spaces(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !last_multiline {
|
||||||
|
fmt_comments_only(buf, last_after.iter(), NewlineAt::Bottom, indent)
|
||||||
|
} else {
|
||||||
|
fmt_spaces(buf, last_after.iter(), indent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -441,7 +827,7 @@ impl<'a> Formattable for ValueDef<'a> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Body(loc_pattern, loc_expr) => {
|
Body(loc_pattern, loc_expr) => {
|
||||||
fmt_body(buf, &loc_pattern.value, &loc_expr.value, indent);
|
fmt_body(buf, true, &loc_pattern.value, &loc_expr.value, indent);
|
||||||
}
|
}
|
||||||
Dbg { condition, .. } => fmt_dbg_in_def(buf, condition, self.is_multiline(), indent),
|
Dbg { condition, .. } => fmt_dbg_in_def(buf, condition, self.is_multiline(), indent),
|
||||||
Expect { condition, .. } => fmt_expect(buf, condition, self.is_multiline(), indent),
|
Expect { condition, .. } => fmt_expect(buf, condition, self.is_multiline(), indent),
|
||||||
|
@ -457,7 +843,7 @@ impl<'a> Formattable for ValueDef<'a> {
|
||||||
fmt_annotated_body_comment(buf, indent, lines_between);
|
fmt_annotated_body_comment(buf, indent, lines_between);
|
||||||
|
|
||||||
buf.newline();
|
buf.newline();
|
||||||
fmt_body(buf, &body_pattern.value, &body_expr.value, indent);
|
fmt_body(buf, false, &body_pattern.value, &body_expr.value, indent);
|
||||||
}
|
}
|
||||||
ModuleImport(module_import) => module_import.format(buf, indent),
|
ModuleImport(module_import) => module_import.format(buf, indent),
|
||||||
IngestedFileImport(ingested_file_import) => ingested_file_import.format(buf, indent),
|
IngestedFileImport(ingested_file_import) => ingested_file_import.format(buf, indent),
|
||||||
|
@ -483,20 +869,25 @@ fn fmt_general_def<L: Formattable>(
|
||||||
buf.push_str(sep);
|
buf.push_str(sep);
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
|
|
||||||
let should_outdent = should_outdent(rhs);
|
let rhs_lifted = ann_lift_spaces(buf.text.bump(), rhs);
|
||||||
|
|
||||||
if should_outdent {
|
if ty_is_outdentable(&rhs_lifted.item) && rhs_lifted.before.iter().all(|s| s.is_newline()) {
|
||||||
match rhs {
|
rhs_lifted
|
||||||
TypeAnnotation::SpaceBefore(sub_def, _) => {
|
.item
|
||||||
sub_def.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
rhs.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
rhs.format_with_options(buf, Parens::NotNeeded, newlines, indent + INDENT);
|
buf.ensure_ends_with_newline();
|
||||||
|
fmt_comments_only(
|
||||||
|
buf,
|
||||||
|
rhs_lifted.before.iter(),
|
||||||
|
NewlineAt::Bottom,
|
||||||
|
indent + INDENT,
|
||||||
|
);
|
||||||
|
rhs_lifted
|
||||||
|
.item
|
||||||
|
.format_with_options(buf, Parens::NotNeeded, newlines, indent + INDENT);
|
||||||
}
|
}
|
||||||
|
fmt_comments_only(buf, rhs_lifted.after.iter(), NewlineAt::Bottom, indent);
|
||||||
} else {
|
} else {
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
buf.push_str(sep);
|
buf.push_str(sep);
|
||||||
|
@ -505,28 +896,6 @@ fn fmt_general_def<L: Formattable>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn should_outdent(mut rhs: &TypeAnnotation) -> bool {
|
|
||||||
loop {
|
|
||||||
match rhs {
|
|
||||||
TypeAnnotation::SpaceBefore(sub_def, spaces) => {
|
|
||||||
let is_only_newlines = spaces.iter().all(|s| s.is_newline());
|
|
||||||
if !is_only_newlines || !sub_def.is_multiline() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
rhs = sub_def;
|
|
||||||
}
|
|
||||||
TypeAnnotation::Where(ann, _clauses) => {
|
|
||||||
if !ann.is_multiline() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
rhs = &ann.value;
|
|
||||||
}
|
|
||||||
TypeAnnotation::Record { .. } | TypeAnnotation::TagUnion { .. } => return true,
|
|
||||||
_ => return false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmt_dbg_in_def<'a>(buf: &mut Buf, condition: &'a Loc<Expr<'a>>, _: bool, indent: u16) {
|
fn fmt_dbg_in_def<'a>(buf: &mut Buf, condition: &'a Loc<Expr<'a>>, _: bool, indent: u16) {
|
||||||
buf.ensure_ends_with_newline();
|
buf.ensure_ends_with_newline();
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
|
@ -608,21 +977,36 @@ pub fn fmt_annotated_body_comment<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
allow_simplify_empty_record_destructure: bool,
|
||||||
|
pattern: &'a Pattern<'a>,
|
||||||
|
body: &'a Expr<'a>,
|
||||||
|
indent: u16,
|
||||||
|
) {
|
||||||
|
let pattern_extracted = pattern.extract_spaces();
|
||||||
// Check if this is an assignment into the unit value
|
// Check if this is an assignment into the unit value
|
||||||
let is_unit_assignment = if let Pattern::RecordDestructure(collection) = pattern {
|
let is_unit_assignment = if let Pattern::RecordDestructure(collection) = pattern_extracted.item
|
||||||
collection.is_empty()
|
{
|
||||||
|
allow_simplify_empty_record_destructure
|
||||||
|
&& collection.is_empty()
|
||||||
|
&& pattern_extracted.before.iter().all(|s| s.is_newline())
|
||||||
|
&& pattern_extracted.after.iter().all(|s| s.is_newline())
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
// Don't format the `{} =` for defs with this pattern
|
// Don't format the `{} =` for defs with this pattern
|
||||||
if !is_unit_assignment {
|
if is_unit_assignment {
|
||||||
pattern.format_with_options(buf, Parens::InApply, Newlines::No, indent);
|
return body.format_with_options(buf, Parens::NotNeeded, Newlines::No, indent);
|
||||||
buf.indent(indent);
|
|
||||||
buf.push_str(" =");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pattern.format_with_options(buf, Parens::InApply, Newlines::No, indent);
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str(" =");
|
||||||
|
|
||||||
|
let body = expr_lift_and_lower(Parens::NotNeeded, buf.text.bump(), body);
|
||||||
|
|
||||||
if body.is_multiline() {
|
if body.is_multiline() {
|
||||||
match body {
|
match body {
|
||||||
Expr::SpaceBefore(sub_def, spaces) => {
|
Expr::SpaceBefore(sub_def, spaces) => {
|
||||||
|
@ -634,7 +1018,10 @@ pub fn fmt_body<'a>(buf: &mut Buf, pattern: &'a Pattern<'a>, body: &'a Expr<'a>,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if should_outdent {
|
if is_unit_assignment {
|
||||||
|
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
|
||||||
|
sub_def.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||||
|
} else if should_outdent {
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
sub_def.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
sub_def.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||||
} else {
|
} else {
|
||||||
|
@ -646,6 +1033,32 @@ pub fn fmt_body<'a>(buf: &mut Buf, pattern: &'a Pattern<'a>, body: &'a Expr<'a>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Expr::Apply(
|
||||||
|
Loc {
|
||||||
|
value: Expr::Str(StrLiteral::Block(..)),
|
||||||
|
..
|
||||||
|
},
|
||||||
|
..,
|
||||||
|
) => {
|
||||||
|
buf.spaces(1);
|
||||||
|
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
|
||||||
|
}
|
||||||
|
Expr::Str(s) => {
|
||||||
|
if is_str_multiline(&s) {
|
||||||
|
buf.ensure_ends_with_newline();
|
||||||
|
} else {
|
||||||
|
buf.spaces(1);
|
||||||
|
}
|
||||||
|
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
|
||||||
|
}
|
||||||
|
_ if starts_with_block_string_literal(&body) => {
|
||||||
|
buf.ensure_ends_with_newline();
|
||||||
|
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
|
||||||
|
}
|
||||||
|
Expr::When(..) => {
|
||||||
|
buf.ensure_ends_with_newline();
|
||||||
|
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
|
||||||
|
}
|
||||||
Expr::Defs(..) | Expr::BinOps(_, _) | Expr::Backpassing(..) => {
|
Expr::Defs(..) | Expr::BinOps(_, _) | Expr::Backpassing(..) => {
|
||||||
// Binop chains always get a newline. Otherwise you can have things like:
|
// Binop chains always get a newline. Otherwise you can have things like:
|
||||||
//
|
//
|
||||||
|
@ -662,9 +1075,15 @@ pub fn fmt_body<'a>(buf: &mut Buf, pattern: &'a Pattern<'a>, body: &'a Expr<'a>,
|
||||||
buf.newline();
|
buf.newline();
|
||||||
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
|
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
|
||||||
}
|
}
|
||||||
Expr::When(..) | Expr::Str(StrLiteral::Block(_)) => {
|
Expr::ParensAround(&Expr::SpaceBefore(sub_def, _)) => {
|
||||||
buf.ensure_ends_with_newline();
|
let needs_indent = !sub_expr_requests_parens(sub_def);
|
||||||
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent + INDENT);
|
let indent = if needs_indent {
|
||||||
|
indent + INDENT
|
||||||
|
} else {
|
||||||
|
indent
|
||||||
|
};
|
||||||
|
buf.spaces(1);
|
||||||
|
body.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
|
@ -677,6 +1096,18 @@ pub fn fmt_body<'a>(buf: &mut Buf, pattern: &'a Pattern<'a>, body: &'a Expr<'a>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn starts_with_block_string_literal(expr: &Expr<'_>) -> bool {
|
||||||
|
match expr {
|
||||||
|
Expr::Str(s) => is_str_multiline(s),
|
||||||
|
Expr::SpaceAfter(inner, _) | Expr::SpaceBefore(inner, _) => {
|
||||||
|
starts_with_block_string_literal(inner)
|
||||||
|
}
|
||||||
|
Expr::Apply(inner, _, _) => starts_with_block_string_literal(&inner.value),
|
||||||
|
Expr::TrySuffix { target: _, expr } => starts_with_block_string_literal(expr),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Formattable for AbilityMember<'a> {
|
impl<'a> Formattable for AbilityMember<'a> {
|
||||||
fn is_multiline(&self) -> bool {
|
fn is_multiline(&self) -> bool {
|
||||||
self.name.value.is_multiline() || self.typ.is_multiline()
|
self.name.value.is_multiline() || self.typ.is_multiline()
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -18,12 +18,14 @@ pub struct Buf<'a> {
|
||||||
spaces_to_flush: usize,
|
spaces_to_flush: usize,
|
||||||
newlines_to_flush: usize,
|
newlines_to_flush: usize,
|
||||||
beginning_of_line: bool,
|
beginning_of_line: bool,
|
||||||
|
line_indent: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Buf<'a> {
|
impl<'a> Buf<'a> {
|
||||||
pub fn new_in(arena: &'a Bump) -> Buf<'a> {
|
pub fn new_in(arena: &'a Bump) -> Buf<'a> {
|
||||||
Buf {
|
Buf {
|
||||||
text: String::new_in(arena),
|
text: String::new_in(arena),
|
||||||
|
line_indent: 0,
|
||||||
spaces_to_flush: 0,
|
spaces_to_flush: 0,
|
||||||
newlines_to_flush: 0,
|
newlines_to_flush: 0,
|
||||||
beginning_of_line: true,
|
beginning_of_line: true,
|
||||||
|
@ -40,11 +42,18 @@ impl<'a> Buf<'a> {
|
||||||
|
|
||||||
pub fn indent(&mut self, indent: u16) {
|
pub fn indent(&mut self, indent: u16) {
|
||||||
if self.beginning_of_line {
|
if self.beginning_of_line {
|
||||||
|
self.line_indent = indent;
|
||||||
self.spaces_to_flush = indent as usize;
|
self.spaces_to_flush = indent as usize;
|
||||||
}
|
}
|
||||||
self.beginning_of_line = false;
|
self.beginning_of_line = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cur_line_indent(&self) -> u16 {
|
||||||
|
debug_assert!(!self.beginning_of_line, "cur_line_indent before indent");
|
||||||
|
self.line_indent
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
pub fn push(&mut self, ch: char) {
|
pub fn push(&mut self, ch: char) {
|
||||||
debug_assert!(!self.beginning_of_line);
|
debug_assert!(!self.beginning_of_line);
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
|
@ -61,6 +70,7 @@ impl<'a> Buf<'a> {
|
||||||
self.text.push(ch);
|
self.text.push(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
pub fn push_str_allow_spaces(&mut self, s: &str) {
|
pub fn push_str_allow_spaces(&mut self, s: &str) {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
!self.beginning_of_line,
|
!self.beginning_of_line,
|
||||||
|
@ -73,6 +83,7 @@ impl<'a> Buf<'a> {
|
||||||
self.text.push_str(s);
|
self.text.push_str(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
pub fn push_str(&mut self, s: &str) {
|
pub fn push_str(&mut self, s: &str) {
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
!self.beginning_of_line,
|
!self.beginning_of_line,
|
||||||
|
@ -129,6 +140,12 @@ impl<'a> Buf<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ensure_ends_with_whitespace(&mut self) {
|
||||||
|
if !self.text.is_empty() && self.newlines_to_flush == 0 && self.spaces_to_flush == 0 {
|
||||||
|
self.spaces_to_flush = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn flush_spaces(&mut self) {
|
fn flush_spaces(&mut self) {
|
||||||
for _ in 0..self.newlines_to_flush {
|
for _ in 0..self.newlines_to_flush {
|
||||||
self.text.push('\n');
|
self.text.push('\n');
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
use crate::annotation::{Formattable, Newlines, Parens};
|
use crate::annotation::{Formattable, Newlines, Parens};
|
||||||
use crate::expr::{fmt_str_literal, format_sq_literal, is_str_multiline};
|
use crate::expr::{
|
||||||
|
expr_is_multiline, expr_lift_spaces_after, fmt_str_literal, format_sq_literal, is_str_multiline,
|
||||||
|
};
|
||||||
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_parse::ast::{Base, CommentOrNewline, Pattern, PatternAs};
|
use bumpalo::Bump;
|
||||||
|
use roc_parse::ast::{
|
||||||
|
Base, CommentOrNewline, Pattern, PatternAs, Spaceable, Spaces, SpacesAfter, SpacesBefore,
|
||||||
|
};
|
||||||
|
use roc_parse::expr::merge_spaces;
|
||||||
|
use roc_region::all::Loc;
|
||||||
|
|
||||||
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) {
|
||||||
pattern.format_with_options(buf, parens, Newlines::No, indent);
|
pattern.format_with_options(buf, parens, Newlines::No, indent);
|
||||||
|
@ -54,7 +61,7 @@ impl<'a> Formattable for Pattern<'a> {
|
||||||
Pattern::RecordDestructure(fields) => fields.iter().any(|f| f.is_multiline()),
|
Pattern::RecordDestructure(fields) => fields.iter().any(|f| f.is_multiline()),
|
||||||
Pattern::RequiredField(_, subpattern) => subpattern.is_multiline(),
|
Pattern::RequiredField(_, subpattern) => subpattern.is_multiline(),
|
||||||
|
|
||||||
Pattern::OptionalField(_, expr) => expr.is_multiline(),
|
Pattern::OptionalField(_, expr) => expr_is_multiline(&expr.value, true),
|
||||||
|
|
||||||
Pattern::As(pattern, pattern_as) => pattern.is_multiline() || pattern_as.is_multiline(),
|
Pattern::As(pattern, pattern_as) => pattern.is_multiline() || pattern_as.is_multiline(),
|
||||||
Pattern::ListRest(opt_pattern_as) => match opt_pattern_as {
|
Pattern::ListRest(opt_pattern_as) => match opt_pattern_as {
|
||||||
|
@ -86,222 +93,307 @@ impl<'a> Formattable for Pattern<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
|
fn format_with_options(&self, buf: &mut Buf, parens: Parens, _newlines: Newlines, indent: u16) {
|
||||||
use self::Pattern::*;
|
fmt_pattern_inner(self, buf, parens, indent, self.is_multiline());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
match self {
|
fn fmt_pattern_inner(
|
||||||
Identifier { ident: string } => {
|
pat: &Pattern<'_>,
|
||||||
buf.indent(indent);
|
buf: &mut Buf,
|
||||||
buf.push_str(string);
|
parens: Parens,
|
||||||
}
|
indent: u16,
|
||||||
Tag(name) | OpaqueRef(name) => {
|
outer_is_multiline: bool,
|
||||||
buf.indent(indent);
|
) {
|
||||||
buf.push_str(name);
|
use self::Pattern::*;
|
||||||
}
|
|
||||||
Apply(loc_pattern, loc_arg_patterns) => {
|
|
||||||
buf.indent(indent);
|
|
||||||
// Sometimes, an Apply pattern needs parens around it.
|
|
||||||
// In particular when an Apply's argument is itself an Apply (> 0) arguments
|
|
||||||
let parens = !loc_arg_patterns.is_empty() && (parens == Parens::InApply);
|
|
||||||
|
|
||||||
let indent_more = if self.is_multiline() {
|
let me = pattern_lift_spaces(buf.text.bump(), pat);
|
||||||
indent + INDENT
|
|
||||||
|
if !me.before.is_empty() {
|
||||||
|
if !outer_is_multiline {
|
||||||
|
fmt_comments_only(buf, me.before.iter(), NewlineAt::Bottom, indent)
|
||||||
|
} else {
|
||||||
|
fmt_spaces(buf, me.before.iter(), indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_multiline = me.item.is_multiline();
|
||||||
|
|
||||||
|
match me.item {
|
||||||
|
Identifier { ident: string } => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str(string);
|
||||||
|
}
|
||||||
|
Tag(name) | OpaqueRef(name) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str(name);
|
||||||
|
}
|
||||||
|
Apply(loc_pattern, loc_arg_patterns) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
// Sometimes, an Apply pattern needs parens around it.
|
||||||
|
// In particular when an Apply's argument is itself an Apply (> 0) arguments
|
||||||
|
let parens = !loc_arg_patterns.is_empty() && (parens == Parens::InApply);
|
||||||
|
|
||||||
|
let indent_more = if is_multiline {
|
||||||
|
indent + INDENT
|
||||||
|
} else {
|
||||||
|
indent
|
||||||
|
};
|
||||||
|
|
||||||
|
if parens {
|
||||||
|
buf.push('(');
|
||||||
|
}
|
||||||
|
|
||||||
|
let pat = pattern_lift_spaces(buf.text.bump(), &loc_pattern.value);
|
||||||
|
|
||||||
|
if !pat.before.is_empty() {
|
||||||
|
if !is_multiline {
|
||||||
|
fmt_comments_only(buf, pat.before.iter(), NewlineAt::Bottom, indent)
|
||||||
} else {
|
} else {
|
||||||
indent
|
fmt_spaces(buf, pat.before.iter(), indent);
|
||||||
};
|
|
||||||
|
|
||||||
if parens {
|
|
||||||
buf.push('(');
|
|
||||||
}
|
|
||||||
|
|
||||||
loc_pattern.format_with_options(buf, Parens::InApply, Newlines::No, indent);
|
|
||||||
|
|
||||||
for loc_arg in loc_arg_patterns.iter() {
|
|
||||||
buf.spaces(1);
|
|
||||||
loc_arg.format_with_options(buf, Parens::InApply, Newlines::No, indent_more);
|
|
||||||
}
|
|
||||||
|
|
||||||
if parens {
|
|
||||||
buf.push(')');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RecordDestructure(loc_patterns) => {
|
|
||||||
buf.indent(indent);
|
|
||||||
buf.push_str("{");
|
|
||||||
|
|
||||||
if !loc_patterns.is_empty() {
|
fmt_pattern_inner(&pat.item, buf, Parens::InApply, indent, is_multiline);
|
||||||
buf.spaces(1);
|
|
||||||
let mut it = loc_patterns.iter().peekable();
|
|
||||||
while let Some(loc_pattern) = it.next() {
|
|
||||||
loc_pattern.format(buf, indent);
|
|
||||||
|
|
||||||
if it.peek().is_some() {
|
if !pat.after.is_empty() {
|
||||||
buf.push_str(",");
|
if !is_multiline {
|
||||||
buf.spaces(1);
|
fmt_comments_only(buf, pat.after.iter(), NewlineAt::Bottom, indent_more)
|
||||||
|
} else {
|
||||||
|
fmt_spaces(buf, pat.after.iter(), indent_more);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for loc_arg in loc_arg_patterns.iter() {
|
||||||
|
buf.spaces(1);
|
||||||
|
fmt_pattern_inner(
|
||||||
|
&loc_arg.value,
|
||||||
|
buf,
|
||||||
|
Parens::InApply,
|
||||||
|
indent_more,
|
||||||
|
is_multiline,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if parens {
|
||||||
|
buf.push(')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RecordDestructure(loc_patterns) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str("{");
|
||||||
|
|
||||||
|
if !loc_patterns.is_empty() {
|
||||||
|
buf.spaces(1);
|
||||||
|
let mut it = loc_patterns.iter().peekable();
|
||||||
|
while let Some(loc_pattern) = it.next() {
|
||||||
|
let item = pattern_lift_spaces(buf.text.bump(), &loc_pattern.value);
|
||||||
|
|
||||||
|
if !item.before.is_empty() {
|
||||||
|
if !is_multiline {
|
||||||
|
fmt_comments_only(buf, item.before.iter(), NewlineAt::Bottom, indent)
|
||||||
|
} else {
|
||||||
|
fmt_spaces(buf, item.before.iter(), indent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt_pattern_inner(&item.item, buf, Parens::NotNeeded, indent, is_multiline);
|
||||||
|
|
||||||
|
let is_multiline = item.item.is_multiline();
|
||||||
|
|
||||||
|
if it.peek().is_some() {
|
||||||
|
buf.push_str(",");
|
||||||
|
buf.spaces(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !item.after.is_empty() {
|
||||||
|
if starts_with_inline_comment(item.after.iter()) {
|
||||||
|
buf.spaces(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_multiline {
|
||||||
|
fmt_comments_only(buf, item.after.iter(), NewlineAt::Bottom, indent)
|
||||||
|
} else {
|
||||||
|
fmt_spaces(buf, item.after.iter(), indent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.spaces(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
RequiredField(name, loc_pattern) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str(name);
|
||||||
|
buf.push_str(":");
|
||||||
|
buf.spaces(1);
|
||||||
|
fmt_pattern_inner(
|
||||||
|
&loc_pattern.value,
|
||||||
|
buf,
|
||||||
|
Parens::NotNeeded,
|
||||||
|
indent,
|
||||||
|
is_multiline,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionalField(name, loc_pattern) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str(name);
|
||||||
|
buf.push_str(" ?");
|
||||||
|
buf.spaces(1);
|
||||||
|
loc_pattern.format(buf, indent);
|
||||||
|
}
|
||||||
|
|
||||||
|
NumLiteral(string) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str(string);
|
||||||
|
}
|
||||||
|
NonBase10Literal {
|
||||||
|
base,
|
||||||
|
string,
|
||||||
|
is_negative,
|
||||||
|
} => {
|
||||||
|
buf.indent(indent);
|
||||||
|
if is_negative {
|
||||||
|
buf.push('-');
|
||||||
|
}
|
||||||
|
|
||||||
|
match base {
|
||||||
|
Base::Hex => buf.push_str("0x"),
|
||||||
|
Base::Octal => buf.push_str("0o"),
|
||||||
|
Base::Binary => buf.push_str("0b"),
|
||||||
|
Base::Decimal => { /* nothing */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.push_str(string);
|
||||||
|
}
|
||||||
|
FloatLiteral(string) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str(string);
|
||||||
|
}
|
||||||
|
StrLiteral(literal) => fmt_str_literal(buf, literal, indent),
|
||||||
|
SingleQuote(string) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
format_sq_literal(buf, string);
|
||||||
|
}
|
||||||
|
Underscore(name) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push('_');
|
||||||
|
buf.push_str(name);
|
||||||
|
}
|
||||||
|
Tuple(loc_patterns) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str("(");
|
||||||
|
|
||||||
|
let mut it = loc_patterns.iter().peekable();
|
||||||
|
while let Some(loc_pattern) = it.next() {
|
||||||
|
fmt_pattern_inner(
|
||||||
|
&loc_pattern.value,
|
||||||
|
buf,
|
||||||
|
Parens::NotNeeded,
|
||||||
|
indent,
|
||||||
|
is_multiline,
|
||||||
|
);
|
||||||
|
|
||||||
|
if it.peek().is_some() {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str(",");
|
||||||
buf.spaces(1);
|
buf.spaces(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.push_str("}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RequiredField(name, loc_pattern) => {
|
buf.indent(indent);
|
||||||
buf.indent(indent);
|
buf.push_str(")");
|
||||||
buf.push_str(name);
|
}
|
||||||
buf.push_str(":");
|
List(loc_patterns) => {
|
||||||
buf.spaces(1);
|
buf.indent(indent);
|
||||||
loc_pattern.format(buf, indent);
|
buf.push_str("[");
|
||||||
}
|
|
||||||
|
|
||||||
OptionalField(name, loc_pattern) => {
|
let mut it = loc_patterns.iter().peekable();
|
||||||
buf.indent(indent);
|
while let Some(loc_pattern) = it.next() {
|
||||||
buf.push_str(name);
|
fmt_pattern_inner(
|
||||||
buf.push_str(" ?");
|
&loc_pattern.value,
|
||||||
buf.spaces(1);
|
buf,
|
||||||
loc_pattern.format(buf, indent);
|
Parens::NotNeeded,
|
||||||
}
|
indent,
|
||||||
|
is_multiline,
|
||||||
|
);
|
||||||
|
|
||||||
&NumLiteral(string) => {
|
if it.peek().is_some() {
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str(string);
|
buf.push_str(",");
|
||||||
}
|
buf.spaces(1);
|
||||||
&NonBase10Literal {
|
|
||||||
base,
|
|
||||||
string,
|
|
||||||
is_negative,
|
|
||||||
} => {
|
|
||||||
buf.indent(indent);
|
|
||||||
if is_negative {
|
|
||||||
buf.push('-');
|
|
||||||
}
|
|
||||||
|
|
||||||
match base {
|
|
||||||
Base::Hex => buf.push_str("0x"),
|
|
||||||
Base::Octal => buf.push_str("0o"),
|
|
||||||
Base::Binary => buf.push_str("0b"),
|
|
||||||
Base::Decimal => { /* nothing */ }
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.push_str(string);
|
|
||||||
}
|
|
||||||
&FloatLiteral(string) => {
|
|
||||||
buf.indent(indent);
|
|
||||||
buf.push_str(string);
|
|
||||||
}
|
|
||||||
StrLiteral(literal) => fmt_str_literal(buf, *literal, indent),
|
|
||||||
SingleQuote(string) => {
|
|
||||||
buf.indent(indent);
|
|
||||||
format_sq_literal(buf, string);
|
|
||||||
}
|
|
||||||
Underscore(name) => {
|
|
||||||
buf.indent(indent);
|
|
||||||
buf.push('_');
|
|
||||||
buf.push_str(name);
|
|
||||||
}
|
|
||||||
Tuple(loc_patterns) => {
|
|
||||||
buf.indent(indent);
|
|
||||||
buf.push_str("(");
|
|
||||||
|
|
||||||
let mut it = loc_patterns.iter().peekable();
|
|
||||||
while let Some(loc_pattern) = it.next() {
|
|
||||||
loc_pattern.format(buf, indent);
|
|
||||||
|
|
||||||
if it.peek().is_some() {
|
|
||||||
buf.push_str(",");
|
|
||||||
buf.spaces(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.push_str(")");
|
|
||||||
}
|
|
||||||
List(loc_patterns) => {
|
|
||||||
buf.indent(indent);
|
|
||||||
buf.push_str("[");
|
|
||||||
|
|
||||||
let mut it = loc_patterns.iter().peekable();
|
|
||||||
while let Some(loc_pattern) = it.next() {
|
|
||||||
loc_pattern.format(buf, indent);
|
|
||||||
|
|
||||||
if it.peek().is_some() {
|
|
||||||
buf.push_str(",");
|
|
||||||
buf.spaces(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.push_str("]");
|
|
||||||
}
|
|
||||||
ListRest(opt_pattern_as) => {
|
|
||||||
buf.indent(indent);
|
|
||||||
buf.push_str("..");
|
|
||||||
|
|
||||||
if let Some((list_rest_spaces, pattern_as)) = opt_pattern_as {
|
|
||||||
// these spaces "belong" to the `..`, which can never be multiline
|
|
||||||
fmt_comments_only(buf, list_rest_spaces.iter(), NewlineAt::Bottom, indent);
|
|
||||||
|
|
||||||
pattern_as.format(buf, indent + INDENT);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
As(pattern, pattern_as) => {
|
buf.indent(indent);
|
||||||
let needs_parens = parens == Parens::InAsPattern;
|
buf.push_str("]");
|
||||||
|
}
|
||||||
|
ListRest(opt_pattern_as) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str("..");
|
||||||
|
|
||||||
if needs_parens {
|
if let Some((list_rest_spaces, pattern_as)) = opt_pattern_as {
|
||||||
buf.push('(');
|
// these spaces "belong" to the `..`, which can never be multiline
|
||||||
}
|
fmt_comments_only(buf, list_rest_spaces.iter(), NewlineAt::Bottom, indent);
|
||||||
|
|
||||||
fmt_pattern(buf, &pattern.value, indent, parens);
|
|
||||||
|
|
||||||
pattern_as.format(buf, indent + INDENT);
|
pattern_as.format(buf, indent + INDENT);
|
||||||
|
|
||||||
if needs_parens {
|
|
||||||
buf.push(')');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Space
|
As(pattern, pattern_as) => {
|
||||||
SpaceBefore(sub_pattern, spaces) => {
|
let needs_parens = parens == Parens::InAsPattern;
|
||||||
if !sub_pattern.is_multiline() {
|
|
||||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent)
|
|
||||||
} else {
|
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub_pattern.format_with_options(buf, parens, newlines, indent);
|
if needs_parens {
|
||||||
}
|
|
||||||
SpaceAfter(sub_pattern, spaces) => {
|
|
||||||
sub_pattern.format_with_options(buf, parens, newlines, indent);
|
|
||||||
|
|
||||||
if starts_with_inline_comment(spaces.iter()) {
|
|
||||||
buf.spaces(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !sub_pattern.is_multiline() {
|
|
||||||
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent)
|
|
||||||
} else {
|
|
||||||
fmt_spaces(buf, spaces.iter(), indent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Malformed
|
|
||||||
Malformed(string) | MalformedIdent(string, _) => {
|
|
||||||
buf.indent(indent);
|
buf.indent(indent);
|
||||||
buf.push_str(string);
|
buf.push('(');
|
||||||
}
|
}
|
||||||
QualifiedIdentifier { module_name, ident } => {
|
|
||||||
buf.indent(indent);
|
|
||||||
if !module_name.is_empty() {
|
|
||||||
buf.push_str(module_name);
|
|
||||||
buf.push('.');
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.push_str(ident);
|
fmt_pattern(buf, &pattern.value, indent, parens);
|
||||||
|
|
||||||
|
pattern_as.format(buf, indent + INDENT);
|
||||||
|
|
||||||
|
if needs_parens {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push(')');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpaceBefore(..) | SpaceAfter(..) => unreachable!("handled by lift_spaces"),
|
||||||
|
|
||||||
|
// Malformed
|
||||||
|
Malformed(string) | MalformedIdent(string, _) => {
|
||||||
|
buf.indent(indent);
|
||||||
|
buf.push_str(string);
|
||||||
|
}
|
||||||
|
QualifiedIdentifier { module_name, ident } => {
|
||||||
|
buf.indent(indent);
|
||||||
|
if !module_name.is_empty() {
|
||||||
|
buf.push_str(module_name);
|
||||||
|
buf.push('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.push_str(ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !me.after.is_empty() {
|
||||||
|
if starts_with_inline_comment(me.after.iter()) {
|
||||||
|
buf.spaces(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !outer_is_multiline {
|
||||||
|
fmt_comments_only(buf, me.after.iter(), NewlineAt::Bottom, indent)
|
||||||
|
} else {
|
||||||
|
fmt_spaces(buf, me.after.iter(), indent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn starts_with_inline_comment<'a, I: IntoIterator<Item = &'a CommentOrNewline<'a>>>(
|
pub fn starts_with_inline_comment<'a, I: IntoIterator<Item = &'a CommentOrNewline<'a>>>(
|
||||||
spaces: I,
|
spaces: I,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
|
@ -309,3 +401,102 @@ fn starts_with_inline_comment<'a, I: IntoIterator<Item = &'a CommentOrNewline<'a
|
||||||
Some(CommentOrNewline::LineComment(_))
|
Some(CommentOrNewline::LineComment(_))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pattern_lift_spaces<'a, 'b: 'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
pat: &Pattern<'b>,
|
||||||
|
) -> Spaces<'a, Pattern<'a>> {
|
||||||
|
match pat {
|
||||||
|
Pattern::Apply(func, args) => {
|
||||||
|
let func_lifted = pattern_lift_spaces(arena, &func.value);
|
||||||
|
let args = arena.alloc_slice_copy(args);
|
||||||
|
let (before, func, after) = if let Some(last) = args.last_mut() {
|
||||||
|
let last_lifted = pattern_lift_spaces(arena, &last.value);
|
||||||
|
if last_lifted.before.is_empty() {
|
||||||
|
*last = Loc::at(last.region, last_lifted.item)
|
||||||
|
} else {
|
||||||
|
*last = Loc::at(
|
||||||
|
last.region,
|
||||||
|
Pattern::SpaceBefore(arena.alloc(last_lifted.item), last_lifted.before),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let f = if func_lifted.after.is_empty() {
|
||||||
|
func_lifted.item
|
||||||
|
} else {
|
||||||
|
Pattern::SpaceAfter(arena.alloc(func_lifted.item), func_lifted.after)
|
||||||
|
};
|
||||||
|
|
||||||
|
(
|
||||||
|
func_lifted.before,
|
||||||
|
Loc::at(func.region, f),
|
||||||
|
last_lifted.after,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
func_lifted.before,
|
||||||
|
Loc::at(func.region, func_lifted.item),
|
||||||
|
func_lifted.after,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
Spaces {
|
||||||
|
before,
|
||||||
|
item: Pattern::Apply(arena.alloc(func), args),
|
||||||
|
after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pattern::OptionalField(name, expr) => {
|
||||||
|
let lifted = expr_lift_spaces_after(Parens::NotNeeded, arena, &expr.value);
|
||||||
|
Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: Pattern::OptionalField(name, arena.alloc(Loc::at(expr.region, lifted.item))),
|
||||||
|
after: lifted.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pattern::RequiredField(name, pat) => {
|
||||||
|
let lifted = pattern_lift_spaces_after(arena, &pat.value);
|
||||||
|
Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: Pattern::RequiredField(name, arena.alloc(Loc::at(pat.region, lifted.item))),
|
||||||
|
after: lifted.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Pattern::SpaceBefore(expr, spaces) => {
|
||||||
|
let mut inner = pattern_lift_spaces(arena, expr);
|
||||||
|
inner.before = merge_spaces(arena, spaces, inner.before);
|
||||||
|
inner
|
||||||
|
}
|
||||||
|
Pattern::SpaceAfter(expr, spaces) => {
|
||||||
|
let mut inner = pattern_lift_spaces(arena, expr);
|
||||||
|
inner.after = merge_spaces(arena, inner.after, spaces);
|
||||||
|
inner
|
||||||
|
}
|
||||||
|
_ => Spaces {
|
||||||
|
before: &[],
|
||||||
|
item: *pat,
|
||||||
|
after: &[],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pattern_lift_spaces_before<'a, 'b: 'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
pat: &Pattern<'b>,
|
||||||
|
) -> SpacesBefore<'a, Pattern<'a>> {
|
||||||
|
let lifted = pattern_lift_spaces(arena, pat);
|
||||||
|
SpacesBefore {
|
||||||
|
before: lifted.before,
|
||||||
|
item: lifted.item.maybe_after(arena, lifted.after),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pattern_lift_spaces_after<'a, 'b: 'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
pat: &Pattern<'b>,
|
||||||
|
) -> SpacesAfter<'a, Pattern<'a>> {
|
||||||
|
let lifted = pattern_lift_spaces(arena, pat);
|
||||||
|
SpacesAfter {
|
||||||
|
item: lifted.item.maybe_before(arena, lifted.before),
|
||||||
|
after: lifted.after,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -28,12 +28,26 @@ pub struct Spaces<'a, T> {
|
||||||
pub after: &'a [CommentOrNewline<'a>],
|
pub after: &'a [CommentOrNewline<'a>],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, T: Copy> ExtractSpaces<'a> for Spaces<'a, T> {
|
||||||
|
type Item = T;
|
||||||
|
|
||||||
|
fn extract_spaces(&self) -> Spaces<'a, T> {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct SpacesBefore<'a, T> {
|
pub struct SpacesBefore<'a, T> {
|
||||||
pub before: &'a [CommentOrNewline<'a>],
|
pub before: &'a [CommentOrNewline<'a>],
|
||||||
pub item: T,
|
pub item: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub struct SpacesAfter<'a, T> {
|
||||||
|
pub after: &'a [CommentOrNewline<'a>],
|
||||||
|
pub item: T,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
pub enum Spaced<'a, T> {
|
pub enum Spaced<'a, T> {
|
||||||
Item(T),
|
Item(T),
|
||||||
|
@ -491,7 +505,11 @@ pub enum Expr<'a> {
|
||||||
Backpassing(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
Backpassing(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
||||||
|
|
||||||
Dbg,
|
Dbg,
|
||||||
DbgStmt(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
DbgStmt {
|
||||||
|
first: &'a Loc<Expr<'a>>,
|
||||||
|
extra_args: &'a [&'a Loc<Expr<'a>>],
|
||||||
|
continuation: &'a Loc<Expr<'a>>,
|
||||||
|
},
|
||||||
|
|
||||||
// This form of debug is a desugared call to roc_dbg
|
// This form of debug is a desugared call to roc_dbg
|
||||||
LowLevelDbg(&'a (&'a str, &'a str), &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
LowLevelDbg(&'a (&'a str, &'a str), &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
||||||
|
@ -671,7 +689,15 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
|
||||||
Expr::OpaqueRef(_) => false,
|
Expr::OpaqueRef(_) => false,
|
||||||
Expr::Backpassing(_, _, _) => false, // TODO: we might want to check this?
|
Expr::Backpassing(_, _, _) => false, // TODO: we might want to check this?
|
||||||
Expr::Dbg => false,
|
Expr::Dbg => false,
|
||||||
Expr::DbgStmt(a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
|
Expr::DbgStmt {
|
||||||
|
first,
|
||||||
|
extra_args,
|
||||||
|
continuation,
|
||||||
|
} => {
|
||||||
|
is_expr_suffixed(&first.value)
|
||||||
|
|| extra_args.iter().any(|a| is_expr_suffixed(&a.value))
|
||||||
|
|| is_expr_suffixed(&continuation.value)
|
||||||
|
}
|
||||||
Expr::LowLevelDbg(_, a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
|
Expr::LowLevelDbg(_, a, b) => is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value),
|
||||||
Expr::Try => false,
|
Expr::Try => false,
|
||||||
Expr::UnaryOp(a, _) => is_expr_suffixed(&a.value),
|
Expr::UnaryOp(a, _) => is_expr_suffixed(&a.value),
|
||||||
|
@ -930,10 +956,17 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
|
||||||
expr_stack.push(&a.value);
|
expr_stack.push(&a.value);
|
||||||
expr_stack.push(&b.value);
|
expr_stack.push(&b.value);
|
||||||
}
|
}
|
||||||
DbgStmt(condition, cont) => {
|
DbgStmt {
|
||||||
|
first,
|
||||||
|
extra_args,
|
||||||
|
continuation,
|
||||||
|
} => {
|
||||||
expr_stack.reserve(2);
|
expr_stack.reserve(2);
|
||||||
expr_stack.push(&condition.value);
|
expr_stack.push(&first.value);
|
||||||
expr_stack.push(&cont.value);
|
for arg in extra_args.iter() {
|
||||||
|
expr_stack.push(&arg.value);
|
||||||
|
}
|
||||||
|
expr_stack.push(&continuation.value);
|
||||||
}
|
}
|
||||||
LowLevelDbg(_, condition, cont) => {
|
LowLevelDbg(_, condition, cont) => {
|
||||||
expr_stack.reserve(2);
|
expr_stack.reserve(2);
|
||||||
|
@ -2311,6 +2344,7 @@ impl_extract_spaces!(Tag);
|
||||||
impl_extract_spaces!(AssignedField<T>);
|
impl_extract_spaces!(AssignedField<T>);
|
||||||
impl_extract_spaces!(TypeAnnotation);
|
impl_extract_spaces!(TypeAnnotation);
|
||||||
impl_extract_spaces!(ImplementsAbility);
|
impl_extract_spaces!(ImplementsAbility);
|
||||||
|
impl_extract_spaces!(ImplementsAbilities);
|
||||||
|
|
||||||
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
|
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
|
||||||
type Item = T;
|
type Item = T;
|
||||||
|
@ -2476,7 +2510,7 @@ impl<'a> Malformed for Expr<'a> {
|
||||||
Defs(defs, body) => defs.is_malformed() || body.is_malformed(),
|
Defs(defs, body) => defs.is_malformed() || body.is_malformed(),
|
||||||
Backpassing(args, call, body) => args.iter().any(|arg| arg.is_malformed()) || call.is_malformed() || body.is_malformed(),
|
Backpassing(args, call, body) => args.iter().any(|arg| arg.is_malformed()) || call.is_malformed() || body.is_malformed(),
|
||||||
Dbg => false,
|
Dbg => false,
|
||||||
DbgStmt(condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
|
DbgStmt { first, extra_args, continuation } => first.is_malformed() || extra_args.iter().any(|a| a.is_malformed()) || continuation.is_malformed(),
|
||||||
LowLevelDbg(_, condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
|
LowLevelDbg(_, condition, continuation) => condition.is_malformed() || continuation.is_malformed(),
|
||||||
Try => false,
|
Try => false,
|
||||||
Return(return_value, after_return) => return_value.is_malformed() || after_return.is_some_and(|ar| ar.is_malformed()),
|
Return(return_value, after_return) => return_value.is_malformed() || after_return.is_some_and(|ar| ar.is_malformed()),
|
||||||
|
|
|
@ -298,8 +298,11 @@ fn loc_possibly_negative_or_negated_term<'a>(
|
||||||
let parse_unary_negate = move |arena, state: State<'a>, min_indent: u32| {
|
let parse_unary_negate = move |arena, state: State<'a>, min_indent: u32| {
|
||||||
let initial = state.clone();
|
let initial = state.clone();
|
||||||
|
|
||||||
let (_, (loc_op, loc_expr), state) =
|
let (_, (loc_op, loc_expr), state) = and(
|
||||||
and(loc(unary_negate()), loc_term(options)).parse(arena, state, min_indent)?;
|
loc(unary_negate()),
|
||||||
|
loc_possibly_negative_or_negated_term(options),
|
||||||
|
)
|
||||||
|
.parse(arena, state, min_indent)?;
|
||||||
|
|
||||||
let loc_expr = numeric_negate_expression(arena, initial, loc_op, loc_expr, &[]);
|
let loc_expr = numeric_negate_expression(arena, initial, loc_op, loc_expr, &[]);
|
||||||
|
|
||||||
|
@ -307,19 +310,23 @@ fn loc_possibly_negative_or_negated_term<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
one_of![
|
one_of![
|
||||||
parse_unary_negate,
|
parse_unary_negate.trace("d"),
|
||||||
// this will parse negative numbers, which the unary negate thing up top doesn't (for now)
|
// this will parse negative numbers, which the unary negate thing up top doesn't (for now)
|
||||||
loc(specialize_err(EExpr::Number, number_literal_help())),
|
loc(specialize_err(EExpr::Number, number_literal_help())).trace("c"),
|
||||||
loc(map_with_arena(
|
loc(map_with_arena(
|
||||||
and(
|
and(
|
||||||
loc(byte(b'!', EExpr::Start)),
|
loc(byte(b'!', EExpr::Start)),
|
||||||
space0_before_e(loc_term(options), EExpr::IndentStart)
|
space0_before_e(
|
||||||
|
loc_possibly_negative_or_negated_term(options),
|
||||||
|
EExpr::IndentStart
|
||||||
|
)
|
||||||
),
|
),
|
||||||
|arena: &'a Bump, (loc_op, loc_expr): (Loc<_>, _)| {
|
|arena: &'a Bump, (loc_op, loc_expr): (Loc<_>, _)| {
|
||||||
Expr::UnaryOp(arena.alloc(loc_expr), Loc::at(loc_op.region, UnaryOp::Not))
|
Expr::UnaryOp(arena.alloc(loc_expr), Loc::at(loc_op.region, UnaryOp::Not))
|
||||||
}
|
}
|
||||||
)),
|
))
|
||||||
loc_term_or_underscore_or_conditional(options)
|
.trace("b"),
|
||||||
|
loc_term_or_underscore_or_conditional(options).trace("a")
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +335,7 @@ fn fail_expr_start_e<'a, T: 'a>() -> impl Parser<'a, T, EExpr<'a>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> {
|
fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> {
|
||||||
move |_arena: &'a Bump, state: State<'a>, _min_indent: u32| {
|
move |_arena: &'a Bump, state: State<'a>, min_indent: u32| {
|
||||||
// a minus is unary iff
|
// a minus is unary iff
|
||||||
//
|
//
|
||||||
// - it is preceded by whitespace (spaces, newlines, comments)
|
// - it is preceded by whitespace (spaces, newlines, comments)
|
||||||
|
@ -339,7 +346,10 @@ fn unary_negate<'a>() -> impl Parser<'a, (), EExpr<'a>> {
|
||||||
.map(|c| c.is_ascii_whitespace() || *c == b'#')
|
.map(|c| c.is_ascii_whitespace() || *c == b'#')
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
if state.bytes().starts_with(b"-") && !followed_by_whitespace {
|
if state.bytes().starts_with(b"-")
|
||||||
|
&& !followed_by_whitespace
|
||||||
|
&& state.column() >= min_indent
|
||||||
|
{
|
||||||
// the negate is only unary if it is not followed by whitespace
|
// the negate is only unary if it is not followed by whitespace
|
||||||
let state = state.advance(1);
|
let state = state.advance(1);
|
||||||
Ok((MadeProgress, (), state))
|
Ok((MadeProgress, (), state))
|
||||||
|
@ -459,7 +469,7 @@ fn parse_expr_after_apply<'a>(
|
||||||
before_op: State<'a>,
|
before_op: State<'a>,
|
||||||
initial_state: State<'a>,
|
initial_state: State<'a>,
|
||||||
) -> Result<(Progress, Expr<'a>, State<'a>), (Progress, EExpr<'a>)> {
|
) -> Result<(Progress, Expr<'a>, State<'a>), (Progress, EExpr<'a>)> {
|
||||||
match loc(bin_op(check_for_defs)).parse(arena, state.clone(), min_indent) {
|
match loc(bin_op(check_for_defs)).parse(arena, state.clone(), call_min_indent) {
|
||||||
Err((MadeProgress, f)) => Err((MadeProgress, f)),
|
Err((MadeProgress, f)) => Err((MadeProgress, f)),
|
||||||
Ok((_, loc_op, state)) => {
|
Ok((_, loc_op, state)) => {
|
||||||
expr_state.consume_spaces(arena);
|
expr_state.consume_spaces(arena);
|
||||||
|
@ -845,13 +855,13 @@ fn numeric_negate_expression<'a, T>(
|
||||||
let region = Region::new(start, expr.region.end());
|
let region = Region::new(start, expr.region.end());
|
||||||
|
|
||||||
let new_expr = match expr.value {
|
let new_expr = match expr.value {
|
||||||
Expr::Num(string) => {
|
Expr::Num(string) if !string.starts_with('-') => {
|
||||||
let new_string =
|
let new_string =
|
||||||
unsafe { std::str::from_utf8_unchecked(&state.bytes()[..string.len() + 1]) };
|
unsafe { std::str::from_utf8_unchecked(&state.bytes()[..string.len() + 1]) };
|
||||||
|
|
||||||
Expr::Num(new_string)
|
Expr::Num(new_string)
|
||||||
}
|
}
|
||||||
Expr::Float(string) => {
|
Expr::Float(string) if !string.starts_with('-') => {
|
||||||
let new_string =
|
let new_string =
|
||||||
unsafe { std::str::from_utf8_unchecked(&state.bytes()[..string.len() + 1]) };
|
unsafe { std::str::from_utf8_unchecked(&state.bytes()[..string.len() + 1]) };
|
||||||
|
|
||||||
|
@ -860,11 +870,11 @@ fn numeric_negate_expression<'a, T>(
|
||||||
Expr::NonBase10Int {
|
Expr::NonBase10Int {
|
||||||
string,
|
string,
|
||||||
base,
|
base,
|
||||||
is_negative,
|
is_negative: false,
|
||||||
} => {
|
} => {
|
||||||
// don't include the minus sign here; it will not be parsed right
|
// don't include the minus sign here; it will not be parsed right
|
||||||
Expr::NonBase10Int {
|
Expr::NonBase10Int {
|
||||||
is_negative: !is_negative,
|
is_negative: true,
|
||||||
string,
|
string,
|
||||||
base,
|
base,
|
||||||
}
|
}
|
||||||
|
@ -1151,9 +1161,7 @@ fn parse_stmt_alias_or_opaque<'a>(
|
||||||
AliasOrOpaque::Alias => {
|
AliasOrOpaque::Alias => {
|
||||||
let (_, signature, state) = alias_signature().parse(arena, state, min_indent)?;
|
let (_, signature, state) = alias_signature().parse(arena, state, min_indent)?;
|
||||||
|
|
||||||
// TODO: this code used to be broken and it dropped the spaces after the operator.
|
let signature = signature.map(|v| v.maybe_before(arena, spaces_after_operator));
|
||||||
// The formatter is not expecting this, so let's keep it as is for now.
|
|
||||||
// let signature = signature.map(|v| v.maybe_before(arena, spaces_after_operator));
|
|
||||||
|
|
||||||
let header = TypeHeader {
|
let header = TypeHeader {
|
||||||
name: Loc::at(expr.region, name),
|
name: Loc::at(expr.region, name),
|
||||||
|
@ -1172,9 +1180,7 @@ fn parse_stmt_alias_or_opaque<'a>(
|
||||||
let (_, (signature, derived), state) =
|
let (_, (signature, derived), state) =
|
||||||
opaque_signature().parse(arena, state, indented_more)?;
|
opaque_signature().parse(arena, state, indented_more)?;
|
||||||
|
|
||||||
// TODO: this code used to be broken and it dropped the spaces after the operator.
|
let signature = signature.map(|v| v.maybe_before(arena, spaces_after_operator));
|
||||||
// The formatter is not expecting this, so let's keep it as is for now.
|
|
||||||
// let signature = signature.map(|v| v.maybe_before(arena, spaces_after_operator));
|
|
||||||
|
|
||||||
let header = TypeHeader {
|
let header = TypeHeader {
|
||||||
name: Loc::at(expr.region, name),
|
name: Loc::at(expr.region, name),
|
||||||
|
@ -1855,7 +1861,7 @@ fn parse_expr_end<'a>(
|
||||||
Err((NoProgress, _)) => {
|
Err((NoProgress, _)) => {
|
||||||
let before_op = state.clone();
|
let before_op = state.clone();
|
||||||
// try an operator
|
// try an operator
|
||||||
match loc(bin_op(check_for_defs)).parse(arena, state.clone(), min_indent) {
|
match loc(bin_op(check_for_defs)).parse(arena, state.clone(), call_min_indent) {
|
||||||
Err((MadeProgress, f)) => Err((MadeProgress, f)),
|
Err((MadeProgress, f)) => Err((MadeProgress, f)),
|
||||||
Ok((_, loc_op, state)) => {
|
Ok((_, loc_op, state)) => {
|
||||||
expr_state.consume_spaces(arena);
|
expr_state.consume_spaces(arena);
|
||||||
|
@ -1899,7 +1905,7 @@ fn parse_stmt_after_apply<'a>(
|
||||||
initial_state: State<'a>,
|
initial_state: State<'a>,
|
||||||
) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> {
|
) -> ParseResult<'a, Stmt<'a>, EExpr<'a>> {
|
||||||
let before_op = state.clone();
|
let before_op = state.clone();
|
||||||
match loc(operator()).parse(arena, state.clone(), min_indent) {
|
match loc(operator()).parse(arena, state.clone(), call_min_indent) {
|
||||||
Err((MadeProgress, f)) => Err((MadeProgress, f)),
|
Err((MadeProgress, f)) => Err((MadeProgress, f)),
|
||||||
Ok((_, loc_op, state)) => {
|
Ok((_, loc_op, state)) => {
|
||||||
expr_state.consume_spaces(arena);
|
expr_state.consume_spaces(arena);
|
||||||
|
@ -2172,7 +2178,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
||||||
| Expr::If { .. }
|
| Expr::If { .. }
|
||||||
| Expr::When(_, _)
|
| Expr::When(_, _)
|
||||||
| Expr::Dbg
|
| Expr::Dbg
|
||||||
| Expr::DbgStmt(_, _)
|
| Expr::DbgStmt { .. }
|
||||||
| Expr::LowLevelDbg(_, _, _)
|
| Expr::LowLevelDbg(_, _, _)
|
||||||
| Expr::Return(_, _)
|
| Expr::Return(_, _)
|
||||||
| Expr::MalformedSuffixed(..)
|
| Expr::MalformedSuffixed(..)
|
||||||
|
@ -3086,16 +3092,13 @@ fn stmts_to_defs<'a>(
|
||||||
_,
|
_,
|
||||||
) = e
|
) = e
|
||||||
{
|
{
|
||||||
if args.len() != 1 {
|
|
||||||
// TODO: this should be done in can, not parsing!
|
|
||||||
return Err(EExpr::Dbg(
|
|
||||||
EExpect::DbgArity(sp_stmt.item.region.start()),
|
|
||||||
sp_stmt.item.region.start(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let condition = &args[0];
|
let condition = &args[0];
|
||||||
let rest = stmts_to_expr(&stmts[i + 1..], arena)?;
|
let rest = stmts_to_expr(&stmts[i + 1..], arena)?;
|
||||||
let e = Expr::DbgStmt(condition, arena.alloc(rest));
|
let e = Expr::DbgStmt {
|
||||||
|
first: condition,
|
||||||
|
extra_args: &args[1..],
|
||||||
|
continuation: arena.alloc(rest),
|
||||||
|
};
|
||||||
|
|
||||||
let e = if sp_stmt.before.is_empty() {
|
let e = if sp_stmt.before.is_empty() {
|
||||||
e
|
e
|
||||||
|
@ -3211,7 +3214,11 @@ fn stmts_to_defs<'a>(
|
||||||
if exprify_dbg {
|
if exprify_dbg {
|
||||||
let e = if i + 1 < stmts.len() {
|
let e = if i + 1 < stmts.len() {
|
||||||
let rest = stmts_to_expr(&stmts[i + 1..], arena)?;
|
let rest = stmts_to_expr(&stmts[i + 1..], arena)?;
|
||||||
Expr::DbgStmt(arena.alloc(condition), arena.alloc(rest))
|
Expr::DbgStmt {
|
||||||
|
first: arena.alloc(condition),
|
||||||
|
extra_args: &[],
|
||||||
|
continuation: arena.alloc(rest),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Expr::Apply(
|
Expr::Apply(
|
||||||
arena.alloc(Loc {
|
arena.alloc(Loc {
|
||||||
|
@ -3799,9 +3806,9 @@ enum OperatorOrDef {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bin_op<'a>(check_for_defs: bool) -> impl Parser<'a, BinOp, EExpr<'a>> {
|
fn bin_op<'a>(check_for_defs: bool) -> impl Parser<'a, BinOp, EExpr<'a>> {
|
||||||
move |_, state: State<'a>, _m| {
|
move |_, state: State<'a>, min_indent| {
|
||||||
let start = state.pos();
|
let start = state.pos();
|
||||||
let (_, op, state) = operator_help(EExpr::Start, EExpr::BadOperator, state)?;
|
let (_, op, state) = operator_help(EExpr::Start, EExpr::BadOperator, state, min_indent)?;
|
||||||
let err_progress = if check_for_defs {
|
let err_progress = if check_for_defs {
|
||||||
MadeProgress
|
MadeProgress
|
||||||
} else {
|
} else {
|
||||||
|
@ -3822,7 +3829,8 @@ fn bin_op<'a>(check_for_defs: bool) -> impl Parser<'a, BinOp, EExpr<'a>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operator<'a>() -> impl Parser<'a, OperatorOrDef, EExpr<'a>> {
|
fn operator<'a>() -> impl Parser<'a, OperatorOrDef, EExpr<'a>> {
|
||||||
(move |_, state, _m| operator_help(EExpr::Start, EExpr::BadOperator, state)).trace("operator")
|
(move |_, state, min_indent| operator_help(EExpr::Start, EExpr::BadOperator, state, min_indent))
|
||||||
|
.trace("operator")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -3830,6 +3838,7 @@ fn operator_help<'a, F, G, E>(
|
||||||
to_expectation: F,
|
to_expectation: F,
|
||||||
to_error: G,
|
to_error: G,
|
||||||
mut state: State<'a>,
|
mut state: State<'a>,
|
||||||
|
min_indent: u32,
|
||||||
) -> ParseResult<'a, OperatorOrDef, E>
|
) -> ParseResult<'a, OperatorOrDef, E>
|
||||||
where
|
where
|
||||||
F: Fn(Position) -> E,
|
F: Fn(Position) -> E,
|
||||||
|
@ -3855,7 +3864,21 @@ where
|
||||||
match chomped {
|
match chomped {
|
||||||
"" => Err((NoProgress, to_expectation(state.pos()))),
|
"" => Err((NoProgress, to_expectation(state.pos()))),
|
||||||
"+" => good!(OperatorOrDef::BinOp(BinOp::Plus), 1),
|
"+" => good!(OperatorOrDef::BinOp(BinOp::Plus), 1),
|
||||||
"-" => good!(OperatorOrDef::BinOp(BinOp::Minus), 1),
|
"-" => {
|
||||||
|
// A unary minus must only match if we are at the correct indent level; indent level doesn't
|
||||||
|
// matter for the rest of the operators.
|
||||||
|
|
||||||
|
// Note that a unary minus is distinguished by not having a space after it
|
||||||
|
let has_whitespace = matches!(
|
||||||
|
state.bytes().get(1),
|
||||||
|
Some(b' ' | b'#' | b'\n' | b'\r' | b'\t') | None
|
||||||
|
);
|
||||||
|
if !has_whitespace && state.column() < min_indent {
|
||||||
|
return Err((NoProgress, to_expectation(state.pos())));
|
||||||
|
}
|
||||||
|
|
||||||
|
good!(OperatorOrDef::BinOp(BinOp::Minus), 1)
|
||||||
|
}
|
||||||
"*" => good!(OperatorOrDef::BinOp(BinOp::Star), 1),
|
"*" => good!(OperatorOrDef::BinOp(BinOp::Star), 1),
|
||||||
"/" => good!(OperatorOrDef::BinOp(BinOp::Slash), 1),
|
"/" => good!(OperatorOrDef::BinOp(BinOp::Slash), 1),
|
||||||
"%" => good!(OperatorOrDef::BinOp(BinOp::Percent), 1),
|
"%" => good!(OperatorOrDef::BinOp(BinOp::Percent), 1),
|
||||||
|
@ -3883,6 +3906,10 @@ where
|
||||||
}
|
}
|
||||||
"<-" => good!(OperatorOrDef::Backpassing, 2),
|
"<-" => good!(OperatorOrDef::Backpassing, 2),
|
||||||
"!" => Err((NoProgress, to_error("!", state.pos()))),
|
"!" => Err((NoProgress, to_error("!", state.pos()))),
|
||||||
|
"&" => {
|
||||||
|
// makes no progress, so it does not interfere with record updaters / `&foo`
|
||||||
|
Err((NoProgress, to_error("&", state.pos())))
|
||||||
|
}
|
||||||
_ => bad_made_progress!(chomped),
|
_ => bad_made_progress!(chomped),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -396,10 +396,22 @@ impl<'a> Normalize<'a> for ValueDef<'a> {
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
Annotation(a, b) => Annotation(a.normalize(arena), b.normalize(arena)),
|
Annotation(a, b) => Annotation(a.normalize(arena), b.normalize(arena)),
|
||||||
Body(a, b) => Body(
|
Body(a, b) => {
|
||||||
arena.alloc(a.normalize(arena)),
|
let a = a.normalize(arena);
|
||||||
arena.alloc(b.normalize(arena)),
|
let b = b.normalize(arena);
|
||||||
),
|
|
||||||
|
let is_unit_assignment = if let Pattern::RecordDestructure(collection) = a.value {
|
||||||
|
collection.is_empty()
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_unit_assignment {
|
||||||
|
Stmt(arena.alloc(b))
|
||||||
|
} else {
|
||||||
|
Body(arena.alloc(a), arena.alloc(b))
|
||||||
|
}
|
||||||
|
}
|
||||||
AnnotatedBody {
|
AnnotatedBody {
|
||||||
ann_pattern,
|
ann_pattern,
|
||||||
ann_type,
|
ann_type,
|
||||||
|
@ -560,26 +572,6 @@ impl<'a> Normalize<'a> for StrLiteral<'a> {
|
||||||
match *self {
|
match *self {
|
||||||
StrLiteral::PlainLine(t) => StrLiteral::PlainLine(t),
|
StrLiteral::PlainLine(t) => StrLiteral::PlainLine(t),
|
||||||
StrLiteral::Line(t) => {
|
StrLiteral::Line(t) => {
|
||||||
let mut needs_merge = false;
|
|
||||||
let mut last_was_mergable = false;
|
|
||||||
for segment in t.iter() {
|
|
||||||
let mergable = matches!(
|
|
||||||
segment,
|
|
||||||
StrSegment::Plaintext(_)
|
|
||||||
| StrSegment::Unicode(_)
|
|
||||||
| StrSegment::EscapedChar(_)
|
|
||||||
);
|
|
||||||
if mergable && last_was_mergable {
|
|
||||||
needs_merge = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
last_was_mergable = mergable;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !needs_merge {
|
|
||||||
return StrLiteral::Line(t.normalize(arena));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut new_segments = Vec::new_in(arena);
|
let mut new_segments = Vec::new_in(arena);
|
||||||
let mut last_text = String::new_in(arena);
|
let mut last_text = String::new_in(arena);
|
||||||
|
|
||||||
|
@ -713,33 +705,22 @@ impl<'a> Normalize<'a> for Expr<'a> {
|
||||||
arena.alloc(b.normalize(arena)),
|
arena.alloc(b.normalize(arena)),
|
||||||
),
|
),
|
||||||
Expr::Crash => Expr::Crash,
|
Expr::Crash => Expr::Crash,
|
||||||
Expr::Defs(a, b) => {
|
Expr::Defs(a, b) => fold_defs(arena, a.defs(), b.value.normalize(arena)),
|
||||||
let mut defs = a.clone();
|
|
||||||
defs.space_before = vec![Default::default(); defs.len()];
|
|
||||||
defs.space_after = vec![Default::default(); defs.len()];
|
|
||||||
defs.regions = vec![Region::zero(); defs.len()];
|
|
||||||
defs.spaces.clear();
|
|
||||||
|
|
||||||
for type_def in defs.type_defs.iter_mut() {
|
|
||||||
*type_def = type_def.normalize(arena);
|
|
||||||
}
|
|
||||||
|
|
||||||
for value_def in defs.value_defs.iter_mut() {
|
|
||||||
*value_def = value_def.normalize(arena);
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::Defs(arena.alloc(defs), arena.alloc(b.normalize(arena)))
|
|
||||||
}
|
|
||||||
Expr::Backpassing(a, b, c) => Expr::Backpassing(
|
Expr::Backpassing(a, b, c) => Expr::Backpassing(
|
||||||
arena.alloc(a.normalize(arena)),
|
arena.alloc(a.normalize(arena)),
|
||||||
arena.alloc(b.normalize(arena)),
|
arena.alloc(b.normalize(arena)),
|
||||||
arena.alloc(c.normalize(arena)),
|
arena.alloc(c.normalize(arena)),
|
||||||
),
|
),
|
||||||
Expr::Dbg => Expr::Dbg,
|
Expr::Dbg => Expr::Dbg,
|
||||||
Expr::DbgStmt(a, b) => Expr::DbgStmt(
|
Expr::DbgStmt {
|
||||||
arena.alloc(a.normalize(arena)),
|
first,
|
||||||
arena.alloc(b.normalize(arena)),
|
extra_args,
|
||||||
),
|
continuation,
|
||||||
|
} => Expr::DbgStmt {
|
||||||
|
first: arena.alloc(first.normalize(arena)),
|
||||||
|
extra_args: extra_args.normalize(arena),
|
||||||
|
continuation: arena.alloc(continuation.normalize(arena)),
|
||||||
|
},
|
||||||
Expr::LowLevelDbg(x, a, b) => Expr::LowLevelDbg(
|
Expr::LowLevelDbg(x, a, b) => Expr::LowLevelDbg(
|
||||||
x,
|
x,
|
||||||
arena.alloc(a.normalize(arena)),
|
arena.alloc(a.normalize(arena)),
|
||||||
|
@ -755,7 +736,22 @@ impl<'a> Normalize<'a> for Expr<'a> {
|
||||||
}
|
}
|
||||||
Expr::BinOps(a, b) => Expr::BinOps(a.normalize(arena), arena.alloc(b.normalize(arena))),
|
Expr::BinOps(a, b) => Expr::BinOps(a.normalize(arena), arena.alloc(b.normalize(arena))),
|
||||||
Expr::UnaryOp(a, b) => {
|
Expr::UnaryOp(a, b) => {
|
||||||
Expr::UnaryOp(arena.alloc(a.normalize(arena)), b.normalize(arena))
|
let a = a.normalize(arena);
|
||||||
|
match (a.value, b.value) {
|
||||||
|
(Expr::Num(text), UnaryOp::Negate) if !text.starts_with('-') => {
|
||||||
|
let mut res = String::new_in(arena);
|
||||||
|
res.push('-');
|
||||||
|
res.push_str(text);
|
||||||
|
Expr::Num(res.into_bump_str())
|
||||||
|
}
|
||||||
|
(Expr::Float(text), UnaryOp::Negate) if !text.starts_with('-') => {
|
||||||
|
let mut res = String::new_in(arena);
|
||||||
|
res.push('-');
|
||||||
|
res.push_str(text);
|
||||||
|
Expr::Float(res.into_bump_str())
|
||||||
|
}
|
||||||
|
_ => Expr::UnaryOp(arena.alloc(a), b.normalize(arena)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Expr::If {
|
Expr::If {
|
||||||
if_thens,
|
if_thens,
|
||||||
|
@ -776,7 +772,7 @@ impl<'a> Normalize<'a> for Expr<'a> {
|
||||||
Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a),
|
Expr::PrecedenceConflict(a) => Expr::PrecedenceConflict(a),
|
||||||
Expr::SpaceBefore(a, _) => a.normalize(arena),
|
Expr::SpaceBefore(a, _) => a.normalize(arena),
|
||||||
Expr::SpaceAfter(a, _) => a.normalize(arena),
|
Expr::SpaceAfter(a, _) => a.normalize(arena),
|
||||||
Expr::SingleQuote(a) => Expr::Num(a),
|
Expr::SingleQuote(a) => Expr::SingleQuote(a),
|
||||||
Expr::EmptyRecordBuilder(a) => {
|
Expr::EmptyRecordBuilder(a) => {
|
||||||
Expr::EmptyRecordBuilder(arena.alloc(a.normalize(arena)))
|
Expr::EmptyRecordBuilder(arena.alloc(a.normalize(arena)))
|
||||||
}
|
}
|
||||||
|
@ -791,6 +787,61 @@ impl<'a> Normalize<'a> for Expr<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fold_defs<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
mut defs: impl Iterator<Item = Result<&'a TypeDef<'a>, &'a ValueDef<'a>>>,
|
||||||
|
final_expr: Expr<'a>,
|
||||||
|
) -> Expr<'a> {
|
||||||
|
let mut new_defs = Defs::default();
|
||||||
|
|
||||||
|
while let Some(def) = defs.next() {
|
||||||
|
match def {
|
||||||
|
Ok(td) => {
|
||||||
|
let td = td.normalize(arena);
|
||||||
|
new_defs.push_type_def(td, Region::zero(), &[], &[]);
|
||||||
|
}
|
||||||
|
Err(vd) => {
|
||||||
|
let vd = vd.normalize(arena);
|
||||||
|
|
||||||
|
match vd {
|
||||||
|
ValueDef::Stmt(&Loc {
|
||||||
|
value:
|
||||||
|
Expr::Apply(
|
||||||
|
&Loc {
|
||||||
|
value: Expr::Dbg, ..
|
||||||
|
},
|
||||||
|
args,
|
||||||
|
_,
|
||||||
|
),
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let rest = fold_defs(arena, defs, final_expr);
|
||||||
|
let new_final = Expr::DbgStmt {
|
||||||
|
first: args[0],
|
||||||
|
extra_args: &args[1..],
|
||||||
|
continuation: arena.alloc(Loc::at_zero(rest)),
|
||||||
|
};
|
||||||
|
if new_defs.is_empty() {
|
||||||
|
return new_final;
|
||||||
|
}
|
||||||
|
return Expr::Defs(
|
||||||
|
arena.alloc(new_defs),
|
||||||
|
arena.alloc(Loc::at_zero(new_final)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
new_defs.push_value_def(vd, Region::zero(), &[], &[]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if new_defs.is_empty() {
|
||||||
|
return final_expr;
|
||||||
|
}
|
||||||
|
Expr::Defs(arena.alloc(new_defs), arena.alloc(Loc::at_zero(final_expr)))
|
||||||
|
}
|
||||||
|
|
||||||
fn remove_spaces_bad_ident(ident: BadIdent) -> BadIdent {
|
fn remove_spaces_bad_ident(ident: BadIdent) -> BadIdent {
|
||||||
match ident {
|
match ident {
|
||||||
BadIdent::Start(_) => BadIdent::Start(Position::zero()),
|
BadIdent::Start(_) => BadIdent::Start(Position::zero()),
|
||||||
|
@ -847,7 +898,7 @@ impl<'a> Normalize<'a> for Pattern<'a> {
|
||||||
is_negative,
|
is_negative,
|
||||||
},
|
},
|
||||||
Pattern::FloatLiteral(a) => Pattern::FloatLiteral(a),
|
Pattern::FloatLiteral(a) => Pattern::FloatLiteral(a),
|
||||||
Pattern::StrLiteral(a) => Pattern::StrLiteral(a),
|
Pattern::StrLiteral(a) => Pattern::StrLiteral(a.normalize(arena)),
|
||||||
Pattern::Underscore(a) => Pattern::Underscore(a),
|
Pattern::Underscore(a) => Pattern::Underscore(a),
|
||||||
Pattern::Malformed(a) => Pattern::Malformed(a),
|
Pattern::Malformed(a) => Pattern::Malformed(a),
|
||||||
Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)),
|
Pattern::MalformedIdent(a, b) => Pattern::MalformedIdent(a, remove_spaces_bad_ident(b)),
|
||||||
|
@ -1465,7 +1516,6 @@ impl<'a> Normalize<'a> for EExpect<'a> {
|
||||||
EExpect::Continuation(arena.alloc(inner_err.normalize(arena)), Position::zero())
|
EExpect::Continuation(arena.alloc(inner_err.normalize(arena)), Position::zero())
|
||||||
}
|
}
|
||||||
EExpect::IndentCondition(_) => EExpect::IndentCondition(Position::zero()),
|
EExpect::IndentCondition(_) => EExpect::IndentCondition(Position::zero()),
|
||||||
EExpect::DbgArity(_) => EExpect::DbgArity(Position::zero()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -513,7 +513,6 @@ pub enum EExpect<'a> {
|
||||||
Condition(&'a EExpr<'a>, Position),
|
Condition(&'a EExpr<'a>, Position),
|
||||||
Continuation(&'a EExpr<'a>, Position),
|
Continuation(&'a EExpr<'a>, Position),
|
||||||
IndentCondition(Position),
|
IndentCondition(Position),
|
||||||
DbgArity(Position),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -836,7 +835,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should be enough for anyone. Right? RIGHT?
|
// This should be enough for anyone. Right? RIGHT?
|
||||||
let indent_text = "| ; : ! ".repeat(20);
|
let indent_text = "| ; : ! ".repeat(100);
|
||||||
|
|
||||||
let cur_indent = INDENT.with(|i| *i.borrow());
|
let cur_indent = INDENT.with(|i| *i.borrow());
|
||||||
|
|
||||||
|
@ -1060,7 +1059,7 @@ where
|
||||||
Some(
|
Some(
|
||||||
b' ' | b'#' | b'\n' | b'\r' | b'\t' | b',' | b'(' | b')' | b'[' | b']' | b'{'
|
b' ' | b'#' | b'\n' | b'\r' | b'\t' | b',' | b'(' | b')' | b'[' | b']' | b'{'
|
||||||
| b'}' | b'"' | b'\'' | b'/' | b'\\' | b'+' | b'*' | b'%' | b'^' | b'&' | b'|'
|
| b'}' | b'"' | b'\'' | b'/' | b'\\' | b'+' | b'*' | b'%' | b'^' | b'&' | b'|'
|
||||||
| b'<' | b'>' | b'=' | b'!' | b'~' | b'`' | b';' | b':' | b'?' | b'.',
|
| b'<' | b'>' | b'=' | b'!' | b'~' | b'`' | b';' | b':' | b'?' | b'.' | b'@' | b'-',
|
||||||
) => {
|
) => {
|
||||||
state = state.advance(width);
|
state = state.advance(width);
|
||||||
Ok((MadeProgress, (), state))
|
Ok((MadeProgress, (), state))
|
||||||
|
@ -1669,6 +1668,21 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a parser that fails if the next byte is the given byte.
|
||||||
|
pub fn error_on_byte<'a, T, E, F>(byte_to_match: u8, to_error: F) -> impl Parser<'a, T, E>
|
||||||
|
where
|
||||||
|
T: 'a,
|
||||||
|
E: 'a,
|
||||||
|
F: Fn(Position) -> E,
|
||||||
|
{
|
||||||
|
debug_assert_ne!(byte_to_match, b'\n');
|
||||||
|
|
||||||
|
move |_arena: &'a Bump, state: State<'a>, _min_indent: u32| match state.bytes().first() {
|
||||||
|
Some(x) if *x == byte_to_match => Err((MadeProgress, to_error(state.pos()))),
|
||||||
|
_ => Err((NoProgress, to_error(state.pos()))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Runs two parsers in succession. If both parsers succeed, the output is a tuple of both outputs.
|
/// Runs two parsers in succession. If both parsers succeed, the output is a tuple of both outputs.
|
||||||
/// Both parsers must have the same error type.
|
/// Both parsers must have the same error type.
|
||||||
///
|
///
|
||||||
|
|
|
@ -10,9 +10,9 @@ use crate::expr::record_field;
|
||||||
use crate::ident::{lowercase_ident, lowercase_ident_keyword_e};
|
use crate::ident::{lowercase_ident, lowercase_ident_keyword_e};
|
||||||
use crate::keyword;
|
use crate::keyword;
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
absolute_column_min_indent, and, collection_trailing_sep_e, either, increment_min_indent,
|
absolute_column_min_indent, and, collection_trailing_sep_e, either, error_on_byte,
|
||||||
indented_seq, loc, map, map_with_arena, skip_first, skip_second, succeed, then, zero_or_more,
|
increment_min_indent, indented_seq, loc, map, map_with_arena, skip_first, skip_second, succeed,
|
||||||
ERecord, ETypeAbilityImpl,
|
then, zero_or_more, ERecord, ETypeAbilityImpl,
|
||||||
};
|
};
|
||||||
use crate::parser::{
|
use crate::parser::{
|
||||||
allocated, backtrackable, byte, fail, optional, specialize_err, specialize_err_ref, two_bytes,
|
allocated, backtrackable, byte, fail, optional, specialize_err, specialize_err_ref, two_bytes,
|
||||||
|
@ -582,37 +582,39 @@ fn expression<'a>(
|
||||||
let (p1, first, state) = space0_before_e(term(stop_at_surface_has), EType::TIndentStart)
|
let (p1, first, state) = space0_before_e(term(stop_at_surface_has), EType::TIndentStart)
|
||||||
.parse(arena, state, min_indent)?;
|
.parse(arena, state, min_indent)?;
|
||||||
|
|
||||||
let result = and(
|
let (p2, rest, rest_state) = zero_or_more(skip_first(
|
||||||
zero_or_more(skip_first(
|
backtrackable(byte(b',', EType::TFunctionArgument)),
|
||||||
byte(b',', EType::TFunctionArgument),
|
one_of![
|
||||||
one_of![
|
map_with_arena(
|
||||||
space0_around_ee(
|
and(
|
||||||
term(stop_at_surface_has),
|
backtrackable(space0_e(EType::TIndentStart)),
|
||||||
EType::TIndentStart,
|
and(term(stop_at_surface_has), space0_e(EType::TIndentEnd)),
|
||||||
EType::TIndentEnd
|
|
||||||
),
|
),
|
||||||
fail(EType::TFunctionArgument)
|
comma_args_help,
|
||||||
],
|
),
|
||||||
))
|
error_on_byte(b',', EType::TFunctionArgument)
|
||||||
.trace("type_annotation:expression:rest_args"),
|
],
|
||||||
and(
|
))
|
||||||
space0_e(EType::TIndentStart),
|
.trace("type_annotation:expression:rest_args")
|
||||||
one_of![
|
.parse(arena, state.clone(), min_indent)?;
|
||||||
map(two_bytes(b'-', b'>', EType::TStart), |_| {
|
|
||||||
FunctionArrow::Pure
|
let result = and(
|
||||||
}),
|
space0_e(EType::TIndentStart),
|
||||||
map(two_bytes(b'=', b'>', EType::TStart), |_| {
|
one_of![
|
||||||
FunctionArrow::Effectful
|
map(two_bytes(b'-', b'>', EType::TStart), |_| {
|
||||||
}),
|
FunctionArrow::Pure
|
||||||
],
|
}),
|
||||||
)
|
map(two_bytes(b'=', b'>', EType::TStart), |_| {
|
||||||
.trace("type_annotation:expression:arrow"),
|
FunctionArrow::Effectful
|
||||||
|
}),
|
||||||
|
],
|
||||||
)
|
)
|
||||||
.parse(arena, state.clone(), min_indent);
|
.trace("type_annotation:expression:arrow")
|
||||||
|
.parse(arena, rest_state, min_indent);
|
||||||
|
|
||||||
let (progress, annot, state) = match result {
|
let (progress, annot, state) = match result {
|
||||||
Ok((p2, (rest, (space_before_arrow, arrow)), state)) => {
|
Ok((p3, (space_before_arrow, arrow), state)) => {
|
||||||
let (p3, return_type, state) =
|
let (p4, return_type, state) =
|
||||||
space0_before_e(term(stop_at_surface_has), EType::TIndentStart)
|
space0_before_e(term(stop_at_surface_has), EType::TIndentStart)
|
||||||
.parse(arena, state, min_indent)?;
|
.parse(arena, state, min_indent)?;
|
||||||
|
|
||||||
|
@ -636,7 +638,7 @@ fn expression<'a>(
|
||||||
region,
|
region,
|
||||||
value: TypeAnnotation::Function(output, arrow, arena.alloc(return_type)),
|
value: TypeAnnotation::Function(output, arrow, arena.alloc(return_type)),
|
||||||
};
|
};
|
||||||
let progress = p1.or(p2).or(p3);
|
let progress = p1.or(p2).or(p3).or(p4);
|
||||||
(progress, result, state)
|
(progress, result, state)
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -694,6 +696,36 @@ fn expression<'a>(
|
||||||
.trace("type_annotation:expression")
|
.trace("type_annotation:expression")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn comma_args_help<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
(spaces_before, (loc_val, spaces_after)): (
|
||||||
|
&'a [CommentOrNewline<'a>],
|
||||||
|
(Loc<TypeAnnotation<'a>>, &'a [CommentOrNewline<'a>]),
|
||||||
|
),
|
||||||
|
) -> Loc<TypeAnnotation<'a>> {
|
||||||
|
if spaces_before.is_empty() {
|
||||||
|
if spaces_after.is_empty() {
|
||||||
|
loc_val
|
||||||
|
} else {
|
||||||
|
arena
|
||||||
|
.alloc(loc_val.value)
|
||||||
|
.with_spaces_after(spaces_after, loc_val.region)
|
||||||
|
}
|
||||||
|
} else if spaces_after.is_empty() {
|
||||||
|
arena
|
||||||
|
.alloc(loc_val.value)
|
||||||
|
.with_spaces_before(spaces_before, loc_val.region)
|
||||||
|
} else {
|
||||||
|
let wrapped_expr = arena
|
||||||
|
.alloc(loc_val.value)
|
||||||
|
.with_spaces_after(spaces_after, loc_val.region);
|
||||||
|
|
||||||
|
arena
|
||||||
|
.alloc(wrapped_expr.value)
|
||||||
|
.with_spaces_before(spaces_before, wrapped_expr.region)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a basic type annotation that's a combination of variables
|
/// Parse a basic type annotation that's a combination of variables
|
||||||
/// (which are lowercase and unqualified, e.g. `a` in `List a`),
|
/// (which are lowercase and unqualified, e.g. `a` in `List a`),
|
||||||
/// type applications (which are uppercase and optionally qualified, e.g.
|
/// type applications (which are uppercase and optionally qualified, e.g.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
use libfuzzer_sys::fuzz_target;
|
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
|
use libfuzzer_sys::fuzz_target;
|
||||||
use test_syntax::test_helpers::Input;
|
use test_syntax::test_helpers::Input;
|
||||||
|
|
||||||
fuzz_target!(|data: &[u8]| {
|
fuzz_target!(|data: &[u8]| {
|
||||||
|
|
|
@ -102,6 +102,12 @@ fn round_trip_once(input: Input<'_>) -> Option<String> {
|
||||||
return Some("Different ast".to_string());
|
return Some("Different ast".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let reformatted = reparsed_ast.format();
|
||||||
|
|
||||||
|
if output != reformatted {
|
||||||
|
return Some("Formatting not stable".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -239,7 +239,7 @@ impl<'a> Input<'a> {
|
||||||
self.as_str(),
|
self.as_str(),
|
||||||
output.as_ref().as_str(),
|
output.as_ref().as_str(),
|
||||||
actual,
|
actual,
|
||||||
reparsed_ast_normalized
|
reparsed_ast
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Expr(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(Type(TInParens(End(@78), @74), @72), @71), @69), @68), @66), @65), @61), @60), @58), @57), @53), @52), @50), @49), @47), @46), @44), @43), @41), @40), @39), @38), @36), @35), @31), @30), @28), @27), @23), @22), @20), @19), @17), @16), @11), @10), @8), @7), @3), @2), @2), @0)
|
|
@ -0,0 +1 @@
|
||||||
|
.:(i,i,(i,(i,ii,(i,(i,(i,i,(i,(i,i,(i,(J(i,(i,(i,(i,(i,i,(i,(i,i,(i,(i,(i,(J[]
|
|
@ -0,0 +1,2 @@
|
||||||
|
M : r
|
||||||
|
h
|
|
@ -0,0 +1,43 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(0),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-6,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [
|
||||||
|
Alias {
|
||||||
|
header: TypeHeader {
|
||||||
|
name: @0-1 "M",
|
||||||
|
vars: [],
|
||||||
|
},
|
||||||
|
ann: @4-5 SpaceBefore(
|
||||||
|
BoundVariable(
|
||||||
|
"r",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value_defs: [],
|
||||||
|
},
|
||||||
|
@7-8 SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "h",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
M:(
|
||||||
|
r)
|
||||||
|
h
|
|
@ -0,0 +1,3 @@
|
||||||
|
A #
|
||||||
|
p : e
|
||||||
|
A
|
|
@ -0,0 +1,48 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(0),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-7,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [
|
||||||
|
Alias {
|
||||||
|
header: TypeHeader {
|
||||||
|
name: @0-1 "A",
|
||||||
|
vars: [
|
||||||
|
@4-5 SpaceBefore(
|
||||||
|
Identifier {
|
||||||
|
ident: "p",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
LineComment(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ann: @6-7 BoundVariable(
|
||||||
|
"e",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value_defs: [],
|
||||||
|
},
|
||||||
|
@8-9 SpaceBefore(
|
||||||
|
Tag(
|
||||||
|
"A",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
A#
|
||||||
|
p:e
|
||||||
|
A
|
|
@ -0,0 +1,3 @@
|
||||||
|
K : #
|
||||||
|
s
|
||||||
|
K
|
|
@ -0,0 +1,44 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(0),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-7,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [
|
||||||
|
Alias {
|
||||||
|
header: TypeHeader {
|
||||||
|
name: @0-1 "K",
|
||||||
|
vars: [],
|
||||||
|
},
|
||||||
|
ann: @5-6 SpaceBefore(
|
||||||
|
BoundVariable(
|
||||||
|
"s",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
LineComment(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value_defs: [],
|
||||||
|
},
|
||||||
|
@8-9 SpaceBefore(
|
||||||
|
Tag(
|
||||||
|
"K",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
K:(#
|
||||||
|
s)
|
||||||
|
K
|
|
@ -0,0 +1,4 @@
|
||||||
|
O : O z
|
||||||
|
#
|
||||||
|
|
||||||
|
b #
|
|
@ -0,0 +1,59 @@
|
||||||
|
SpaceAfter(
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(0),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-9,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [
|
||||||
|
Alias {
|
||||||
|
header: TypeHeader {
|
||||||
|
name: @0-1 "O",
|
||||||
|
vars: [],
|
||||||
|
},
|
||||||
|
ann: @2-9 Apply(
|
||||||
|
"",
|
||||||
|
"O",
|
||||||
|
[
|
||||||
|
@4-5 SpaceAfter(
|
||||||
|
BoundVariable(
|
||||||
|
"z",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
LineComment(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value_defs: [],
|
||||||
|
},
|
||||||
|
@10-11 SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "b",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
LineComment(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,4 @@
|
||||||
|
O:O(z
|
||||||
|
#
|
||||||
|
)
|
||||||
|
b#
|
|
@ -0,0 +1,2 @@
|
||||||
|
p
|
||||||
|
! .p!!
|
|
@ -0,0 +1,47 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-1,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Stmt(
|
||||||
|
@0-1 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "p",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@2-8 SpaceBefore(
|
||||||
|
UnaryOp(
|
||||||
|
@4-7 SpaceBefore(
|
||||||
|
TrySuffix {
|
||||||
|
target: Task,
|
||||||
|
expr: AccessorFunction(
|
||||||
|
RecordField(
|
||||||
|
"p!",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
@2-3 Not,
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
p
|
||||||
|
!
|
||||||
|
.p!!
|
|
@ -0,0 +1,6 @@
|
||||||
|
r :
|
||||||
|
r
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
h
|
|
@ -0,0 +1,58 @@
|
||||||
|
SpaceAfter(
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-11,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Annotation(
|
||||||
|
@0-1 Identifier {
|
||||||
|
ident: "r",
|
||||||
|
},
|
||||||
|
@4-5 SpaceBefore(
|
||||||
|
SpaceAfter(
|
||||||
|
BoundVariable(
|
||||||
|
"r",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
LineComment(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
LineComment(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@12-13 SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "h",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,6 @@
|
||||||
|
r:(
|
||||||
|
r
|
||||||
|
#
|
||||||
|
#
|
||||||
|
)
|
||||||
|
h
|
|
@ -0,0 +1,3 @@
|
||||||
|
{ l: s #
|
||||||
|
} : s
|
||||||
|
o
|
|
@ -0,0 +1,51 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-9,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Annotation(
|
||||||
|
@0-7 RecordDestructure(
|
||||||
|
[
|
||||||
|
@1-6 SpaceAfter(
|
||||||
|
RequiredField(
|
||||||
|
"l",
|
||||||
|
@5-6 Identifier {
|
||||||
|
ident: "s",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
[
|
||||||
|
LineComment(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
@8-9 BoundVariable(
|
||||||
|
"s",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@10-11 SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "o",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
{l#
|
||||||
|
:s}:s
|
||||||
|
o
|
|
@ -0,0 +1,4 @@
|
||||||
|
k : [
|
||||||
|
T,
|
||||||
|
]m #
|
||||||
|
D
|
|
@ -0,0 +1,58 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-9,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Annotation(
|
||||||
|
@0-1 Identifier {
|
||||||
|
ident: "k",
|
||||||
|
},
|
||||||
|
@3-9 SpaceBefore(
|
||||||
|
TagUnion {
|
||||||
|
ext: Some(
|
||||||
|
@8-9 BoundVariable(
|
||||||
|
"m",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
tags: Collection {
|
||||||
|
items: [
|
||||||
|
@4-5 Apply {
|
||||||
|
name: @4-5 "T",
|
||||||
|
args: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
final_comments: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@11-12 SpaceBefore(
|
||||||
|
Tag(
|
||||||
|
"D",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
LineComment(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,4 @@
|
||||||
|
k:
|
||||||
|
[T,
|
||||||
|
]m#
|
||||||
|
D
|
|
@ -0,0 +1,5 @@
|
||||||
|
1 : (
|
||||||
|
f,
|
||||||
|
(ww -> p),
|
||||||
|
)e
|
||||||
|
Mh
|
|
@ -0,0 +1,61 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-13,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Annotation(
|
||||||
|
@0-1 NumLiteral(
|
||||||
|
"1",
|
||||||
|
),
|
||||||
|
@2-13 Tuple {
|
||||||
|
elems: [
|
||||||
|
@3-4 SpaceAfter(
|
||||||
|
BoundVariable(
|
||||||
|
"f",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
@6-11 Function(
|
||||||
|
[
|
||||||
|
@6-8 BoundVariable(
|
||||||
|
"ww",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Pure,
|
||||||
|
@10-11 BoundVariable(
|
||||||
|
"p",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ext: Some(
|
||||||
|
@12-13 BoundVariable(
|
||||||
|
"e",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@14-16 SpaceBefore(
|
||||||
|
Tag(
|
||||||
|
"Mh",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
1:(f
|
||||||
|
,ww->p)e
|
||||||
|
Mh
|
|
@ -0,0 +1,3 @@
|
||||||
|
E : B
|
||||||
|
{} = B
|
||||||
|
B
|
|
@ -0,0 +1,50 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-8,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
AnnotatedBody {
|
||||||
|
ann_pattern: Apply(
|
||||||
|
@0-1 Tag(
|
||||||
|
"E",
|
||||||
|
),
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
ann_type: @2-3 Apply(
|
||||||
|
"",
|
||||||
|
"B",
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
lines_between: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
body_pattern: @4-6 RecordDestructure(
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
body_expr: @7-8 Tag(
|
||||||
|
"B",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@9-10 SpaceBefore(
|
||||||
|
Tag(
|
||||||
|
"B",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
E:B
|
||||||
|
{}=B
|
||||||
|
B
|
|
@ -0,0 +1,2 @@
|
||||||
|
A p : e
|
||||||
|
A
|
|
@ -0,0 +1,51 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(0),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-7,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [
|
||||||
|
Alias {
|
||||||
|
header: TypeHeader {
|
||||||
|
name: @0-1 "A",
|
||||||
|
vars: [
|
||||||
|
@3-4 SpaceBefore(
|
||||||
|
Identifier {
|
||||||
|
ident: "p",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ann: @6-7 SpaceBefore(
|
||||||
|
BoundVariable(
|
||||||
|
"e",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value_defs: [],
|
||||||
|
},
|
||||||
|
@8-9 SpaceBefore(
|
||||||
|
Tag(
|
||||||
|
"A",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,4 @@
|
||||||
|
A
|
||||||
|
p:
|
||||||
|
e
|
||||||
|
A
|
|
@ -0,0 +1,3 @@
|
||||||
|
A e # g
|
||||||
|
: A
|
||||||
|
AA
|
|
@ -0,0 +1,55 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(0),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-9,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [
|
||||||
|
Alias {
|
||||||
|
header: TypeHeader {
|
||||||
|
name: @0-1 "A",
|
||||||
|
vars: [
|
||||||
|
@3-4 SpaceAfter(
|
||||||
|
SpaceBefore(
|
||||||
|
Identifier {
|
||||||
|
ident: "e",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
LineComment(
|
||||||
|
"g",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ann: @8-9 Apply(
|
||||||
|
"",
|
||||||
|
"A",
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
value_defs: [],
|
||||||
|
},
|
||||||
|
@10-12 SpaceBefore(
|
||||||
|
Tag(
|
||||||
|
"AA",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,4 @@
|
||||||
|
A
|
||||||
|
e#g
|
||||||
|
:A
|
||||||
|
AA
|
|
@ -0,0 +1,5 @@
|
||||||
|
3 :
|
||||||
|
( #
|
||||||
|
)n
|
||||||
|
-> n
|
||||||
|
0
|
|
@ -0,0 +1,56 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-10,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Annotation(
|
||||||
|
@0-1 NumLiteral(
|
||||||
|
"3",
|
||||||
|
),
|
||||||
|
@2-10 Function(
|
||||||
|
[
|
||||||
|
@2-7 Tuple {
|
||||||
|
elems: Collection {
|
||||||
|
items: [],
|
||||||
|
final_comments: [
|
||||||
|
LineComment(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ext: Some(
|
||||||
|
@6-7 BoundVariable(
|
||||||
|
"n",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Pure,
|
||||||
|
@9-10 BoundVariable(
|
||||||
|
"n",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@12-13 SpaceBefore(
|
||||||
|
Num(
|
||||||
|
"0",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
3:(#
|
||||||
|
)n->n
|
||||||
|
0
|
|
@ -0,0 +1,4 @@
|
||||||
|
d : (
|
||||||
|
J,
|
||||||
|
)g
|
||||||
|
2
|
|
@ -0,0 +1,52 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-8,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Annotation(
|
||||||
|
@0-1 Identifier {
|
||||||
|
ident: "d",
|
||||||
|
},
|
||||||
|
@2-8 Tuple {
|
||||||
|
elems: Collection {
|
||||||
|
items: [
|
||||||
|
@3-4 Apply(
|
||||||
|
"",
|
||||||
|
"J",
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
final_comments: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ext: Some(
|
||||||
|
@7-8 BoundVariable(
|
||||||
|
"g",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@9-10 SpaceBefore(
|
||||||
|
Num(
|
||||||
|
"2",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
d:(J,
|
||||||
|
)g
|
||||||
|
2
|
|
@ -0,0 +1,4 @@
|
||||||
|
p : (
|
||||||
|
)(
|
||||||
|
i)
|
||||||
|
{}
|
|
@ -0,0 +1,51 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-9,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Annotation(
|
||||||
|
@0-1 Identifier {
|
||||||
|
ident: "p",
|
||||||
|
},
|
||||||
|
@2-9 Tuple {
|
||||||
|
elems: Collection {
|
||||||
|
items: [],
|
||||||
|
final_comments: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ext: Some(
|
||||||
|
@7-8 SpaceBefore(
|
||||||
|
BoundVariable(
|
||||||
|
"i",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@10-12 SpaceBefore(
|
||||||
|
Record(
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,4 @@
|
||||||
|
p:(
|
||||||
|
)(
|
||||||
|
i)
|
||||||
|
{}
|
|
@ -0,0 +1,3 @@
|
||||||
|
Str.getUnsafe haystack haystackIndex
|
||||||
|
==
|
||||||
|
Str.getUnsafe needle needleIndex
|
|
@ -0,0 +1,56 @@
|
||||||
|
SpaceAfter(
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@0-36 SpaceAfter(
|
||||||
|
Apply(
|
||||||
|
@0-13 Var {
|
||||||
|
module_name: "Str",
|
||||||
|
ident: "getUnsafe",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
@14-22 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "haystack",
|
||||||
|
},
|
||||||
|
@23-36 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "haystackIndex",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
@37-39 Equals,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@40-72 Apply(
|
||||||
|
@40-53 SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "Str",
|
||||||
|
ident: "getUnsafe",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
[
|
||||||
|
@54-60 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "needle",
|
||||||
|
},
|
||||||
|
@61-72 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "needleIndex",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
Str.getUnsafe haystack haystackIndex
|
||||||
|
==
|
||||||
|
Str.getUnsafe needle needleIndex
|
|
@ -0,0 +1,2 @@
|
||||||
|
i < 2
|
||||||
|
-6
|
|
@ -0,0 +1,51 @@
|
||||||
|
SpaceAfter(
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-3,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Stmt(
|
||||||
|
@0-3 BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@0-1 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "i",
|
||||||
|
},
|
||||||
|
@1-2 LessThan,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@2-3 Num(
|
||||||
|
"2",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@4-8 SpaceBefore(
|
||||||
|
ParensAround(
|
||||||
|
Num(
|
||||||
|
"-6",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,2 @@
|
||||||
|
i<2
|
||||||
|
(-6)
|
|
@ -0,0 +1,2 @@
|
||||||
|
foo
|
||||||
|
|> Dict.keepIf \(k, _v) -> List.contains keysToDelete k |> Bool.not
|
|
@ -0,0 +1,72 @@
|
||||||
|
SpaceAfter(
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@0-3 SpaceAfter(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "foo",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
@4-6 Pizza,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@7-71 Apply(
|
||||||
|
@7-18 Var {
|
||||||
|
module_name: "Dict",
|
||||||
|
ident: "keepIf",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
@19-71 Closure(
|
||||||
|
[
|
||||||
|
@20-27 Tuple(
|
||||||
|
[
|
||||||
|
@21-22 Identifier {
|
||||||
|
ident: "k",
|
||||||
|
},
|
||||||
|
@24-26 Underscore(
|
||||||
|
"v",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@31-71 BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@31-59 Apply(
|
||||||
|
@31-44 Var {
|
||||||
|
module_name: "List",
|
||||||
|
ident: "contains",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
@45-57 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "keysToDelete",
|
||||||
|
},
|
||||||
|
@58-59 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "k",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
@60-62 Pizza,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@63-71 Var {
|
||||||
|
module_name: "Bool",
|
||||||
|
ident: "not",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,2 @@
|
||||||
|
foo
|
||||||
|
|> Dict.keepIf \(k, _v) -> List.contains keysToDelete k |> Bool.not
|
|
@ -0,0 +1,5 @@
|
||||||
|
a :
|
||||||
|
N {
|
||||||
|
h,
|
||||||
|
}
|
||||||
|
g
|
|
@ -0,0 +1,58 @@
|
||||||
|
SpaceAfter(
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-8,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Annotation(
|
||||||
|
@0-1 Identifier {
|
||||||
|
ident: "a",
|
||||||
|
},
|
||||||
|
@2-8 Apply(
|
||||||
|
"",
|
||||||
|
"N",
|
||||||
|
[
|
||||||
|
@3-8 Record {
|
||||||
|
fields: Collection {
|
||||||
|
items: [
|
||||||
|
@4-5 LabelOnly(
|
||||||
|
@4-5 "h",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
final_comments: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
ext: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@9-10 SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "g",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
a:N{h,
|
||||||
|
}
|
||||||
|
g
|
|
@ -0,0 +1,4 @@
|
||||||
|
ex <- f #
|
||||||
|
s # q
|
||||||
|
<- f #
|
||||||
|
s
|
|
@ -0,0 +1,52 @@
|
||||||
|
SpaceBefore(
|
||||||
|
Backpassing(
|
||||||
|
[
|
||||||
|
@1-3 Identifier {
|
||||||
|
ident: "ex",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
@5-6 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "f",
|
||||||
|
},
|
||||||
|
@8-18 SpaceBefore(
|
||||||
|
Backpassing(
|
||||||
|
[
|
||||||
|
@8-9 SpaceAfter(
|
||||||
|
Identifier {
|
||||||
|
ident: "s",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
LineComment(
|
||||||
|
"q",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@14-15 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "f",
|
||||||
|
},
|
||||||
|
@17-18 SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "s",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
LineComment(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
LineComment(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
ex<-f#
|
||||||
|
s#q
|
||||||
|
<-f#
|
||||||
|
s
|
|
@ -0,0 +1,3 @@
|
||||||
|
0
|
||||||
|
! .d
|
||||||
|
.d
|
|
@ -0,0 +1,56 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
EitherIndex(2147483649),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-1,
|
||||||
|
@2-6,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 1 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 1, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Stmt(
|
||||||
|
@0-1 Num(
|
||||||
|
"0",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Stmt(
|
||||||
|
@2-6 UnaryOp(
|
||||||
|
@4-6 SpaceBefore(
|
||||||
|
AccessorFunction(
|
||||||
|
RecordField(
|
||||||
|
"d",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
@2-3 Not,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@7-9 SpaceBefore(
|
||||||
|
AccessorFunction(
|
||||||
|
RecordField(
|
||||||
|
"d",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,4 @@
|
||||||
|
0
|
||||||
|
!
|
||||||
|
.d
|
||||||
|
.d
|
|
@ -0,0 +1 @@
|
||||||
|
N < l (r * N)
|
|
@ -0,0 +1,40 @@
|
||||||
|
SpaceAfter(
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@0-1 Tag(
|
||||||
|
"N",
|
||||||
|
),
|
||||||
|
@1-2 LessThan,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@2-7 Apply(
|
||||||
|
@2-3 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "l",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
@4-7 ParensAround(
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@4-5 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "r",
|
||||||
|
},
|
||||||
|
@5-6 Star,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@6-7 Tag(
|
||||||
|
"N",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
N<l(r*N)
|
|
@ -0,0 +1,8 @@
|
||||||
|
5
|
||||||
|
- (
|
||||||
|
(
|
||||||
|
e =
|
||||||
|
r
|
||||||
|
1
|
||||||
|
)
|
||||||
|
)
|
|
@ -0,0 +1,60 @@
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@0-1 Num(
|
||||||
|
"5",
|
||||||
|
),
|
||||||
|
@1-2 Minus,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@3-15 ParensAround(
|
||||||
|
ParensAround(
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@4-12,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Body(
|
||||||
|
@4-5 Identifier {
|
||||||
|
ident: "e",
|
||||||
|
},
|
||||||
|
@6-12 ParensAround(
|
||||||
|
ParensAround(
|
||||||
|
SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "r",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@13-14 SpaceBefore(
|
||||||
|
Num(
|
||||||
|
"1",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
5-((e=((
|
||||||
|
r))
|
||||||
|
1))
|
|
@ -0,0 +1,2 @@
|
||||||
|
"$(g)" : q
|
||||||
|
f
|
|
@ -0,0 +1,51 @@
|
||||||
|
SpaceAfter(
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-12,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Annotation(
|
||||||
|
@0-10 StrLiteral(
|
||||||
|
Line(
|
||||||
|
[
|
||||||
|
Interpolated(
|
||||||
|
@5-6 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "g",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
@11-12 BoundVariable(
|
||||||
|
"q",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@13-14 SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "f",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,2 @@
|
||||||
|
"""$(g)""":q
|
||||||
|
f
|
|
@ -0,0 +1,6 @@
|
||||||
|
t =
|
||||||
|
"""
|
||||||
|
"
|
||||||
|
"""
|
||||||
|
""
|
||||||
|
S
|
|
@ -0,0 +1,48 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-12,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Body(
|
||||||
|
@0-1 Identifier {
|
||||||
|
ident: "t",
|
||||||
|
},
|
||||||
|
@2-12 Apply(
|
||||||
|
@2-10 Str(
|
||||||
|
PlainLine(
|
||||||
|
"\" ",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
[
|
||||||
|
@10-12 Str(
|
||||||
|
PlainLine(
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@13-14 SpaceBefore(
|
||||||
|
Tag(
|
||||||
|
"S",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,2 @@
|
||||||
|
t="""" """""
|
||||||
|
S
|
|
@ -0,0 +1,3 @@
|
||||||
|
a =
|
||||||
|
6
|
||||||
|
a
|
|
@ -0,0 +1,44 @@
|
||||||
|
Defs(
|
||||||
|
Defs {
|
||||||
|
tags: [
|
||||||
|
EitherIndex(2147483648),
|
||||||
|
],
|
||||||
|
regions: [
|
||||||
|
@0-6,
|
||||||
|
],
|
||||||
|
space_before: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
space_after: [
|
||||||
|
Slice<roc_parse::ast::CommentOrNewline> { start: 0, length: 0 },
|
||||||
|
],
|
||||||
|
spaces: [],
|
||||||
|
type_defs: [],
|
||||||
|
value_defs: [
|
||||||
|
Body(
|
||||||
|
@0-1 Identifier {
|
||||||
|
ident: "a",
|
||||||
|
},
|
||||||
|
@2-6 ParensAround(
|
||||||
|
SpaceBefore(
|
||||||
|
Num(
|
||||||
|
"6",
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
@7-8 SpaceBefore(
|
||||||
|
Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "a",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
a=(
|
||||||
|
6)
|
||||||
|
a
|
|
@ -0,0 +1,2 @@
|
||||||
|
m0 \w -> w? e
|
||||||
|
/ s
|
|
@ -0,0 +1,48 @@
|
||||||
|
BinOps(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
@0-9 SpaceAfter(
|
||||||
|
Apply(
|
||||||
|
@0-2 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "m0",
|
||||||
|
},
|
||||||
|
[
|
||||||
|
@2-9 Closure(
|
||||||
|
[
|
||||||
|
@3-4 Identifier {
|
||||||
|
ident: "w",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
@6-9 Apply(
|
||||||
|
@6-7 TrySuffix {
|
||||||
|
target: Result,
|
||||||
|
expr: Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "w",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[
|
||||||
|
@8-9 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "e",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
Space,
|
||||||
|
),
|
||||||
|
[
|
||||||
|
Newline,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
@10-11 Slash,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
@11-12 Var {
|
||||||
|
module_name: "",
|
||||||
|
ident: "s",
|
||||||
|
},
|
||||||
|
)
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue