This commit is contained in:
Folkert 2021-03-10 00:26:54 +01:00
parent 91271d0308
commit 4b8f5fb477

View file

@ -2,13 +2,10 @@ use crate::ast::{CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation};
use crate::blankspace::space0;
use crate::ident::lowercase_ident;
use crate::parser::{
ascii_char, optional, peek_utf8_char, specialize, unexpected_eof, Either, ParseResult, Parser,
Progress, Progress::*, State, SyntaxError,
ascii_char, optional, specialize, Either, 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;
@ -274,24 +271,22 @@ pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>, SyntaxError<'a>>
}
pub fn package_or_path<'a>() -> impl Parser<'a, PackageOrPath<'a>, SyntaxError<'a>> {
map!(
either!(
one_of![
map!(
specialize(
|e, r, c| SyntaxError::Expr(crate::parser::EExpr::Str(e, r, c)),
string_literal::parse()
),
PackageOrPath::Path
),
map!(
and!(
package_name(),
skip_first!(one_or_more!(ascii_char(b' ')), package_version())
)
),
|answer| {
match answer {
Either::First(str_literal) => PackageOrPath::Path(str_literal),
Either::Second((name, version)) => PackageOrPath::Package(name, version),
}
}
)
),
|(name, version)| { PackageOrPath::Package(name, version) }
)
]
}
fn package_version<'a>() -> impl Parser<'a, Version<'a>, SyntaxError<'a>> {
@ -300,45 +295,57 @@ fn package_version<'a>() -> impl Parser<'a, Version<'a>, SyntaxError<'a>> {
#[inline(always)]
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, SyntaxError<'a>> {
use encode_unicode::CharExt;
// 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 } }
)
}
|_, mut state: State<'a>| match chomp_package_part(state.bytes) {
Err(progress) => Err((progress, SyntaxError::ConditionFailed, state)),
Ok(account) => {
let mut chomped = account.len();
if let Ok(('/', width)) = char::from_utf8_slice_start(&state.bytes[chomped..]) {
chomped += width;
match chomp_package_part(&state.bytes[chomped..]) {
Err(progress) => Err((progress, SyntaxError::ConditionFailed, state)),
Ok(pkg) => {
chomped += pkg.len();
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.)
state.column += chomped as u16;
state.bytes = &state.bytes[chomped..];
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));
let value = PackageName { account, pkg };
Ok((MadeProgress, value, state))
}
}
}
Err(reason) => {
let progress = Progress::progress_when(!part_buf.is_empty());
return state.fail(arena, progress, reason);
} else {
Err((MadeProgress, SyntaxError::ConditionFailed, state))
}
}
}
Err(unexpected_eof(arena, state, 0))
}
fn chomp_package_part<'a>(buffer: &'a [u8]) -> Result<&'a str, Progress> {
use encode_unicode::CharExt;
let mut chomped = 0;
while let Ok((ch, width)) = char::from_utf8_slice_start(&buffer[chomped..]) {
if ch == '-' || ch.is_ascii_alphanumeric() {
chomped += width;
} else {
// we're done
break;
}
}
if chomped == 0 {
Err(Progress::NoProgress)
} else {
let name = unsafe { std::str::from_utf8_unchecked(&buffer[..chomped]) };
Ok(name)
}
}