#[allow(clippy::crate_in_macro_def)]
#[macro_export]
macro_rules! define_tokens {
(#is_reserved $str:tt => $warning:literal) => {
true
};
(#is_reserved $str:tt) => {
false
};
(#warning_msg $str:tt => $warning:literal) => {
Some(format!($warning, $str))
};
(#warning_msg $str:tt) => {
None
};
(#parser $parser:expr => $str:tt => $name:ident::$variant:ident => $warning:literal) => {
$parser($str)
.map_with(|_, e| {
use $crate::chumsky::LaburnumSpanExt;
($name::$variant, e.create_span(), true)
})
.boxed()
};
(#parser $parser:expr => $str:tt => $name:ident::$variant:ident) => {
$parser($str)
.map_with(|_, e| {
use $crate::chumsky::LaburnumSpanExt;
($name::$variant, e.create_span(), false)
})
.boxed()
};
(
#[$parser:expr]
Token::$token_ident:ident(
$name:ident -> [
$(
$str:tt => $variant:ident $(#[warn $warning:tt])?
),* $(,)?
]
$([ $($extra:ident),* $(,)? ])?
)
) => {
paste::paste! {
use {
crate::Token,
chumsky::prelude::*,
$crate::chumsky::LaburnumSpanExt,
crate::parser::LexerMapExtraExt,
};
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub enum $name {
$(
#[doc = "The token `" $str "`"]
$variant,
)*
$($($extra,)*)?
Error,
Unknown,
}
impl std::convert::AsRef<str> for $name {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl $name {
pub fn as_str(&self) -> &str {
match self {
$(
| $name::$variant => $str,
)*
| _ => "",
}
}
pub fn as_variant(&self) -> &str {
match self {
$(
| $name::$variant => stringify!($variant),
)*
| _ => "",
}
}
pub fn warning_message(&self) -> Option<String> {
match self {
$(
| $name::$variant => $crate::define_tokens!(#warning_msg $str $(=> $warning)?),
)*
| _ => None,
}
}
pub fn is_reserved(&self) -> bool {
match self {
$(
| $name::$variant => $crate::define_tokens!(#is_reserved $str $(=> $warning)?),
)*
| _ => false,
}
}
$(
#[doc = "Check this is `" $name "::" $variant "`"]
pub fn [<is _ $variant:snake>](&self) -> bool {
match self {
| $name::$variant => true,
| _ => false,
}
}
)*
}
impl From<&str> for $name {
fn from(value: &str) -> Self {
match value {
$(
| $str => $name::$variant,
)*
| _ => $name::Unknown,
}
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
$(
| $name::$variant => write!(f, "{}", $str),
)*
| _ => write!(f, ""),
}
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
$(
| $name::$variant => write!(f, "{}: {}", stringify!($variant), $str),
)*
| _ => write!(f, ""),
}
}
}
#[doc = "Returns a parser to match all characters in this enum and map \
them to `" $name "`, with any leading or trailing trivia."]
pub fn lexer<'src, E>() -> impl crate::Lexer<'src, E>
where
E: chumsky::error::Error<'src, &'src str>
+ chumsky::error::LabelError<'src, &'src str, chumsky::text::TextExpected<&'src str>>
+ chumsky::error::LabelError<'src, &'src str, chumsky::text::TextExpected<char>>
+ chumsky::error::LabelError<'src, &'src str, chumsky::text::TextExpected<()>>
+ 'src,
{
use chumsky::prelude::*;
crate::trivia::wrap!(
{
choice([$(
$crate::define_tokens!(
#parser
$parser => $str => $name::$variant $(=> $warning)?
)
,)*])
.boxed()
} -> |((leading, (variant, inner_span, is_reserved)), trailing), e| {
if is_reserved {
use crate::parser::LexerMapExtraExt;
e.record_token_error_with_span(
leading,
crate::TokenError::[<Reserved$name>] { [<$name:snake>]: variant },
inner_span,
trailing,
)
} else {
Token::$token_ident(leading, (variant, inner_span), trailing)
}
}
)
}
#[allow(clippy::crate_in_macro_def)]
#[macro_export]
macro_rules! [< _just_ $name:snake>] {
$(
($variant) => {{
chumsky::prelude::select! {
crate::lexer::Token::$token_ident(_, (crate::lexer::[<$name:snake>]::$name::$variant, _), _)
=> crate::lexer::[<$name:snake>]::$name::$variant
}.labelled($str).boxed()
}};
)*
}
#[doc(hidden)]
pub use [< _just_ $name:snake>] as just;
#[allow(clippy::crate_in_macro_def)]
#[macro_export]
macro_rules! [< _spanned_ $name:snake>] {
$(
($variant) => {{
chumsky::prelude::select! {
crate::lexer::Token::$token_ident(_leading, (crate::lexer::[<$name:snake>]::$name::$variant, inner_span), _trailing)
=> crate::lexer::[<$name:snake>]::[<$name Span>] {
[<$name:snake>]: crate::lexer::[<$name:snake>]::$name::$variant,
span: inner_span,
}
}
.boxed()
.labelled($str)
}};
)*
}
#[doc(hidden)]
pub use [< _spanned_ $name:snake>] as spanned;
#[allow(clippy::crate_in_macro_def)]
#[macro_export]
macro_rules! [<_spanned_unboxed_ $name:snake>] {
$(
($variant) => {{
chumsky::prelude::select! {
crate::lexer::Token::$token_ident(_leading, (crate::lexer::[<$name:snake>]::$name::$variant, inner_span), _trailing)
=> crate::lexer::[<$name:snake>]::[<$name Span>] {
[<$name:snake>]: crate::lexer::[<$name:snake>]::$name::$variant,
span: inner_span,
}
}
.labelled($str)
}};
)*
}
#[doc(hidden)]
pub use [<_spanned_unboxed_ $name:snake>] as spanned_unboxed;
#[allow(clippy::crate_in_macro_def)]
#[macro_export]
macro_rules! [<_spanned_with_trivia_ $name:snake>] {
$(
($variant) => {{
chumsky::prelude::select! {
crate::lexer::Token::$token_ident(leading, (crate::lexer::[<$name:snake>]::$name::$variant, inner_span), trailing)
=> (leading, crate::lexer::[<$name:snake>]::$name::$variant, trailing, inner_span)
}
.boxed()
.labelled($str)
}};
)*
}
#[doc(hidden)]
pub use [<_spanned_with_trivia_ $name:snake>] as spanned_with_trivia;
#[allow(clippy::crate_in_macro_def)]
#[macro_export]
macro_rules! [<_spanned_no_trivia_ $name:snake>] {
$(
($variant) => {{
chumsky::prelude::select! {
crate::lexer::Token::$token_ident(None, (crate::lexer::[<$name:snake>]::$name::$variant, inner_span), None)
=> crate::lexer::[<$name:snake>]::[<$name Span>] {
[<$name:snake>]: crate::lexer::[<$name:snake>]::$name::$variant,
span: inner_span,
}
}
.boxed()
.labelled($str)
}};
)*
}
#[doc(hidden)]
pub use [<_spanned_no_trivia_ $name:snake>] as spanned_no_trivia;
#[allow(clippy::crate_in_macro_def)]
#[macro_export]
macro_rules! [<_spanned_no_trailing_trivia_ $name:snake>] {
$(
($variant) => {{
chumsky::prelude::select! {
crate::lexer::Token::$token_ident(leading, (crate::lexer::[<$name:snake>]::$name::$variant, inner_span), None)
=> (
leading,
crate::lexer::[<$name:snake>]::[<$name Span>] {
[<$name:snake>]: crate::lexer::[<$name:snake>]::$name::$variant,
span: inner_span,
}
)
}
.boxed()
.labelled($str)
}};
)*
}
#[doc(hidden)]
pub use [<_spanned_no_trailing_trivia_ $name:snake>] as spanned_no_trailing_trivia;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct [<$name Span>] {
pub [<$name:snake>]: $name,
pub span: $crate::Span,
}
impl [<$name Span>] {
pub fn value(&self) -> $name {
self.[<$name:snake>]
}
pub fn span(&self) -> $crate::Span {
self.span.clone()
}
pub fn as_str(&self) -> &str {
self.[<$name:snake>].as_str()
}
}
impl From<($name, $crate::Span)> for [<$name Span>] {
fn from(value: ($name, $crate::Span)) -> Self {
[<$name Span>] {
[<$name:snake>]: value.0,
span: value.1,
}
}
}
impl std::fmt::Display for [<$name Span>] {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.[<$name:snake>])
}
}
impl std::fmt::Debug for [<$name Span>] {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{}Span({},{:?})",
stringify!($name),
self.[<$name:snake>],
self.span,
)
}
}
impl bluegum::Bluegum for [<$name Span>] {
fn node(&self, b: &mut bluegum::Builder) {
use owo_colors::OwoColorize;
match self.[<$name:snake>] {
| $name::Error => b.name(format!("{}", "ERROR".bright_red()).as_str()),
| _ => b.name(stringify!($name)),
};
b.field(
&format!(
"{}::{}",
stringify!($name),
self.[<$name:snake>].as_variant()
),
"",
)
.debug("span", format!("{:?}", &self.span))
.alt(&format!("{}", &self.[<$name:snake>].as_str()));
}
}
}
};
}
pub use define_tokens;