Parse has-derived clauses

This commit is contained in:
Ayaz Hafiz 2022-05-19 14:43:59 -04:00
parent a6ec43af5e
commit fcf464e9da
No known key found for this signature in database
GPG key ID: 0E2A37416A25EF58
9 changed files with 421 additions and 105 deletions

View file

@ -297,6 +297,7 @@ pub enum TypeDef<'a> {
Opaque { Opaque {
header: TypeHeader<'a>, header: TypeHeader<'a>,
typ: Loc<TypeAnnotation<'a>>, typ: Loc<TypeAnnotation<'a>>,
derived: Option<Loc<Derived<'a>>>,
}, },
/// An ability definition. E.g. /// An ability definition. E.g.
@ -380,11 +381,23 @@ impl<'a> From<ValueDef<'a>> for Def<'a> {
} }
} }
/// Should always be a zero-argument `Apply`; we'll check this in canonicalization
pub type AbilityName<'a> = Loc<TypeAnnotation<'a>>;
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct HasClause<'a> { pub struct HasClause<'a> {
pub var: Loc<Spaced<'a, &'a str>>, pub var: Loc<Spaced<'a, &'a str>>,
// Should always be a zero-argument `Apply`; we'll check this in canonicalization pub ability: AbilityName<'a>,
pub ability: Loc<TypeAnnotation<'a>>, }
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Derived<'a> {
/// `has [ Eq, Hash ]`
Has(Collection<'a, AbilityName<'a>>),
// We preserve this for the formatter; canonicalization ignores it.
SpaceBefore(&'a Derived<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a Derived<'a>, &'a [CommentOrNewline<'a>]),
} }
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
@ -901,6 +914,15 @@ impl<'a> Spaceable<'a> for Has<'a> {
} }
} }
impl<'a> Spaceable<'a> for Derived<'a> {
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
Derived::SpaceBefore(self, spaces)
}
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
Derived::SpaceAfter(self, spaces)
}
}
impl<'a> Expr<'a> { impl<'a> Expr<'a> {
pub fn loc_ref(&'a self, region: Region) -> Loc<&'a Self> { pub fn loc_ref(&'a self, region: Region) -> Loc<&'a Self> {
Loc { Loc {

View file

@ -1,6 +1,6 @@
use crate::ast::{ use crate::ast::{
AssignedField, Collection, CommentOrNewline, Def, Expr, ExtractSpaces, Has, Pattern, Spaceable, AssignedField, Collection, CommentOrNewline, Def, Derived, Expr, ExtractSpaces, Has, Pattern,
TypeAnnotation, TypeDef, TypeHeader, ValueDef, Spaceable, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
}; };
use crate::blankspace::{ use crate::blankspace::{
space0_after_e, space0_around_ee, space0_before_e, space0_before_optional_after, space0_e, space0_after_e, space0_around_ee, space0_before_e, space0_before_optional_after, space0_e,
@ -426,21 +426,21 @@ impl<'a> ExprState<'a> {
mut self, mut self,
arena: &'a Bump, arena: &'a Bump,
loc_op: Loc<BinOp>, loc_op: Loc<BinOp>,
kind: TypeKind, kind: AliasOrOpaque,
) -> Result<(Loc<Expr<'a>>, Vec<'a, &'a Loc<Expr<'a>>>), EExpr<'a>> { ) -> Result<(Loc<Expr<'a>>, Vec<'a, &'a Loc<Expr<'a>>>), EExpr<'a>> {
debug_assert_eq!( debug_assert_eq!(
loc_op.value, loc_op.value,
match kind { match kind {
TypeKind::Alias => BinOp::IsAliasType, AliasOrOpaque::Alias => BinOp::IsAliasType,
TypeKind::Opaque => BinOp::IsOpaqueType, AliasOrOpaque::Opaque => BinOp::IsOpaqueType,
} }
); );
if !self.operators.is_empty() { if !self.operators.is_empty() {
// this `:`/`:=` likely occurred inline; treat it as an invalid operator // this `:`/`:=` likely occurred inline; treat it as an invalid operator
let op = match kind { let op = match kind {
TypeKind::Alias => ":", AliasOrOpaque::Alias => ":",
TypeKind::Opaque => ":=", AliasOrOpaque::Opaque => ":=",
}; };
let fail = EExpr::BadOperator(op, loc_op.region.start()); let fail = EExpr::BadOperator(op, loc_op.region.start());
@ -690,7 +690,10 @@ fn append_annotation_definition<'a>(
spaces: &'a [CommentOrNewline<'a>], spaces: &'a [CommentOrNewline<'a>],
loc_pattern: Loc<Pattern<'a>>, loc_pattern: Loc<Pattern<'a>>,
loc_ann: Loc<TypeAnnotation<'a>>, loc_ann: Loc<TypeAnnotation<'a>>,
kind: TypeKind,
// If this turns out to be an alias
kind: AliasOrOpaque,
opt_derived: Option<Loc<Derived<'a>>>,
) { ) {
let region = Region::span_across(&loc_pattern.region, &loc_ann.region); let region = Region::span_across(&loc_pattern.region, &loc_ann.region);
@ -702,7 +705,7 @@ fn append_annotation_definition<'a>(
.. ..
}, },
alias_arguments, alias_arguments,
) => append_type_definition( ) => append_alias_or_opaque_definition(
arena, arena,
defs, defs,
region, region,
@ -711,8 +714,9 @@ fn append_annotation_definition<'a>(
alias_arguments, alias_arguments,
loc_ann, loc_ann,
kind, kind,
opt_derived,
), ),
Pattern::Tag(name) => append_type_definition( Pattern::Tag(name) => append_alias_or_opaque_definition(
arena, arena,
defs, defs,
region, region,
@ -721,6 +725,7 @@ fn append_annotation_definition<'a>(
&[], &[],
loc_ann, loc_ann,
kind, kind,
opt_derived,
), ),
_ => { _ => {
let mut loc_def = Loc::at( let mut loc_def = Loc::at(
@ -762,7 +767,7 @@ fn append_expect_definition<'a>(
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn append_type_definition<'a>( fn append_alias_or_opaque_definition<'a>(
arena: &'a Bump, arena: &'a Bump,
defs: &mut Vec<'a, &'a Loc<Def<'a>>>, defs: &mut Vec<'a, &'a Loc<Def<'a>>>,
region: Region, region: Region,
@ -770,23 +775,26 @@ fn append_type_definition<'a>(
name: Loc<&'a str>, name: Loc<&'a str>,
pattern_arguments: &'a [Loc<Pattern<'a>>], pattern_arguments: &'a [Loc<Pattern<'a>>],
loc_ann: Loc<TypeAnnotation<'a>>, loc_ann: Loc<TypeAnnotation<'a>>,
kind: TypeKind, kind: AliasOrOpaque,
opt_derived: Option<Loc<Derived<'a>>>,
) { ) {
let header = TypeHeader { let header = TypeHeader {
name, name,
vars: pattern_arguments, vars: pattern_arguments,
}; };
let def = match kind {
TypeKind::Alias => TypeDef::Alias { let type_def = match kind {
AliasOrOpaque::Alias => TypeDef::Alias {
header, header,
ann: loc_ann, ann: loc_ann,
}, },
TypeKind::Opaque => TypeDef::Opaque { AliasOrOpaque::Opaque => TypeDef::Opaque {
header, header,
typ: loc_ann, typ: loc_ann,
derived: opt_derived,
}, },
}; };
let mut loc_def = Loc::at(region, Def::Type(def)); let mut loc_def = Loc::at(region, Def::Type(type_def));
if !spaces.is_empty() { if !spaces.is_empty() {
loc_def = arena loc_def = arena
@ -922,16 +930,9 @@ fn parse_defs_end<'a>(
parse_defs_end(options, column, def_state, arena, state) parse_defs_end(options, column, def_state, arena, state)
} }
Ok((_, op @ (BinOp::IsAliasType | BinOp::IsOpaqueType), state)) => { Ok((_, BinOp::IsAliasType, state)) => {
let (_, ann_type, state) = specialize( let (_, ann_type, state) =
EExpr::Type, alias_signature_with_space_before(min_indent + 1).parse(arena, state)?;
space0_before_e(
type_annotation::located_help(min_indent + 1, false),
min_indent + 1,
EType::TIndentStart,
),
)
.parse(arena, state)?;
append_annotation_definition( append_annotation_definition(
arena, arena,
@ -939,11 +940,24 @@ fn parse_defs_end<'a>(
def_state.spaces_after, def_state.spaces_after,
loc_pattern, loc_pattern,
ann_type, ann_type,
match op { AliasOrOpaque::Alias,
BinOp::IsAliasType => TypeKind::Alias, None,
BinOp::IsOpaqueType => TypeKind::Opaque, );
_ => unreachable!(),
}, parse_defs_end(options, column, def_state, arena, state)
}
Ok((_, BinOp::IsOpaqueType, state)) => {
let (_, (signature, derived), state) =
opaque_signature_with_space_before(min_indent + 1).parse(arena, state)?;
append_annotation_definition(
arena,
&mut def_state.defs,
def_state.spaces_after,
loc_pattern,
signature,
AliasOrOpaque::Opaque,
derived,
); );
parse_defs_end(options, column, def_state, arena, state) parse_defs_end(options, column, def_state, arena, state)
@ -994,8 +1008,44 @@ fn parse_defs_expr<'a>(
} }
} }
fn alias_signature_with_space_before<'a>(
min_indent: u32,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EExpr<'a>> {
specialize(
EExpr::Type,
space0_before_e(
type_annotation::located(min_indent + 1, false),
min_indent + 1,
EType::TIndentStart,
),
)
}
fn opaque_signature_with_space_before<'a>(
min_indent: u32,
) -> impl Parser<'a, (Loc<TypeAnnotation<'a>>, Option<Loc<Derived<'a>>>), EExpr<'a>> {
and!(
specialize(
EExpr::Type,
space0_before_e(
type_annotation::located_opaque_signature(min_indent, true),
min_indent,
EType::TIndentStart,
),
),
optional(specialize(
EExpr::Type,
space0_before_e(
type_annotation::has_derived(min_indent),
min_indent,
EType::TIndentStart,
),
))
)
}
#[derive(Copy, Clone, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq)]
enum TypeKind { enum AliasOrOpaque {
Alias, Alias,
Opaque, Opaque,
} }
@ -1010,7 +1060,7 @@ fn finish_parsing_alias_or_opaque<'a>(
arena: &'a Bump, arena: &'a Bump,
state: State<'a>, state: State<'a>,
spaces_after_operator: &'a [CommentOrNewline<'a>], spaces_after_operator: &'a [CommentOrNewline<'a>],
kind: TypeKind, kind: AliasOrOpaque,
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> { ) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let expr_region = expr_state.expr.region; let expr_region = expr_state.expr.region;
let indented_more = start_column + 1; let indented_more = start_column + 1;
@ -1032,34 +1082,48 @@ fn finish_parsing_alias_or_opaque<'a>(
} }
} }
let (_, ann_type, state) = specialize( let (loc_def, state) = match kind {
EExpr::Type, AliasOrOpaque::Alias => {
space0_before_e( let (_, signature, state) =
type_annotation::located_help(indented_more, true), alias_signature_with_space_before(indented_more).parse(arena, state)?;
min_indent,
EType::TIndentStart,
),
)
.parse(arena, state)?;
let def_region = Region::span_across(&expr.region, &ann_type.region); let def_region = Region::span_across(&expr.region, &signature.region);
let header = TypeHeader { let header = TypeHeader {
name: Loc::at(expr.region, name), name: Loc::at(expr.region, name),
vars: type_arguments.into_bump_slice(), vars: type_arguments.into_bump_slice(),
}; };
let type_def = match kind {
TypeKind::Alias => TypeDef::Alias { let def = TypeDef::Alias {
header, header,
ann: ann_type, ann: signature,
},
TypeKind::Opaque => TypeDef::Opaque {
header,
typ: ann_type,
},
}; };
(&*arena.alloc(Loc::at(def_region, type_def.into())), state) (Loc::at(def_region, Def::Type(def)), state)
}
AliasOrOpaque::Opaque => {
let (_, (signature, derived), state) =
opaque_signature_with_space_before(indented_more).parse(arena, state)?;
let def_region = Region::span_across(&expr.region, &signature.region);
let header = TypeHeader {
name: Loc::at(expr.region, name),
vars: type_arguments.into_bump_slice(),
};
let def = TypeDef::Opaque {
header,
typ: signature,
derived,
};
(Loc::at(def_region, Def::Type(def)), state)
}
};
(&*arena.alloc(loc_def), state)
} }
_ => { _ => {
@ -1070,7 +1134,7 @@ fn finish_parsing_alias_or_opaque<'a>(
let parser = specialize( let parser = specialize(
EExpr::Type, EExpr::Type,
space0_before_e( space0_before_e(
type_annotation::located_help(indented_more, false), type_annotation::located(indented_more, false),
min_indent, min_indent,
EType::TIndentStart, EType::TIndentStart,
), ),
@ -1097,8 +1161,8 @@ fn finish_parsing_alias_or_opaque<'a>(
Err(_) => { Err(_) => {
// this `:`/`:=` likely occurred inline; treat it as an invalid operator // this `:`/`:=` likely occurred inline; treat it as an invalid operator
let op = match kind { let op = match kind {
TypeKind::Alias => ":", AliasOrOpaque::Alias => ":",
TypeKind::Opaque => ":=", AliasOrOpaque::Opaque => ":=",
}; };
let fail = EExpr::BadOperator(op, loc_op.region.start()); let fail = EExpr::BadOperator(op, loc_op.region.start());
@ -1139,7 +1203,7 @@ mod ability {
specialize( specialize(
EAbility::Type, EAbility::Type,
// Require the type to be more indented than the name // Require the type to be more indented than the name
type_annotation::located_help(start_column + 1, true) type_annotation::located(start_column + 1, true)
) )
) )
), ),
@ -1463,8 +1527,8 @@ fn parse_expr_operator<'a>(
state, state,
spaces_after_operator, spaces_after_operator,
match op { match op {
BinOp::IsAliasType => TypeKind::Alias, BinOp::IsAliasType => AliasOrOpaque::Alias,
BinOp::IsOpaqueType => TypeKind::Opaque, BinOp::IsOpaqueType => AliasOrOpaque::Opaque,
_ => unreachable!(), _ => unreachable!(),
}, },
), ),

View file

@ -816,7 +816,7 @@ fn typed_ident<'a>() -> impl Parser<'a, Spaced<'a, TypedIdent<'a>>, ETypedIdent<
space0_before_e( space0_before_e(
specialize( specialize(
ETypedIdent::Type, ETypedIdent::Type,
type_annotation::located_help(min_indent, true) type_annotation::located(min_indent, true)
), ),
min_indent, min_indent,
ETypedIdent::IndentType, ETypedIdent::IndentType,

View file

@ -1,5 +1,6 @@
use crate::ast::{ use crate::ast::{
AssignedField, CommentOrNewline, HasClause, Pattern, Spaced, Tag, TypeAnnotation, TypeHeader, AssignedField, CommentOrNewline, Derived, HasClause, Pattern, Spaced, Tag, TypeAnnotation,
TypeHeader,
}; };
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e}; use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::ident::lowercase_ident; use crate::ident::lowercase_ident;
@ -15,19 +16,29 @@ use bumpalo::collections::vec::Vec;
use bumpalo::Bump; use bumpalo::Bump;
use roc_region::all::{Loc, Position, Region}; use roc_region::all::{Loc, Position, Region};
pub fn located_help<'a>( pub fn located<'a>(
min_indent: u32, min_indent: u32,
is_trailing_comma_valid: bool, is_trailing_comma_valid: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> { ) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
expression(min_indent, is_trailing_comma_valid) expression(min_indent, is_trailing_comma_valid, false)
}
pub fn located_opaque_signature<'a>(
min_indent: u32,
is_trailing_comma_valid: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
expression(min_indent, is_trailing_comma_valid, true)
} }
#[inline(always)] #[inline(always)]
fn tag_union_type<'a>(min_indent: u32) -> impl Parser<'a, TypeAnnotation<'a>, ETypeTagUnion<'a>> { fn tag_union_type<'a>(
min_indent: u32,
stop_at_surface_has: bool,
) -> impl Parser<'a, TypeAnnotation<'a>, ETypeTagUnion<'a>> {
move |arena, state| { move |arena, state| {
let (_, tags, state) = collection_trailing_sep_e!( let (_, tags, state) = collection_trailing_sep_e!(
word1(b'[', ETypeTagUnion::Open), word1(b'[', ETypeTagUnion::Open),
loc!(tag_type(min_indent)), loc!(tag_type(min_indent, false)),
word1(b',', ETypeTagUnion::End), word1(b',', ETypeTagUnion::End),
word1(b']', ETypeTagUnion::End), word1(b']', ETypeTagUnion::End),
min_indent, min_indent,
@ -40,7 +51,7 @@ fn tag_union_type<'a>(min_indent: u32) -> impl Parser<'a, TypeAnnotation<'a>, ET
// This could be an open tag union, e.g. `[ Foo, Bar ]a` // This could be an open tag union, e.g. `[ Foo, Bar ]a`
let (_, ext, state) = optional(allocated(specialize_ref( let (_, ext, state) = optional(allocated(specialize_ref(
ETypeTagUnion::Type, ETypeTagUnion::Type,
term(min_indent), term(min_indent, stop_at_surface_has),
))) )))
.parse(arena, state)?; .parse(arena, state)?;
@ -90,7 +101,7 @@ fn check_type_alias(
fn parse_type_alias_after_as<'a>(min_indent: u32) -> impl Parser<'a, TypeHeader<'a>, EType<'a>> { fn parse_type_alias_after_as<'a>(min_indent: u32) -> impl Parser<'a, TypeHeader<'a>, EType<'a>> {
move |arena, state| { move |arena, state| {
space0_before_e(term(min_indent), min_indent, EType::TAsIndentStart) space0_before_e(term(min_indent, false), min_indent, EType::TAsIndentStart)
.parse(arena, state) .parse(arena, state)
.and_then(|(p, annot, state)| { .and_then(|(p, annot, state)| {
specialize(EType::TInlineAlias, check_type_alias(p, annot)).parse(arena, state) specialize(EType::TInlineAlias, check_type_alias(p, annot)).parse(arena, state)
@ -102,17 +113,26 @@ fn fail_type_start<'a, T: 'a>() -> impl Parser<'a, T, EType<'a>> {
|_arena, state: State<'a>| Err((NoProgress, EType::TStart(state.pos()), state)) |_arena, state: State<'a>| Err((NoProgress, EType::TStart(state.pos()), state))
} }
fn term<'a>(min_indent: u32) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> { fn term<'a>(
min_indent: u32,
stop_at_surface_has: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
map_with_arena!( map_with_arena!(
and!( and!(
one_of!( one_of!(
loc_wildcard(), loc_wildcard(),
loc_inferred(), loc_inferred(),
specialize(EType::TInParens, loc_type_in_parens(min_indent)), specialize(EType::TInParens, loc_type_in_parens(min_indent)),
loc!(specialize(EType::TRecord, record_type(min_indent))), loc!(specialize(
loc!(specialize(EType::TTagUnion, tag_union_type(min_indent))), EType::TRecord,
loc!(applied_type(min_indent)), record_type(min_indent, stop_at_surface_has)
loc!(parse_type_variable), )),
loc!(specialize(
EType::TTagUnion,
tag_union_type(min_indent, stop_at_surface_has)
)),
loc!(applied_type(min_indent, stop_at_surface_has)),
loc!(parse_type_variable(stop_at_surface_has)),
fail_type_start(), fail_type_start(),
), ),
// Inline alias notation, e.g. [ Nil, Cons a (List a) ] as List a // Inline alias notation, e.g. [ Nil, Cons a (List a) ] as List a
@ -163,7 +183,10 @@ fn loc_inferred<'a>() -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
}) })
} }
fn loc_applied_arg<'a>(min_indent: u32) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> { fn loc_applied_arg<'a>(
min_indent: u32,
stop_at_surface_has: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
use crate::ast::Spaceable; use crate::ast::Spaceable;
map_with_arena!( map_with_arena!(
@ -173,10 +196,16 @@ fn loc_applied_arg<'a>(min_indent: u32) -> impl Parser<'a, Loc<TypeAnnotation<'a
loc_wildcard(), loc_wildcard(),
loc_inferred(), loc_inferred(),
specialize(EType::TInParens, loc_type_in_parens(min_indent)), specialize(EType::TInParens, loc_type_in_parens(min_indent)),
loc!(specialize(EType::TRecord, record_type(min_indent))), loc!(specialize(
loc!(specialize(EType::TTagUnion, tag_union_type(min_indent))), EType::TRecord,
record_type(min_indent, stop_at_surface_has)
)),
loc!(specialize(
EType::TTagUnion,
tag_union_type(min_indent, stop_at_surface_has)
)),
loc!(specialize(EType::TApply, parse_concrete_type)), loc!(specialize(EType::TApply, parse_concrete_type)),
loc!(parse_type_variable) loc!(parse_type_variable(stop_at_surface_has))
) )
), ),
|arena: &'a Bump, (spaces, argument): (&'a [_], Loc<TypeAnnotation<'a>>)| { |arena: &'a Bump, (spaces, argument): (&'a [_], Loc<TypeAnnotation<'a>>)| {
@ -196,7 +225,10 @@ fn loc_type_in_parens<'a>(
between!( between!(
word1(b'(', ETypeInParens::Open), word1(b'(', ETypeInParens::Open),
space0_around_ee( space0_around_ee(
move |arena, state| specialize_ref(ETypeInParens::Type, expression(min_indent, true)) move |arena, state| specialize_ref(
ETypeInParens::Type,
expression(min_indent, true, false)
)
.parse(arena, state), .parse(arena, state),
min_indent, min_indent,
ETypeInParens::IndentOpen, ETypeInParens::IndentOpen,
@ -207,11 +239,17 @@ fn loc_type_in_parens<'a>(
} }
#[inline(always)] #[inline(always)]
fn tag_type<'a>(min_indent: u32) -> impl Parser<'a, Tag<'a>, ETypeTagUnion<'a>> { fn tag_type<'a>(
min_indent: u32,
stop_at_surface_has: bool,
) -> impl Parser<'a, Tag<'a>, ETypeTagUnion<'a>> {
move |arena, state: State<'a>| { move |arena, state: State<'a>| {
let (_, name, state) = loc!(parse_tag_name(ETypeTagUnion::End)).parse(arena, state)?; let (_, name, state) = loc!(parse_tag_name(ETypeTagUnion::End)).parse(arena, state)?;
let (_, args, state) = specialize_ref(ETypeTagUnion::Type, loc_applied_args_e(min_indent)) let (_, args, state) = specialize_ref(
ETypeTagUnion::Type,
loc_applied_args_e(min_indent, stop_at_surface_has),
)
.parse(arena, state)?; .parse(arena, state)?;
let result = Tag::Apply { let result = Tag::Apply {
@ -262,7 +300,7 @@ fn record_type_field<'a>(
)) ))
.parse(arena, state)?; .parse(arena, state)?;
let val_parser = specialize_ref(ETypeRecord::Type, expression(min_indent, true)); let val_parser = specialize_ref(ETypeRecord::Type, expression(min_indent, true, false));
match opt_loc_val { match opt_loc_val {
Some(First(_)) => { Some(First(_)) => {
@ -304,7 +342,10 @@ fn record_type_field<'a>(
} }
#[inline(always)] #[inline(always)]
fn record_type<'a>(min_indent: u32) -> impl Parser<'a, TypeAnnotation<'a>, ETypeRecord<'a>> { fn record_type<'a>(
min_indent: u32,
stop_at_surface_has: bool,
) -> impl Parser<'a, TypeAnnotation<'a>, ETypeRecord<'a>> {
use crate::type_annotation::TypeAnnotation::*; use crate::type_annotation::TypeAnnotation::*;
(move |arena, state| { (move |arena, state| {
@ -322,7 +363,7 @@ fn record_type<'a>(min_indent: u32) -> impl Parser<'a, TypeAnnotation<'a>, EType
) )
.parse(arena, state)?; .parse(arena, state)?;
let field_term = specialize_ref(ETypeRecord::Type, term(min_indent)); let field_term = specialize_ref(ETypeRecord::Type, term(min_indent, stop_at_surface_has));
let (_, ext, state) = optional(allocated(field_term)).parse(arena, state)?; let (_, ext, state) = optional(allocated(field_term)).parse(arena, state)?;
let result = Record { fields, ext }; let result = Record { fields, ext };
@ -332,13 +373,16 @@ fn record_type<'a>(min_indent: u32) -> impl Parser<'a, TypeAnnotation<'a>, EType
.trace("type_annotation:record_type") .trace("type_annotation:record_type")
} }
fn applied_type<'a>(min_indent: u32) -> impl Parser<'a, TypeAnnotation<'a>, EType<'a>> { fn applied_type<'a>(
min_indent: u32,
stop_at_surface_has: bool,
) -> impl Parser<'a, TypeAnnotation<'a>, EType<'a>> {
map!( map!(
and!( and!(
specialize(EType::TApply, parse_concrete_type), specialize(EType::TApply, parse_concrete_type),
// Optionally parse space-separated arguments for the constructor, // Optionally parse space-separated arguments for the constructor,
// e.g. `Str Float` in `Map Str Float` // e.g. `Str Float` in `Map Str Float`
loc_applied_args_e(min_indent) loc_applied_args_e(min_indent, stop_at_surface_has)
), ),
|(ctor, args): (TypeAnnotation<'a>, Vec<'a, Loc<TypeAnnotation<'a>>>)| { |(ctor, args): (TypeAnnotation<'a>, Vec<'a, Loc<TypeAnnotation<'a>>>)| {
match &ctor { match &ctor {
@ -360,8 +404,9 @@ fn applied_type<'a>(min_indent: u32) -> impl Parser<'a, TypeAnnotation<'a>, ETyp
fn loc_applied_args_e<'a>( fn loc_applied_args_e<'a>(
min_indent: u32, min_indent: u32,
stop_at_surface_has: bool,
) -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, EType<'a>> { ) -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, EType<'a>> {
zero_or_more!(loc_applied_arg(min_indent)) zero_or_more!(loc_applied_arg(min_indent, stop_at_surface_has))
} }
fn has_clause<'a>(min_indent: u32) -> impl Parser<'a, Loc<HasClause<'a>>, EType<'a>> { fn has_clause<'a>(min_indent: u32) -> impl Parser<'a, Loc<HasClause<'a>>, EType<'a>> {
@ -433,12 +478,43 @@ fn has_clause_chain<'a>(
} }
} }
/// Parse a has-derived clause, e.g. `has [ Eq, Hash ]`.
pub fn has_derived<'a>(min_indent: u32) -> impl Parser<'a, Loc<Derived<'a>>, EType<'a>> {
skip_first!(
// Parse "has"; we don't care about this keyword
word3(b'h', b'a', b's', EType::THasClause),
// Parse "Hash"; this may be qualified from another module like "Hash.Hash"
space0_before_e(
loc!(map!(
collection_trailing_sep_e!(
word1(b'[', EType::TStart),
specialize(EType::TApply, loc!(parse_concrete_type)),
word1(b',', EType::TEnd),
word1(b']', EType::TEnd),
min_indent + 1,
EType::TStart,
EType::TIndentEnd,
TypeAnnotation::SpaceBefore
),
Derived::Has
)),
min_indent + 1,
EType::TIndentEnd
)
)
}
fn expression<'a>( fn expression<'a>(
min_indent: u32, min_indent: u32,
is_trailing_comma_valid: bool, is_trailing_comma_valid: bool,
stop_at_surface_has: bool,
) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> { ) -> impl Parser<'a, Loc<TypeAnnotation<'a>>, EType<'a>> {
(move |arena, state: State<'a>| { (move |arena, state: State<'a>| {
let (p1, first, state) = space0_before_e(term(min_indent), min_indent, EType::TIndentStart) let (p1, first, state) = space0_before_e(
term(min_indent, stop_at_surface_has),
min_indent,
EType::TIndentStart,
)
.parse(arena, state)?; .parse(arena, state)?;
let result = and![ let result = and![
@ -446,7 +522,7 @@ fn expression<'a>(
word1(b',', EType::TFunctionArgument), word1(b',', EType::TFunctionArgument),
one_of![ one_of![
space0_around_ee( space0_around_ee(
term(min_indent), term(min_indent, stop_at_surface_has),
min_indent, min_indent,
EType::TIndentStart, EType::TIndentStart,
EType::TIndentEnd EType::TIndentEnd
@ -471,8 +547,11 @@ fn expression<'a>(
let (progress, annot, state) = match result { let (progress, annot, state) = match result {
Ok((p2, (rest, _dropped_spaces), state)) => { Ok((p2, (rest, _dropped_spaces), state)) => {
let (p3, return_type, state) = let (p3, return_type, state) = space0_before_e(
space0_before_e(term(min_indent), min_indent, EType::TIndentStart) term(min_indent, stop_at_surface_has),
min_indent,
EType::TIndentStart,
)
.parse(arena, state)?; .parse(arena, state)?;
let region = Region::span_across(&first.region, &return_type.region); let region = Region::span_across(&first.region, &return_type.region);
@ -590,15 +669,18 @@ fn parse_concrete_type<'a>(
} }
fn parse_type_variable<'a>( fn parse_type_variable<'a>(
arena: &'a Bump, stop_at_surface_has: bool,
state: State<'a>, ) -> impl Parser<'a, TypeAnnotation<'a>, EType<'a>> {
) -> ParseResult<'a, TypeAnnotation<'a>, EType<'a>> { move |arena, state: State<'a>| match crate::ident::lowercase_ident().parse(arena, state) {
match crate::ident::lowercase_ident().parse(arena, state) {
Ok((_, name, state)) => { Ok((_, name, state)) => {
if name == "has" && stop_at_surface_has {
Err((NoProgress, EType::TEnd(state.pos()), state))
} else {
let answer = TypeAnnotation::BoundVariable(name); let answer = TypeAnnotation::BoundVariable(name);
Ok((MadeProgress, answer, state)) Ok((MadeProgress, answer, state))
} }
}
Err((progress, _, state)) => Err((progress, EType::TBadTypeVariable(state.pos()), state)), Err((progress, _, state)) => Err((progress, EType::TBadTypeVariable(state.pos()), state)),
} }
} }

View file

@ -0,0 +1,137 @@
Defs(
[
@0-7 Type(
Opaque {
header: TypeHeader {
name: @0-1 "A",
vars: [],
},
typ: @5-7 Apply(
"",
"U8",
[],
),
derived: Some(
@12-22 Has(
[
@13-15 Apply(
"",
"Eq",
[],
),
@17-21 Apply(
"",
"Hash",
[],
),
],
),
),
},
),
@24-44 SpaceBefore(
Type(
Opaque {
header: TypeHeader {
name: @24-25 "A",
vars: [],
},
typ: @29-44 Where(
@29-30 BoundVariable(
"a",
),
[
@33-44 HasClause {
var: @33-34 "a",
ability: @39-44 Apply(
"",
"Other",
[],
),
},
],
),
derived: Some(
@49-59 Has(
[
@50-52 Apply(
"",
"Eq",
[],
),
@54-58 Apply(
"",
"Hash",
[],
),
],
),
),
},
),
[
Newline,
Newline,
],
),
@61-81 SpaceBefore(
Type(
Opaque {
header: TypeHeader {
name: @61-62 "A",
vars: [],
},
typ: @66-81 Where(
@66-67 BoundVariable(
"a",
),
[
@70-81 HasClause {
var: @70-71 "a",
ability: @76-81 Apply(
"",
"Other",
[],
),
},
],
),
derived: Some(
@91-101 SpaceBefore(
Has(
[
@92-94 Apply(
"",
"Eq",
[],
),
@96-100 Apply(
"",
"Hash",
[],
),
],
),
[
Newline,
],
),
),
},
),
[
Newline,
Newline,
],
),
],
@103-104 SpaceBefore(
Num(
"0",
),
[
Newline,
Newline,
],
),
)

View file

@ -0,0 +1,8 @@
A := U8 has [Eq, Hash]
A := a | a has Other has [Eq, Hash]
A := a | a has Other
has [Eq, Hash]
0

View file

@ -12,6 +12,7 @@
"U8", "U8",
[], [],
), ),
derived: None,
}, },
), ),
[], [],

View file

@ -41,6 +41,7 @@
], ],
ext: None, ext: None,
}, },
derived: None,
}, },
), ),
[], [],

View file

@ -211,6 +211,7 @@ mod test_parse {
pass/one_minus_two.expr, pass/one_minus_two.expr,
pass/one_plus_two.expr, pass/one_plus_two.expr,
pass/one_spaced_def.expr, pass/one_spaced_def.expr,
pass/opaque_has_abilities.expr,
pass/opaque_simple.module, pass/opaque_simple.module,
pass/opaque_with_type_arguments.module, pass/opaque_with_type_arguments.module,
pass/opaque_reference_expr.expr, pass/opaque_reference_expr.expr,