Parse platform headers

This commit is contained in:
Richard Feldman 2020-10-17 16:28:36 -04:00
parent a31ed6943f
commit dfa61b4c18
7 changed files with 240 additions and 23 deletions

View file

@ -1,34 +1,41 @@
use crate::ast::{
AppHeader, Attempting, CommentOrNewline, Def, ExposesEntry, ImportsEntry, InterfaceHeader,
Module,
AppHeader, Attempting, CommentOrNewline, Def, EffectsEntry, ExposesEntry, ImportsEntry,
InterfaceHeader, Module, PlatformHeader,
};
use crate::blankspace::{space0_around, space1};
use crate::expr::def;
use crate::header::ModuleName;
use crate::header::{ModuleName, PackageName};
use crate::ident::unqualified_ident;
use crate::parser::{
self, ascii_char, ascii_string, loc, optional, peek_utf8_char, peek_utf8_char_at, unexpected,
Parser, State,
unexpected_eof, ParseResult, Parser, State,
};
use crate::type_annotation;
use bumpalo::collections::{String, Vec};
use bumpalo::Bump;
use roc_region::all::Located;
pub fn header<'a>() -> impl Parser<'a, Module<'a>> {
one_of!(interface_module(), app_module())
one_of!(interface_module(), app_module(), platform_module())
}
#[inline(always)]
pub fn interface_module<'a>() -> impl Parser<'a, Module<'a>> {
fn app_module<'a>() -> impl Parser<'a, Module<'a>> {
map!(app_header(), |header| { Module::App { header } })
}
#[inline(always)]
fn platform_module<'a>() -> impl Parser<'a, Module<'a>> {
map!(platform_header(), |header| { Module::Platform { header } })
}
#[inline(always)]
fn interface_module<'a>() -> impl Parser<'a, Module<'a>> {
map!(interface_header(), |header| {
Module::Interface { header }
})
}
#[inline(always)]
pub fn app_module<'a>() -> impl Parser<'a, Module<'a>> {
map!(app_header(), |header| { Module::App { header } })
}
#[inline(always)]
pub fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>> {
parser::map(
@ -40,7 +47,7 @@ pub fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>> {
and!(exposes(), imports())
),
|(
(after_interface, name),
(after_interface_keyword, name),
(
((before_exposes, after_exposes), exposes),
((before_imports, after_imports), imports),
@ -50,7 +57,7 @@ pub fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>> {
name,
exposes,
imports,
after_interface,
after_interface_keyword,
before_exposes,
after_exposes,
before_imports,
@ -60,6 +67,46 @@ pub fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>> {
)
}
#[inline(always)]
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>> {
// e.g. rtfeldman/blah
//
// Package names and accounts can be capitalized and can contain dashes.
// They cannot contain underscores or other special characters.
// They must be ASCII.
map!(
and!(
move |arena, state| parse_package_part(arena, state),
skip_first!(ascii_char('/'), move |arena, state| parse_package_part(
arena, state
))
),
|(account, pkg)| { PackageName { account, pkg } }
)
}
pub fn parse_package_part<'a>(arena: &'a Bump, mut state: State<'a>) -> ParseResult<'a, &'a str> {
let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.)
while !state.bytes.is_empty() {
match peek_utf8_char(&state) {
Ok((ch, bytes_parsed)) => {
if ch == '-' || ch.is_ascii_alphanumeric() {
part_buf.push(ch);
state = state.advance_without_indenting(bytes_parsed)?;
} else {
return Ok((part_buf.into_bump_str(), state));
}
}
Err(reason) => return state.fail(reason),
}
}
Err(unexpected_eof(0, state.attempting, state))
}
#[inline(always)]
pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>> {
move |arena, mut state: State<'a>| {
@ -135,7 +182,7 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>> {
and!(provides(), imports())
),
|(
(after_interface, name),
(after_app_keyword, name),
(
((before_provides, after_provides), provides),
((before_imports, after_imports), imports),
@ -145,7 +192,7 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>> {
name,
provides,
imports,
after_interface,
after_app_keyword,
before_provides,
after_provides,
before_imports,
@ -155,6 +202,49 @@ fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>> {
)
}
#[inline(always)]
fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>> {
parser::map(
and!(
skip_first!(
ascii_string("platform"),
and!(space1(1), loc!(package_name()))
),
and!(provides(), and!(requires(), and!(imports(), effects())))
),
|(
(after_platform_keyword, name),
(
((before_provides, after_provides), provides),
(
((before_requires, after_requires), requires),
(
((before_imports, after_imports), imports),
((before_effects, after_effects), effects),
),
),
),
)| {
PlatformHeader {
name,
provides,
requires,
imports,
effects,
after_platform_keyword,
before_provides,
after_provides,
before_requires,
after_requires,
before_imports,
after_imports,
before_effects,
after_effects,
}
},
)
}
#[inline(always)]
pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located<Def<'a>>>> {
zero_or_more!(space0_around(loc(def(0)), 0))
@ -180,6 +270,26 @@ fn provides<'a>() -> impl Parser<
)
}
#[inline(always)]
fn requires<'a>() -> impl Parser<
'a,
(
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Vec<'a, Located<ExposesEntry<'a>>>,
),
> {
and!(
and!(skip_second!(space1(1), ascii_string("requires")), space1(1)),
collection!(
ascii_char('['),
loc!(exposes_entry()),
ascii_char(','),
ascii_char(']'),
1
)
)
}
#[inline(always)]
fn exposes<'a>() -> impl Parser<
'a,
@ -220,6 +330,37 @@ fn imports<'a>() -> impl Parser<
)
}
#[inline(always)]
fn effects<'a>() -> impl Parser<
'a,
(
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
Vec<'a, Located<EffectsEntry<'a>>>,
),
> {
and!(
and!(skip_second!(space1(1), ascii_string("effects")), space1(1)),
collection!(
ascii_char('{'),
loc!(effects_entry()),
ascii_char(','),
ascii_char('}'),
1
)
)
}
#[inline(always)]
fn effects_entry<'a>() -> impl Parser<'a, EffectsEntry<'a>> {
// e.g.
//
// printLine : Str -> Effect {}
map!(
and!(loc(unqualified_ident()), type_annotation::located(0)),
|(ident, ann)| { EffectsEntry::Effect { ident, ann } }
)
}
#[inline(always)]
fn exposes_entry<'a>() -> impl Parser<'a, ExposesEntry<'a>> {
map!(unqualified_ident(), ExposesEntry::Ident)