mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-16 00:35:01 +00:00
Merge pull request #6708 from roc-lang/module-params-syntax
Parse and format module params
This commit is contained in:
commit
e5ea6dc461
46 changed files with 989 additions and 162 deletions
|
@ -246,6 +246,7 @@ impl<'a> Module<'a> {
|
|||
name,
|
||||
},
|
||||
},
|
||||
params: None,
|
||||
alias: None,
|
||||
exposed: new_exposed,
|
||||
})
|
||||
|
@ -784,43 +785,6 @@ pub enum ValueDef<'a> {
|
|||
}
|
||||
|
||||
impl<'a> ValueDef<'a> {
|
||||
pub fn expr(&self) -> Option<&'a Expr<'a>> {
|
||||
match self {
|
||||
ValueDef::Body(_, body) => Some(&body.value),
|
||||
|
||||
ValueDef::AnnotatedBody {
|
||||
ann_pattern: _,
|
||||
ann_type: _,
|
||||
comment: _,
|
||||
body_pattern: _,
|
||||
body_expr,
|
||||
} => Some(&body_expr.value),
|
||||
|
||||
ValueDef::Dbg {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
}
|
||||
| ValueDef::Expect {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
}
|
||||
| ValueDef::ExpectFx {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
} => Some(&condition.value),
|
||||
|
||||
ValueDef::Annotation(_, _)
|
||||
| ValueDef::ModuleImport(ModuleImport {
|
||||
before_name: _,
|
||||
name: _,
|
||||
alias: _,
|
||||
exposed: _,
|
||||
})
|
||||
| ValueDef::IngestedFileImport(_) => None,
|
||||
ValueDef::Stmt(loc_expr) => Some(&loc_expr.value),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_expr(&mut self, new_expr: &'a Loc<Expr<'a>>) {
|
||||
match self {
|
||||
ValueDef::Body(_, expr) => *expr = new_expr,
|
||||
|
@ -1019,8 +983,47 @@ impl<'a, 'b> Iterator for RecursiveValueDefIter<'a, 'b> {
|
|||
let def = &self.current.value_defs[def_index.index()];
|
||||
let region = &self.current.regions[self.index];
|
||||
|
||||
if let Some(expr) = def.expr() {
|
||||
self.push_pending_from_expr(expr);
|
||||
match def {
|
||||
ValueDef::Body(_, body) => self.push_pending_from_expr(&body.value),
|
||||
|
||||
ValueDef::AnnotatedBody {
|
||||
ann_pattern: _,
|
||||
ann_type: _,
|
||||
comment: _,
|
||||
body_pattern: _,
|
||||
body_expr,
|
||||
} => self.push_pending_from_expr(&body_expr.value),
|
||||
|
||||
ValueDef::Dbg {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
}
|
||||
| ValueDef::Expect {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
}
|
||||
| ValueDef::ExpectFx {
|
||||
condition,
|
||||
preceding_comment: _,
|
||||
} => self.push_pending_from_expr(&condition.value),
|
||||
|
||||
ValueDef::ModuleImport(ModuleImport {
|
||||
before_name: _,
|
||||
name: _,
|
||||
alias: _,
|
||||
exposed: _,
|
||||
params,
|
||||
}) => {
|
||||
if let Some(ModuleImportParams { before: _, params }) = params {
|
||||
for loc_assigned_field in params.items {
|
||||
if let Some(expr) = loc_assigned_field.value.value() {
|
||||
self.push_pending_from_expr(&expr.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ValueDef::Stmt(loc_expr) => self.push_pending_from_expr(&loc_expr.value),
|
||||
ValueDef::Annotation(_, _) | ValueDef::IngestedFileImport(_) => {}
|
||||
}
|
||||
|
||||
self.index += 1;
|
||||
|
@ -1046,6 +1049,7 @@ impl<'a, 'b> Iterator for RecursiveValueDefIter<'a, 'b> {
|
|||
pub struct ModuleImport<'a> {
|
||||
pub before_name: &'a [CommentOrNewline<'a>],
|
||||
pub name: Loc<ImportedModuleName<'a>>,
|
||||
pub params: Option<ModuleImportParams<'a>>,
|
||||
pub alias: Option<header::KeywordItem<'a, ImportAsKeyword, Loc<ImportAlias<'a>>>>,
|
||||
pub exposed: Option<
|
||||
header::KeywordItem<
|
||||
|
@ -1056,6 +1060,12 @@ pub struct ModuleImport<'a> {
|
|||
>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct ModuleImportParams<'a> {
|
||||
pub before: &'a [CommentOrNewline<'a>],
|
||||
pub params: Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct IngestedFileImport<'a> {
|
||||
pub before_path: &'a [CommentOrNewline<'a>],
|
||||
|
@ -1537,6 +1547,20 @@ pub enum AssignedField<'a, Val> {
|
|||
Malformed(&'a str),
|
||||
}
|
||||
|
||||
impl<'a, Val> AssignedField<'a, Val> {
|
||||
pub fn value(&self) -> Option<&Loc<Val>> {
|
||||
let mut current = self;
|
||||
|
||||
loop {
|
||||
match current {
|
||||
Self::RequiredValue(_, _, val) | Self::OptionalValue(_, _, val) => break Some(val),
|
||||
Self::LabelOnly(_) | Self::Malformed(_) => break None,
|
||||
Self::SpaceBefore(next, _) | Self::SpaceAfter(next, _) => current = *next,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum RecordBuilderField<'a> {
|
||||
// A field with a value, e.g. `{ name: "blah" }`
|
||||
|
@ -2635,9 +2659,10 @@ impl<'a> Malformed for ValueDef<'a> {
|
|||
ValueDef::ModuleImport(ModuleImport {
|
||||
before_name: _,
|
||||
name: _,
|
||||
params,
|
||||
alias: _,
|
||||
exposed: _,
|
||||
}) => false,
|
||||
}) => params.is_malformed(),
|
||||
ValueDef::IngestedFileImport(IngestedFileImport {
|
||||
before_path: _,
|
||||
path,
|
||||
|
@ -2649,6 +2674,14 @@ impl<'a> Malformed for ValueDef<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Malformed for ModuleImportParams<'a> {
|
||||
fn is_malformed(&self) -> bool {
|
||||
let Self { before: _, params } = self;
|
||||
|
||||
params.is_malformed()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Malformed for TypeAnnotation<'a> {
|
||||
fn is_malformed(&self) -> bool {
|
||||
match self {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use crate::ast::{
|
||||
is_expr_suffixed, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces,
|
||||
Implements, ImplementsAbilities, ImportAlias, ImportAsKeyword, ImportExposingKeyword,
|
||||
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport, Pattern,
|
||||
RecordBuilderField, Spaceable, Spaced, Spaces, TypeAnnotation, TypeDef, TypeHeader, ValueDef,
|
||||
ImportedModuleName, IngestedFileAnnotation, IngestedFileImport, ModuleImport,
|
||||
ModuleImportParams, Pattern, RecordBuilderField, Spaceable, Spaced, Spaces, TypeAnnotation,
|
||||
TypeDef, TypeHeader, ValueDef,
|
||||
};
|
||||
use crate::blankspace::{
|
||||
space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e,
|
||||
|
@ -15,8 +16,8 @@ use crate::module::module_name_help;
|
|||
use crate::parser::{
|
||||
self, backtrackable, byte, byte_indent, increment_min_indent, line_min_indent, optional,
|
||||
reset_min_indent, sep_by1, sep_by1_e, set_min_indent, specialize_err, specialize_err_ref, then,
|
||||
two_bytes, EClosure, EExpect, EExpr, EIf, EImport, EInParens, EList, ENumber, EPattern,
|
||||
ERecord, EString, EType, EWhen, Either, ParseResult, Parser,
|
||||
two_bytes, EClosure, EExpect, EExpr, EIf, EImport, EImportParams, EInParens, EList, ENumber,
|
||||
EPattern, ERecord, EString, EType, EWhen, Either, ParseResult, Parser,
|
||||
};
|
||||
use crate::pattern::{closure_param, loc_implements_parser};
|
||||
use crate::state::State;
|
||||
|
@ -979,6 +980,7 @@ fn import_body<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> {
|
|||
record!(ModuleImport {
|
||||
before_name: space0_e(EImport::IndentStart),
|
||||
name: loc!(imported_module_name()),
|
||||
params: optional(specialize_err(EImport::Params, import_params())),
|
||||
alias: optional(import_as()),
|
||||
exposed: optional(import_exposing())
|
||||
}),
|
||||
|
@ -986,6 +988,37 @@ fn import_body<'a>() -> impl Parser<'a, ValueDef<'a>, EImport<'a>> {
|
|||
)
|
||||
}
|
||||
|
||||
fn import_params<'a>() -> impl Parser<'a, ModuleImportParams<'a>, EImportParams<'a>> {
|
||||
then(
|
||||
and!(
|
||||
backtrackable(space0_e(EImportParams::Indent)),
|
||||
specialize_err(EImportParams::Record, record_help())
|
||||
),
|
||||
|arena, state, _, (before, record): (_, RecordHelp<'a>)| {
|
||||
if let Some(update) = record.update {
|
||||
return Err((
|
||||
MadeProgress,
|
||||
EImportParams::RecordUpdateFound(update.region),
|
||||
));
|
||||
}
|
||||
|
||||
let params = record.fields.map_items_result(arena, |loc_field| {
|
||||
match loc_field.value.to_assigned_field(arena) {
|
||||
Ok(field) => Ok(Loc::at(loc_field.region, field)),
|
||||
Err(FoundApplyValue) => Err((
|
||||
MadeProgress,
|
||||
EImportParams::RecordApplyFound(loc_field.region),
|
||||
)),
|
||||
}
|
||||
})?;
|
||||
|
||||
let import_params = ModuleImportParams { before, params };
|
||||
|
||||
Ok((MadeProgress, import_params, state))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn imported_module_name<'a>() -> impl Parser<'a, ImportedModuleName<'a>, EImport<'a>> {
|
||||
record!(ImportedModuleName {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::ast::{
|
||||
Collection, CommentOrNewline, Malformed, Spaced, Spaces, StrLiteral, TypeAnnotation,
|
||||
Collection, CommentOrNewline, Malformed, Pattern, Spaced, Spaces, StrLiteral, TypeAnnotation,
|
||||
};
|
||||
use crate::blankspace::space0_e;
|
||||
use crate::expr::merge_spaces;
|
||||
|
@ -242,13 +242,21 @@ pub struct KeywordItem<'a, K, V> {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ModuleHeader<'a> {
|
||||
pub before_exposes: &'a [CommentOrNewline<'a>],
|
||||
pub after_keyword: &'a [CommentOrNewline<'a>],
|
||||
pub params: Option<ModuleParams<'a>>,
|
||||
pub exposes: Collection<'a, Loc<Spaced<'a, ExposedName<'a>>>>,
|
||||
|
||||
// Keeping this so we can format old interface header into module headers
|
||||
pub interface_imports: Option<KeywordItem<'a, ImportsKeyword, ImportsCollection<'a>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ModuleParams<'a> {
|
||||
pub params: Collection<'a, Loc<Pattern<'a>>>,
|
||||
pub before_arrow: &'a [CommentOrNewline<'a>],
|
||||
pub after_arrow: &'a [CommentOrNewline<'a>],
|
||||
}
|
||||
|
||||
pub type ImportsKeywordItem<'a> = KeywordItem<'a, ImportsKeyword, ImportsCollection<'a>>;
|
||||
pub type ImportsCollection<'a> = Collection<'a, Loc<Spaced<'a, ImportsEntry<'a>>>>;
|
||||
|
||||
|
|
|
@ -4,17 +4,18 @@ use crate::expr::merge_spaces;
|
|||
use crate::header::{
|
||||
package_entry, package_name, AppHeader, ExposedName, ExposesKeyword, GeneratesKeyword,
|
||||
HostedHeader, ImportsCollection, ImportsEntry, ImportsKeyword, ImportsKeywordItem, Keyword,
|
||||
KeywordItem, ModuleHeader, ModuleName, PackageEntry, PackageHeader, PackagesKeyword,
|
||||
PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo, RequiresKeyword, To, ToKeyword,
|
||||
TypedIdent, WithKeyword,
|
||||
KeywordItem, ModuleHeader, ModuleName, ModuleParams, PackageEntry, PackageHeader,
|
||||
PackagesKeyword, PlatformHeader, PlatformRequires, ProvidesKeyword, ProvidesTo,
|
||||
RequiresKeyword, To, ToKeyword, TypedIdent, WithKeyword,
|
||||
};
|
||||
use crate::ident::{self, lowercase_ident, unqualified_ident, uppercase, UppercaseIdent};
|
||||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{
|
||||
backtrackable, byte, increment_min_indent, optional, reset_min_indent, specialize_err,
|
||||
two_bytes, EExposes, EGenerates, EGeneratesWith, EHeader, EImports, EPackages, EProvides,
|
||||
ERequires, ETypedIdent, Parser, SourceError, SpaceProblem, SyntaxError,
|
||||
two_bytes, EExposes, EGenerates, EGeneratesWith, EHeader, EImports, EPackages, EParams,
|
||||
EProvides, ERequires, ETypedIdent, Parser, SourceError, SpaceProblem, SyntaxError,
|
||||
};
|
||||
use crate::pattern::record_pattern_fields;
|
||||
use crate::state::State;
|
||||
use crate::string_literal::{self, parse_str_literal};
|
||||
use crate::type_annotation;
|
||||
|
@ -111,13 +112,25 @@ pub fn header<'a>() -> impl Parser<'a, Module<'a>, EHeader<'a>> {
|
|||
#[inline(always)]
|
||||
fn module_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
|
||||
record!(ModuleHeader {
|
||||
before_exposes: space0_e(EHeader::IndentStart),
|
||||
after_keyword: space0_e(EHeader::IndentStart),
|
||||
params: optional(specialize_err(EHeader::Params, module_params())),
|
||||
exposes: specialize_err(EHeader::Exposes, exposes_list()),
|
||||
interface_imports: succeed!(None)
|
||||
})
|
||||
.trace("module_header")
|
||||
}
|
||||
|
||||
fn module_params<'a>() -> impl Parser<'a, ModuleParams<'a>, EParams<'a>> {
|
||||
record!(ModuleParams {
|
||||
params: specialize_err(EParams::Pattern, record_pattern_fields()),
|
||||
before_arrow: skip_second!(
|
||||
space0_e(EParams::BeforeArrow),
|
||||
loc!(two_bytes(b'-', b'>', EParams::Arrow))
|
||||
),
|
||||
after_arrow: space0_e(EParams::AfterArrow),
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! merge_n_spaces {
|
||||
($arena:expr, $($slice:expr),*) => {
|
||||
{
|
||||
|
@ -131,7 +144,7 @@ macro_rules! merge_n_spaces {
|
|||
/// Parse old interface headers so we can format them into module headers
|
||||
#[inline(always)]
|
||||
fn interface_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
|
||||
let before_exposes = map_with_arena!(
|
||||
let after_keyword = map_with_arena!(
|
||||
and!(
|
||||
skip_second!(
|
||||
space0_e(EHeader::IndentStart),
|
||||
|
@ -146,7 +159,8 @@ fn interface_header<'a>() -> impl Parser<'a, ModuleHeader<'a>, EHeader<'a>> {
|
|||
);
|
||||
|
||||
record!(ModuleHeader {
|
||||
before_exposes: before_exposes,
|
||||
after_keyword: after_keyword,
|
||||
params: succeed!(None),
|
||||
exposes: specialize_err(EHeader::Exposes, exposes_list()).trace("exposes_list"),
|
||||
interface_imports: map!(
|
||||
specialize_err(EHeader::Imports, imports()),
|
||||
|
|
|
@ -88,7 +88,9 @@ impl_space_problem! {
|
|||
EHeader<'a>,
|
||||
EIf<'a>,
|
||||
EImport<'a>,
|
||||
EParams<'a>,
|
||||
EImports,
|
||||
EImportParams<'a>,
|
||||
EInParens<'a>,
|
||||
EClosure<'a>,
|
||||
EList<'a>,
|
||||
|
@ -115,6 +117,7 @@ impl_space_problem! {
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EHeader<'a> {
|
||||
Provides(EProvides<'a>, Position),
|
||||
Params(EParams<'a>, Position),
|
||||
Exposes(EExposes, Position),
|
||||
Imports(EImports, Position),
|
||||
Requires(ERequires<'a>, Position),
|
||||
|
@ -149,6 +152,15 @@ pub enum EProvides<'a> {
|
|||
Space(BadInputError, Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EParams<'a> {
|
||||
Pattern(PRecord<'a>, Position),
|
||||
BeforeArrow(Position),
|
||||
Arrow(Position),
|
||||
AfterArrow(Position),
|
||||
Space(BadInputError, Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EExposes {
|
||||
Exposes(Position),
|
||||
|
@ -530,6 +542,7 @@ pub enum EImport<'a> {
|
|||
PackageShorthand(Position),
|
||||
PackageShorthandDot(Position),
|
||||
ModuleName(Position),
|
||||
Params(EImportParams<'a>, Position),
|
||||
IndentAs(Position),
|
||||
As(Position),
|
||||
IndentAlias(Position),
|
||||
|
@ -552,6 +565,15 @@ pub enum EImport<'a> {
|
|||
EndNewline(Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EImportParams<'a> {
|
||||
Indent(Position),
|
||||
Record(ERecord<'a>, Position),
|
||||
RecordUpdateFound(Region),
|
||||
RecordApplyFound(Region),
|
||||
Space(BadInputError, Position),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EPattern<'a> {
|
||||
Record(PRecord<'a>, Position),
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::ast::{Implements, Pattern, PatternAs, Spaceable};
|
||||
use crate::ast::{Collection, Implements, Pattern, PatternAs, Spaceable};
|
||||
use crate::blankspace::{space0_e, spaces, spaces_before};
|
||||
use crate::ident::{lowercase_ident, parse_ident, Accessor, Ident};
|
||||
use crate::keyword;
|
||||
|
@ -468,15 +468,17 @@ fn lowercase_ident_pattern<'a>() -> impl Parser<'a, &'a str, EPattern<'a>> {
|
|||
|
||||
#[inline(always)]
|
||||
fn record_pattern_help<'a>() -> impl Parser<'a, Pattern<'a>, PRecord<'a>> {
|
||||
map!(
|
||||
collection_trailing_sep_e!(
|
||||
byte(b'{', PRecord::Open),
|
||||
record_pattern_field(),
|
||||
byte(b',', PRecord::End),
|
||||
byte(b'}', PRecord::End),
|
||||
Pattern::SpaceBefore
|
||||
),
|
||||
Pattern::RecordDestructure
|
||||
map!(record_pattern_fields(), Pattern::RecordDestructure)
|
||||
}
|
||||
|
||||
pub fn record_pattern_fields<'a>() -> impl Parser<'a, Collection<'a, Loc<Pattern<'a>>>, PRecord<'a>>
|
||||
{
|
||||
collection_trailing_sep_e!(
|
||||
byte(b'{', PRecord::Open),
|
||||
record_pattern_field(),
|
||||
byte(b',', PRecord::End),
|
||||
byte(b'}', PRecord::End),
|
||||
Pattern::SpaceBefore
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue