mirror of
https://github.com/roc-lang/roc.git
synced 2025-07-24 15:03:46 +00:00
Merge branch 'main' into builtin-task
This commit is contained in:
commit
f61b303a59
82 changed files with 2661 additions and 5699 deletions
|
@ -462,10 +462,6 @@ pub enum Expr<'a> {
|
|||
/// Multiple defs in a row
|
||||
Defs(&'a Defs<'a>, &'a Loc<Expr<'a>>),
|
||||
|
||||
/// Used in place of an expression when the final expression is empty
|
||||
/// This may happen if the final expression is actually a suffixed statement
|
||||
EmptyDefsFinal,
|
||||
|
||||
Backpassing(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
||||
Expect(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
||||
Dbg(&'a Loc<Expr<'a>>, &'a Loc<Expr<'a>>),
|
||||
|
@ -536,6 +532,19 @@ pub fn split_loc_exprs_around<'a>(
|
|||
(before, after)
|
||||
}
|
||||
|
||||
/// Checks if the bang suffix is applied only at the top level of expression
|
||||
pub fn is_top_level_suffixed(expr: &Expr) -> bool {
|
||||
// TODO: should we check BinOps with pizza where the last expression is TaskAwaitBang?
|
||||
match expr {
|
||||
Expr::TaskAwaitBang(..) => true,
|
||||
Expr::Apply(a, _, _) => is_top_level_suffixed(&a.value),
|
||||
Expr::SpaceBefore(a, _) => is_top_level_suffixed(a),
|
||||
Expr::SpaceAfter(a, _) => is_top_level_suffixed(a),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the bang suffix is applied recursevely in expression
|
||||
pub fn is_expr_suffixed(expr: &Expr) -> bool {
|
||||
match expr {
|
||||
// expression without arguments, `read!`
|
||||
|
@ -613,7 +622,6 @@ pub fn is_expr_suffixed(expr: &Expr) -> bool {
|
|||
Expr::Crash => false,
|
||||
Expr::Tag(_) => false,
|
||||
Expr::OpaqueRef(_) => false,
|
||||
Expr::EmptyDefsFinal => false,
|
||||
Expr::Backpassing(_, _, _) => false, // TODO: we might want to check this?
|
||||
Expr::Expect(a, b) | Expr::Dbg(a, b) => {
|
||||
is_expr_suffixed(&a.value) || is_expr_suffixed(&b.value)
|
||||
|
@ -975,8 +983,7 @@ impl<'a, 'b> RecursiveValueDefIter<'a, 'b> {
|
|||
| MalformedIdent(_, _)
|
||||
| MalformedClosure
|
||||
| PrecedenceConflict(_)
|
||||
| MalformedSuffixed(_)
|
||||
| EmptyDefsFinal => { /* terminal */ }
|
||||
| MalformedSuffixed(_) => { /* terminal */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1183,51 +1190,39 @@ impl<'a> Defs<'a> {
|
|||
})
|
||||
}
|
||||
|
||||
// We could have a type annotation as the last tag,
|
||||
// this helper ensures we refer to the last value_def
|
||||
// and that we remove the correct tag
|
||||
pub fn last_value_suffixed(&self) -> Option<(Self, &'a Loc<Expr<'a>>)> {
|
||||
let value_indexes =
|
||||
self.tags
|
||||
.clone()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.filter_map(|(tag_index, tag)| match tag.split() {
|
||||
Ok(_) => None,
|
||||
Err(value_index) => Some((tag_index, value_index.index())),
|
||||
});
|
||||
pub fn pop_last_value(&mut self) -> Option<&'a Loc<Expr<'a>>> {
|
||||
let last_value_suffix = self
|
||||
.tags
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.find_map(|(tag_index, tag)| match tag.split() {
|
||||
Ok(_) => None,
|
||||
Err(value_index) => match self.value_defs[value_index.index()] {
|
||||
ValueDef::Body(
|
||||
Loc {
|
||||
value: Pattern::RecordDestructure(collection),
|
||||
..
|
||||
},
|
||||
loc_expr,
|
||||
) if collection.is_empty() => Some((tag_index, loc_expr)),
|
||||
ValueDef::Stmt(loc_expr) => Some((tag_index, loc_expr)),
|
||||
_ => None,
|
||||
},
|
||||
});
|
||||
|
||||
if let Some((tag_index, value_index)) = value_indexes.last() {
|
||||
match self.value_defs[value_index] {
|
||||
ValueDef::Body(
|
||||
Loc {
|
||||
value: Pattern::RecordDestructure(collection),
|
||||
..
|
||||
},
|
||||
loc_expr,
|
||||
) if collection.is_empty() && is_expr_suffixed(&loc_expr.value) => {
|
||||
let mut new_defs = self.clone();
|
||||
new_defs.remove_value_def(tag_index);
|
||||
|
||||
return Some((new_defs, loc_expr));
|
||||
}
|
||||
ValueDef::Stmt(loc_expr) if is_expr_suffixed(&loc_expr.value) => {
|
||||
let mut new_defs = self.clone();
|
||||
new_defs.remove_value_def(tag_index);
|
||||
|
||||
return Some((new_defs, loc_expr));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Some((tag_index, loc_expr)) = last_value_suffix {
|
||||
self.remove_tag(tag_index);
|
||||
Some(loc_expr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn remove_value_def(&mut self, index: usize) {
|
||||
pub fn remove_tag(&mut self, tag_index: usize) {
|
||||
match self
|
||||
.tags
|
||||
.get(index)
|
||||
.get(tag_index)
|
||||
.expect("got an invalid index for Defs")
|
||||
.split()
|
||||
{
|
||||
|
@ -1260,10 +1255,10 @@ impl<'a> Defs<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
self.tags.remove(index);
|
||||
self.regions.remove(index);
|
||||
self.space_after.remove(index);
|
||||
self.space_before.remove(index);
|
||||
self.tags.remove(tag_index);
|
||||
self.regions.remove(tag_index);
|
||||
self.space_after.remove(tag_index);
|
||||
self.space_before.remove(tag_index);
|
||||
}
|
||||
|
||||
/// NOTE assumes the def itself is pushed already!
|
||||
|
@ -2394,7 +2389,6 @@ impl<'a> Malformed for Expr<'a> {
|
|||
Tag(_) |
|
||||
OpaqueRef(_) |
|
||||
SingleQuote(_) | // This is just a &str - not a bunch of segments
|
||||
EmptyDefsFinal |
|
||||
Crash => false,
|
||||
|
||||
Str(inner) => inner.is_malformed(),
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use crate::ast::{
|
||||
is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces,
|
||||
Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
|
||||
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
|
||||
ModuleImportParams, Pattern, RecordBuilderField, Spaceable, Spaced, Spaces, TypeAnnotation,
|
||||
TypeDef, TypeHeader, ValueDef,
|
||||
is_expr_suffixed, is_top_level_suffixed, AssignedField, Collection, CommentOrNewline, Defs,
|
||||
Expr, ExtractSpaces, Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword,
|
||||
ImportExposingKeyword, ImportedModuleName, IngestedFileAnnotation, IngestedFileImport,
|
||||
ModuleImport, ModuleImportParams, Pattern, RecordBuilderField, Spaceable, Spaced, Spaces,
|
||||
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
};
|
||||
use crate::blankspace::{
|
||||
space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e,
|
||||
|
@ -384,7 +384,7 @@ fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a
|
|||
Ok((progress, expr, new_state)) => {
|
||||
// We need to check if we have just parsed a suffixed statement,
|
||||
// if so, this is a defs node.
|
||||
if is_expr_suffixed(&expr) {
|
||||
if is_top_level_suffixed(&expr) {
|
||||
let def_region = Region::new(end, new_state.pos());
|
||||
let value_def = ValueDef::Stmt(arena.alloc(Loc::at(def_region, expr)));
|
||||
|
||||
|
@ -1154,13 +1154,13 @@ pub fn parse_single_def_assignment<'a>(
|
|||
|
||||
// If the expression is actually a suffixed statement, then we need to continue
|
||||
// to parse the rest of the expression
|
||||
if crate::ast::is_expr_suffixed(&first_loc_expr.value) {
|
||||
if is_top_level_suffixed(&first_loc_expr.value) {
|
||||
let mut defs = Defs::default();
|
||||
// Take the suffixed value and make it a e.g. Body(`{}=`, Apply(Var(...)))
|
||||
// we will keep the pattern `def_loc_pattern` for the new Defs
|
||||
defs.push_value_def(
|
||||
ValueDef::Stmt(arena.alloc(first_loc_expr)),
|
||||
Region::span_across(&def_loc_pattern.region, &first_loc_expr.region),
|
||||
region,
|
||||
spaces_before_current,
|
||||
&[],
|
||||
);
|
||||
|
@ -1169,16 +1169,16 @@ pub fn parse_single_def_assignment<'a>(
|
|||
match parse_defs_expr(
|
||||
options,
|
||||
min_indent,
|
||||
defs.clone(),
|
||||
defs,
|
||||
arena,
|
||||
state_after_first_expression.clone(),
|
||||
state_after_first_expression,
|
||||
) {
|
||||
Ok((progress_after_rest_of_def, expr, state_after_rest_of_def)) => {
|
||||
let final_loc_expr = arena.alloc(Loc::at(region, expr));
|
||||
|
||||
let value_def = ValueDef::Body(arena.alloc(def_loc_pattern), final_loc_expr);
|
||||
|
||||
return Ok((
|
||||
Ok((
|
||||
progress_after_rest_of_def,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::Second(value_def),
|
||||
|
@ -1187,45 +1187,24 @@ pub fn parse_single_def_assignment<'a>(
|
|||
spaces_after: &[],
|
||||
}),
|
||||
state_after_rest_of_def,
|
||||
));
|
||||
}
|
||||
Err(_) => {
|
||||
// Unable to parse more defs, continue and return the first parsed expression as a stement
|
||||
let empty_return =
|
||||
arena.alloc(Loc::at(first_loc_expr.region, Expr::EmptyDefsFinal));
|
||||
let value_def = ValueDef::Body(
|
||||
arena.alloc(def_loc_pattern),
|
||||
arena.alloc(Loc::at(
|
||||
first_loc_expr.region,
|
||||
Expr::Defs(arena.alloc(defs), empty_return),
|
||||
)),
|
||||
);
|
||||
return Ok((
|
||||
progress_after_first,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::Second(value_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
spaces_after: &[],
|
||||
}),
|
||||
state_after_first_expression,
|
||||
));
|
||||
))
|
||||
}
|
||||
Err((progress, err)) => Err((progress, err)),
|
||||
}
|
||||
} else {
|
||||
let value_def = ValueDef::Body(arena.alloc(def_loc_pattern), arena.alloc(first_loc_expr));
|
||||
|
||||
Ok((
|
||||
progress_after_first,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::Second(value_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
spaces_after: &[],
|
||||
}),
|
||||
state_after_first_expression,
|
||||
))
|
||||
}
|
||||
|
||||
let value_def = ValueDef::Body(arena.alloc(def_loc_pattern), arena.alloc(first_loc_expr));
|
||||
|
||||
Ok((
|
||||
progress_after_first,
|
||||
Some(SingleDef {
|
||||
type_or_value: Either::Second(value_def),
|
||||
region,
|
||||
spaces_before: spaces_before_current,
|
||||
spaces_after: &[],
|
||||
}),
|
||||
state_after_first_expression,
|
||||
))
|
||||
}
|
||||
|
||||
/// e.g. Things that can be on their own line in a def, e.g. `expect`, `expect-fx`, or `dbg`
|
||||
|
@ -1368,16 +1347,20 @@ fn parse_defs_end<'a>(
|
|||
Either::Second(value_def) => {
|
||||
// If we got a ValueDef::Body, check if a type annotation preceded it.
|
||||
// If so, we may need to combine them into an AnnotatedBody.
|
||||
let joined = match value_def {
|
||||
ValueDef::Body(loc_pattern, loc_def_expr)
|
||||
if spaces_before_current.len() <= 1 =>
|
||||
{
|
||||
let joined_def = match value_def {
|
||||
ValueDef::Body(loc_pattern, loc_def_expr) => {
|
||||
let region =
|
||||
Region::span_across(&loc_pattern.region, &loc_def_expr.region);
|
||||
|
||||
let signature_must_match_value = spaces_before_current.len() <= 1;
|
||||
let value_name = &loc_pattern.value;
|
||||
|
||||
match defs.last() {
|
||||
Some(Err(ValueDef::Annotation(ann_pattern, ann_type))) => {
|
||||
let (value_def, region) = join_ann_to_body!(
|
||||
Some(Err(ValueDef::Annotation(ann_pattern, ann_type)))
|
||||
if signature_must_match_value
|
||||
|| ann_pattern.value.equivalent(value_name) =>
|
||||
{
|
||||
Some(join_ann_to_body!(
|
||||
arena,
|
||||
loc_pattern,
|
||||
loc_def_expr,
|
||||
|
@ -1385,21 +1368,19 @@ fn parse_defs_end<'a>(
|
|||
ann_type,
|
||||
spaces_before_current,
|
||||
region
|
||||
);
|
||||
|
||||
defs.replace_with_value_def(
|
||||
defs.tags.len() - 1,
|
||||
value_def,
|
||||
region,
|
||||
);
|
||||
|
||||
true
|
||||
))
|
||||
}
|
||||
Some(Ok(TypeDef::Alias {
|
||||
header,
|
||||
ann: ann_type,
|
||||
})) => {
|
||||
let (value_def, region) = join_alias_to_body!(
|
||||
})) if signature_must_match_value
|
||||
|| header
|
||||
.vars
|
||||
.first()
|
||||
.map(|var| var.value.equivalent(value_name))
|
||||
.unwrap_or(false) =>
|
||||
{
|
||||
Some(join_alias_to_body!(
|
||||
arena,
|
||||
loc_pattern,
|
||||
loc_def_expr,
|
||||
|
@ -1407,24 +1388,16 @@ fn parse_defs_end<'a>(
|
|||
ann_type,
|
||||
spaces_before_current,
|
||||
region
|
||||
);
|
||||
|
||||
defs.replace_with_value_def(
|
||||
defs.tags.len() - 1,
|
||||
value_def,
|
||||
region,
|
||||
);
|
||||
|
||||
true
|
||||
))
|
||||
}
|
||||
_ => false,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if !joined {
|
||||
// the previous and current def can't be joined up
|
||||
if let Some((joined_def, region)) = joined_def {
|
||||
defs.replace_with_value_def(defs.tags.len() - 1, joined_def, region);
|
||||
} else {
|
||||
defs.push_value_def(
|
||||
value_def,
|
||||
region,
|
||||
|
@ -1466,48 +1439,31 @@ fn parse_defs_expr<'a>(
|
|||
Err(bad) => Err(bad),
|
||||
Ok((_, def_state, state)) => {
|
||||
// this is no def, because there is no `=` or `:`; parse as an expr
|
||||
let parse_final_expr = space0_before_e(expr_start(options), EExpr::IndentEnd);
|
||||
|
||||
match parse_final_expr.parse(arena, state.clone(), min_indent) {
|
||||
match space0_before_e(expr_start(options), EExpr::IndentEnd).parse(
|
||||
arena,
|
||||
state.clone(),
|
||||
min_indent,
|
||||
) {
|
||||
Err((_, fail)) => {
|
||||
// If the last def was a suffixed statement, assume this was
|
||||
// intentional by the application author instead of giving
|
||||
// an error.
|
||||
if let Some((new_defs, loc_ret)) = def_state.last_value_suffixed() {
|
||||
// note we check the tags here and not value_defs, as there may be redundant defs in Defs
|
||||
|
||||
let mut local_defs = new_defs.clone();
|
||||
|
||||
let last_stmt = ValueDef::Stmt(loc_ret);
|
||||
local_defs.push_value_def(last_stmt, loc_ret.region, &[], &[]);
|
||||
|
||||
//check the length of the defs we would return, if we only have one
|
||||
// we can just return the expression
|
||||
// note we use tags here, as we may have redundant defs in Defs
|
||||
if local_defs
|
||||
.tags
|
||||
.iter()
|
||||
.filter(|tag| tag.split().is_err())
|
||||
.count()
|
||||
== 1
|
||||
{
|
||||
return Ok((MadeProgress, loc_ret.value, state));
|
||||
let mut def_state = def_state;
|
||||
match def_state.pop_last_value() {
|
||||
Some(loc_ret) => {
|
||||
// If the poped value was the only item in defs - just return it as an expression
|
||||
if def_state.is_empty() {
|
||||
Ok((MadeProgress, loc_ret.value, state))
|
||||
} else {
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Expr::Defs(arena.alloc(def_state), arena.alloc(loc_ret)),
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
return Ok((
|
||||
None => Err((
|
||||
MadeProgress,
|
||||
Expr::Defs(
|
||||
arena.alloc(local_defs),
|
||||
arena.alloc(Loc::at_zero(Expr::EmptyDefsFinal)),
|
||||
),
|
||||
state,
|
||||
));
|
||||
EExpr::DefMissingFinalExpr2(arena.alloc(fail), state.pos()),
|
||||
)),
|
||||
}
|
||||
|
||||
Err((
|
||||
MadeProgress,
|
||||
EExpr::DefMissingFinalExpr2(arena.alloc(fail), state.pos()),
|
||||
))
|
||||
}
|
||||
Ok((_, loc_ret, state)) => Ok((
|
||||
MadeProgress,
|
||||
|
@ -2414,8 +2370,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
|||
Expr::SpaceBefore(..)
|
||||
| Expr::SpaceAfter(..)
|
||||
| Expr::ParensAround(..)
|
||||
| Expr::RecordBuilder(..)
|
||||
| Expr::EmptyDefsFinal => unreachable!(),
|
||||
| Expr::RecordBuilder(..) => unreachable!(),
|
||||
|
||||
Expr::Record(fields) => {
|
||||
let patterns = fields.map_items_result(arena, |loc_assigned_field| {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue