mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
Merge remote-tracking branch 'origin/trunk' into builtins-in-roc-delayed-alias
This commit is contained in:
commit
4e1197165b
181 changed files with 9495 additions and 2273 deletions
|
@ -23,6 +23,20 @@ pub enum Spaced<'a, T> {
|
|||
SpaceAfter(&'a Spaced<'a, T>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
impl<'a, T> Spaced<'a, T> {
|
||||
/// A `Spaced` is multiline if it has newlines or comments before or after the item, since
|
||||
/// comments induce newlines!
|
||||
pub fn is_multiline(&self) -> bool {
|
||||
match self {
|
||||
Spaced::Item(_) => false,
|
||||
Spaced::SpaceBefore(_, spaces) | Spaced::SpaceAfter(_, spaces) => {
|
||||
debug_assert!(!spaces.is_empty());
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Debug> Debug for Spaced<'a, T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
|
@ -248,6 +262,22 @@ impl<'a> TypeHeader<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// The `has` keyword associated with ability definitions.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Has<'a> {
|
||||
Has,
|
||||
SpaceBefore(&'a Has<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a Has<'a>, &'a [CommentOrNewline<'a>]),
|
||||
}
|
||||
|
||||
/// An ability demand is a value defining the ability; for example `hash : a -> U64 | a has Hash`
|
||||
/// for a `Hash` ability.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct AbilityDemand<'a> {
|
||||
pub name: Loc<Spaced<'a, &'a str>>,
|
||||
pub typ: Loc<TypeAnnotation<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Def<'a> {
|
||||
// TODO in canonicalization, validate the pattern; only certain patterns
|
||||
|
@ -269,6 +299,15 @@ pub enum Def<'a> {
|
|||
typ: Loc<TypeAnnotation<'a>>,
|
||||
},
|
||||
|
||||
/// An ability definition. E.g.
|
||||
/// Hash has
|
||||
/// hash : a -> U64 | a has Hash
|
||||
Ability {
|
||||
header: TypeHeader<'a>,
|
||||
loc_has: Loc<Has<'a>>,
|
||||
demands: &'a [AbilityDemand<'a>],
|
||||
},
|
||||
|
||||
// TODO in canonicalization, check to see if there are any newlines after the
|
||||
// annotation; if not, and if it's followed by a Body, then the annotation
|
||||
// applies to that expr! (TODO: verify that the pattern for both annotation and body match.)
|
||||
|
@ -304,6 +343,13 @@ impl<'a> Def<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct HasClause<'a> {
|
||||
pub var: Loc<Spaced<'a, &'a str>>,
|
||||
// Should always be a zero-argument `Apply`; we'll check this in canonicalization
|
||||
pub ability: Loc<TypeAnnotation<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum TypeAnnotation<'a> {
|
||||
/// A function. The types of its arguments, then the type of its return value.
|
||||
|
@ -343,6 +389,9 @@ pub enum TypeAnnotation<'a> {
|
|||
/// The `*` type variable, e.g. in (List *)
|
||||
Wildcard,
|
||||
|
||||
/// A "where" clause demanding abilities designated by a `|`, e.g. `a -> U64 | a has Hash`
|
||||
Where(&'a Loc<TypeAnnotation<'a>>, &'a [Loc<HasClause<'a>>]),
|
||||
|
||||
// We preserve this for the formatter; canonicalization ignores it.
|
||||
SpaceBefore(&'a TypeAnnotation<'a>, &'a [CommentOrNewline<'a>]),
|
||||
SpaceAfter(&'a TypeAnnotation<'a>, &'a [CommentOrNewline<'a>]),
|
||||
|
@ -814,6 +863,15 @@ impl<'a> Spaceable<'a> for Def<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Spaceable<'a> for Has<'a> {
|
||||
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
Has::SpaceBefore(self, spaces)
|
||||
}
|
||||
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
|
||||
Has::SpaceAfter(self, spaces)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Expr<'a> {
|
||||
pub fn loc_ref(&'a self, region: Region) -> Loc<&'a Self> {
|
||||
Loc {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ast::{
|
||||
AssignedField, Collection, CommentOrNewline, Def, Expr, ExtractSpaces, Pattern, Spaceable,
|
||||
AssignedField, Collection, CommentOrNewline, Def, Expr, ExtractSpaces, Has, Pattern, Spaceable,
|
||||
TypeAnnotation, TypeHeader,
|
||||
};
|
||||
use crate::blankspace::{space0_after_e, space0_around_ee, space0_before_e, space0_e};
|
||||
|
@ -1071,6 +1071,187 @@ fn finish_parsing_alias_or_opaque<'a>(
|
|||
parse_defs_expr(options, start_column, def_state, arena, state)
|
||||
}
|
||||
|
||||
mod ability {
|
||||
use super::*;
|
||||
use crate::{
|
||||
ast::{AbilityDemand, Spaceable, Spaced},
|
||||
parser::EAbility,
|
||||
};
|
||||
|
||||
/// Parses a single ability demand line; see `parse_demand`.
|
||||
fn parse_demand_help<'a>(
|
||||
start_column: u32,
|
||||
) -> impl Parser<'a, AbilityDemand<'a>, EAbility<'a>> {
|
||||
map!(
|
||||
and!(
|
||||
specialize(|_, pos| EAbility::DemandName(pos), loc!(lowercase_ident())),
|
||||
skip_first!(
|
||||
and!(
|
||||
// TODO: do we get anything from picking up spaces here?
|
||||
space0_e(start_column, EAbility::DemandName),
|
||||
word1(b':', EAbility::DemandColon)
|
||||
),
|
||||
specialize(
|
||||
EAbility::Type,
|
||||
// Require the type to be more indented than the name
|
||||
type_annotation::located_help(start_column + 1, true)
|
||||
)
|
||||
)
|
||||
),
|
||||
|(name, typ): (Loc<&'a str>, Loc<TypeAnnotation<'a>>)| {
|
||||
AbilityDemand {
|
||||
name: name.map_owned(Spaced::Item),
|
||||
typ,
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub enum IndentLevel {
|
||||
PendingMin(u32),
|
||||
Exact(u32),
|
||||
}
|
||||
|
||||
/// Parses an ability demand like `hash : a -> U64 | a has Hash`, in the context of a larger
|
||||
/// ability definition.
|
||||
/// This is basically the same as parsing a free-floating annotation, but with stricter rules.
|
||||
pub fn parse_demand<'a>(
|
||||
indent: IndentLevel,
|
||||
) -> impl Parser<'a, (u32, AbilityDemand<'a>), EAbility<'a>> {
|
||||
move |arena, state: State<'a>| {
|
||||
let initial = state.clone();
|
||||
|
||||
// Put no restrictions on the indent after the spaces; we'll check it manually.
|
||||
match space0_e(0, EAbility::DemandName).parse(arena, state) {
|
||||
Err((MadeProgress, fail, _)) => Err((NoProgress, fail, initial)),
|
||||
Err((NoProgress, fail, _)) => Err((NoProgress, fail, initial)),
|
||||
|
||||
Ok((_progress, spaces, state)) => {
|
||||
match indent {
|
||||
IndentLevel::PendingMin(min_indent) if state.column() < min_indent => {
|
||||
let indent_difference = state.column() as i32 - min_indent as i32;
|
||||
Err((
|
||||
MadeProgress,
|
||||
EAbility::DemandAlignment(indent_difference, state.pos()),
|
||||
initial,
|
||||
))
|
||||
}
|
||||
IndentLevel::Exact(wanted) if state.column() < wanted => {
|
||||
// This demand is not indented correctly
|
||||
let indent_difference = state.column() as i32 - wanted as i32;
|
||||
Err((
|
||||
// Rollback because the deindent may be because there is a next
|
||||
// expression
|
||||
NoProgress,
|
||||
EAbility::DemandAlignment(indent_difference, state.pos()),
|
||||
initial,
|
||||
))
|
||||
}
|
||||
IndentLevel::Exact(wanted) if state.column() > wanted => {
|
||||
// This demand is not indented correctly
|
||||
let indent_difference = state.column() as i32 - wanted as i32;
|
||||
Err((
|
||||
MadeProgress,
|
||||
EAbility::DemandAlignment(indent_difference, state.pos()),
|
||||
initial,
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
let indent_column = state.column();
|
||||
|
||||
let parser = parse_demand_help(indent_column);
|
||||
|
||||
match parser.parse(arena, state) {
|
||||
Err((MadeProgress, fail, state)) => {
|
||||
Err((MadeProgress, fail, state))
|
||||
}
|
||||
Err((NoProgress, fail, _)) => {
|
||||
// We made progress relative to the entire ability definition,
|
||||
// so this is an error.
|
||||
Err((MadeProgress, fail, initial))
|
||||
}
|
||||
|
||||
Ok((_, mut demand, state)) => {
|
||||
// Tag spaces onto the parsed demand name
|
||||
if !spaces.is_empty() {
|
||||
demand.name = arena
|
||||
.alloc(demand.name.value)
|
||||
.with_spaces_before(spaces, demand.name.region);
|
||||
}
|
||||
|
||||
Ok((MadeProgress, (indent_column, demand), state))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn finish_parsing_ability<'a>(
|
||||
start_column: u32,
|
||||
options: ExprParseOptions,
|
||||
name: Loc<&'a str>,
|
||||
args: &'a [Loc<Pattern<'a>>],
|
||||
loc_has: Loc<Has<'a>>,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||
let mut demands = Vec::with_capacity_in(2, arena);
|
||||
|
||||
let min_indent_for_demand = start_column + 1;
|
||||
|
||||
// Parse the first demand. This will determine the indentation level all the
|
||||
// other demands must observe.
|
||||
let (_, (demand_indent_level, first_demand), mut state) =
|
||||
ability::parse_demand(ability::IndentLevel::PendingMin(min_indent_for_demand))
|
||||
.parse(arena, state)
|
||||
.map_err(|(progress, err, state)| {
|
||||
(progress, EExpr::Ability(err, state.pos()), state)
|
||||
})?;
|
||||
demands.push(first_demand);
|
||||
|
||||
let demand_indent = ability::IndentLevel::Exact(demand_indent_level);
|
||||
let demand_parser = ability::parse_demand(demand_indent);
|
||||
|
||||
loop {
|
||||
match demand_parser.parse(arena, state.clone()) {
|
||||
Ok((_, (_indent, demand), next_state)) => {
|
||||
state = next_state;
|
||||
demands.push(demand);
|
||||
}
|
||||
Err((MadeProgress, problem, old_state)) => {
|
||||
return Err((
|
||||
MadeProgress,
|
||||
EExpr::Ability(problem, old_state.pos()),
|
||||
old_state,
|
||||
));
|
||||
}
|
||||
Err((NoProgress, _, old_state)) => {
|
||||
state = old_state;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let def_region = Region::span_across(&name.region, &demands.last().unwrap().typ.region);
|
||||
let def = Def::Ability {
|
||||
header: TypeHeader { name, vars: args },
|
||||
loc_has,
|
||||
demands: demands.into_bump_slice(),
|
||||
};
|
||||
let loc_def = &*(arena.alloc(Loc::at(def_region, def)));
|
||||
|
||||
let def_state = DefState {
|
||||
defs: bumpalo::vec![in arena; loc_def],
|
||||
spaces_after: &[],
|
||||
};
|
||||
|
||||
parse_defs_expr(options, start_column, def_state, arena, state)
|
||||
}
|
||||
|
||||
fn parse_expr_operator<'a>(
|
||||
min_indent: u32,
|
||||
options: ExprParseOptions,
|
||||
|
@ -1290,6 +1471,62 @@ fn parse_expr_end<'a>(
|
|||
|
||||
match parser.parse(arena, state.clone()) {
|
||||
Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)),
|
||||
Ok((
|
||||
_,
|
||||
has @ Loc {
|
||||
value:
|
||||
Expr::Var {
|
||||
module_name: "",
|
||||
ident: "has",
|
||||
},
|
||||
..
|
||||
},
|
||||
state,
|
||||
)) if matches!(expr_state.expr.value, Expr::GlobalTag(..)) => {
|
||||
// This is an ability definition, `Ability arg1 ... has ...`.
|
||||
|
||||
let name = expr_state.expr.map_owned(|e| match e {
|
||||
Expr::GlobalTag(name) => name,
|
||||
_ => unreachable!(),
|
||||
});
|
||||
|
||||
let mut arguments = Vec::with_capacity_in(expr_state.arguments.len(), arena);
|
||||
for argument in expr_state.arguments {
|
||||
match expr_to_pattern_help(arena, &argument.value) {
|
||||
Ok(good) => {
|
||||
arguments.push(Loc::at(argument.region, good));
|
||||
}
|
||||
Err(_) => {
|
||||
let start = argument.region.start();
|
||||
let err = &*arena.alloc(EPattern::Start(start));
|
||||
return Err((
|
||||
MadeProgress,
|
||||
EExpr::Pattern(err, argument.region.start()),
|
||||
state,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attach any spaces to the `has` keyword
|
||||
let has = if !expr_state.spaces_after.is_empty() {
|
||||
arena
|
||||
.alloc(Has::Has)
|
||||
.with_spaces_before(expr_state.spaces_after, has.region)
|
||||
} else {
|
||||
Loc::at(has.region, Has::Has)
|
||||
};
|
||||
|
||||
finish_parsing_ability(
|
||||
start_column,
|
||||
options,
|
||||
name,
|
||||
arguments.into_bump_slice(),
|
||||
has,
|
||||
arena,
|
||||
state,
|
||||
)
|
||||
}
|
||||
Ok((_, mut arg, state)) => {
|
||||
let new_end = state.pos();
|
||||
|
||||
|
@ -1762,6 +1999,7 @@ mod when {
|
|||
((_, _), _),
|
||||
State<'a>,
|
||||
) = branch_alternatives(min_indent, options, None).parse(arena, state)?;
|
||||
|
||||
let original_indent = pattern_indent_level;
|
||||
|
||||
state.indent_column = pattern_indent_level;
|
||||
|
|
|
@ -104,6 +104,7 @@ impl_space_problem! {
|
|||
ETypeTagUnion<'a>,
|
||||
ETypedIdent<'a>,
|
||||
EWhen<'a>,
|
||||
EAbility<'a>,
|
||||
PInParens<'a>,
|
||||
PRecord<'a>
|
||||
}
|
||||
|
@ -331,6 +332,7 @@ pub enum EExpr<'a> {
|
|||
DefMissingFinalExpr2(&'a EExpr<'a>, Position),
|
||||
Type(EType<'a>, Position),
|
||||
Pattern(&'a EPattern<'a>, Position),
|
||||
Ability(EAbility<'a>, Position),
|
||||
IndentDefBody(Position),
|
||||
IndentEquals(Position),
|
||||
IndentAnnotation(Position),
|
||||
|
@ -472,6 +474,16 @@ pub enum EWhen<'a> {
|
|||
PatternAlignment(u32, Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EAbility<'a> {
|
||||
Space(BadInputError, Position),
|
||||
Type(EType<'a>, Position),
|
||||
|
||||
DemandAlignment(i32, Position),
|
||||
DemandName(Position),
|
||||
DemandColon(Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EIf<'a> {
|
||||
Space(BadInputError, Position),
|
||||
|
@ -564,6 +576,8 @@ pub enum EType<'a> {
|
|||
TStart(Position),
|
||||
TEnd(Position),
|
||||
TFunctionArgument(Position),
|
||||
TWhereBar(Position),
|
||||
THasClause(Position),
|
||||
///
|
||||
TIndentStart(Position),
|
||||
TIndentEnd(Position),
|
||||
|
@ -1406,6 +1420,32 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn word3<'a, ToError, E>(
|
||||
word_1: u8,
|
||||
word_2: u8,
|
||||
word_3: u8,
|
||||
to_error: ToError,
|
||||
) -> impl Parser<'a, (), E>
|
||||
where
|
||||
ToError: Fn(Position) -> E,
|
||||
E: 'a,
|
||||
{
|
||||
debug_assert_ne!(word_1, b'\n');
|
||||
debug_assert_ne!(word_2, b'\n');
|
||||
debug_assert_ne!(word_3, b'\n');
|
||||
|
||||
let needle = [word_1, word_2, word_3];
|
||||
|
||||
move |_arena: &'a Bump, state: State<'a>| {
|
||||
if state.bytes().starts_with(&needle) {
|
||||
let state = state.advance(3);
|
||||
Ok((MadeProgress, (), state))
|
||||
} else {
|
||||
Err((NoProgress, to_error(state.pos()), state))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! word1_check_indent {
|
||||
($word:expr, $word_problem:expr, $min_indent:expr, $indent_problem:expr) => {
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
use crate::ast::{AssignedField, Pattern, Tag, TypeAnnotation, TypeHeader};
|
||||
use crate::ast::{
|
||||
AssignedField, CommentOrNewline, HasClause, Pattern, Spaced, Tag, TypeAnnotation, TypeHeader,
|
||||
};
|
||||
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
|
||||
use crate::ident::lowercase_ident;
|
||||
use crate::keyword;
|
||||
use crate::parser::then;
|
||||
use crate::parser::{
|
||||
allocated, backtrackable, optional, specialize, specialize_ref, word1, word2, EType,
|
||||
allocated, backtrackable, optional, specialize, specialize_ref, word1, word2, word3, EType,
|
||||
ETypeApply, ETypeInParens, ETypeInlineAlias, ETypeRecord, ETypeTagUnion, ParseResult, Parser,
|
||||
Progress::{self, *},
|
||||
};
|
||||
|
@ -240,7 +244,6 @@ where
|
|||
fn record_type_field<'a>(
|
||||
min_indent: u32,
|
||||
) -> impl Parser<'a, AssignedField<'a, TypeAnnotation<'a>>, ETypeRecord<'a>> {
|
||||
use crate::ident::lowercase_ident;
|
||||
use crate::parser::Either::*;
|
||||
use AssignedField::*;
|
||||
|
||||
|
@ -368,6 +371,75 @@ fn loc_applied_args_e<'a>(
|
|||
zero_or_more!(loc_applied_arg(min_indent))
|
||||
}
|
||||
|
||||
fn has_clause<'a>(min_indent: u32) -> impl Parser<'a, Loc<HasClause<'a>>, EType<'a>> {
|
||||
map!(
|
||||
// Suppose we are trying to parse "a has Hash"
|
||||
and!(
|
||||
space0_around_ee(
|
||||
// Parse "a", with appropriate spaces
|
||||
specialize(
|
||||
|_, pos| EType::TBadTypeVariable(pos),
|
||||
loc!(map!(lowercase_ident(), Spaced::Item)),
|
||||
),
|
||||
min_indent,
|
||||
EType::TIndentStart,
|
||||
EType::TIndentEnd
|
||||
),
|
||||
then(
|
||||
// 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"
|
||||
|arena, state, _progress, _output| {
|
||||
space0_before_e(
|
||||
specialize(EType::TApply, loc!(parse_concrete_type)),
|
||||
state.column() + 1,
|
||||
EType::TIndentStart,
|
||||
)
|
||||
.parse(arena, state)
|
||||
}
|
||||
)
|
||||
),
|
||||
|(var, ability): (Loc<Spaced<'a, &'a str>>, Loc<TypeAnnotation<'a>>)| {
|
||||
let region = Region::span_across(&var.region, &ability.region);
|
||||
let has_clause = HasClause { var, ability };
|
||||
Loc::at(region, has_clause)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/// Parse a chain of `has` clauses, e.g. " | a has Hash, b has Eq".
|
||||
/// Returns the clauses and spaces before the starting "|", if there were any.
|
||||
fn has_clause_chain<'a>(
|
||||
min_indent: u32,
|
||||
) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], &'a [Loc<HasClause<'a>>]), EType<'a>> {
|
||||
move |arena, state: State<'a>| {
|
||||
let (_, (spaces_before, ()), state) = and!(
|
||||
space0_e(min_indent, EType::TIndentStart),
|
||||
word1(b'|', EType::TWhereBar)
|
||||
)
|
||||
.parse(arena, state)?;
|
||||
|
||||
let min_demand_indent = state.column() + 1;
|
||||
// Parse the first clause (there must be one), then the rest
|
||||
let (_, first_clause, state) = has_clause(min_demand_indent).parse(arena, state)?;
|
||||
|
||||
let (_, mut clauses, state) = zero_or_more!(skip_first!(
|
||||
word1(b',', EType::THasClause),
|
||||
has_clause(min_demand_indent)
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
|
||||
// Usually the number of clauses shouldn't be too large, so this is okay
|
||||
clauses.insert(0, first_clause);
|
||||
|
||||
Ok((
|
||||
MadeProgress,
|
||||
(spaces_before, clauses.into_bump_slice()),
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn expression<'a>(
|
||||
min_indent: u32,
|
||||
is_trailing_comma_valid: bool,
|
||||
|
@ -404,7 +476,7 @@ fn expression<'a>(
|
|||
]
|
||||
.parse(arena, state.clone());
|
||||
|
||||
match result {
|
||||
let (progress, annot, state) = match result {
|
||||
Ok((p2, (rest, _dropped_spaces), state)) => {
|
||||
let (p3, return_type, state) =
|
||||
space0_before_e(term(min_indent), min_indent, EType::TIndentStart)
|
||||
|
@ -421,7 +493,7 @@ fn expression<'a>(
|
|||
value: TypeAnnotation::Function(output, arena.alloc(return_type)),
|
||||
};
|
||||
let progress = p1.or(p2).or(p3);
|
||||
Ok((progress, result, state))
|
||||
(progress, result, state)
|
||||
}
|
||||
Err(err) => {
|
||||
if !is_trailing_comma_valid {
|
||||
|
@ -442,7 +514,36 @@ fn expression<'a>(
|
|||
}
|
||||
|
||||
// We ran into trouble parsing the function bits; just return the single term
|
||||
Ok((p1, first, state))
|
||||
(p1, first, state)
|
||||
}
|
||||
};
|
||||
|
||||
// Finally, try to parse a where clause if there is one.
|
||||
// The where clause must be at least as deep as where the type annotation started.
|
||||
let min_where_clause_indent = min_indent;
|
||||
match has_clause_chain(min_where_clause_indent).parse(arena, state.clone()) {
|
||||
Ok((where_progress, (spaces_before, has_chain), state)) => {
|
||||
use crate::ast::Spaceable;
|
||||
|
||||
let region = Region::span_across(&annot.region, &has_chain.last().unwrap().region);
|
||||
let type_annot = if !spaces_before.is_empty() {
|
||||
let spaced = arena
|
||||
.alloc(annot.value)
|
||||
.with_spaces_before(spaces_before, annot.region);
|
||||
&*arena.alloc(spaced)
|
||||
} else {
|
||||
&*arena.alloc(annot)
|
||||
};
|
||||
let where_annot = TypeAnnotation::Where(type_annot, has_chain);
|
||||
Ok((
|
||||
where_progress.or(progress),
|
||||
Loc::at(region, where_annot),
|
||||
state,
|
||||
))
|
||||
}
|
||||
Err(_) => {
|
||||
// Ran into a problem parsing a where clause; don't suppose there is one.
|
||||
Ok((progress, annot, state))
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue