use std::borrow::Cow;
#[cfg(feature = "serde")]
use serde::Serialize;
use crate::Span;
use super::ArenaVec;
#[derive(Clone, Copy)]
pub struct Ident<'src> {
name: &'src str,
pub span: Span,
}
impl<'src> Ident<'src> {
#[inline]
pub fn new(name: &'src str, span: Span) -> Self {
debug_assert!(!name.is_empty(), "Ident::new() called with empty name");
Self { name, span }
}
#[inline]
pub const fn error(span: Span) -> Self {
Self { name: "", span }
}
#[inline]
pub fn as_str(&self) -> Option<&'src str> {
if self.name.is_empty() {
None
} else {
Some(self.name)
}
}
#[inline]
pub fn is_error(&self) -> bool {
self.name.is_empty()
}
#[inline]
pub fn or_error(&self) -> &'src str {
if self.name.is_empty() {
"<error>"
} else {
self.name
}
}
}
impl<'src> std::fmt::Debug for Ident<'src> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.name.is_empty() {
write!(f, "Ident::error({:?})", self.span)
} else {
write!(f, "Ident({:?}, {:?})", self.name, self.span)
}
}
}
impl<'src> std::fmt::Display for Ident<'src> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.or_error())
}
}
#[cfg(feature = "serde")]
impl<'src> serde::Serialize for Ident<'src> {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeStruct;
if self.name.is_empty() {
return s.serialize_none();
}
let mut st = s.serialize_struct("Ident", 2)?;
st.serialize_field("name", self.name)?;
st.serialize_field("span", &self.span)?;
st.end()
}
}
impl<'src> PartialEq<&str> for Ident<'src> {
fn eq(&self, other: &&str) -> bool {
!self.name.is_empty() && self.name == *other
}
}
#[non_exhaustive]
pub enum Name<'arena, 'src> {
Simple {
value: &'src str,
span: Span,
},
Complex {
parts: ArenaVec<'arena, &'src str>,
kind: NameKind,
span: Span,
},
Error {
span: Span,
},
}
impl<'arena, 'src> Name<'arena, 'src> {
#[inline]
pub fn span(&self) -> Span {
match self {
Self::Simple { span, .. } | Self::Complex { span, .. } | Self::Error { span } => *span,
}
}
#[inline]
pub fn kind(&self) -> NameKind {
match self {
Self::Simple { .. } => NameKind::Unqualified,
Self::Complex { kind, .. } => *kind,
Self::Error { .. } => NameKind::Error,
}
}
#[inline]
pub fn src_repr(&self, src: &'src str) -> &'src str {
match self {
Self::Simple { value, .. } => value,
Self::Complex { span, .. } => &src[span.start as usize..span.end as usize],
Self::Error { .. } => "",
}
}
#[inline]
pub fn to_string_repr(&self) -> Cow<'src, str> {
match self {
Self::Complex {
parts,
kind: NameKind::FullyQualified,
..
} => Cow::Owned(fully_qualified(parts)),
_ => self.join_parts(),
}
}
#[inline]
pub fn join_parts(&self) -> Cow<'src, str> {
match self {
Self::Simple { value, .. } => Cow::Borrowed(value),
Self::Complex { parts, .. } if parts.len() == 1 => Cow::Borrowed(parts[0]),
Self::Complex { parts, .. } => Cow::Owned(parts.join("\\")),
Self::Error { .. } => Cow::Borrowed(""),
}
}
pub fn join_parts_in(&self, src: &'src str) -> Cow<'src, str> {
let Self::Complex { parts, .. } = self else {
return self.join_parts();
};
if parts.len() == 1 {
return Cow::Borrowed(parts[0]);
}
match contiguous_in(parts, src) {
Some(joined) => Cow::Borrowed(joined),
None => Cow::Owned(parts.join("\\")),
}
}
#[inline]
pub fn parts_slice(&self) -> &[&'src str] {
match self {
Self::Simple { value, .. } => std::slice::from_ref(value),
Self::Complex { parts, .. } => parts,
Self::Error { .. } => &[],
}
}
}
pub(crate) fn fully_qualified<S: AsRef<str>>(parts: &[S]) -> String {
let mut s =
String::with_capacity(parts.iter().map(|p| p.as_ref().len() + 1).sum::<usize>() + 1);
s.push('\\');
for (i, part) in parts.iter().enumerate() {
if i > 0 {
s.push('\\');
}
s.push_str(part.as_ref());
}
s
}
fn contiguous_in<'src>(parts: &[&'src str], src: &'src str) -> Option<&'src str> {
let base = src.as_ptr() as usize;
let start = (parts.first()?.as_ptr() as usize).checked_sub(base)?;
let len = parts.iter().map(|p| p.len()).sum::<usize>() + parts.len() - 1;
let joined = src.get(start..start.checked_add(len)?)?;
let bytes = joined.as_bytes();
let mut offset = 0;
for (i, part) in parts.iter().enumerate() {
if part.as_ptr() as usize != base + start + offset {
return None;
}
offset += part.len();
if i + 1 < parts.len() {
if bytes[offset] != b'\\' {
return None;
}
offset += 1;
}
}
Some(joined)
}
impl<'arena, 'src> std::fmt::Debug for Name<'arena, 'src> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Simple { value, span } => f
.debug_struct("Name")
.field("parts", &std::slice::from_ref(value))
.field("kind", &NameKind::Unqualified)
.field("span", span)
.finish(),
Self::Complex { parts, kind, span } => f
.debug_struct("Name")
.field("parts", parts)
.field("kind", kind)
.field("span", span)
.finish(),
Self::Error { span } => {
let empty: [&str; 0] = [];
f.debug_struct("Name")
.field("parts", &empty)
.field("kind", &NameKind::Error)
.field("span", span)
.finish()
}
}
}
}
#[cfg(feature = "serde")]
impl<'arena, 'src> serde::Serialize for Name<'arena, 'src> {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeStruct;
let mut st = s.serialize_struct("Name", 3)?;
match self {
Self::Simple { value, span } => {
st.serialize_field("parts", std::slice::from_ref(value))?;
st.serialize_field("kind", &NameKind::Unqualified)?;
st.serialize_field("span", span)?;
}
Self::Complex { parts, kind, span } => {
st.serialize_field("parts", parts)?;
st.serialize_field("kind", kind)?;
st.serialize_field("span", span)?;
}
Self::Error { span } => {
let empty: [&str; 0] = [];
st.serialize_field("parts", &empty[..])?;
st.serialize_field("kind", &NameKind::Error)?;
st.serialize_field("span", span)?;
}
}
st.end()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[non_exhaustive]
pub enum NameKind {
Unqualified,
Qualified,
FullyQualified,
Relative,
Error,
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize))]
#[non_exhaustive]
pub enum BuiltinType {
Int,
Float,
String,
Bool,
Void,
Never,
Mixed,
Object,
Iterable,
Callable,
Array,
Self_,
Parent_,
Static,
Null,
True,
False,
}
impl BuiltinType {
#[inline]
pub fn as_str(self) -> &'static str {
match self {
Self::Int => "int",
Self::Float => "float",
Self::String => "string",
Self::Bool => "bool",
Self::Void => "void",
Self::Never => "never",
Self::Mixed => "mixed",
Self::Object => "object",
Self::Iterable => "iterable",
Self::Callable => "callable",
Self::Array => "array",
Self::Self_ => "self",
Self::Parent_ => "parent",
Self::Static => "static",
Self::Null => "null",
Self::True => "true",
Self::False => "false",
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct TypeHint<'arena, 'src> {
pub kind: TypeHintKind<'arena, 'src>,
pub span: Span,
}
#[derive(Debug)]
#[non_exhaustive]
pub enum TypeHintKind<'arena, 'src> {
Named(Name<'arena, 'src>),
Keyword(BuiltinType, Span),
Nullable(&'arena TypeHint<'arena, 'src>),
Union(ArenaVec<'arena, TypeHint<'arena, 'src>>),
Intersection(ArenaVec<'arena, TypeHint<'arena, 'src>>),
}
#[cfg(feature = "serde")]
impl<'arena, 'src> serde::Serialize for TypeHintKind<'arena, 'src> {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
match self {
Self::Named(name) => s.serialize_newtype_variant("TypeHintKind", 0, "Named", name),
Self::Nullable(inner) => {
s.serialize_newtype_variant("TypeHintKind", 2, "Nullable", inner)
}
Self::Union(types) => s.serialize_newtype_variant("TypeHintKind", 3, "Union", types),
Self::Intersection(types) => {
s.serialize_newtype_variant("TypeHintKind", 4, "Intersection", types)
}
Self::Keyword(builtin, span) => {
struct BuiltinNameRepr<'a>(&'a BuiltinType, &'a Span);
impl serde::Serialize for BuiltinNameRepr<'_> {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeStruct;
let mut st = s.serialize_struct("Name", 3)?;
st.serialize_field("parts", &[self.0.as_str()])?;
st.serialize_field("kind", &NameKind::Unqualified)?;
st.serialize_field("span", self.1)?;
st.end()
}
}
s.serialize_newtype_variant(
"TypeHintKind",
0,
"Named",
&BuiltinNameRepr(builtin, span),
)
}
}
}
}