Merge branch 'main' into rust-1-76-0-upgrade

This commit is contained in:
Anton-4 2024-04-30 19:36:01 +02:00 committed by GitHub
commit c303be7553
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
244 changed files with 1776 additions and 1841 deletions

View file

@ -251,7 +251,7 @@ pub enum Expr<'a> {
is_negative: bool,
},
// String Literals
/// String Literals
Str(StrLiteral<'a>), // string without escapes in it
/// eg 'b'
SingleQuote(&'a str),
@ -265,6 +265,9 @@ pub enum Expr<'a> {
/// Look up exactly one field on a tuple, e.g. `(x, y).1`.
TupleAccess(&'a Expr<'a>, &'a str),
/// Task await bang - i.e. the ! in `File.readUtf8! path`
TaskAwaitBang(&'a Expr<'a>),
// Collection Literals
List(Collection<'a, &'a Loc<Expr<'a>>>),
@ -287,7 +290,6 @@ pub enum Expr<'a> {
Var {
module_name: &'a str, // module_name will only be filled if the original Roc code stated something like `5 + SomeModule.myVar`, module_name will be blank if it was `5 + myVar`
ident: &'a str,
suffixed: u8, // how many `!` suffixes, for example `doTheThing!!` executes a Task that returns a Task
},
Underscore(&'a str),
@ -354,17 +356,6 @@ pub enum Expr<'a> {
}
impl Expr<'_> {
pub fn increment_var_suffix(&mut self, count: u8) {
match self {
Expr::Var { suffixed, .. } => {
*suffixed += count;
}
_ => {
internal_error!("increment_var_suffix called on non-Var expression");
}
}
}
pub fn get_region_spanning_binops(&self) -> Region {
match self {
Expr::BinOps(firsts, last) => {
@ -391,45 +382,43 @@ pub fn split_loc_exprs_around<'a>(
(before, after)
}
pub fn is_loc_expr_suffixed(loc_expr: &Loc<Expr>) -> bool {
match loc_expr.value.extract_spaces().item {
pub fn is_expr_suffixed(expr: &Expr) -> bool {
match expr {
// expression without arguments, `read!`
Expr::Var { suffixed, .. } => suffixed > 0,
Expr::Var { .. } => false,
Expr::TaskAwaitBang(..) => true,
// expression with arguments, `line! "Foo"`
Expr::Apply(sub_loc_expr, apply_args, _) => {
let is_function_suffixed = is_loc_expr_suffixed(sub_loc_expr);
let any_args_suffixed = apply_args.iter().any(|arg| is_loc_expr_suffixed(arg));
let is_function_suffixed = is_expr_suffixed(&sub_loc_expr.value);
let any_args_suffixed = apply_args.iter().any(|arg| is_expr_suffixed(&arg.value));
any_args_suffixed || is_function_suffixed
}
// expression in a pipeline, `"hi" |> say!`
Expr::BinOps(firsts, last) => {
let is_expr_suffixed = is_loc_expr_suffixed(last);
let any_chain_suffixed = firsts
firsts
.iter()
.any(|(chain_loc_expr, _)| is_loc_expr_suffixed(chain_loc_expr));
is_expr_suffixed || any_chain_suffixed
.any(|(chain_loc_expr, _)| is_expr_suffixed(&chain_loc_expr.value))
|| is_expr_suffixed(&last.value)
}
// expression in a if-then-else, `if isOk! then "ok" else doSomething!`
Expr::If(if_thens, final_else) => {
let any_if_thens_suffixed = if_thens.iter().any(|(if_then, else_expr)| {
is_loc_expr_suffixed(if_then) || is_loc_expr_suffixed(else_expr)
is_expr_suffixed(&if_then.value) || is_expr_suffixed(&else_expr.value)
});
is_loc_expr_suffixed(final_else) || any_if_thens_suffixed
is_expr_suffixed(&final_else.value) || any_if_thens_suffixed
}
// expression in parens `(read!)`
Expr::ParensAround(sub_loc_expr) => {
is_loc_expr_suffixed(&Loc::at(loc_expr.region, *sub_loc_expr))
}
Expr::ParensAround(sub_loc_expr) => is_expr_suffixed(sub_loc_expr),
// expression in a closure
Expr::Closure(_, sub_loc_expr) => is_loc_expr_suffixed(sub_loc_expr),
Expr::Closure(_, sub_loc_expr) => is_expr_suffixed(&sub_loc_expr.value),
// expressions inside a Defs
// note we ignore the final expression as it should not be suffixed
@ -437,16 +426,81 @@ pub fn is_loc_expr_suffixed(loc_expr: &Loc<Expr>) -> bool {
let any_defs_suffixed = defs.tags.iter().any(|tag| match tag.split() {
Ok(_) => false,
Err(value_index) => match defs.value_defs[value_index.index()] {
ValueDef::Body(_, loc_expr) => is_loc_expr_suffixed(loc_expr),
ValueDef::AnnotatedBody { body_expr, .. } => is_loc_expr_suffixed(body_expr),
ValueDef::Body(_, loc_expr) => is_expr_suffixed(&loc_expr.value),
ValueDef::AnnotatedBody { body_expr, .. } => is_expr_suffixed(&body_expr.value),
_ => false,
},
});
any_defs_suffixed
}
Expr::Float(_) => false,
Expr::Num(_) => false,
Expr::NonBase10Int { .. } => false,
Expr::Str(_) => false,
Expr::SingleQuote(_) => false,
Expr::RecordAccess(a, _) => is_expr_suffixed(a),
Expr::AccessorFunction(_) => false,
Expr::TupleAccess(a, _) => is_expr_suffixed(a),
Expr::List(items) => items.iter().any(|x| is_expr_suffixed(&x.value)),
Expr::RecordUpdate { update, fields } => {
is_expr_suffixed(&update.value)
|| fields
.iter()
.any(|field| is_assigned_value_suffixed(&field.value))
}
Expr::Record(items) => items
.iter()
.any(|field| is_assigned_value_suffixed(&field.value)),
Expr::Tuple(items) => items.iter().any(|x| is_expr_suffixed(&x.value)),
Expr::RecordBuilder(items) => items
.iter()
.any(|rbf| is_record_builder_field_suffixed(&rbf.value)),
Expr::IngestedFile(_, _) => false,
Expr::Underscore(_) => false,
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)
}
Expr::LowLevelDbg(_, _, _) => todo!(),
Expr::UnaryOp(a, _) => is_expr_suffixed(&a.value),
Expr::When(a, _) => is_expr_suffixed(&a.value),
Expr::SpaceBefore(a, _) => is_expr_suffixed(a),
Expr::SpaceAfter(a, _) => is_expr_suffixed(a),
Expr::MalformedIdent(_, _) => false,
Expr::MalformedClosure => false,
Expr::MalformedSuffixed(_) => false,
Expr::PrecedenceConflict(_) => false,
Expr::MultipleRecordBuilders(_) => false,
Expr::UnappliedRecordBuilder(_) => false,
}
}
_ => false,
fn is_assigned_value_suffixed<'a>(value: &AssignedField<'a, Expr<'a>>) -> bool {
match value {
AssignedField::RequiredValue(_, _, a) | AssignedField::OptionalValue(_, _, a) => {
is_expr_suffixed(&a.value)
}
AssignedField::LabelOnly(_) => false,
AssignedField::SpaceBefore(a, _) | AssignedField::SpaceAfter(a, _) => {
is_assigned_value_suffixed(a)
}
AssignedField::Malformed(_) => false,
}
}
fn is_record_builder_field_suffixed(field: &RecordBuilderField<'_>) -> bool {
match field {
RecordBuilderField::Value(_, _, a) => is_expr_suffixed(&a.value),
RecordBuilderField::ApplyValue(_, _, _, a) => is_expr_suffixed(&a.value),
RecordBuilderField::LabelOnly(_) => false,
RecordBuilderField::SpaceBefore(a, _) => is_record_builder_field_suffixed(a),
RecordBuilderField::SpaceAfter(a, _) => is_record_builder_field_suffixed(a),
RecordBuilderField::Malformed(_) => false,
}
}
@ -647,13 +701,13 @@ impl<'a> Defs<'a> {
..
},
loc_expr,
) if collection.is_empty() && is_loc_expr_suffixed(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_loc_expr_suffixed(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);
@ -1076,12 +1130,10 @@ pub enum Pattern<'a> {
// Identifier
Identifier {
ident: &'a str,
suffixed: u8,
},
QualifiedIdentifier {
module_name: &'a str,
ident: &'a str,
suffixed: u8,
},
Tag(&'a str),
@ -1199,21 +1251,11 @@ impl<'a> Pattern<'a> {
// { x, y } : { x : Int, y ? Bool }
// { x, y ? False } = rec
OptionalField(x, _) => match other {
Identifier {
ident: y,
suffixed: 0,
}
| OptionalField(y, _) => x == y,
Identifier { ident: y } | OptionalField(y, _) => x == y,
_ => false,
},
Identifier {
ident: x,
suffixed: a,
} => match other {
Identifier {
ident: y,
suffixed: b,
} => x == y && a == b,
Identifier { ident: x } => match other {
Identifier { ident: y } => x == y,
OptionalField(y, _) => x == y,
_ => false,
},
@ -1275,15 +1317,13 @@ impl<'a> Pattern<'a> {
QualifiedIdentifier {
module_name: a,
ident: x,
suffixed: i,
} => {
if let QualifiedIdentifier {
module_name: b,
ident: y,
suffixed: j,
} = other
{
a == b && x == y && i == j
a == b && x == y
} else {
false
}
@ -1348,15 +1388,6 @@ impl<'a> Pattern<'a> {
}
}
}
// used to check if a pattern is suffixed to report as an error
pub fn is_suffixed(&self) -> bool {
match self {
Pattern::Identifier { suffixed, .. } => *suffixed > 0,
Pattern::QualifiedIdentifier { suffixed, .. } => *suffixed > 0,
_ => false,
}
}
}
#[derive(Copy, Clone)]
pub struct Collection<'a, T> {
@ -1613,13 +1644,11 @@ impl<'a> Expr<'a> {
pub const REPL_OPAQUE_FUNCTION: Self = Expr::Var {
module_name: "",
ident: "<function>",
suffixed: 0,
};
pub const REPL_RUNTIME_CRASH: Self = Expr::Var {
module_name: "",
ident: "*",
suffixed: 0,
};
pub fn loc_ref(&'a self, region: Region) -> Loc<&'a Self> {
@ -1854,7 +1883,8 @@ impl<'a> Malformed for Expr<'a> {
Str(inner) => inner.is_malformed(),
RecordAccess(inner, _) |
TupleAccess(inner, _) => inner.is_malformed(),
TupleAccess(inner, _) |
TaskAwaitBang(inner) => inner.is_malformed(),
List(items) => items.is_malformed(),

View file

@ -197,17 +197,12 @@ where
)
}
pub fn check_indent<'a, E>(
indent_problem: fn(Position) -> E,
inside_suffixed_statement: bool,
) -> impl Parser<'a, (), E>
pub fn check_indent<'a, E>(indent_problem: fn(Position) -> E) -> impl Parser<'a, (), E>
where
E: 'a,
{
let extra_spaces = if inside_suffixed_statement { 1 } else { 0 };
move |_, state: State<'a>, min_indent: u32| {
if state.column() >= (min_indent + extra_spaces) {
if state.column() >= min_indent {
Ok((NoProgress, (), state))
} else {
Err((NoProgress, indent_problem(state.pos())))

View file

@ -1,5 +1,5 @@
use crate::ast::{
is_loc_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces,
is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces,
Implements, ImplementsAbilities, Pattern, RecordBuilderField, Spaceable, Spaces,
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
};
@ -7,7 +7,7 @@ use crate::blankspace::{
space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e,
space0_before_optional_after, space0_e, spaces, spaces_around, spaces_before,
};
use crate::ident::{integer_ident, lowercase_ident, parse_ident, Accessor, Ident};
use crate::ident::{integer_ident, lowercase_ident, parse_ident, Accessor, Ident, Suffix};
use crate::keyword;
use crate::parser::{
self, backtrackable, byte, byte_indent, increment_min_indent, line_min_indent, optional,
@ -135,7 +135,7 @@ fn loc_expr_in_parens_etc_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>
specialize_err(EExpr::InParens, loc_expr_in_parens_help()),
record_field_access_chain()
)),
move |arena: &'a Bump, value: Loc<(Loc<Expr<'a>>, Vec<'a, Accessor<'a>>)>| {
move |arena: &'a Bump, value: Loc<(Loc<Expr<'a>>, Vec<'a, Suffix<'a>>)>| {
let Loc {
mut region,
value: (loc_expr, field_accesses),
@ -156,16 +156,23 @@ fn loc_expr_in_parens_etc_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>
)
}
fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, Accessor<'a>>, EExpr<'a>> {
zero_or_more!(skip_first!(
byte(b'.', EExpr::Access),
specialize_err(
|_, pos| EExpr::Access(pos),
one_of!(
map!(lowercase_ident(), Accessor::RecordField),
map!(integer_ident(), Accessor::TupleIndex),
fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, Suffix<'a>>, EExpr<'a>> {
zero_or_more!(one_of!(
skip_first!(
byte(b'.', EExpr::Access),
specialize_err(
|_, pos| EExpr::Access(pos),
one_of!(
map!(lowercase_ident(), |x| Suffix::Accessor(
Accessor::RecordField(x)
)),
map!(integer_ident(), |x| Suffix::Accessor(Accessor::TupleIndex(
x
))),
)
)
)
),
map!(byte(b'!', EExpr::Access), |_| Suffix::TaskAwaitBang),
))
}
@ -188,10 +195,7 @@ fn loc_term_or_underscore_or_conditional<'a>(
loc!(underscore_expression()),
loc!(record_literal_help()),
loc!(specialize_err(EExpr::List, list_literal_help())),
loc!(map_with_arena!(
assign_or_destructure_identifier(),
ident_to_expr
)),
ident_seq(),
)
}
@ -211,10 +215,7 @@ fn loc_term_or_underscore<'a>(
loc!(underscore_expression()),
loc!(record_literal_help()),
loc!(specialize_err(EExpr::List, list_literal_help())),
loc!(map_with_arena!(
assign_or_destructure_identifier(),
ident_to_expr
)),
ident_seq(),
)
}
@ -229,13 +230,31 @@ fn loc_term<'a>(options: ExprParseOptions) -> impl Parser<'a, Loc<Expr<'a>>, EEx
loc!(specialize_err(EExpr::Closure, closure_help(options))),
loc!(record_literal_help()),
loc!(specialize_err(EExpr::List, list_literal_help())),
loc!(map_with_arena!(
assign_or_destructure_identifier(),
ident_to_expr
)),
ident_seq(),
)
}
fn ident_seq<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
(|arena: &'a Bump, state: State<'a>, min_indent: u32| parse_ident_seq(arena, state, min_indent))
.trace("ident_seq")
}
fn parse_ident_seq<'a>(
arena: &'a Bump,
state: State<'a>,
min_indent: u32,
) -> ParseResult<'a, Loc<Expr<'a>>, EExpr<'a>> {
let (_, loc_ident, state) =
loc!(assign_or_destructure_identifier()).parse(arena, state, min_indent)?;
let expr = ident_to_expr(arena, loc_ident.value);
let (_p, suffixes, state) = record_field_access_chain()
.trace("record_field_access_chain")
.parse(arena, state, min_indent)
.map_err(|(_p, e)| (MadeProgress, e))?;
let expr = apply_expr_access_chain(arena, expr, suffixes);
Ok((MadeProgress, Loc::at(loc_ident.region, expr), state))
}
fn underscore_expression<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
move |arena: &'a Bump, state: State<'a>, min_indent: u32| {
let start = state.pos();
@ -343,7 +362,7 @@ fn expr_operator_chain<'a>(options: ExprParseOptions) -> impl Parser<'a, Expr<'a
let initial_state = state.clone();
let end = state.pos();
let new_options = if is_loc_expr_suffixed(&expr) {
let new_options = if is_expr_suffixed(&expr.value) {
options.set_suffixed_found()
} else {
options
@ -372,7 +391,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_loc_expr_suffixed(&Loc::at_zero(expr)) {
if is_expr_suffixed(&expr) {
let def_region = Region::new(end, new_state.pos());
let value_def = ValueDef::Stmt(arena.alloc(Loc::at(def_region, expr)));
@ -444,7 +463,7 @@ impl<'a> ExprState<'a> {
} else if !self.expr.value.is_tag()
&& !self.expr.value.is_opaque()
&& !self.arguments.is_empty()
&& !is_loc_expr_suffixed(&self.expr)
&& !is_expr_suffixed(&self.expr.value)
{
let region = Region::across_all(self.arguments.iter().map(|v| &v.region));
@ -671,7 +690,9 @@ pub fn parse_single_def<'a>(
|_, loc_def_expr| -> ValueDef<'a> { ValueDef::Stmt(arena.alloc(loc_def_expr)) },
) {
Ok((_, Some(single_def), state)) => match single_def.type_or_value {
Either::Second(ValueDef::Stmt(loc_expr)) if is_loc_expr_suffixed(loc_expr) => {
Either::Second(ValueDef::Stmt(loc_expr))
if is_expr_suffixed(&loc_expr.value) =>
{
Ok((MadeProgress, Some(single_def), state))
}
_ => Ok((NoProgress, None, initial)), // a hacky way to get expression-based error messages. TODO fix this
@ -904,7 +925,9 @@ pub fn parse_single_def<'a>(
|_, loc_def_expr| -> ValueDef<'a> { ValueDef::Stmt(arena.alloc(loc_def_expr)) },
) {
Ok((_, Some(single_def), state)) => match single_def.type_or_value {
Either::Second(ValueDef::Stmt(loc_expr)) if is_loc_expr_suffixed(loc_expr) => {
Either::Second(ValueDef::Stmt(loc_expr))
if is_expr_suffixed(&loc_expr.value) =>
{
Ok((MadeProgress, Some(single_def), state))
}
_ => Ok((NoProgress, None, initial)),
@ -933,7 +956,7 @@ 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_loc_expr_suffixed(&first_loc_expr) {
if crate::ast::is_expr_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
@ -1640,11 +1663,13 @@ fn finish_parsing_ability_def_help<'a>(
Ok((MadeProgress, (type_def, def_region), state))
}
#[allow(clippy::too_many_arguments)]
fn parse_expr_operator<'a>(
min_indent: u32,
options: ExprParseOptions,
mut expr_state: ExprState<'a>,
loc_op: Loc<BinOp>,
line_indent: u32,
arena: &'a Bump,
state: State<'a>,
initial_state: State<'a>,
@ -1689,7 +1714,8 @@ fn parse_expr_operator<'a>(
}
BinOp::Assignment => {
let expr_region = expr_state.expr.region;
let indented_more = min_indent + 1;
let indented_more = line_indent + 1;
let call = expr_state
.validate_assignment_or_backpassing(arena, loc_op, EExpr::ElmStyleFunction)
@ -1833,7 +1859,7 @@ fn parse_expr_operator<'a>(
expr_state.end = new_end;
expr_state.spaces_after = spaces;
let new_options = if is_loc_expr_suffixed(&new_expr) {
let new_options = if is_expr_suffixed(&new_expr.value) {
options.set_suffixed_found()
} else {
options
@ -1852,7 +1878,7 @@ fn parse_expr_operator<'a>(
let def_region = expr.get_region_spanning_binops();
let mut new_expr = Loc::at(def_region, expr);
if is_loc_expr_suffixed(&new_expr) {
if is_expr_suffixed(&new_expr.value) {
// We have parsed a statement such as `"hello" |> line!`
// put the spaces from after the operator in front of the call
if !spaces_after_operator.is_empty() {
@ -1896,12 +1922,18 @@ fn parse_expr_end<'a>(
state: State<'a>,
initial_state: State<'a>,
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let inner_min_indent = if options.suffixed_found {
min_indent + 1
} else {
min_indent
};
let parser = skip_first!(
crate::blankspace::check_indent(EExpr::IndentEnd, options.suffixed_found),
crate::blankspace::check_indent(EExpr::IndentEnd),
loc_term_or_underscore(options)
);
match parser.parse(arena, state.clone(), min_indent) {
match parser.parse(arena, state.clone(), inner_min_indent) {
Err((MadeProgress, f)) => Err((MadeProgress, f)),
Ok((
_,
@ -1959,7 +1991,7 @@ fn parse_expr_end<'a>(
Ok((_, mut arg, state)) => {
let new_end = state.pos();
let new_options = if is_loc_expr_suffixed(&arg) {
let new_options = if is_expr_suffixed(&arg.value) {
options.set_suffixed_found()
} else {
options
@ -2003,6 +2035,7 @@ fn parse_expr_end<'a>(
Err((NoProgress, _)) => {
let before_op = state.clone();
// try an operator
let line_indent = state.line_indent();
match loc!(operator()).parse(arena, state.clone(), min_indent) {
Err((MadeProgress, f)) => Err((MadeProgress, f)),
Ok((_, loc_op, state)) => {
@ -2013,6 +2046,7 @@ fn parse_expr_end<'a>(
options,
expr_state,
loc_op,
line_indent,
arena,
state,
initial_state,
@ -2147,19 +2181,11 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
}
let mut pat = match expr.item {
Expr::Var {
module_name,
ident,
suffixed,
} => {
Expr::Var { module_name, ident } => {
if module_name.is_empty() {
Pattern::Identifier { ident, suffixed }
Pattern::Identifier { ident }
} else {
Pattern::QualifiedIdentifier {
module_name,
ident,
suffixed,
}
Pattern::QualifiedIdentifier { module_name, ident }
}
}
Expr::Underscore(opt_name) => Pattern::Underscore(opt_name),
@ -2240,6 +2266,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
| Expr::UnappliedRecordBuilder { .. }
| Expr::RecordUpdate { .. }
| Expr::UnaryOp(_, _)
| Expr::TaskAwaitBang(..)
| Expr::Crash => return Err(()),
Expr::Str(string) => Pattern::StrLiteral(string),
@ -2294,10 +2321,7 @@ fn assigned_expr_field_to_pattern_help<'a>(
)
}
}
AssignedField::LabelOnly(name) => Pattern::Identifier {
ident: name.value,
suffixed: 0,
},
AssignedField::LabelOnly(name) => Pattern::Identifier { ident: name.value },
AssignedField::SpaceBefore(nested, spaces) => Pattern::SpaceBefore(
arena.alloc(assigned_expr_field_to_pattern_help(arena, nested)?),
spaces,
@ -2789,21 +2813,13 @@ fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
match src {
Ident::Tag(string) => Expr::Tag(string),
Ident::OpaqueRef(string) => Expr::OpaqueRef(string),
Ident::Access {
module_name,
parts,
suffixed,
} => {
Ident::Access { module_name, parts } => {
let mut iter = parts.iter();
// The first value in the iterator is the variable name,
// e.g. `foo` in `foo.bar.baz`
let mut answer = match iter.next() {
Some(Accessor::RecordField(ident)) => Expr::Var {
module_name,
ident,
suffixed,
},
Some(Accessor::RecordField(ident)) => Expr::Var { module_name, ident },
Some(Accessor::TupleIndex(_)) => {
// TODO: make this state impossible to represent in Ident::Access,
// by splitting out parts[0] into a separate field with a type of `&'a str`,
@ -3165,13 +3181,18 @@ fn record_builder_help<'a>(
fn apply_expr_access_chain<'a>(
arena: &'a Bump,
value: Expr<'a>,
accessors: Vec<'a, Accessor<'a>>,
accessors: Vec<'a, Suffix<'a>>,
) -> Expr<'a> {
accessors
.into_iter()
.fold(value, |value, accessor| match accessor {
Accessor::RecordField(field) => Expr::RecordAccess(arena.alloc(value), field),
Accessor::TupleIndex(field) => Expr::TupleAccess(arena.alloc(value), field),
Suffix::Accessor(Accessor::RecordField(field)) => {
Expr::RecordAccess(arena.alloc(value), field)
}
Suffix::Accessor(Accessor::TupleIndex(field)) => {
Expr::TupleAccess(arena.alloc(value), field)
}
Suffix::TaskAwaitBang => Expr::TaskAwaitBang(arena.alloc(value)),
})
}

View file

@ -42,7 +42,6 @@ pub enum Ident<'a> {
Access {
module_name: &'a str,
parts: &'a [Accessor<'a>],
suffixed: u8,
},
/// `.foo { foo: 42 }` or `.1 (1, 2, 3)`
AccessorFunction(Accessor<'a>),
@ -193,12 +192,7 @@ pub fn parse_ident<'a>(
match chomp_identifier_chain(arena, state.bytes(), state.pos()) {
Ok((width, ident)) => {
let state = advance_state!(state, width as usize)?;
if let Ident::Access {
module_name,
parts,
suffixed,
} = ident
{
if let Ident::Access { module_name, parts } = ident {
if module_name.is_empty() {
if let Some(first) = parts.first() {
for keyword in crate::keyword::KEYWORDS.iter() {
@ -209,15 +203,7 @@ pub fn parse_ident<'a>(
}
}
return Ok((
MadeProgress,
Ident::Access {
module_name,
parts,
suffixed,
},
state,
));
return Ok((MadeProgress, Ident::Access { module_name, parts }, state));
}
Ok((MadeProgress, ident, state))
@ -388,6 +374,12 @@ impl<'a> Accessor<'a> {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Suffix<'a> {
Accessor(Accessor<'a>),
TaskAwaitBang,
}
/// a `.foo` or `.1` accessor function
fn chomp_accessor(buffer: &[u8], pos: Position) -> Result<Accessor, BadIdent> {
// assumes the leading `.` has been chomped already
@ -528,22 +520,9 @@ fn chomp_identifier_chain<'a>(
chomped += width as usize;
// Parse any `!` suffixes
let mut suffixed = 0;
while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) {
if ch == '!' {
suffixed += 1;
chomped += width;
} else {
// we're done
break;
}
}
let ident = Ident::Access {
module_name,
parts: parts.into_bump_slice(),
suffixed,
};
Ok((chomped as u32, ident))
@ -578,22 +557,9 @@ fn chomp_identifier_chain<'a>(
// just one segment, starting with a lowercase letter; that's a normal identifier
let value = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) };
// Parse any `!` suffixes
let mut suffixed = 0;
while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) {
if ch == '!' {
suffixed += 1;
chomped += width;
} else {
// we're done
break;
}
}
let ident = Ident::Access {
module_name: "",
parts: arena.alloc([Accessor::RecordField(value)]),
suffixed,
};
Ok((chomped as u32, ident))

View file

@ -12,7 +12,6 @@ use crate::string_literal::StrLikeLiteral;
use bumpalo::collections::string::String;
use bumpalo::collections::Vec;
use bumpalo::Bump;
use roc_error_macros::internal_error;
use roc_region::all::{Loc, Region};
/// Different patterns are supported in different circumstances.
@ -46,18 +45,10 @@ pub fn closure_param<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
pub fn loc_pattern_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
move |arena, state: State<'a>, min_indent| {
let (_, pattern, state) = loc_pattern_help_help().parse(arena, state, min_indent)?;
let (_, pattern, state) = loc_pattern_help_help(true).parse(arena, state, min_indent)?;
let pattern_state = state.clone();
// Return early with the suffixed statement
match pattern.value {
Pattern::Identifier { suffixed, .. } if suffixed > 0 => {
return Ok((MadeProgress, pattern, pattern_state))
}
_ => {}
}
let (pattern_spaces, state) =
match space0_e(EPattern::AsKeyword).parse(arena, state, min_indent) {
Err(_) => return Ok((MadeProgress, pattern, pattern_state)),
@ -82,11 +73,13 @@ pub fn loc_pattern_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>>
}
}
fn loc_pattern_help_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
fn loc_pattern_help_help<'a>(
can_have_arguments: bool,
) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
one_of!(
specialize_err(EPattern::PInParens, loc_pattern_in_parens_help()),
loc!(underscore_pattern_help()),
loc_ident_pattern_help(true),
loc_ident_pattern_help(can_have_arguments),
loc!(specialize_err(
EPattern::Record,
crate::pattern::record_pattern_help()
@ -143,7 +136,8 @@ fn loc_tag_pattern_arg<'a>(
min_indent,
)?;
let (_, loc_pat, state) = loc_parse_tag_pattern_arg().parse(arena, state, min_indent)?;
// Cannot have arguments here, pass `false` to make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
let (_, loc_pat, state) = loc_pattern_help_help(false).parse(arena, state, min_indent)?;
let Loc { region, value } = loc_pat;
@ -194,21 +188,6 @@ pub fn loc_implements_parser<'a>() -> impl Parser<'a, Loc<Implements<'a>>, EPatt
)
}
fn loc_parse_tag_pattern_arg<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
one_of!(
specialize_err(EPattern::PInParens, loc_pattern_in_parens_help()),
loc!(underscore_pattern_help()),
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
loc_ident_pattern_help(false),
loc!(specialize_err(
EPattern::Record,
crate::pattern::record_pattern_help()
)),
loc!(string_like_pattern_help()),
loc!(number_pattern_help())
)
}
fn loc_pattern_in_parens_help<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PInParens<'a>> {
then(
loc!(collection_trailing_sep_e!(
@ -405,46 +384,6 @@ fn loc_ident_pattern_help<'a>(
Ok((MadeProgress, loc_pat, state))
}
}
// Parse a statement that begins with a suffixed identifier, e.g. `Stdout.line! "Hello"`
Ident::Access {
module_name,
parts,
suffixed,
..
} if suffixed > 0 => {
if module_name.is_empty() && parts.len() == 1 {
if let Accessor::RecordField(var) = &parts[0] {
Ok((
MadeProgress,
Loc {
region: loc_ident.region,
value: Pattern::Identifier {
ident: var,
suffixed,
},
},
state,
))
} else {
internal_error!("unexpected suffixed TupleIndex");
}
} else if let Accessor::RecordField(var) = &parts[0] {
return Ok((
MadeProgress,
Loc {
region: loc_ident.region,
value: Pattern::QualifiedIdentifier {
module_name,
ident: var,
suffixed,
},
},
state,
));
} else {
internal_error!("unexpected suffixed TupleIndex");
}
}
Ident::Access {
module_name, parts, ..
} => {
@ -463,10 +402,7 @@ fn loc_ident_pattern_help<'a>(
MadeProgress,
Loc {
region: loc_ident.region,
value: Pattern::Identifier {
ident: var,
suffixed: 0,
},
value: Pattern::Identifier { ident: var },
},
state,
));
@ -627,18 +563,9 @@ fn record_pattern_field<'a>() -> impl Parser<'a, Loc<Pattern<'a>>, PRecord<'a>>
None => {
let Loc { value, region } = loc_label;
let value = if !spaces.is_empty() {
Pattern::SpaceAfter(
arena.alloc(Pattern::Identifier {
ident: value,
suffixed: 0,
}),
spaces,
)
Pattern::SpaceAfter(arena.alloc(Pattern::Identifier { ident: value }), spaces)
} else {
Pattern::Identifier {
ident: value,
suffixed: 0,
}
Pattern::Identifier { ident: value }
};
Ok((MadeProgress, Loc::at(region, value), state))

View file

@ -70,13 +70,7 @@ fn check_type_alias<'a>(
var_names.reserve(vars.len());
for var in vars {
if let TypeAnnotation::BoundVariable(v) = var.value {
var_names.push(Loc::at(
var.region,
Pattern::Identifier {
ident: v,
suffixed: 0,
},
));
var_names.push(Loc::at(var.region, Pattern::Identifier { ident: v }));
} else {
return Err(ETypeInlineAlias::ArgumentNotLowercase(var.region.start()));
}

View file

@ -175,7 +175,6 @@ mod test_parse {
let expr = arena.alloc(Var {
module_name: "",
ident: "name",
suffixed: 0,
});
bumpalo::vec![in arena;
@ -192,7 +191,6 @@ mod test_parse {
let expr = arena.alloc(Var {
module_name: "",
ident: "name",
suffixed: 0,
});
bumpalo::vec![in arena;
@ -238,7 +236,6 @@ mod test_parse {
let expr = arena.alloc(Var {
module_name: "",
ident: "name",
suffixed: 0,
});
bumpalo::vec![in arena;
@ -254,13 +251,11 @@ mod test_parse {
let expr1 = arena.alloc(Var {
module_name: "",
ident: "name",
suffixed: 0,
});
let expr2 = arena.alloc(Var {
module_name: "",
ident: "project",
suffixed: 0,
});
bumpalo::vec![in arena;
@ -281,13 +276,11 @@ mod test_parse {
let expr1 = arena.alloc(Var {
module_name: "",
ident: "name",
suffixed: 0,
});
let expr2 = arena.alloc(Var {
module_name: "",
ident: "project",
suffixed: 0,
});
bumpalo::vec![in arena;