Parse params in module header

module {echo, read} -> [menu]

Formatter isn't implemented yet.
This commit is contained in:
Agus Zubiaga 2024-05-01 22:35:59 -03:00
parent 010aed88f9
commit 5b1a3c8f03
No known key found for this signature in database
16 changed files with 145 additions and 32 deletions

View file

@ -177,7 +177,7 @@ pub fn fmt_module_header<'a>(buf: &mut Buf, header: &'a ModuleHeader<'a>) {
buf.indent(0);
buf.push_str("module");
let indent = fmt_spaces_with_outdent(buf, header.before_exposes, INDENT);
let indent = fmt_spaces_with_outdent(buf, header.after_keyword, INDENT);
fmt_exposes(buf, header.exposes, indent);
}

View file

@ -12,8 +12,8 @@ use roc_parse::{
},
header::{
AppHeader, ExposedName, HostedHeader, ImportsEntry, KeywordItem, ModuleHeader, ModuleName,
PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires, ProvidesTo, To,
TypedIdent,
ModuleParams, PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires,
ProvidesTo, To, TypedIdent,
},
ident::{BadIdent, UppercaseIdent},
};
@ -285,7 +285,8 @@ impl<'a> RemoveSpaces<'a> for Module<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
let header = match &self.header {
Header::Module(header) => Header::Module(ModuleHeader {
before_exposes: &[],
after_keyword: &[],
params: header.params.remove_spaces(arena),
exposes: header.exposes.remove_spaces(arena),
interface_imports: header.interface_imports.remove_spaces(arena),
}),
@ -330,6 +331,16 @@ impl<'a> RemoveSpaces<'a> for Module<'a> {
}
}
impl<'a> RemoveSpaces<'a> for ModuleParams<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self {
ModuleParams {
params: self.params.remove_spaces(arena),
before_arrow: &[],
after_arrow: &[],
}
}
}
impl<'a> RemoveSpaces<'a> for Region {
fn remove_spaces(&self, _arena: &'a Bump) -> Self {
Region::zero()

View file

@ -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>>>>;

View file

@ -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()),

View file

@ -88,6 +88,7 @@ impl_space_problem! {
EHeader<'a>,
EIf<'a>,
EImport<'a>,
EParams<'a>,
EImports,
EInParens<'a>,
EClosure<'a>,
@ -115,6 +116,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 +151,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),

View file

@ -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
)
}

View file

@ -2,7 +2,8 @@ Module {
comments: [],
header: Module(
ModuleHeader {
before_exposes: [],
after_keyword: [],
params: None,
exposes: [],
interface_imports: None,
},

View file

@ -2,7 +2,8 @@ Module {
comments: [],
header: Module(
ModuleHeader {
before_exposes: [],
after_keyword: [],
params: None,
exposes: [
@8-9 ExposedName(
"a",

View file

@ -2,7 +2,8 @@ Module {
comments: [],
header: Module(
ModuleHeader {
before_exposes: [],
after_keyword: [],
params: None,
exposes: [],
interface_imports: None,
},

View file

@ -0,0 +1,28 @@
Module {
comments: [],
header: Module(
ModuleHeader {
after_keyword: [],
params: Some(
ModuleParams {
params: [
@8-12 Identifier {
ident: "echo",
},
@14-18 Identifier {
ident: "read",
},
],
before_arrow: [],
after_arrow: [],
},
),
exposes: [
@26-30 ExposedName(
"menu",
),
],
interface_imports: None,
},
),
}

View file

@ -0,0 +1 @@
module {echo, read } -> [menu]

View file

@ -2,7 +2,8 @@ Module {
comments: [],
header: Module(
ModuleHeader {
before_exposes: [],
after_keyword: [],
params: None,
exposes: [
@23-26 ExposedName(
"Foo",

View file

@ -351,6 +351,7 @@ mod test_snapshots {
pass/module_def_newline.moduledefs,
pass/module_multiline_exposes.header,
pass/module_with_newline.header,
pass/module_with_params.header,
pass/multi_backpassing.expr,
pass/multi_backpassing_in_def.moduledefs,
pass/multi_backpassing_with_apply.expr,

View file

@ -11,8 +11,9 @@ use roc_parse::{
WhenBranch,
},
header::{
AppHeader, ExposedName, HostedHeader, ImportsEntry, ModuleHeader, ModuleName, PackageEntry,
PackageHeader, PackageName, PlatformHeader, PlatformRequires, ProvidesTo, To, TypedIdent,
AppHeader, ExposedName, HostedHeader, ImportsEntry, ModuleHeader, ModuleName, ModuleParams,
PackageEntry, PackageHeader, PackageName, PlatformHeader, PlatformRequires, ProvidesTo, To,
TypedIdent,
},
ident::{Accessor, UppercaseIdent},
};
@ -213,12 +214,41 @@ impl IterTokens for Header<'_> {
impl IterTokens for ModuleHeader<'_> {
fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc<Token>> {
let Self {
before_exposes: _,
after_keyword: _,
params,
exposes,
interface_imports: _,
} = self;
exposes.iter_tokens(arena)
params
.iter_tokens(arena)
.into_iter()
.chain(exposes.iter_tokens(arena))
.collect_in(arena)
}
}
impl<T> IterTokens for Option<T>
where
T: IterTokens,
{
fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc<Token>> {
match self {
Some(params) => params.iter_tokens(arena),
None => bumpvec![in arena;],
}
}
}
impl IterTokens for ModuleParams<'_> {
fn iter_tokens<'a>(&self, arena: &'a Bump) -> BumpVec<'a, Loc<Token>> {
let Self {
params,
before_arrow: _,
after_arrow: _,
} = self;
params.iter_tokens(arena)
}
}

View file

@ -3392,6 +3392,8 @@ fn to_header_report<'a>(
to_provides_report(alloc, lines, filename, provides, *pos)
}
EHeader::Params(_params, _pos) => todo!(),
EHeader::Exposes(exposes, pos) => to_exposes_report(alloc, lines, filename, exposes, *pos),
EHeader::Imports(imports, pos) => to_imports_report(alloc, lines, filename, imports, *pos),