Parse opaque types

This commit is contained in:
ayazhafiz 2022-02-19 18:38:31 -05:00
parent ab6019d402
commit fa24e51593
13 changed files with 201 additions and 113 deletions

2
Cargo.lock generated
View file

@ -3304,6 +3304,7 @@ dependencies = [
"pretty_assertions", "pretty_assertions",
"roc_builtins", "roc_builtins",
"roc_collections", "roc_collections",
"roc_error_macros",
"roc_module", "roc_module",
"roc_parse", "roc_parse",
"roc_problem", "roc_problem",
@ -3572,6 +3573,7 @@ dependencies = [
"roc_can", "roc_can",
"roc_collections", "roc_collections",
"roc_constrain", "roc_constrain",
"roc_error_macros",
"roc_module", "roc_module",
"roc_mono", "roc_mono",
"roc_parse", "roc_parse",

View file

@ -13,6 +13,7 @@
// use crate::pattern::{bindings_from_patterns, canonicalize_pattern, Pattern}; // use crate::pattern::{bindings_from_patterns, canonicalize_pattern, Pattern};
// use crate::procedure::References; // use crate::procedure::References;
use roc_collections::all::{default_hasher, ImMap, MutMap, MutSet, SendMap}; use roc_collections::all::{default_hasher, ImMap, MutMap, MutSet, SendMap};
use roc_error_macros::todo_opaques;
use roc_module::ident::Lowercase; use roc_module::ident::Lowercase;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_parse::ast::{self, TypeHeader}; use roc_parse::ast::{self, TypeHeader};
@ -260,6 +261,8 @@ fn to_pending_def<'a>(
} }
} }
Opaque { .. } => todo_opaques!(),
Expect(_) => todo!(), Expect(_) => todo!(),
SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) => { SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) => {

View file

@ -9,8 +9,8 @@ use roc_fmt::module::fmt_module;
use roc_fmt::Buf; use roc_fmt::Buf;
use roc_module::called_via::{BinOp, UnaryOp}; use roc_module::called_via::{BinOp, UnaryOp};
use roc_parse::ast::{ use roc_parse::ast::{
TypeHeader, AssignedField, Collection, Expr, Pattern, Spaced, StrLiteral, StrSegment, Tag, AssignedField, Collection, Expr, Pattern, Spaced, StrLiteral, StrSegment, Tag, TypeAnnotation,
TypeAnnotation, WhenBranch, TypeHeader, WhenBranch,
}; };
use roc_parse::header::{ use roc_parse::header::{
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry, AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, ModuleName, PackageEntry,
@ -411,6 +411,16 @@ impl<'a> RemoveSpaces<'a> for Def<'a> {
}, },
ann: ann.remove_spaces(arena), ann: ann.remove_spaces(arena),
}, },
Def::Opaque {
header: TypeHeader { name, vars },
typ,
} => Def::Opaque {
header: TypeHeader {
name: name.remove_spaces(arena),
vars: vars.remove_spaces(arena),
},
typ: typ.remove_spaces(arena),
},
Def::Body(a, b) => Def::Body( Def::Body(a, b) => Def::Body(
arena.alloc(a.remove_spaces(arena)), arena.alloc(a.remove_spaces(arena)),
arena.alloc(b.remove_spaces(arena)), arena.alloc(b.remove_spaces(arena)),

View file

@ -7,6 +7,7 @@ edition = "2018"
[dependencies] [dependencies]
roc_collections = { path = "../collections" } roc_collections = { path = "../collections" }
roc_error_macros = { path = "../../error_macros" }
roc_region = { path = "../region" } roc_region = { path = "../region" }
roc_module = { path = "../module" } roc_module = { path = "../module" }
roc_parse = { path = "../parse" } roc_parse = { path = "../parse" }

View file

@ -12,6 +12,7 @@ use crate::procedure::References;
use crate::scope::create_alias; use crate::scope::create_alias;
use crate::scope::Scope; use crate::scope::Scope;
use roc_collections::all::{default_hasher, ImMap, ImSet, MutMap, MutSet, SendMap}; use roc_collections::all::{default_hasher, ImMap, ImSet, MutMap, MutSet, SendMap};
use roc_error_macros::todo_opaques;
use roc_module::ident::Lowercase; use roc_module::ident::Lowercase;
use roc_module::symbol::Symbol; use roc_module::symbol::Symbol;
use roc_parse::ast; use roc_parse::ast;
@ -1504,6 +1505,8 @@ fn to_pending_def<'a>(
} }
} }
Opaque { .. } => todo_opaques!(),
Expect(_condition) => todo!(), Expect(_condition) => todo!(),
SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) => { SpaceBefore(sub_def, _) | SpaceAfter(sub_def, _) => {

View file

@ -95,6 +95,7 @@ pub fn desugar_def<'a>(arena: &'a Bump, def: &'a Def<'a>) -> Def<'a> {
Body(loc_pattern, loc_expr) => Body(loc_pattern, desugar_expr(arena, loc_expr)), Body(loc_pattern, loc_expr) => Body(loc_pattern, desugar_expr(arena, loc_expr)),
SpaceBefore(def, _) | SpaceAfter(def, _) => desugar_def(arena, def), SpaceBefore(def, _) | SpaceAfter(def, _) => desugar_def(arena, def),
alias @ Alias { .. } => *alias, alias @ Alias { .. } => *alias,
opaque @ Opaque { .. } => *opaque,
ann @ Annotation(_, _) => *ann, ann @ Annotation(_, _) => *ann,
AnnotatedBody { AnnotatedBody {
ann_pattern, ann_pattern,
@ -415,7 +416,8 @@ fn binop_to_function(binop: BinOp) -> (&'static str, &'static str) {
Or => (ModuleName::BOOL, "or"), Or => (ModuleName::BOOL, "or"),
Pizza => unreachable!("Cannot desugar the |> operator"), Pizza => unreachable!("Cannot desugar the |> operator"),
Assignment => unreachable!("Cannot desugar the = operator"), Assignment => unreachable!("Cannot desugar the = operator"),
HasType => unreachable!("Cannot desugar the : operator"), IsAliasType => unreachable!("Cannot desugar the : operator"),
IsOpaqueType => unreachable!("Cannot desugar the := operator"),
Backpassing => unreachable!("Cannot desugar the <- operator"), Backpassing => unreachable!("Cannot desugar the <- operator"),
} }
} }

View file

@ -2,7 +2,7 @@ use crate::annotation::{Formattable, Newlines, Parens};
use crate::pattern::fmt_pattern; use crate::pattern::fmt_pattern;
use crate::spaces::{fmt_spaces, INDENT}; use crate::spaces::{fmt_spaces, INDENT};
use crate::Buf; use crate::Buf;
use roc_parse::ast::{TypeHeader, Def, Expr, Pattern}; use roc_parse::ast::{Def, Expr, Pattern, TypeHeader};
use roc_region::all::Loc; use roc_region::all::Loc;
/// A Located formattable value is also formattable /// A Located formattable value is also formattable
@ -12,6 +12,7 @@ impl<'a> Formattable for Def<'a> {
match self { match self {
Alias { ann, .. } => ann.is_multiline(), Alias { ann, .. } => ann.is_multiline(),
Opaque { typ, .. } => typ.is_multiline(),
Annotation(loc_pattern, loc_annotation) => { Annotation(loc_pattern, loc_annotation) => {
loc_pattern.is_multiline() || loc_annotation.is_multiline() loc_pattern.is_multiline() || loc_annotation.is_multiline()
} }
@ -60,6 +61,10 @@ impl<'a> Formattable for Def<'a> {
Alias { Alias {
header: TypeHeader { name, vars }, header: TypeHeader { name, vars },
ann, ann,
}
| Opaque {
header: TypeHeader { name, vars },
typ: ann,
} => { } => {
buf.indent(indent); buf.indent(indent);
buf.push_str(name.value); buf.push_str(name.value);
@ -69,7 +74,11 @@ impl<'a> Formattable for Def<'a> {
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded); fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
} }
buf.push_str(" :"); buf.push_str(match self {
Alias { .. } => " :",
Opaque { .. } => " :=",
_ => unreachable!(),
});
buf.spaces(1); buf.spaces(1);
ann.format(buf, indent + INDENT) ann.format(buf, indent + INDENT)

View file

@ -347,7 +347,8 @@ fn push_op(buf: &mut Buf, op: BinOp) {
called_via::BinOp::Or => buf.push_str("||"), called_via::BinOp::Or => buf.push_str("||"),
called_via::BinOp::Pizza => buf.push_str("|>"), called_via::BinOp::Pizza => buf.push_str("|>"),
called_via::BinOp::Assignment => unreachable!(), called_via::BinOp::Assignment => unreachable!(),
called_via::BinOp::HasType => unreachable!(), called_via::BinOp::IsAliasType => unreachable!(),
called_via::BinOp::IsOpaqueType => unreachable!(),
called_via::BinOp::Backpassing => unreachable!(), called_via::BinOp::Backpassing => unreachable!(),
} }
} }
@ -1067,7 +1068,11 @@ fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool {
| BinOp::GreaterThanOrEq | BinOp::GreaterThanOrEq
| BinOp::And | BinOp::And
| BinOp::Or => true, | BinOp::Or => true,
BinOp::Pizza | BinOp::Assignment | BinOp::HasType | BinOp::Backpassing => false, BinOp::Pizza
| BinOp::Assignment
| BinOp::IsAliasType
| BinOp::IsOpaqueType
| BinOp::Backpassing => false,
}) })
} }
Expr::If(_, _) => true, Expr::If(_, _) => true,

View file

@ -7,6 +7,7 @@ edition = "2018"
[dependencies] [dependencies]
roc_collections = { path = "../collections" } roc_collections = { path = "../collections" }
roc_error_macros = { path = "../../error_macros" }
roc_region = { path = "../region" } roc_region = { path = "../region" }
roc_module = { path = "../module" } roc_module = { path = "../module" }
roc_types = { path = "../types" } roc_types = { path = "../types" }

View file

@ -4,6 +4,7 @@ use crate::docs::TypeAnnotation::{
}; };
use crate::file::LoadedModule; use crate::file::LoadedModule;
use roc_can::scope::Scope; use roc_can::scope::Scope;
use roc_error_macros::todo_opaques;
use roc_module::ident::ModuleName; use roc_module::ident::ModuleName;
use roc_module::symbol::IdentIds; use roc_module::symbol::IdentIds;
use roc_parse::ast::CommentOrNewline; use roc_parse::ast::CommentOrNewline;
@ -228,6 +229,8 @@ fn generate_entry_doc<'a>(
(acc, None) (acc, None)
} }
Def::Opaque { .. } => todo_opaques!("figure out documentation for opaques"),
Def::Body(_, _) => (acc, None), Def::Body(_, _) => (acc, None),
Def::Expect(c) => todo!("documentation for tests {:?}", c), Def::Expect(c) => todo!("documentation for tests {:?}", c),

View file

@ -47,7 +47,8 @@ pub enum BinOp {
Or, Or,
Pizza, Pizza,
Assignment, Assignment,
HasType, IsAliasType,
IsOpaqueType,
Backpassing, Backpassing,
// lowest precedence // lowest precedence
} }
@ -59,7 +60,7 @@ impl BinOp {
Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan => 1, Caret | Star | Slash | Percent | Plus | Minus | LessThan | GreaterThan => 1,
DoubleSlash | DoublePercent | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq DoubleSlash | DoublePercent | Equals | NotEquals | LessThanOrEq | GreaterThanOrEq
| And | Or | Pizza => 2, | And | Or | Pizza => 2,
Assignment | HasType | Backpassing => unreachable!(), Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(),
} }
} }
} }
@ -103,7 +104,7 @@ impl BinOp {
Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => { Equals | NotEquals | LessThan | GreaterThan | LessThanOrEq | GreaterThanOrEq => {
NonAssociative NonAssociative
} }
Assignment | HasType | Backpassing => unreachable!(), Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(),
} }
} }
@ -116,7 +117,7 @@ impl BinOp {
And => 3, And => 3,
Or => 2, Or => 2,
Pizza => 1, Pizza => 1,
Assignment | HasType | Backpassing => unreachable!(), Assignment | IsAliasType | IsOpaqueType | Backpassing => unreachable!(),
} }
} }
} }
@ -154,7 +155,8 @@ impl std::fmt::Display for BinOp {
Or => "||", Or => "||",
Pizza => "|>", Pizza => "|>",
Assignment => "=", Assignment => "=",
HasType => ":", IsAliasType => ":",
IsOpaqueType => ":=",
Backpassing => "<-", Backpassing => "<-",
}; };

View file

@ -257,6 +257,12 @@ pub enum Def<'a> {
ann: Loc<TypeAnnotation<'a>>, ann: Loc<TypeAnnotation<'a>>,
}, },
/// An opaque type, wrapping its inner type. E.g. Age := U64.
Opaque {
header: TypeHeader<'a>,
typ: Loc<TypeAnnotation<'a>>,
},
// TODO in canonicalization, check to see if there are any newlines after the // 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 // 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.) // applies to that expr! (TODO: verify that the pattern for both annotation and body match.)

View file

@ -1,6 +1,6 @@
use crate::ast::{ use crate::ast::{
TypeHeader, AssignedField, Collection, CommentOrNewline, Def, Expr, ExtractSpaces, Pattern, AssignedField, Collection, CommentOrNewline, Def, Expr, ExtractSpaces, Pattern, Spaceable,
Spaceable, TypeAnnotation, TypeAnnotation, TypeHeader,
}; };
use crate::blankspace::{space0_after_e, space0_around_ee, space0_before_e, space0_e}; use crate::blankspace::{space0_after_e, space0_around_ee, space0_before_e, space0_e};
use crate::ident::{lowercase_ident, parse_ident, Ident}; use crate::ident::{lowercase_ident, parse_ident, Ident};
@ -422,7 +422,7 @@ impl<'a> ExprState<'a> {
arena: &'a Bump, arena: &'a Bump,
loc_op: Loc<BinOp>, loc_op: Loc<BinOp>,
) -> 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!(loc_op.value, BinOp::HasType); debug_assert_eq!(loc_op.value, BinOp::IsAliasType);
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
@ -853,7 +853,7 @@ fn parse_defs_end<'a>(
parse_defs_end(options, column, def_state, arena, state) parse_defs_end(options, column, def_state, arena, state)
} }
Ok((_, BinOp::HasType, state)) => { Ok((_, BinOp::IsAliasType, state)) => {
let (_, ann_type, state) = specialize( let (_, ann_type, state) = specialize(
EExpr::Type, EExpr::Type,
space0_before_e( space0_before_e(
@ -919,6 +919,127 @@ fn parse_defs_expr<'a>(
} }
} }
#[derive(Copy, Clone, PartialEq, Eq)]
enum TypeKind {
Alias,
Opaque,
}
fn finish_parsing_alias_or_opaque<'a>(
min_indent: u32,
options: ExprParseOptions,
start_column: u32,
expr_state: ExprState<'a>,
loc_op: Loc<BinOp>,
arena: &'a Bump,
state: State<'a>,
spaces_after_operator: &'a [CommentOrNewline<'a>],
kind: TypeKind,
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
let expr_region = expr_state.expr.region;
let indented_more = start_column + 1;
let (expr, arguments) = expr_state
.validate_has_type(arena, loc_op)
.map_err(|fail| (MadeProgress, fail, state.clone()))?;
let (loc_def, state) = match &expr.value {
Expr::GlobalTag(name) => {
let mut type_arguments = Vec::with_capacity_in(arguments.len(), arena);
for argument in arguments {
match expr_to_pattern_help(arena, &argument.value) {
Ok(good) => {
type_arguments.push(Loc::at(argument.region, good));
}
Err(_) => panic!(),
}
}
let (_, ann_type, state) = specialize(
EExpr::Type,
space0_before_e(
type_annotation::located_help(indented_more, true),
min_indent,
EType::TIndentStart,
),
)
.parse(arena, state)?;
let def_region = Region::span_across(&expr.region, &ann_type.region);
let header = TypeHeader {
name: Loc::at(expr.region, name),
vars: type_arguments.into_bump_slice(),
};
let type_def = match kind {
TypeKind::Alias => Def::Alias {
header,
ann: ann_type,
},
TypeKind::Opaque => Def::Opaque {
header,
typ: ann_type,
},
};
(&*arena.alloc(Loc::at(def_region, type_def)), state)
}
_ => {
let call = to_call(arena, arguments, expr);
match expr_to_pattern_help(arena, &call.value) {
Ok(good) => {
let parser = specialize(
EExpr::Type,
space0_before_e(
type_annotation::located_help(indented_more, false),
min_indent,
EType::TIndentStart,
),
);
match parser.parse(arena, state) {
Err((_, fail, state)) => return Err((MadeProgress, fail, state)),
Ok((_, mut ann_type, 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);
}
let def_region = Region::span_across(&call.region, &ann_type.region);
let alias = Def::Annotation(Loc::at(expr_region, good), ann_type);
(&*arena.alloc(Loc::at(def_region, alias)), state)
}
}
}
Err(_) => {
// this `:`/`:=` likely occurred inline; treat it as an invalid operator
let op = match kind {
TypeKind::Alias => ":",
TypeKind::Opaque => ":=",
};
let fail = EExpr::BadOperator(op, loc_op.region.start());
return Err((MadeProgress, fail, state));
}
}
}
};
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>( fn parse_expr_operator<'a>(
min_indent: u32, min_indent: u32,
options: ExprParseOptions, options: ExprParseOptions,
@ -1059,102 +1180,21 @@ fn parse_expr_operator<'a>(
Ok((MadeProgress, ret, state)) Ok((MadeProgress, ret, state))
} }
BinOp::HasType => { BinOp::IsAliasType | BinOp::IsOpaqueType => finish_parsing_alias_or_opaque(
let expr_region = expr_state.expr.region;
let indented_more = start_column + 1;
let (expr, arguments) = expr_state
.validate_has_type(arena, loc_op)
.map_err(|fail| (MadeProgress, fail, state.clone()))?;
let (loc_def, state) = match &expr.value {
Expr::GlobalTag(name) => {
let mut type_arguments = Vec::with_capacity_in(arguments.len(), arena);
for argument in arguments {
match expr_to_pattern_help(arena, &argument.value) {
Ok(good) => {
type_arguments.push(Loc::at(argument.region, good));
}
Err(_) => panic!(),
}
}
let (_, ann_type, state) = specialize(
EExpr::Type,
space0_before_e(
type_annotation::located_help(indented_more, true),
min_indent, min_indent,
EType::TIndentStart, options,
), start_column,
) expr_state,
.parse(arena, state)?; loc_op,
arena,
let alias_region = Region::span_across(&expr.region, &ann_type.region); state,
let alias = Def::Alias {
header: TypeHeader {
name: Loc::at(expr.region, name),
vars: type_arguments.into_bump_slice(),
},
ann: ann_type,
};
(&*arena.alloc(Loc::at(alias_region, alias)), state)
}
_ => {
let call = to_call(arena, arguments, expr);
match expr_to_pattern_help(arena, &call.value) {
Ok(good) => {
let parser = specialize(
EExpr::Type,
space0_before_e(
type_annotation::located_help(indented_more, false),
min_indent,
EType::TIndentStart,
),
);
match parser.parse(arena, state) {
Err((_, fail, state)) => return Err((MadeProgress, fail, state)),
Ok((_, mut ann_type, 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, spaces_after_operator,
ann_type.region, match op {
); BinOp::IsAliasType => TypeKind::Alias,
} BinOp::IsOpaqueType => TypeKind::Opaque,
_ => unreachable!(),
let alias_region = },
Region::span_across(&call.region, &ann_type.region); ),
let alias =
Def::Annotation(Loc::at(expr_region, good), ann_type);
(&*arena.alloc(Loc::at(alias_region, alias)), state)
}
}
}
Err(_) => {
// this `:` likely occurred inline; treat it as an invalid operator
let fail = EExpr::BadOperator(":", loc_op.region.start());
return Err((MadeProgress, fail, state));
}
}
}
};
let def_state = DefState {
defs: bumpalo::vec![in arena; loc_def],
spaces_after: &[],
};
parse_defs_expr(options, start_column, def_state, arena, state)
}
_ => match loc_possibly_negative_or_negated_term(min_indent, options).parse(arena, state) { _ => match loc_possibly_negative_or_negated_term(min_indent, options).parse(arena, state) {
Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)), Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)),
Ok((_, mut new_expr, state)) => { Ok((_, mut new_expr, state)) => {
@ -2372,7 +2412,8 @@ where
Err((NoProgress, to_error(".", state.pos()), state)) Err((NoProgress, to_error(".", state.pos()), state))
} }
"=" => good!(BinOp::Assignment, 1), "=" => good!(BinOp::Assignment, 1),
":" => good!(BinOp::HasType, 1), ":=" => good!(BinOp::IsOpaqueType, 2),
":" => good!(BinOp::IsAliasType, 1),
"|>" => good!(BinOp::Pizza, 2), "|>" => good!(BinOp::Pizza, 2),
"==" => good!(BinOp::Equals, 2), "==" => good!(BinOp::Equals, 2),
"!=" => good!(BinOp::NotEquals, 2), "!=" => good!(BinOp::NotEquals, 2),