Parse opaque references

This commit is contained in:
ayazhafiz 2022-02-19 21:28:39 -05:00
parent a38f1d1e8d
commit 6104a27b45
22 changed files with 206 additions and 25 deletions

View file

@ -38,6 +38,8 @@ pub enum Ident<'a> {
GlobalTag(&'a str),
/// @Foo or @Bar
PrivateTag(&'a str),
/// $Foo or $Bar
OpaqueRef(&'a str),
/// foo or foo.bar or Foo.Bar.baz.qux
Access {
module_name: &'a str,
@ -54,7 +56,7 @@ impl<'a> Ident<'a> {
use self::Ident::*;
match self {
GlobalTag(string) | PrivateTag(string) => string.len(),
GlobalTag(string) | PrivateTag(string) | OpaqueRef(string) => string.len(),
Access { module_name, parts } => {
let mut len = if module_name.is_empty() {
0
@ -100,7 +102,11 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, ()> {
pub fn tag_name<'a>() -> impl Parser<'a, &'a str, ()> {
move |arena, state: State<'a>| {
if state.bytes().starts_with(b"@") {
match chomp_private_tag(state.bytes(), state.pos()) {
match chomp_private_tag_or_opaque(
/* private tag */ true,
state.bytes(),
state.pos(),
) {
Err(BadIdent::Start(_)) => Err((NoProgress, (), state)),
Err(_) => Err((MadeProgress, (), state)),
Ok(ident) => {
@ -236,6 +242,7 @@ pub enum BadIdent {
WeirdDotQualified(Position),
StrayDot(Position),
BadPrivateTag(Position),
BadOpaqueRef(Position),
}
fn chomp_lowercase_part(buffer: &[u8]) -> Result<&str, Progress> {
@ -304,23 +311,33 @@ fn chomp_accessor(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> {
}
/// a `@Token` private tag
fn chomp_private_tag(buffer: &[u8], pos: Position) -> Result<&str, BadIdent> {
fn chomp_private_tag_or_opaque(
private_tag: bool, // If false, opaque
buffer: &[u8],
pos: Position,
) -> Result<&str, BadIdent> {
// assumes the leading `@` has NOT been chomped already
debug_assert_eq!(buffer.get(0), Some(&b'@'));
debug_assert_eq!(buffer.get(0), Some(if private_tag { &b'@' } else { &b'$' }));
use encode_unicode::CharExt;
let bad_ident = if private_tag {
BadIdent::BadPrivateTag
} else {
BadIdent::BadOpaqueRef
};
match chomp_uppercase_part(&buffer[1..]) {
Ok(name) => {
let width = 1 + name.len();
if let Ok(('.', _)) = char::from_utf8_slice_start(&buffer[width..]) {
Err(BadIdent::BadPrivateTag(pos.bump_column(width as u32)))
Err(bad_ident(pos.bump_column(width as u32)))
} else {
let value = unsafe { std::str::from_utf8_unchecked(&buffer[..width]) };
Ok(value)
}
}
Err(_) => Err(BadIdent::BadPrivateTag(pos.bump_column(1))),
Err(_) => Err(bad_ident(pos.bump_column(1))),
}
}
@ -344,11 +361,17 @@ fn chomp_identifier_chain<'a>(
}
Err(fail) => return Err((1, fail)),
},
'@' => match chomp_private_tag(buffer, pos) {
c @ ('@' | '$') => match chomp_private_tag_or_opaque(c == '@', buffer, pos) {
Ok(tagname) => {
let bytes_parsed = tagname.len();
return Ok((bytes_parsed as u32, Ident::PrivateTag(tagname)));
let ident = if c == '@' {
Ident::PrivateTag
} else {
Ident::OpaqueRef
};
return Ok((bytes_parsed as u32, ident(tagname)));
}
Err(fail) => return Err((1, fail)),
},