use super::{ToTokens, TokenStream};
use crate::lex::{Cursor, LexError, Scan};
use crate::{Span, TokenTree};
#[macro_export]
macro_rules! ident {
($x:ident) => { $crate::Ident::lex(stringify!($x).to_string()).expect("invalid syntax") };
($x:expr) => { $crate::Ident::lex($x).expect("invalid syntax") };
($head:ident, $($tail:tt)+) => {{
let mut __ident = stringify!($head).to_string();
ident!(@accum __ident, $($tail)+)
}};
($head:expr, $($tail:tt)+) => {{
let mut __ident = $head.to_string();
ident!(@accum __ident, $($tail)+)
}};
(@accum $acc:ident, $next:ident, $($tail:tt)+) => {{
$acc += stringify!($next);
ident!(@accum $acc, $($tail)+)
}};
(@accum $acc:ident, $next:expr, $($tail:tt)+) => {{
$acc += &$next.to_string();
ident!(@accum $acc, $($tail)+)
}};
(@accum $acc:ident, $last:ident) => {{
$acc += stringify!($last);
$crate::Ident::lex($acc).expect("invalid syntax")
}};
(@accum $acc:ident, $last:expr) => {{
$acc += &$last.to_string();
$crate::Ident::lex($acc).expect("invalid syntax")
}};
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Ident {
text: Box<str>,
span: Span,
}
impl Ident {
#[inline]
pub fn new(text: impl std::fmt::Display) -> Self {
Self {
text: text.to_string().into_boxed_str(),
span: Span::default(),
}
}
#[inline]
pub fn lex(input: impl std::fmt::Display) -> Result<Self, LexError> {
std::str::FromStr::from_str(&input.to_string())
}
#[inline]
pub fn with_span(mut self, span: Span) -> Self {
self.span = span;
self
}
#[inline]
pub fn text(&self) -> &str {
match self.text.strip_prefix("r#") {
Some(rest) => rest,
None => &self.text,
}
}
#[inline]
pub fn is_raw(&self) -> bool {
self.text.starts_with("r#")
}
#[inline]
pub fn span(&self) -> Span {
self.span
}
#[inline]
pub fn set_span(&mut self, span: Span) {
self.span = span;
}
#[inline]
pub fn to_lowercase(mut self) -> Self {
self.text = self.text.to_lowercase().into_boxed_str();
self
}
#[inline]
pub fn to_uppercase(mut self) -> Self {
self.text = self.text.to_uppercase().into_boxed_str();
self
}
#[inline]
pub fn to_token_tree(&self) -> TokenTree {
TokenTree::Ident(self.clone())
}
#[inline]
pub fn into_token_tree(self) -> TokenTree {
TokenTree::Ident(self)
}
}
impl Scan for Ident {
fn scan(cursor: Cursor<'_>) -> Result<(Cursor<'_>, Self), LexError> {
if cursor.starts_with("r#") {
let after = cursor.advance(2);
let end = after.skip_while(unicode_ident::is_xid_continue);
if end.offset() == after.offset() {
return cursor.error().into();
}
let span = cursor.span_to(&end);
let name = &cursor.rest()[..end.offset() as usize - cursor.offset() as usize];
return Ok((end, Self::new(name).with_span(span)));
}
let first = cursor.first().ok_or(cursor.error())?;
if first != '_' && !unicode_ident::is_xid_start(first) {
return cursor.error().into();
}
let end = cursor.advance(first.len_utf8()).skip_while(unicode_ident::is_xid_continue);
let span = cursor.span_to(&end);
let text = &cursor.rest()[..end.offset() as usize - cursor.offset() as usize];
Ok((end, Self::new(text).with_span(span)))
}
}
impl std::fmt::Display for Ident {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.text)
}
}
impl ToTokens for Ident {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.extend_one(TokenTree::Ident(self.clone()));
}
}
impl crate::Spanner for Ident {
fn span(&self) -> Span {
self.span
}
}
impl crate::Parse for Ident {
fn parse(stream: &mut crate::parser::ParseStream) -> Result<Self, crate::parser::ParseError> {
match stream.advance().cloned() {
Some(crate::TokenTree::Ident(v)) => Ok(v.clone()),
Some(other) => Err(crate::lex::LexError::new(stream.span())
.message(format!("expected Ident, received \"{}\"", other))
.into()),
None => Err(crate::lex::LexError::new(stream.span())
.message(format!("expected Ident, received \"{}\"", "<EOF>"))
.into()),
}
}
}
impl Ident {
pub fn parse_any(stream: &mut crate::parser::ParseStream) -> Result<Self, crate::parser::ParseError> {
match stream.advance().cloned() {
Some(TokenTree::Ident(v)) => Ok(v),
Some(TokenTree::Keyword(v)) => Ok(Ident::new(v.as_str()).with_span(v.span())),
Some(other) => Err(crate::lex::LexError::new(stream.span())
.message(format!("expected Ident, received \"{}\"", other))
.into()),
None => Err(crate::lex::LexError::new(stream.span())
.message(format!("expected Ident, received \"{}\"", "<EOF>"))
.into()),
}
}
}
impl std::str::FromStr for Ident {
type Err = LexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let cursor = Cursor::new(s, 0);
let (_, ident) = Self::scan(cursor)?;
Ok(ident)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Ident {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.text.serialize(s)
}
}
impl PartialEq<str> for Ident {
fn eq(&self, other: &str) -> bool {
self.text.as_ref() == other
}
}
impl PartialEq<&str> for Ident {
fn eq(&self, other: &&str) -> bool {
self.text.as_ref() == *other
}
}
impl std::hash::Hash for Ident {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.text.hash(state);
}
}