mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 11:52:19 +00:00
Implement initial tuple syntax
This commit is contained in:
parent
9abed3472a
commit
1753c9cf5b
11 changed files with 152 additions and 52 deletions
|
@ -596,6 +596,9 @@ pub fn canonicalize_expr<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
ast::Expr::Tuple(_fields) => {
|
||||
todo!("canonicalize tuple");
|
||||
}
|
||||
ast::Expr::RecordUpdate {
|
||||
fields,
|
||||
update: loc_update,
|
||||
|
|
|
@ -163,7 +163,9 @@ pub fn desugar_expr<'a>(arena: &'a Bump, loc_expr: &'a Loc<Expr<'a>>) -> &'a Loc
|
|||
}
|
||||
})),
|
||||
}),
|
||||
|
||||
Tuple(_fields) => {
|
||||
todo!("desugar_expr: Tuple");
|
||||
}
|
||||
RecordUpdate { fields, update } => {
|
||||
// NOTE the `update` field is always a `Var { .. }`, we only desugar it to get rid of
|
||||
// any spaces before/after
|
||||
|
|
|
@ -106,6 +106,7 @@ impl<'a> Formattable for Expr<'a> {
|
|||
}
|
||||
|
||||
Record(fields) => fields.iter().any(|loc_field| loc_field.is_multiline()),
|
||||
Tuple(fields) => fields.iter().any(|loc_field| loc_field.is_multiline()),
|
||||
RecordUpdate { fields, .. } => fields.iter().any(|loc_field| loc_field.is_multiline()),
|
||||
}
|
||||
}
|
||||
|
@ -323,6 +324,9 @@ impl<'a> Formattable for Expr<'a> {
|
|||
Record(fields) => {
|
||||
fmt_record(buf, None, *fields, indent);
|
||||
}
|
||||
Tuple(_fields) => {
|
||||
todo!("format tuple");
|
||||
}
|
||||
RecordUpdate { update, fields } => {
|
||||
fmt_record(buf, Some(*update), *fields, indent);
|
||||
}
|
||||
|
|
|
@ -648,6 +648,7 @@ impl<'a> RemoveSpaces<'a> for Expr<'a> {
|
|||
fields: fields.remove_spaces(arena),
|
||||
},
|
||||
Expr::Record(a) => Expr::Record(a.remove_spaces(arena)),
|
||||
Expr::Tuple(a) => Expr::Tuple(a.remove_spaces(arena)),
|
||||
Expr::Var { module_name, ident } => Expr::Var { module_name, ident },
|
||||
Expr::Underscore(a) => Expr::Underscore(a),
|
||||
Expr::Tag(a) => Expr::Tag(a),
|
||||
|
|
|
@ -180,6 +180,8 @@ pub enum Expr<'a> {
|
|||
|
||||
Record(Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>),
|
||||
|
||||
Tuple(Collection<'a, Loc<Expr<'a>>>),
|
||||
|
||||
// Lookups
|
||||
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`
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::parser::{
|
|||
self, backtrackable, increment_min_indent, line_min_indent, optional, reset_min_indent,
|
||||
sep_by1, sep_by1_e, set_min_indent, specialize, specialize_ref, then, trailing_sep_by0, word1,
|
||||
word1_indent, word2, EClosure, EExpect, EExpr, EIf, EInParens, EList, ENumber, EPattern,
|
||||
ERecord, EString, EType, EWhen, Either, ParseResult, Parser,
|
||||
ERecord, EString, ETuple, EType, EWhen, Either, ParseResult, Parser,
|
||||
};
|
||||
use crate::pattern::{loc_closure_param, loc_has_parser};
|
||||
use crate::state::State;
|
||||
|
@ -81,72 +81,79 @@ pub fn expr_help<'a>() -> impl Parser<'a, Expr<'a>, EExpr<'a>> {
|
|||
}
|
||||
|
||||
fn loc_expr_in_parens_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EInParens<'a>> {
|
||||
move |arena, state, min_indent| {
|
||||
let (_, loc_expr, state) =
|
||||
loc_expr_in_parens_help_help().parse(arena, state, min_indent)?;
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Loc {
|
||||
region: loc_expr.region,
|
||||
value: Expr::ParensAround(arena.alloc(loc_expr.value)),
|
||||
},
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn loc_expr_in_parens_help_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EInParens<'a>> {
|
||||
between!(
|
||||
word1(b'(', EInParens::Open),
|
||||
space0_around_ee(
|
||||
specialize_ref(EInParens::Expr, loc_expr()),
|
||||
EInParens::IndentOpen,
|
||||
then(
|
||||
loc!(collection_trailing_sep_e!(
|
||||
word1(b'(', EInParens::Open),
|
||||
specialize_ref(EInParens::Expr, loc_expr_no_multi_backpassing()),
|
||||
word1(b',', EInParens::End),
|
||||
word1(b')', EInParens::End),
|
||||
EInParens::Open,
|
||||
EInParens::IndentEnd,
|
||||
),
|
||||
word1(b')', EInParens::End)
|
||||
Expr::SpaceBefore
|
||||
)),
|
||||
move |arena, state, _, loc_elements| {
|
||||
let elements = loc_elements.value;
|
||||
let region = loc_elements.region;
|
||||
|
||||
if elements.len() > 1 {
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Loc::at(region, Expr::List(elements.ptrify_items(arena))),
|
||||
state,
|
||||
))
|
||||
} else if elements.is_empty() {
|
||||
Err((NoProgress, EInParens::Empty(state.pos()), state))
|
||||
} else {
|
||||
// TODO: don't discard comments before/after
|
||||
// (stored in the Collection)
|
||||
Ok((
|
||||
MadeProgress,
|
||||
Loc::at(
|
||||
elements.items[0].region,
|
||||
Expr::ParensAround(&elements.items[0].value),
|
||||
),
|
||||
state,
|
||||
))
|
||||
}
|
||||
},
|
||||
)
|
||||
.trace("in_parens")
|
||||
}
|
||||
|
||||
fn loc_expr_in_parens_etc_help<'a>() -> impl Parser<'a, Loc<Expr<'a>>, EExpr<'a>> {
|
||||
move |arena, state: State<'a>, min_indent: u32| {
|
||||
let parser = loc!(and!(
|
||||
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
|
||||
))]
|
||||
));
|
||||
|
||||
let (
|
||||
_,
|
||||
Loc {
|
||||
)),
|
||||
move |arena: &'a Bump, value: Loc<(Loc<Expr<'a>>, Vec<'a, &'a str>)>| {
|
||||
let Loc {
|
||||
mut region,
|
||||
value: (loc_expr, field_accesses),
|
||||
},
|
||||
state,
|
||||
) = parser.parse(arena, state, min_indent)?;
|
||||
} = value;
|
||||
|
||||
let mut value = loc_expr.value;
|
||||
let mut value = loc_expr.value;
|
||||
|
||||
// if there are field accesses, include the parentheses in the region
|
||||
// otherwise, don't include the parentheses
|
||||
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::Access(arena.alloc(value), field);
|
||||
// if there are field accesses, include the parentheses in the region
|
||||
// otherwise, don't include the parentheses
|
||||
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::Access(arena.alloc(value), field);
|
||||
}
|
||||
}
|
||||
|
||||
Loc::at(region, value)
|
||||
}
|
||||
|
||||
let loc_expr = Loc::at(region, value);
|
||||
|
||||
Ok((MadeProgress, loc_expr, state))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn record_field_access_chain<'a>() -> impl Parser<'a, Vec<'a, &'a str>, EExpr<'a>> {
|
||||
|
@ -1845,6 +1852,10 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
|
|||
Ok(Pattern::RecordDestructure(patterns))
|
||||
}
|
||||
|
||||
Expr::Tuple(_fields) => {
|
||||
todo!("tuple patterns")
|
||||
}
|
||||
|
||||
&Expr::Float(string) => Ok(Pattern::FloatLiteral(string)),
|
||||
&Expr::Num(string) => Ok(Pattern::NumLiteral(string)),
|
||||
Expr::NonBase10Int {
|
||||
|
@ -2435,6 +2446,13 @@ fn list_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EList<'a>> {
|
|||
.trace("list_literal")
|
||||
}
|
||||
|
||||
pub fn tuple_value_field<'a>() -> impl Parser<'a, Loc<Expr<'a>>, ETuple<'a>> {
|
||||
space0_before_e(
|
||||
specialize_ref(ETuple::Expr, loc_expr_no_multi_backpassing()),
|
||||
ETuple::IndentEnd,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn record_value_field<'a>() -> impl Parser<'a, AssignedField<'a, Expr<'a>>, ERecord<'a>> {
|
||||
use AssignedField::*;
|
||||
|
||||
|
|
|
@ -108,7 +108,8 @@ impl_space_problem! {
|
|||
EAbility<'a>,
|
||||
PInParens<'a>,
|
||||
PRecord<'a>,
|
||||
PList<'a>
|
||||
PList<'a>,
|
||||
ETuple<'a>
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -360,6 +361,7 @@ pub enum EExpr<'a> {
|
|||
|
||||
InParens(EInParens<'a>, Position),
|
||||
Record(ERecord<'a>, Position),
|
||||
Tuple(ETuple<'a>, Position),
|
||||
Str(EString<'a>, Position),
|
||||
SingleQuote(EString<'a>, Position),
|
||||
Number(ENumber, Position),
|
||||
|
@ -414,10 +416,43 @@ pub enum ERecord<'a> {
|
|||
IndentEnd(Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ETuple<'a> {
|
||||
// Empty tuples are not allowed
|
||||
Empty(Position),
|
||||
|
||||
// Single element tuples are not allowed
|
||||
Single(Position),
|
||||
|
||||
End(Position),
|
||||
Open(Position),
|
||||
|
||||
Updateable(Position),
|
||||
Field(Position),
|
||||
Colon(Position),
|
||||
QuestionMark(Position),
|
||||
Bar(Position),
|
||||
Ampersand(Position),
|
||||
|
||||
Expr(&'a EExpr<'a>, Position),
|
||||
|
||||
Space(BadInputError, Position),
|
||||
|
||||
IndentOpen(Position),
|
||||
IndentColon(Position),
|
||||
IndentBar(Position),
|
||||
IndentAmpersand(Position),
|
||||
IndentEnd(Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EInParens<'a> {
|
||||
End(Position),
|
||||
Open(Position),
|
||||
|
||||
/// Empty parens, e.g. () is not allowed
|
||||
Empty(Position),
|
||||
|
||||
///
|
||||
Expr(&'a EExpr<'a>, Position),
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
List(
|
||||
[
|
||||
@1-2 Num(
|
||||
"1",
|
||||
),
|
||||
@4-5 Num(
|
||||
"2",
|
||||
),
|
||||
@7-8 Num(
|
||||
"3",
|
||||
),
|
||||
],
|
||||
)
|
|
@ -0,0 +1 @@
|
|||
(1, 2, 3)
|
|
@ -303,6 +303,7 @@ mod test_parse {
|
|||
pass/where_clause_on_newline.expr,
|
||||
pass/zero_float.expr,
|
||||
pass/zero_int.expr,
|
||||
pass/basic_tuple.expr,
|
||||
}
|
||||
|
||||
fn snapshot_test(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue