Merge remote-tracking branch 'origin/main' into https-packages

This commit is contained in:
Richard Feldman 2022-11-25 04:07:37 -05:00
commit bef59299a2
No known key found for this signature in database
GPG Key ID: F1F21AA5B1D9E43B
11 changed files with 110 additions and 65 deletions

View File

@ -535,8 +535,8 @@ pub enum TypeAnnotation<'a> {
Tuple {
fields: Collection<'a, Loc<TypeAnnotation<'a>>>,
/// The row type variable in an open record, e.g. the `r` in `{ name: Str }r`.
/// This is None if it's a closed record annotation like `{ name: Str }`.
/// The row type variable in an open tuple, e.g. the `r` in `( Str, Str )r`.
/// This is None if it's a closed tuple annotation like `( Str, Str )`.
ext: Option<&'a Loc<TypeAnnotation<'a>>>,
},

View File

@ -6,7 +6,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,
};
use crate::ident::{lowercase_ident, parse_ident, Ident};
use crate::ident::{integer_ident, lowercase_ident, parse_ident, Accessor, Ident};
use crate::keyword;
use crate::parser::{
self, backtrackable, increment_min_indent, line_min_indent, optional, reset_min_indent,
@ -124,13 +124,9 @@ fn loc_expr_in_parens_etc_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>
map_with_arena!(
loc!(and!(
specialize(EExpr::InParens, loc_expr_in_parens_help()),
one_of![record_field_access_chain(), |a, s, _m| Ok((
NoProgress,
Vec::new_in(a),
s
))]
record_field_access_chain()
)),
move |arena: &'a Bump, value: Loc<(Loc<Expr<'a>>, Vec<'a, &'a str>)>| {
move |arena: &'a Bump, value: Loc<(Loc<Expr<'a>>, Vec<'a, Accessor<'a>>)>| {
let Loc {
mut region,
value: (loc_expr, field_accesses),
@ -143,12 +139,7 @@ fn loc_expr_in_parens_etc_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>
if field_accesses.is_empty() {
region = loc_expr.region;
} else {
for field in field_accesses {
// Wrap the previous answer in the new one, so we end up
// with a nested Expr. That way, `foo.bar.baz` gets represented
// in the AST as if it had been written (foo.bar).baz all along.
value = Expr::RecordAccess(arena.alloc(value), field);
}
value = apply_expr_access_chain(arena, value, field_accesses);
}
Loc::at(region, value)
@ -156,39 +147,17 @@ 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, &'a str>, EExpr<'a>> {
|arena, state: State<'a>, min_indent| match record_field_access().parse(
arena,
state.clone(),
min_indent,
) {
Ok((_, initial, state)) => {
let mut accesses = Vec::with_capacity_in(1, arena);
accesses.push(initial);
let mut loop_state = state;
loop {
match record_field_access().parse(arena, loop_state.clone(), min_indent) {
Ok((_, next, state)) => {
accesses.push(next);
loop_state = state;
}
Err((MadeProgress, fail)) => return Err((MadeProgress, fail)),
Err((NoProgress, _)) => return Ok((MadeProgress, accesses, loop_state)),
}
}
}
Err((MadeProgress, fail)) => Err((MadeProgress, fail)),
Err((NoProgress, _)) => Err((NoProgress, EExpr::Access(state.pos()))),
}
}
fn record_field_access<'a>() -> impl Parser<'a, &'a str, EExpr<'a>> {
skip_first!(
fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, Accessor<'a>>, EExpr<'a>> {
zero_or_more!(skip_first!(
word1(b'.', EExpr::Access),
specialize(|_, pos| EExpr::Access(pos), lowercase_ident())
)
specialize(
|_, pos| EExpr::Access(pos),
one_of!(
map!(lowercase_ident(), Accessor::RecordField),
map!(integer_ident(), Accessor::TupleIndex),
)
)
))
}
/// In some contexts we want to parse the `_` as an expression, so it can then be turned into a
@ -2621,13 +2590,13 @@ fn record_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
and!(
loc!(specialize(EExpr::Record, record_help())),
// there can be field access, e.g. `{ x : 4 }.x`
optional(record_field_access_chain())
record_field_access_chain()
),
move |arena, state, _, (loc_record, accesses)| {
move |arena, state, _, (loc_record, accessors)| {
let (opt_update, loc_assigned_fields_with_comments) = loc_record.value;
// This is a record literal, not a destructure.
let mut value = match opt_update {
let value = match opt_update {
Some(update) => Expr::RecordUpdate {
update: &*arena.alloc(update),
fields: Collection::with_items_and_comments(
@ -2643,20 +2612,26 @@ fn record_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
)),
};
if let Some(fields) = accesses {
for field in fields {
// Wrap the previous answer in the new one, so we end up
// with a nested Expr. That way, `foo.bar.baz` gets represented
// in the AST as if it had been written (foo.bar).baz all along.
value = Expr::RecordAccess(arena.alloc(value), field);
}
}
let value = apply_expr_access_chain(arena, value, accessors);
Ok((MadeProgress, value, state))
},
)
}
fn apply_expr_access_chain<'a>(
arena: &'a Bump,
value: Expr<'a>,
accessors: Vec<'a, Accessor<'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),
})
}
fn string_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EString<'a>> {
map!(crate::string_literal::parse(), Expr::Str)
}

View File

@ -100,6 +100,17 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, ()> {
}
}
/// This is a tuple accessor, e.g. "1" in `.1`
pub fn integer_ident<'a>() -> impl Parser<'a, &'a str, ()> {
move |_, state: State<'a>, _min_indent: u32| match chomp_integer_part(state.bytes()) {
Err(progress) => Err((progress, ())),
Ok(ident) => {
let width = ident.len();
Ok((MadeProgress, ident, state.advance(width)))
}
}
}
pub fn tag_name<'a>() -> impl Parser<'a, &'a str, ()> {
move |arena, state: State<'a>, min_indent: u32| {
uppercase_ident().parse(arena, state, min_indent)

View File

@ -1791,7 +1791,7 @@ macro_rules! one_or_more {
move |arena, state: State<'a>, min_indent: u32| {
use bumpalo::collections::Vec;
match $parser.parse(arena, state, min_indent) {
match $parser.parse(arena, state.clone(), min_indent) {
Ok((_, first_output, next_state)) => {
let mut state = next_state;
let mut buf = Vec::with_capacity_in(1, arena);
@ -1809,14 +1809,12 @@ macro_rules! one_or_more {
return Ok((MadeProgress, buf, old_state));
}
Err((MadeProgress, fail)) => {
return Err((MadeProgress, fail, old_state));
return Err((MadeProgress, fail));
}
}
}
}
Err((progress, _, new_state)) => {
Err((progress, $to_error(new_state.pos), new_state))
}
Err((progress, _)) => Err((progress, $to_error(state.pos()))),
}
}
};

View File

@ -0,0 +1 @@
({ a: 0 }, { b: 1 }).0.a

View File

@ -0,0 +1,32 @@
RecordAccess(
TupleAccess(
Tuple(
[
@1-7 Record(
[
@2-6 RequiredValue(
@2-3 "a",
[],
@5-6 Num(
"0",
),
),
],
),
@9-15 Record(
[
@10-14 RequiredValue(
@10-11 "b",
[],
@13-14 Num(
"1",
),
),
],
),
],
),
"0",
),
"a",
)

View File

@ -0,0 +1 @@
({a: 0}, {b: 1}).0.a

View File

@ -0,0 +1,24 @@
TupleAccess(
RecordAccess(
Record(
[
@2-11 RequiredValue(
@2-3 "a",
[],
@5-11 Tuple(
[
@6-7 Num(
"1",
),
@9-10 Num(
"2",
),
],
),
),
],
),
"a",
),
"0",
)

View File

@ -0,0 +1 @@
{ a: (1, 2) }.a.0

View File

@ -188,6 +188,8 @@ mod test_parse {
pass/lowest_float.expr,
pass/lowest_int.expr,
pass/tuple_type.expr,
pass/tuple_access_after_record.expr,
pass/record_access_after_tuple.expr,
pass/tuple_type_ext.expr,
pass/malformed_ident_due_to_underscore.expr,
pass/malformed_pattern_field_access.expr, // See https://github.com/roc-lang/roc/issues/399

View File

@ -183,7 +183,7 @@ impl<'a> Renderer<'a> {
let line_col_region = self.to_line_col_region(expect_region, dbg_expr_region);
write!(
writer,
"[{} {}:{}] = ",
"\u{001b}[36m[{} {}:{}] \u{001b}[0m",
self.filename.display(),
line_col_region.start.line,
line_col_region.start.column
@ -197,7 +197,7 @@ impl<'a> Renderer<'a> {
expr.format(&mut buf, 0);
}
write!(writer, "{}", buf.as_str())
writeln!(writer, "{}", buf.as_str())
}
pub fn render_panic<W>(