Merge remote-tracking branch 'origin/trunk' into builtins-in-roc

This commit is contained in:
Folkert 2022-04-08 15:47:11 +02:00
commit 1d0f9e9192
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
178 changed files with 5342 additions and 3304 deletions

View file

@ -273,17 +273,13 @@ pub enum Has<'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 struct AbilityMember<'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
// are allowed in annotations.
Annotation(Loc<Pattern<'a>>, Loc<TypeAnnotation<'a>>),
pub enum TypeDef<'a> {
/// A type alias. This is like a standalone annotation, except the pattern
/// must be a capitalized Identifier, e.g.
///
@ -305,8 +301,15 @@ pub enum Def<'a> {
Ability {
header: TypeHeader<'a>,
loc_has: Loc<Has<'a>>,
demands: &'a [AbilityDemand<'a>],
members: &'a [AbilityMember<'a>],
},
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ValueDef<'a> {
// TODO in canonicalization, validate the pattern; only certain patterns
// are allowed in annotations.
Annotation(Loc<Pattern<'a>>, Loc<TypeAnnotation<'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
@ -323,6 +326,12 @@ pub enum Def<'a> {
},
Expect(&'a Loc<Expr<'a>>),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Def<'a> {
Type(TypeDef<'a>),
Value(ValueDef<'a>),
// Blank Space (e.g. comments, spaces, newlines) before or after a def.
// We preserve this for the formatter; canonicalization ignores it.
@ -341,6 +350,30 @@ impl<'a> Def<'a> {
debug_assert!(!matches!(def, Def::SpaceBefore(_, _)));
(spaces, def)
}
pub fn unroll_def(&self) -> Result<&TypeDef<'a>, &ValueDef<'a>> {
let mut def = self;
loop {
match def {
Def::Type(type_def) => return Ok(type_def),
Def::Value(value_def) => return Err(value_def),
Def::SpaceBefore(def1, _) | Def::SpaceAfter(def1, _) => def = def1,
Def::NotYetImplemented(s) => todo!("{}", s),
}
}
}
}
impl<'a> From<TypeDef<'a>> for Def<'a> {
fn from(def: TypeDef<'a>) -> Self {
Self::Type(def)
}
}
impl<'a> From<ValueDef<'a>> for Def<'a> {
fn from(def: ValueDef<'a>) -> Self {
Self::Value(def)
}
}
#[derive(Debug, Copy, Clone, PartialEq)]

View file

@ -1,6 +1,6 @@
use crate::ast::{
AssignedField, Collection, CommentOrNewline, Def, Expr, ExtractSpaces, Has, Pattern, Spaceable,
TypeAnnotation, TypeHeader,
TypeAnnotation, TypeDef, TypeHeader, ValueDef,
};
use crate::blankspace::{space0_after_e, space0_around_ee, space0_before_e, space0_e};
use crate::ident::{lowercase_ident, parse_ident, Ident};
@ -10,7 +10,7 @@ use crate::parser::{
trailing_sep_by0, word1, word2, EExpect, EExpr, EIf, EInParens, ELambda, EList, ENumber,
EPattern, ERecord, EString, EType, EWhen, Either, ParseResult, Parser,
};
use crate::pattern::loc_closure_param;
use crate::pattern::{loc_closure_param, loc_has_parser};
use crate::state::State;
use crate::type_annotation;
use bumpalo::collections::Vec;
@ -576,7 +576,7 @@ fn append_body_definition<'a>(
if spaces.len() <= 1 {
let last = defs.pop();
match last.map(|d| d.value.unroll_spaces_before()) {
Some((before_ann_spaces, Def::Annotation(ann_pattern, ann_type))) => {
Some((before_ann_spaces, Def::Value(ValueDef::Annotation(ann_pattern, ann_type)))) => {
return append_body_definition_help(
arena,
defs,
@ -591,10 +591,10 @@ fn append_body_definition<'a>(
}
Some((
before_ann_spaces,
Def::Alias {
Def::Type(TypeDef::Alias {
header,
ann: ann_type,
},
}),
)) => {
// This is a case like
// UserId x : [ UserId Int ]
@ -628,7 +628,10 @@ fn append_body_definition<'a>(
// the previous and current def can't be joined up
let mut loc_def = Loc::at(
region,
Def::Body(arena.alloc(loc_pattern), &*arena.alloc(loc_def_body)),
Def::Value(ValueDef::Body(
arena.alloc(loc_pattern),
&*arena.alloc(loc_def_body),
)),
);
if !spaces.is_empty() {
@ -660,13 +663,13 @@ fn append_body_definition_help<'a>(
let mut loc_def = Loc::at(
region,
Def::AnnotatedBody {
Def::Value(ValueDef::AnnotatedBody {
ann_pattern: loc_pattern_ann,
ann_type: loc_ann,
comment,
body_pattern: arena.alloc(loc_pattern_body),
body_expr: &*arena.alloc(loc_def_body),
},
}),
);
if !before_ann_spaces.is_empty() {
@ -717,7 +720,10 @@ fn append_annotation_definition<'a>(
kind,
),
_ => {
let mut loc_def = Loc::at(region, Def::Annotation(loc_pattern, loc_ann));
let mut loc_def = Loc::at(
region,
Def::Value(ValueDef::Annotation(loc_pattern, loc_ann)),
);
if !spaces.is_empty() {
loc_def = arena
.alloc(loc_def.value)
@ -736,7 +742,7 @@ fn append_expect_definition<'a>(
spaces: &'a [CommentOrNewline<'a>],
loc_expect_body: Loc<Expr<'a>>,
) {
let def = Def::Expect(arena.alloc(loc_expect_body));
let def: Def = ValueDef::Expect(arena.alloc(loc_expect_body)).into();
let end = loc_expect_body.region.end();
let region = Region::new(start, end);
@ -768,16 +774,16 @@ fn append_type_definition<'a>(
vars: pattern_arguments,
};
let def = match kind {
TypeKind::Alias => Def::Alias {
TypeKind::Alias => TypeDef::Alias {
header,
ann: loc_ann,
},
TypeKind::Opaque => Def::Opaque {
TypeKind::Opaque => TypeDef::Opaque {
header,
typ: loc_ann,
},
};
let mut loc_def = Loc::at(region, def);
let mut loc_def = Loc::at(region, Def::Type(def));
if !spaces.is_empty() {
loc_def = arena
@ -858,55 +864,89 @@ fn parse_defs_end<'a>(
// a hacky way to get expression-based error messages. TODO fix this
Ok((NoProgress, def_state, initial))
}
Ok((_, loc_pattern, state)) => match operator().parse(arena, state) {
Ok((_, BinOp::Assignment, state)) => {
let parse_def_expr = space0_before_e(
move |a, s| parse_loc_expr(min_indent + 1, a, s),
min_indent,
EExpr::IndentEnd,
);
let (_, loc_def_expr, state) = parse_def_expr.parse(arena, state)?;
append_body_definition(
arena,
&mut def_state.defs,
def_state.spaces_after,
loc_pattern,
loc_def_expr,
);
parse_defs_end(options, column, def_state, arena, state)
}
Ok((_, op @ (BinOp::IsAliasType | BinOp::IsOpaqueType), state)) => {
let (_, ann_type, state) = specialize(
EExpr::Type,
space0_before_e(
type_annotation::located_help(min_indent + 1, false),
min_indent + 1,
EType::TIndentStart,
Ok((_, loc_pattern, state)) => {
// First let's check whether this is an ability definition.
if let Loc {
value:
Pattern::Apply(
loc_name @ Loc {
value: Pattern::GlobalTag(name),
..
},
args,
),
)
.parse(arena, state)?;
..
} = loc_pattern
{
if let Ok((_, loc_has, state)) =
loc_has_parser(min_indent).parse(arena, state.clone())
{
let (_, loc_def, state) = finish_parsing_ability_def(
start_column,
Loc::at(loc_name.region, name),
args,
loc_has,
arena,
state,
)?;
append_annotation_definition(
arena,
&mut def_state.defs,
def_state.spaces_after,
loc_pattern,
ann_type,
match op {
BinOp::IsAliasType => TypeKind::Alias,
BinOp::IsOpaqueType => TypeKind::Opaque,
_ => unreachable!(),
},
);
def_state.defs.push(loc_def);
parse_defs_end(options, column, def_state, arena, state)
return parse_defs_end(options, column, def_state, arena, state);
}
}
_ => Ok((MadeProgress, def_state, initial)),
},
// Otherwise, this is a def or alias.
match operator().parse(arena, state) {
Ok((_, BinOp::Assignment, state)) => {
let parse_def_expr = space0_before_e(
move |a, s| parse_loc_expr(min_indent + 1, a, s),
min_indent,
EExpr::IndentEnd,
);
let (_, loc_def_expr, state) = parse_def_expr.parse(arena, state)?;
append_body_definition(
arena,
&mut def_state.defs,
def_state.spaces_after,
loc_pattern,
loc_def_expr,
);
parse_defs_end(options, column, def_state, arena, state)
}
Ok((_, op @ (BinOp::IsAliasType | BinOp::IsOpaqueType), state)) => {
let (_, ann_type, state) = specialize(
EExpr::Type,
space0_before_e(
type_annotation::located_help(min_indent + 1, false),
min_indent + 1,
EType::TIndentStart,
),
)
.parse(arena, state)?;
append_annotation_definition(
arena,
&mut def_state.defs,
def_state.spaces_after,
loc_pattern,
ann_type,
match op {
BinOp::IsAliasType => TypeKind::Alias,
BinOp::IsOpaqueType => TypeKind::Opaque,
_ => unreachable!(),
},
);
parse_defs_end(options, column, def_state, arena, state)
}
_ => Ok((MadeProgress, def_state, initial)),
}
}
}
}
@ -1004,17 +1044,17 @@ fn finish_parsing_alias_or_opaque<'a>(
vars: type_arguments.into_bump_slice(),
};
let type_def = match kind {
TypeKind::Alias => Def::Alias {
TypeKind::Alias => TypeDef::Alias {
header,
ann: ann_type,
},
TypeKind::Opaque => Def::Opaque {
TypeKind::Opaque => TypeDef::Opaque {
header,
typ: ann_type,
},
};
(&*arena.alloc(Loc::at(def_region, type_def)), state)
(&*arena.alloc(Loc::at(def_region, type_def.into())), state)
}
_ => {
@ -1043,9 +1083,9 @@ fn finish_parsing_alias_or_opaque<'a>(
let def_region = Region::span_across(&call.region, &ann_type.region);
let alias = Def::Annotation(Loc::at(expr_region, good), ann_type);
let alias = ValueDef::Annotation(Loc::at(expr_region, good), ann_type);
(&*arena.alloc(Loc::at(def_region, alias)), state)
(&*arena.alloc(Loc::at(def_region, alias.into())), state)
}
}
}
@ -1074,14 +1114,14 @@ fn finish_parsing_alias_or_opaque<'a>(
mod ability {
use super::*;
use crate::{
ast::{AbilityDemand, Spaceable, Spaced},
ast::{AbilityMember, 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>> {
) -> impl Parser<'a, AbilityMember<'a>, EAbility<'a>> {
map!(
and!(
specialize(|_, pos| EAbility::DemandName(pos), loc!(lowercase_ident())),
@ -1099,7 +1139,7 @@ mod ability {
)
),
|(name, typ): (Loc<&'a str>, Loc<TypeAnnotation<'a>>)| {
AbilityDemand {
AbilityMember {
name: name.map_owned(Spaced::Item),
typ,
}
@ -1117,7 +1157,7 @@ mod ability {
/// 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>> {
) -> impl Parser<'a, (u32, AbilityMember<'a>), EAbility<'a>> {
move |arena, state: State<'a>| {
let initial = state.clone();
@ -1190,15 +1230,14 @@ mod ability {
}
}
fn finish_parsing_ability<'a>(
fn finish_parsing_ability_def<'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>> {
) -> ParseResult<'a, &'a Loc<Def<'a>>, EExpr<'a>> {
let mut demands = Vec::with_capacity_in(2, arena);
let min_indent_for_demand = start_column + 1;
@ -1237,13 +1276,29 @@ fn finish_parsing_ability<'a>(
}
let def_region = Region::span_across(&name.region, &demands.last().unwrap().typ.region);
let def = Def::Ability {
let def = TypeDef::Ability {
header: TypeHeader { name, vars: args },
loc_has,
demands: demands.into_bump_slice(),
};
members: demands.into_bump_slice(),
}
.into();
let loc_def = &*(arena.alloc(Loc::at(def_region, def)));
Ok((MadeProgress, loc_def, 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 (_, loc_def, state) =
finish_parsing_ability_def(start_column, name, args, loc_has, arena, state)?;
let def_state = DefState {
defs: bumpalo::vec![in arena; loc_def],
spaces_after: &[],
@ -1309,23 +1364,24 @@ fn parse_expr_operator<'a>(
let (loc_def, state) = {
match expr_to_pattern_help(arena, &call.value) {
Ok(good) => {
let (_, mut ann_type, state) = parse_loc_expr(indented_more, arena, state)?;
let (_, mut body, state) = parse_loc_expr(indented_more, arena, state)?;
// put the spaces from after the operator in front of the call
if !spaces_after_operator.is_empty() {
ann_type = arena
.alloc(ann_type.value)
.with_spaces_before(spaces_after_operator, ann_type.region);
body = arena
.alloc(body.value)
.with_spaces_before(spaces_after_operator, body.region);
}
let alias_region = Region::span_across(&call.region, &ann_type.region);
let body_region = Region::span_across(&call.region, &body.region);
let alias = Def::Body(
let alias = ValueDef::Body(
arena.alloc(Loc::at(expr_region, good)),
arena.alloc(ann_type),
);
arena.alloc(body),
)
.into();
(&*arena.alloc(Loc::at(alias_region, alias)), state)
(&*arena.alloc(Loc::at(body_region, alias)), state)
}
Err(_) => {
// this `=` likely occurred inline; treat it as an invalid operator

View file

@ -1,9 +1,9 @@
use crate::ast::Pattern;
use crate::ast::{Has, Pattern};
use crate::blankspace::{space0_around_ee, space0_before_e, space0_e};
use crate::ident::{lowercase_ident, parse_ident, Ident};
use crate::parser::Progress::{self, *};
use crate::parser::{
backtrackable, optional, specialize, specialize_ref, word1, EPattern, PInParens, PRecord,
backtrackable, optional, specialize, specialize_ref, then, word1, EPattern, PInParens, PRecord,
ParseResult, Parser,
};
use crate::state::State;
@ -68,32 +68,63 @@ pub fn loc_pattern_help<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>
fn loc_tag_pattern_args_help<'a>(
min_indent: u32,
) -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
zero_or_more!(loc_tag_pattern_arg(min_indent))
zero_or_more!(loc_tag_pattern_arg(min_indent, false))
}
fn loc_tag_pattern_arg<'a>(min_indent: u32) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
/// Like `loc_tag_pattern_args_help`, but stops if a "has" keyword is seen (indicating an ability).
fn loc_type_def_tag_pattern_args_help<'a>(
min_indent: u32,
) -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
zero_or_more!(loc_tag_pattern_arg(min_indent, true))
}
fn loc_tag_pattern_arg<'a>(
min_indent: u32,
stop_on_has_kw: bool,
) -> impl Parser<'a, Loc<Pattern<'a>>, EPattern<'a>> {
// Don't parse operators, because they have a higher precedence than function application.
// If we encounter one, we're done parsing function args!
move |arena, state| {
let (_, spaces, state) =
backtrackable(space0_e(min_indent, EPattern::IndentStart)).parse(arena, state)?;
move |arena, original_state: State<'a>| {
let (_, spaces, state) = backtrackable(space0_e(min_indent, EPattern::IndentStart))
.parse(arena, original_state.clone())?;
let (_, loc_pat, state) = loc_parse_tag_pattern_arg(min_indent, arena, state)?;
let Loc { region, value } = loc_pat;
Ok((
MadeProgress,
if spaces.is_empty() {
Loc::at(region, value)
} else {
Loc::at(region, Pattern::SpaceBefore(arena.alloc(value), spaces))
},
state,
))
if stop_on_has_kw && matches!(value, Pattern::Identifier("has")) {
Err((
NoProgress,
EPattern::End(original_state.pos()),
original_state,
))
} else {
Ok((
MadeProgress,
if spaces.is_empty() {
Loc::at(region, value)
} else {
Loc::at(region, Pattern::SpaceBefore(arena.alloc(value), spaces))
},
state,
))
}
}
}
pub fn loc_has_parser<'a>(min_indent: u32) -> impl Parser<'a, Loc<Has<'a>>, EPattern<'a>> {
then(
loc_tag_pattern_arg(min_indent, false),
|_arena, state, progress, pattern| {
if matches!(pattern.value, Pattern::Identifier("has")) {
Ok((progress, Loc::at(pattern.region, Has::Has), state))
} else {
Err((progress, EPattern::End(state.pos()), state))
}
},
)
}
fn loc_parse_tag_pattern_arg<'a>(
min_indent: u32,
arena: &'a Bump,
@ -191,7 +222,7 @@ fn loc_ident_pattern_help<'a>(
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
if can_have_arguments {
let (_, loc_args, state) =
loc_tag_pattern_args_help(min_indent).parse(arena, state)?;
loc_type_def_tag_pattern_args_help(min_indent).parse(arena, state)?;
if loc_args.is_empty() {
Ok((MadeProgress, loc_tag, state))