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

@ -175,6 +175,9 @@ pub enum Expr<'a> {
GlobalTag(&'a str),
PrivateTag(&'a str),
// Reference to an opaque type, e.g. $Opaq
OpaqueRef(&'a str),
// Pattern Matching
Closure(&'a [Loc<Pattern<'a>>], &'a Loc<Expr<'a>>),
/// Multiple defs in a row
@ -430,6 +433,9 @@ pub enum Pattern<'a> {
GlobalTag(&'a str),
PrivateTag(&'a str),
OpaqueRef(&'a str),
Apply(&'a Loc<Pattern<'a>>, &'a [Loc<Pattern<'a>>]),
/// This is Located<Pattern> rather than Located<str> so we can record comments
@ -482,6 +488,7 @@ impl<'a> Pattern<'a> {
match ident {
Ident::GlobalTag(string) => Pattern::GlobalTag(string),
Ident::PrivateTag(string) => Pattern::PrivateTag(string),
Ident::OpaqueRef(string) => Pattern::OpaqueRef(string),
Ident::Access { module_name, parts } => {
if parts.len() == 1 {
// This is valid iff there is no module.

View file

@ -1454,6 +1454,7 @@ fn expr_to_pattern_help<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<
Expr::Underscore(opt_name) => Ok(Pattern::Underscore(opt_name)),
Expr::GlobalTag(value) => Ok(Pattern::GlobalTag(value)),
Expr::PrivateTag(value) => Ok(Pattern::PrivateTag(value)),
Expr::OpaqueRef(value) => Ok(Pattern::OpaqueRef(value)),
Expr::Apply(loc_val, loc_args, _) => {
let region = loc_val.region;
let value = expr_to_pattern_help(arena, &loc_val.value)?;
@ -2125,6 +2126,7 @@ fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
match src {
Ident::GlobalTag(string) => Expr::GlobalTag(string),
Ident::PrivateTag(string) => Expr::PrivateTag(string),
Ident::OpaqueRef(string) => Expr::OpaqueRef(string),
Ident::Access { module_name, parts } => {
let mut iter = parts.iter();

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

View file

@ -197,31 +197,35 @@ fn loc_ident_pattern_help<'a>(
Ok((MadeProgress, loc_tag, state))
}
}
Ident::PrivateTag(tag) => {
let loc_tag = Loc {
Ident::PrivateTag(name) | Ident::OpaqueRef(name) => {
let loc_pat = Loc {
region: loc_ident.region,
value: Pattern::PrivateTag(tag),
value: if matches!(loc_ident.value, Ident::PrivateTag(..)) {
Pattern::PrivateTag(name)
} else {
Pattern::OpaqueRef(name)
},
};
// Make sure `Foo Bar 1` is parsed as `Foo (Bar) 1`, and not `Foo (Bar 1)`
// Make sure `@Foo Bar 1` is parsed as `@Foo (Bar) 1`, and not `@Foo (Bar 1)`
if can_have_arguments {
let (_, loc_args, state) =
loc_tag_pattern_args_help(min_indent).parse(arena, state)?;
if loc_args.is_empty() {
Ok((MadeProgress, loc_tag, state))
Ok((MadeProgress, loc_pat, state))
} else {
let region = Region::across_all(
std::iter::once(&loc_ident.region)
.chain(loc_args.iter().map(|loc_arg| &loc_arg.region)),
);
let value =
Pattern::Apply(&*arena.alloc(loc_tag), loc_args.into_bump_slice());
Pattern::Apply(&*arena.alloc(loc_pat), loc_args.into_bump_slice());
Ok((MadeProgress, Loc { region, value }, state))
}
} else {
Ok((MadeProgress, loc_tag, state))
Ok((MadeProgress, loc_pat, state))
}
}
Ident::Access { module_name, parts } => {