Parse ! suffixes as an Expr::TaskAwaitBang instead of using suffix field in ident

This commit is contained in:
Joshua Warner 2024-04-23 20:58:32 -07:00 committed by Luke Boswell
parent 4b4aee3c1a
commit 6080c12ca8
No known key found for this signature in database
GPG key ID: F6DB3C9DB47377B0
8 changed files with 94 additions and 89 deletions

View file

@ -342,6 +342,7 @@ pub fn desugar_expr<'a>(
arena.alloc(Loc { region, value })
}
TaskAwaitBang(_) => todo!("desugar task await bang"),
RecordAccess(sub_expr, paths) => {
let region = loc_expr.region;
let loc_sub_expr = Loc {

View file

@ -1171,6 +1171,7 @@ pub fn canonicalize_expr<'a>(
output,
)
}
ast::Expr::TaskAwaitBang(..) => todo!("canonicalize_expr on TaskAwaitBang"),
ast::Expr::Tag(tag) => {
let variant_var = var_store.fresh();
let ext_var = var_store.fresh();
@ -2464,7 +2465,8 @@ pub fn is_valid_interpolation(expr: &ast::Expr<'_>) -> bool {
| ast::Expr::Closure(_, loc_expr) => is_valid_interpolation(&loc_expr.value),
ast::Expr::TupleAccess(sub_expr, _)
| ast::Expr::ParensAround(sub_expr)
| ast::Expr::RecordAccess(sub_expr, _) => is_valid_interpolation(sub_expr),
| ast::Expr::RecordAccess(sub_expr, _)
| ast::Expr::TaskAwaitBang(sub_expr) => is_valid_interpolation(sub_expr),
ast::Expr::Apply(loc_expr, args, _called_via) => {
is_valid_interpolation(&loc_expr.value)
&& args

View file

@ -38,9 +38,7 @@ impl<'a> Formattable for Expr<'a> {
| Num(..)
| NonBase10Int { .. }
| SingleQuote(_)
| RecordAccess(_, _)
| AccessorFunction(_)
| TupleAccess(_, _)
| Var { .. }
| Underscore { .. }
| MalformedIdent(_, _)
@ -51,6 +49,10 @@ impl<'a> Formattable for Expr<'a> {
| EmptyDefsFinal
| Crash => false,
RecordAccess(inner, _) | TupleAccess(inner, _) | TaskAwaitBang(inner) => {
inner.is_multiline()
}
// These expressions always have newlines
Defs(_, _) | When(_, _) => true,
@ -512,60 +514,18 @@ impl<'a> Formattable for Expr<'a> {
}
}
RecordAccess(expr, key) => {
// Check for any `!` suffixes and format these at the end of expression
let (expr_to_format, suffix_count) = if let Var {
module_name,
ident,
suffixed,
} = expr
{
(
Var {
module_name,
ident,
suffixed: 0,
},
suffixed,
)
} else {
(**expr, &0u8)
};
expr_to_format.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
buf.push('.');
buf.push_str(key);
for _ in 0..*suffix_count {
buf.push('!');
}
}
TupleAccess(expr, key) => {
// Check for any `!` suffixes and format these at the end of expression
let (expr_to_format, suffix_count) = if let Var {
module_name,
ident,
suffixed,
} = expr
{
(
Var {
module_name,
ident,
suffixed: 0,
},
suffixed,
)
} else {
(**expr, &0u8)
};
expr_to_format.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
buf.push('.');
buf.push_str(key);
for _ in 0..*suffix_count {
buf.push('!');
}
}
TaskAwaitBang(expr) => {
expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
buf.push('!');
}
MalformedIdent(str, _) => {
buf.indent(indent);

View file

@ -686,6 +686,7 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> {
Expr::RecordAccess(a, b) => Expr::RecordAccess(arena.alloc(a.remove_spaces(arena)), b),
Expr::AccessorFunction(a) => Expr::AccessorFunction(a),
Expr::TupleAccess(a, b) => Expr::TupleAccess(arena.alloc(a.remove_spaces(arena)), b),
Expr::TaskAwaitBang(a) => Expr::TaskAwaitBang(arena.alloc(a.remove_spaces(arena))),
Expr::List(a) => Expr::List(a.remove_spaces(arena)),
Expr::RecordUpdate { update, fields } => Expr::RecordUpdate {
update: arena.alloc(update.remove_spaces(arena)),

View file

@ -252,7 +252,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),
@ -266,6 +266,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>>>),
@ -1873,7 +1876,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

@ -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,36 @@ 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 (p, 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))
}
// loc!(map_with_arena!(
// assign_or_destructure_identifier(),
// ident_to_expr
// ))
// }
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();
@ -2249,6 +2273,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),
@ -3174,13 +3199,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

@ -388,6 +388,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
@ -529,16 +535,16 @@ 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 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,