port effects

This commit is contained in:
Folkert 2021-03-09 21:53:23 +01:00
parent caafcc2888
commit 3eddedc566
5 changed files with 340 additions and 104 deletions

View file

@ -1,13 +1,14 @@
use crate::ast::{CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation};
use crate::blankspace::space0;
use crate::ident::lowercase_ident;
use crate::module::package_name;
use crate::parser::{ascii_char, optional, Either, Parser, Progress::*, State, SyntaxError};
use crate::string_literal;
use crate::{
ast::{CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation},
parser::specialize,
use crate::parser::{
ascii_char, optional, peek_utf8_char, specialize, unexpected_eof, Either, ParseResult, Parser,
Progress, Progress::*, State, SyntaxError,
};
use crate::string_literal;
use bumpalo::collections::String;
use bumpalo::collections::Vec;
use bumpalo::Bump;
use inlinable_string::InlinableString;
use roc_region::all::Loc;
@ -296,3 +297,48 @@ pub fn package_or_path<'a>() -> impl Parser<'a, PackageOrPath<'a>, SyntaxError<'
fn package_version<'a>() -> impl Parser<'a, Version<'a>, SyntaxError<'a>> {
move |_, _| todo!("TODO parse package version")
}
#[inline(always)]
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, SyntaxError<'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!(
parse_package_part,
skip_first!(ascii_char(b'/'), parse_package_part)
),
|(account, pkg)| { PackageName { account, pkg } }
)
}
fn parse_package_part<'a>(
arena: &'a Bump,
mut state: State<'a>,
) -> ParseResult<'a, &'a str, SyntaxError<'a>> {
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 {
let progress = Progress::progress_when(!part_buf.is_empty());
return Ok((progress, part_buf.into_bump_str(), state));
}
}
Err(reason) => {
let progress = Progress::progress_when(!part_buf.is_empty());
return state.fail(arena, progress, reason);
}
}
}
Err(unexpected_eof(arena, state, 0))
}

View file

@ -2,20 +2,19 @@ use crate::ast::{Attempting, CommentOrNewline, Def, Module};
use crate::blankspace::{space0, space0_around, space0_before, space0_before_e, space0_e, space1};
use crate::expr::def;
use crate::header::{
package_entry, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry,
InterfaceHeader, ModuleName, PackageEntry, PackageName, PlatformHeader, To, TypedIdent,
package_entry, package_name, package_or_path, AppHeader, Effects, ExposesEntry, ImportsEntry,
InterfaceHeader, ModuleName, PackageEntry, PlatformHeader, To, TypedIdent,
};
use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident};
use crate::parser::Progress::{self, *};
use crate::parser::{
ascii_char, ascii_string, backtrackable, end_of_file, loc, peek_utf8_char, peek_utf8_char_at,
specialize, unexpected, unexpected_eof, word1, Col, EExposes, EHeader, EImports, EProvides,
ERequires, ETypedIdent, ParseResult, Parser, Row, State, SyntaxError,
specialize, unexpected, word1, Col, EEffects, EExposes, EHeader, EImports, EPackages,
EProvides, ERequires, ETypedIdent, Parser, Row, State, SyntaxError,
};
use crate::string_literal;
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>, SyntaxError<'a>> {
@ -69,51 +68,6 @@ fn interface_header_help<'a>() -> impl Parser<'a, InterfaceHeader<'a>, EHeader<'
}
}
#[inline(always)]
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, SyntaxError<'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!(
parse_package_part,
skip_first!(ascii_char(b'/'), parse_package_part)
),
|(account, pkg)| { PackageName { account, pkg } }
)
}
fn parse_package_part<'a>(
arena: &'a Bump,
mut state: State<'a>,
) -> ParseResult<'a, &'a str, SyntaxError<'a>> {
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 {
let progress = Progress::progress_when(!part_buf.is_empty());
return Ok((progress, part_buf.into_bump_str(), state));
}
}
Err(reason) => {
let progress = Progress::progress_when(!part_buf.is_empty());
return state.fail(arena, progress, reason);
}
}
}
Err(unexpected_eof(arena, state, 0))
}
#[inline(always)]
pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, SyntaxError<'a>> {
move |arena, mut state: State<'a>| {
@ -184,17 +138,28 @@ pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, SyntaxError<'a>> {
#[inline(always)]
fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, SyntaxError<'a>> {
specialize(|e, _, _| SyntaxError::Header(e), app_header_help())
}
#[inline(always)]
fn app_header_help<'a>() -> impl Parser<'a, AppHeader<'a>, EHeader<'a>> {
|arena, state| {
let (_, after_app_keyword, state) = space1(1).parse(arena, state)?;
let min_indent = 1;
let (_, after_app_keyword, state) =
space0_e(min_indent, EHeader::Space, EHeader::IndentStart).parse(arena, state)?;
let (_, name, state) = loc!(crate::parser::specialize(
|e, r, c| SyntaxError::Expr(crate::parser::EExpr::Str(e, r, c)),
EHeader::AppName,
string_literal::parse()
))
.parse(arena, state)?;
let (_, opt_pkgs, state) = maybe!(packages()).parse(arena, state)?;
let (_, opt_imports, state) = maybe!(imports()).parse(arena, state)?;
let (_, provides, state) = provides_to().parse(arena, state)?;
let (_, opt_pkgs, state) =
maybe!(specialize(EHeader::Packages, packages_help())).parse(arena, state)?;
let (_, opt_imports, state) =
maybe!(specialize(EHeader::Imports, imports_help())).parse(arena, state)?;
let (_, provides, state) =
specialize(EHeader::Provides, provides_to()).parse(arena, state)?;
let (before_packages, after_packages, package_entries) = match opt_pkgs {
Some(pkgs) => {
@ -304,14 +269,6 @@ struct ProvidesTo<'a> {
after_to_keyword: &'a [CommentOrNewline<'a>],
}
#[inline(always)]
fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, SyntaxError<'a>> {
specialize(
|e, r, c| SyntaxError::Header(EHeader::Provides(e, r, c)),
provides_to_help(),
)
}
fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, SyntaxError<'a>> {
one_of![
map!(lowercase_ident(), To::ExistingPackage),
@ -320,7 +277,7 @@ fn provides_to_package<'a>() -> impl Parser<'a, To<'a>, SyntaxError<'a>> {
}
#[inline(always)]
fn provides_to_help<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides> {
fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, EProvides> {
let min_indent = 1;
map!(
@ -584,6 +541,7 @@ struct Packages<'a> {
after_packages_keyword: &'a [CommentOrNewline<'a>],
}
/*
#[inline(always)]
fn packages<'a>() -> impl Parser<'a, Packages<'a>, SyntaxError<'a>> {
map!(
@ -609,6 +567,52 @@ fn packages<'a>() -> impl Parser<'a, Packages<'a>, SyntaxError<'a>> {
}
)
}
*/
#[inline(always)]
fn packages<'a>() -> impl Parser<'a, Packages<'a>, SyntaxError<'a>> {
specialize(
|e, r, c| SyntaxError::Header(EHeader::Packages(e, r, c)),
packages_help(),
)
}
#[inline(always)]
fn packages_help<'a>() -> impl Parser<'a, Packages<'a>, EPackages> {
let min_indent = 1;
map!(
and!(
spaces_around_keyword(
min_indent,
"packages",
EPackages::Packages,
EPackages::Space,
EPackages::IndentPackages,
EPackages::IndentListStart
),
collection_e!(
word1(b'{', EPackages::ListStart),
specialize(
|_, r, c| EPackages::PackageEntry(r, c),
loc!(package_entry())
),
word1(b',', EPackages::ListEnd),
word1(b'}', EPackages::ListEnd),
min_indent,
EPackages::Space,
EPackages::IndentListEnd
)
),
|((before_packages_keyword, after_packages_keyword), entries)| {
Packages {
entries,
before_packages_keyword,
after_packages_keyword,
}
}
)
}
#[inline(always)]
fn imports<'a>() -> impl Parser<
@ -659,23 +663,49 @@ fn imports_help<'a>() -> impl Parser<
#[inline(always)]
fn effects<'a>() -> impl Parser<'a, Effects<'a>, SyntaxError<'a>> {
specialize(
|e, r, c| SyntaxError::Header(EHeader::Effects(e, r, c)),
effects_help(),
)
}
#[inline(always)]
fn effects_help<'a>() -> impl Parser<'a, Effects<'a>, EEffects<'a>> {
move |arena, state| {
let (_, spaces_before_effects_keyword, state) =
skip_second!(space1(0), ascii_string("effects")).parse(arena, state)?;
let (_, spaces_after_effects_keyword, state) = space1(0).parse(arena, state)?;
let min_indent = 1;
let (_, (spaces_before_effects_keyword, spaces_after_effects_keyword), state) =
spaces_around_keyword(
min_indent,
"effects",
EEffects::Effects,
EEffects::Space,
EEffects::IndentEffects,
EEffects::IndentListStart,
)
.parse(arena, state)?;
// e.g. `fx.`
let (_, type_shortname, state) =
skip_second!(lowercase_ident(), ascii_char(b'.')).parse(arena, state)?;
let (_, type_shortname, state) = skip_second!(
specialize(|_, r, c| EEffects::Shorthand(r, c), lowercase_ident()),
word1(b'.', EEffects::ShorthandDot)
)
.parse(arena, state)?;
let (_, (type_name, spaces_after_type_name), state) =
and!(uppercase_ident(), space1(0)).parse(arena, state)?;
let (_, entries, state) = collection!(
ascii_char(b'{'),
loc!(typed_ident()),
ascii_char(b','),
ascii_char(b'}'),
1
// the type name, e.g. Effects
let (_, (type_name, spaces_after_type_name), state) = and!(
specialize(|_, r, c| EEffects::TypeName(r, c), uppercase_ident()),
space0_e(min_indent, EEffects::Space, EEffects::IndentListStart)
)
.parse(arena, state)?;
let (_, entries, state) = collection_e!(
word1(b'{', EEffects::ListStart),
specialize(EEffects::TypedIdent, loc!(typed_ident_help())),
word1(b',', EEffects::ListEnd),
word1(b'}', EEffects::ListEnd),
min_indent,
EEffects::Space,
EEffects::IndentListEnd
)
.parse(arena, state)?;
@ -760,7 +790,7 @@ fn typed_ident_help<'a>() -> impl Parser<'a, TypedIdent<'a>, ETypedIdent<'a>> {
}
fn shortname<'a>() -> impl Parser<'a, &'a str, EImports> {
specialize(|_, r, c| EImports::Shortname(r, c), lowercase_ident())
specialize(|_, r, c| EImports::Shorthand(r, c), lowercase_ident())
}
fn module_name_help<'a, F, E>(to_expectation: F) -> impl Parser<'a, ModuleName<'a>, E>

View file

@ -344,9 +344,12 @@ pub enum EHeader<'a> {
Exposes(EExposes, Row, Col),
Imports(EImports, Row, Col),
Requires(ERequires<'a>, Row, Col),
Packages(EPackages, Row, Col),
Effects(EEffects<'a>, Row, Col),
Space(BadInputError, Row, Col),
ModuleName(Row, Col),
AppName(EString<'a>, Row, Col),
IndentStart(Row, Col),
}
@ -401,6 +404,33 @@ pub enum ETypedIdent<'a> {
Identifier(Row, Col),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EPackages {
Space(BadInputError, Row, Col),
Packages(Row, Col),
IndentPackages(Row, Col),
ListStart(Row, Col),
ListEnd(Row, Col),
IndentListStart(Row, Col),
IndentListEnd(Row, Col),
PackageEntry(Row, Col),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EEffects<'a> {
Space(BadInputError, Row, Col),
Effects(Row, Col),
IndentEffects(Row, Col),
ListStart(Row, Col),
ListEnd(Row, Col),
IndentListStart(Row, Col),
IndentListEnd(Row, Col),
TypedIdent(ETypedIdent<'a>, Row, Col),
ShorthandDot(Row, Col),
Shorthand(Row, Col),
TypeName(Row, Col),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EImports {
Imports(Row, Col),
@ -412,7 +442,7 @@ pub enum EImports {
Identifier(Row, Col),
ExposingDot(Row, Col),
ShorthandDot(Row, Col),
Shortname(Row, Col),
Shorthand(Row, Col),
ModuleName(Row, Col),
Space(BadInputError, Row, Col),
IndentSetStart(Row, Col),