use super::span::Span;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub struct Token {
pub kind: TokenKind,
pub span: Span,
}
impl Token {
#[inline]
pub const fn new(kind: TokenKind, span: Span) -> Self {
Self { kind, span }
}
#[inline]
pub fn is_eof(&self) -> bool {
matches!(self.kind, TokenKind::Eof)
}
#[inline]
pub fn is_keyword(&self, kw: Keyword) -> bool {
matches!(&self.kind, TokenKind::Keyword(k) if *k == kw)
}
#[inline]
pub fn is_ident(&self) -> bool {
matches!(self.kind, TokenKind::Ident)
}
#[inline]
pub fn is_literal(&self) -> bool {
matches!(self.kind, TokenKind::Literal { .. })
}
pub fn can_begin_expr(&self) -> bool {
match &self.kind {
TokenKind::Ident
| TokenKind::Literal { .. }
| TokenKind::OpenDelim(_)
| TokenKind::Not
| TokenKind::Minus
| TokenKind::Star
| TokenKind::And
| TokenKind::AndAnd
| TokenKind::Or
| TokenKind::OrOr => true,
TokenKind::Keyword(kw) => matches!(
kw,
Keyword::If
| Keyword::Match
| Keyword::Loop
| Keyword::While
| Keyword::For
| Keyword::Return
| Keyword::Break
| Keyword::Continue
| Keyword::Unsafe
| Keyword::Async
| Keyword::Move
| Keyword::Box
| Keyword::True
| Keyword::False
| Keyword::Self_
| Keyword::SelfType
),
_ => false,
}
}
pub fn can_begin_type(&self) -> bool {
match &self.kind {
TokenKind::Ident
| TokenKind::OpenDelim(Delimiter::Paren)
| TokenKind::OpenDelim(Delimiter::Bracket)
| TokenKind::Not
| TokenKind::Star
| TokenKind::And
| TokenKind::Question
| TokenKind::Lt => true,
TokenKind::Keyword(kw) => matches!(
kw,
Keyword::Fn
| Keyword::Unsafe
| Keyword::Extern
| Keyword::Dyn
| Keyword::Impl
| Keyword::SelfType
),
_ => false,
}
}
pub fn can_begin_pattern(&self) -> bool {
match &self.kind {
TokenKind::Ident
| TokenKind::Literal { .. }
| TokenKind::OpenDelim(Delimiter::Paren)
| TokenKind::OpenDelim(Delimiter::Bracket)
| TokenKind::And
| TokenKind::Minus
| TokenKind::DotDot
| TokenKind::DotDotDot
| TokenKind::DotDotEq => true,
TokenKind::Keyword(kw) => matches!(
kw,
Keyword::Ref | Keyword::Mut | Keyword::True | Keyword::False | Keyword::Box
),
_ => false,
}
}
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.kind)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TokenKind {
Literal {
kind: LiteralKind,
suffix: Option<Box<str>>,
},
Ident,
Lifetime,
RawIdent,
Underscore,
Keyword(Keyword),
Plus,
Minus,
Star,
Slash,
Percent,
Caret,
And,
Or,
Tilde,
Shl,
Shr,
AndAnd,
OrOr,
Not,
EqEq,
Ne,
Lt,
Le,
Gt,
Ge,
Eq,
PlusEq,
MinusEq,
StarEq,
SlashEq,
PercentEq,
CaretEq,
AndEq,
OrEq,
ShlEq,
ShrEq,
Arrow,
FatArrow,
Pipe,
ColonColon,
DotDot,
DotDotDot,
DotDotEq,
OpenDelim(Delimiter),
CloseDelim(Delimiter),
Dot,
Comma,
Colon,
Semi,
Question,
At,
Pound,
Dollar,
Eof,
Whitespace,
Comment {
is_doc: bool,
is_inner: bool,
},
DslBlock {
name: Box<str>,
},
Unknown,
}
impl TokenKind {
pub fn glue(&self, next: &TokenKind) -> Option<TokenKind> {
match (self, next) {
(TokenKind::Plus, TokenKind::Eq) => Some(TokenKind::PlusEq),
(TokenKind::Minus, TokenKind::Eq) => Some(TokenKind::MinusEq),
(TokenKind::Minus, TokenKind::Gt) => Some(TokenKind::Arrow),
(TokenKind::Star, TokenKind::Eq) => Some(TokenKind::StarEq),
(TokenKind::Slash, TokenKind::Eq) => Some(TokenKind::SlashEq),
(TokenKind::Percent, TokenKind::Eq) => Some(TokenKind::PercentEq),
(TokenKind::Caret, TokenKind::Eq) => Some(TokenKind::CaretEq),
(TokenKind::And, TokenKind::And) => Some(TokenKind::AndAnd),
(TokenKind::And, TokenKind::Eq) => Some(TokenKind::AndEq),
(TokenKind::Or, TokenKind::Or) => Some(TokenKind::OrOr),
(TokenKind::Or, TokenKind::Eq) => Some(TokenKind::OrEq),
(TokenKind::Or, TokenKind::Gt) => Some(TokenKind::Pipe),
(TokenKind::Eq, TokenKind::Eq) => Some(TokenKind::EqEq),
(TokenKind::Eq, TokenKind::Gt) => Some(TokenKind::FatArrow),
(TokenKind::Not, TokenKind::Eq) => Some(TokenKind::Ne),
(TokenKind::Lt, TokenKind::Eq) => Some(TokenKind::Le),
(TokenKind::Lt, TokenKind::Lt) => Some(TokenKind::Shl),
(TokenKind::Gt, TokenKind::Eq) => Some(TokenKind::Ge),
(TokenKind::Gt, TokenKind::Gt) => Some(TokenKind::Shr),
(TokenKind::Colon, TokenKind::Colon) => Some(TokenKind::ColonColon),
(TokenKind::Dot, TokenKind::Dot) => Some(TokenKind::DotDot),
(TokenKind::DotDot, TokenKind::Dot) => Some(TokenKind::DotDotDot),
(TokenKind::DotDot, TokenKind::Eq) => Some(TokenKind::DotDotEq),
(TokenKind::Shl, TokenKind::Eq) => Some(TokenKind::ShlEq),
(TokenKind::Shr, TokenKind::Eq) => Some(TokenKind::ShrEq),
_ => None,
}
}
}
impl fmt::Display for TokenKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TokenKind::Literal { kind, suffix } => {
write!(f, "{}", kind)?;
if let Some(s) = suffix {
write!(f, "{}", s)?;
}
Ok(())
}
TokenKind::Ident => write!(f, "identifier"),
TokenKind::Lifetime => write!(f, "lifetime"),
TokenKind::RawIdent => write!(f, "raw identifier"),
TokenKind::Keyword(kw) => write!(f, "`{}`", kw),
TokenKind::Plus => write!(f, "`+`"),
TokenKind::Minus => write!(f, "`-`"),
TokenKind::Star => write!(f, "`*`"),
TokenKind::Slash => write!(f, "`/`"),
TokenKind::Percent => write!(f, "`%`"),
TokenKind::Caret => write!(f, "`^`"),
TokenKind::And => write!(f, "`&`"),
TokenKind::Or => write!(f, "`|`"),
TokenKind::Tilde => write!(f, "`~`"),
TokenKind::Shl => write!(f, "`<<`"),
TokenKind::Shr => write!(f, "`>>`"),
TokenKind::AndAnd => write!(f, "`&&`"),
TokenKind::OrOr => write!(f, "`||`"),
TokenKind::Not => write!(f, "`!`"),
TokenKind::EqEq => write!(f, "`==`"),
TokenKind::Ne => write!(f, "`!=`"),
TokenKind::Lt => write!(f, "`<`"),
TokenKind::Le => write!(f, "`<=`"),
TokenKind::Gt => write!(f, "`>`"),
TokenKind::Ge => write!(f, "`>=`"),
TokenKind::Eq => write!(f, "`=`"),
TokenKind::PlusEq => write!(f, "`+=`"),
TokenKind::MinusEq => write!(f, "`-=`"),
TokenKind::StarEq => write!(f, "`*=`"),
TokenKind::SlashEq => write!(f, "`/=`"),
TokenKind::PercentEq => write!(f, "`%=`"),
TokenKind::CaretEq => write!(f, "`^=`"),
TokenKind::AndEq => write!(f, "`&=`"),
TokenKind::OrEq => write!(f, "`|=`"),
TokenKind::ShlEq => write!(f, "`<<=`"),
TokenKind::ShrEq => write!(f, "`>>=`"),
TokenKind::Arrow => write!(f, "`->`"),
TokenKind::FatArrow => write!(f, "`=>`"),
TokenKind::Pipe => write!(f, "`|>`"),
TokenKind::ColonColon => write!(f, "`::`"),
TokenKind::DotDot => write!(f, "`..`"),
TokenKind::DotDotDot => write!(f, "`...`"),
TokenKind::DotDotEq => write!(f, "`..=`"),
TokenKind::OpenDelim(d) => write!(f, "`{}`", d.open_char()),
TokenKind::CloseDelim(d) => write!(f, "`{}`", d.close_char()),
TokenKind::Dot => write!(f, "`.`"),
TokenKind::Comma => write!(f, "`,`"),
TokenKind::Colon => write!(f, "`:`"),
TokenKind::Semi => write!(f, "`;`"),
TokenKind::Question => write!(f, "`?`"),
TokenKind::At => write!(f, "`@`"),
TokenKind::Pound => write!(f, "`#`"),
TokenKind::Dollar => write!(f, "`$`"),
TokenKind::Eof => write!(f, "end of file"),
TokenKind::Whitespace => write!(f, "whitespace"),
TokenKind::Comment { is_doc, .. } => {
if *is_doc {
write!(f, "doc comment")
} else {
write!(f, "comment")
}
}
TokenKind::DslBlock { name } => write!(f, "`{}!`", name),
TokenKind::Unknown => write!(f, "unknown"),
TokenKind::Underscore => write!(f, "`_`"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Delimiter {
Paren,
Bracket,
Brace,
}
impl Delimiter {
#[inline]
pub const fn open_char(&self) -> char {
match self {
Delimiter::Paren => '(',
Delimiter::Bracket => '[',
Delimiter::Brace => '{',
}
}
#[inline]
pub const fn close_char(&self) -> char {
match self {
Delimiter::Paren => ')',
Delimiter::Bracket => ']',
Delimiter::Brace => '}',
}
}
pub fn from_open_char(c: char) -> Option<Self> {
match c {
'(' => Some(Delimiter::Paren),
'[' => Some(Delimiter::Bracket),
'{' => Some(Delimiter::Brace),
_ => None,
}
}
pub fn from_close_char(c: char) -> Option<Self> {
match c {
')' => Some(Delimiter::Paren),
']' => Some(Delimiter::Bracket),
'}' => Some(Delimiter::Brace),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum LiteralKind {
Int {
base: IntBase,
empty_int: bool,
},
Float {
empty_exponent: bool,
},
Char {
terminated: bool,
},
Byte {
terminated: bool,
},
Str {
terminated: bool,
},
ByteStr {
terminated: bool,
},
RawStr {
n_hashes: Option<u8>,
},
RawByteStr {
n_hashes: Option<u8>,
},
CStr {
terminated: bool,
},
FormatStr {
terminated: bool,
parts: Vec<InterpolatedPart>,
},
Bool(bool),
}
#[derive(Debug, Clone, PartialEq)]
pub enum InterpolatedPart {
Literal(String),
Expr(String),
}
impl fmt::Display for LiteralKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LiteralKind::Int { .. } => write!(f, "integer"),
LiteralKind::Float { .. } => write!(f, "float"),
LiteralKind::Char { .. } => write!(f, "char"),
LiteralKind::Byte { .. } => write!(f, "byte"),
LiteralKind::Str { .. } => write!(f, "string"),
LiteralKind::ByteStr { .. } => write!(f, "byte string"),
LiteralKind::RawStr { .. } => write!(f, "raw string"),
LiteralKind::RawByteStr { .. } => write!(f, "raw byte string"),
LiteralKind::CStr { .. } => write!(f, "C string"),
LiteralKind::FormatStr { .. } => write!(f, "format string"),
LiteralKind::Bool(b) => write!(f, "{}", b),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum IntBase {
Decimal,
Hexadecimal,
Octal,
Binary,
}
impl IntBase {
#[inline]
pub const fn radix(&self) -> u32 {
match self {
IntBase::Decimal => 10,
IntBase::Hexadecimal => 16,
IntBase::Octal => 8,
IntBase::Binary => 2,
}
}
#[inline]
pub const fn prefix(&self) -> &'static str {
match self {
IntBase::Decimal => "",
IntBase::Hexadecimal => "0x",
IntBase::Octal => "0o",
IntBase::Binary => "0b",
}
}
}
pub const INTEGER_SUFFIXES: &[&str] = &[
"i8", "i16", "i32", "i64", "i128", "isize", "u8", "u16", "u32", "u64", "u128", "usize",
];
pub const FLOAT_SUFFIXES: &[&str] = &["f32", "f64"];
pub const NUMERIC_SUFFIXES: &[&str] = &[
"i8", "i16", "i32", "i64", "i128", "isize", "u8", "u16", "u32", "u64", "u128", "usize", "f32",
"f64",
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NumericSuffixKind {
SignedInt,
UnsignedInt,
Float,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NumericSuffixValidation {
None,
Valid {
kind: NumericSuffixKind,
bits: u16,
is_size: bool,
},
Invalid(String),
}
pub fn validate_numeric_suffix(suffix: Option<&str>) -> NumericSuffixValidation {
let suffix = match suffix {
Some(s) => s,
None => return NumericSuffixValidation::None,
};
match suffix {
"i8" => NumericSuffixValidation::Valid {
kind: NumericSuffixKind::SignedInt,
bits: 8,
is_size: false,
},
"i16" => NumericSuffixValidation::Valid {
kind: NumericSuffixKind::SignedInt,
bits: 16,
is_size: false,
},
"i32" => NumericSuffixValidation::Valid {
kind: NumericSuffixKind::SignedInt,
bits: 32,
is_size: false,
},
"i64" => NumericSuffixValidation::Valid {
kind: NumericSuffixKind::SignedInt,
bits: 64,
is_size: false,
},
"i128" => NumericSuffixValidation::Valid {
kind: NumericSuffixKind::SignedInt,
bits: 128,
is_size: false,
},
"isize" => NumericSuffixValidation::Valid {
kind: NumericSuffixKind::SignedInt,
bits: 0,
is_size: true,
},
"u8" => NumericSuffixValidation::Valid {
kind: NumericSuffixKind::UnsignedInt,
bits: 8,
is_size: false,
},
"u16" => NumericSuffixValidation::Valid {
kind: NumericSuffixKind::UnsignedInt,
bits: 16,
is_size: false,
},
"u32" => NumericSuffixValidation::Valid {
kind: NumericSuffixKind::UnsignedInt,
bits: 32,
is_size: false,
},
"u64" => NumericSuffixValidation::Valid {
kind: NumericSuffixKind::UnsignedInt,
bits: 64,
is_size: false,
},
"u128" => NumericSuffixValidation::Valid {
kind: NumericSuffixKind::UnsignedInt,
bits: 128,
is_size: false,
},
"usize" => NumericSuffixValidation::Valid {
kind: NumericSuffixKind::UnsignedInt,
bits: 0,
is_size: true,
},
"f32" => NumericSuffixValidation::Valid {
kind: NumericSuffixKind::Float,
bits: 32,
is_size: false,
},
"f64" => NumericSuffixValidation::Valid {
kind: NumericSuffixKind::Float,
bits: 64,
is_size: false,
},
_ => NumericSuffixValidation::Invalid(suffix.to_string()),
}
}
pub fn is_valid_int_suffix(suffix: &str) -> bool {
INTEGER_SUFFIXES.contains(&suffix) || FLOAT_SUFFIXES.contains(&suffix)
}
pub fn is_valid_float_suffix(suffix: &str) -> bool {
FLOAT_SUFFIXES.contains(&suffix)
}
pub fn is_integer_only_suffix(suffix: &str) -> bool {
INTEGER_SUFFIXES.contains(&suffix)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DocComment {
pub kind: DocCommentKind,
pub content: String,
pub span: Span,
}
impl DocComment {
pub fn new(kind: DocCommentKind, content: String, span: Span) -> Self {
Self {
kind,
content,
span,
}
}
pub fn is_inner(&self) -> bool {
matches!(
self.kind,
DocCommentKind::InnerLine | DocCommentKind::InnerBlock
)
}
pub fn is_outer(&self) -> bool {
matches!(
self.kind,
DocCommentKind::OuterLine | DocCommentKind::OuterBlock
)
}
pub fn is_line(&self) -> bool {
matches!(
self.kind,
DocCommentKind::OuterLine | DocCommentKind::InnerLine
)
}
pub fn is_block(&self) -> bool {
matches!(
self.kind,
DocCommentKind::OuterBlock | DocCommentKind::InnerBlock
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DocCommentKind {
OuterLine,
InnerLine,
OuterBlock,
InnerBlock,
}
impl DocCommentKind {
pub const fn prefix(&self) -> &'static str {
match self {
DocCommentKind::OuterLine => "///",
DocCommentKind::InnerLine => "//!",
DocCommentKind::OuterBlock => "/**",
DocCommentKind::InnerBlock => "/*!",
}
}
}
#[derive(Debug, Clone, Default)]
pub struct DocComments {
comments: Vec<DocComment>,
}
impl DocComments {
pub fn new() -> Self {
Self::default()
}
pub fn from_vec(comments: Vec<DocComment>) -> Self {
Self { comments }
}
pub fn push(&mut self, comment: DocComment) {
self.comments.push(comment);
}
pub fn is_empty(&self) -> bool {
self.comments.is_empty()
}
pub fn len(&self) -> usize {
self.comments.len()
}
pub fn comments(&self) -> &[DocComment] {
&self.comments
}
pub fn outer_comments(&self) -> Vec<&DocComment> {
self.comments.iter().filter(|c| c.is_outer()).collect()
}
pub fn inner_comments(&self) -> Vec<&DocComment> {
self.comments.iter().filter(|c| c.is_inner()).collect()
}
pub fn to_doc_string(&self) -> String {
self.comments
.iter()
.map(|c| c.content.as_str())
.collect::<Vec<_>>()
.join("\n")
}
pub fn to_outer_doc_string(&self) -> String {
self.comments
.iter()
.filter(|c| c.is_outer())
.map(|c| c.content.as_str())
.collect::<Vec<_>>()
.join("\n")
}
pub fn to_inner_doc_string(&self) -> String {
self.comments
.iter()
.filter(|c| c.is_inner())
.map(|c| c.content.as_str())
.collect::<Vec<_>>()
.join("\n")
}
pub fn iter(&self) -> impl Iterator<Item = &DocComment> {
self.comments.iter()
}
}
impl IntoIterator for DocComments {
type Item = DocComment;
type IntoIter = std::vec::IntoIter<DocComment>;
fn into_iter(self) -> Self::IntoIter {
self.comments.into_iter()
}
}
impl<'a> IntoIterator for &'a DocComments {
type Item = &'a DocComment;
type IntoIter = std::slice::Iter<'a, DocComment>;
fn into_iter(self) -> Self::IntoIter {
self.comments.iter()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Keyword {
Fn,
Struct,
Enum,
Trait,
Impl,
Type,
Const,
Static,
Let,
Mut,
Pub,
Mod,
Module,
Use,
As,
Crate,
Super,
Self_,
SelfType,
If,
Else,
Match,
Loop,
While,
For,
In,
Break,
Continue,
Return,
True,
False,
Where,
Dyn,
Typeof,
Sizeof,
Ref,
Move,
Box,
Unsafe,
Extern,
Async,
Await,
With,
Effect,
Handle,
Resume,
Perform,
AI,
Neural,
Infer,
Macro,
MacroRules,
Abstract,
Become,
Do,
Final,
Override,
Priv,
Try,
Yield,
Union,
Default,
Auto,
}
impl Keyword {
pub const fn as_str(&self) -> &'static str {
match self {
Keyword::Fn => "fn",
Keyword::Struct => "struct",
Keyword::Enum => "enum",
Keyword::Trait => "trait",
Keyword::Impl => "impl",
Keyword::Type => "type",
Keyword::Const => "const",
Keyword::Static => "static",
Keyword::Let => "let",
Keyword::Mut => "mut",
Keyword::Pub => "pub",
Keyword::Mod => "mod",
Keyword::Module => "module",
Keyword::Use => "use",
Keyword::As => "as",
Keyword::Crate => "crate",
Keyword::Super => "super",
Keyword::Self_ => "self",
Keyword::SelfType => "Self",
Keyword::If => "if",
Keyword::Else => "else",
Keyword::Match => "match",
Keyword::Loop => "loop",
Keyword::While => "while",
Keyword::For => "for",
Keyword::In => "in",
Keyword::Break => "break",
Keyword::Continue => "continue",
Keyword::Return => "return",
Keyword::True => "true",
Keyword::False => "false",
Keyword::Where => "where",
Keyword::Dyn => "dyn",
Keyword::Typeof => "typeof",
Keyword::Sizeof => "sizeof",
Keyword::Ref => "ref",
Keyword::Move => "move",
Keyword::Box => "box",
Keyword::Unsafe => "unsafe",
Keyword::Extern => "extern",
Keyword::Async => "async",
Keyword::Await => "await",
Keyword::With => "with",
Keyword::Effect => "effect",
Keyword::Handle => "handle",
Keyword::Resume => "resume",
Keyword::Perform => "perform",
Keyword::AI => "ai",
Keyword::Neural => "neural",
Keyword::Infer => "infer",
Keyword::Macro => "macro",
Keyword::MacroRules => "macro_rules",
Keyword::Abstract => "abstract",
Keyword::Become => "become",
Keyword::Do => "do",
Keyword::Final => "final",
Keyword::Override => "override",
Keyword::Priv => "priv",
Keyword::Try => "try",
Keyword::Yield => "yield",
Keyword::Union => "union",
Keyword::Default => "default",
Keyword::Auto => "auto",
}
}
pub fn from_str(s: &str) -> Option<Keyword> {
match s {
"fn" => Some(Keyword::Fn),
"struct" => Some(Keyword::Struct),
"enum" => Some(Keyword::Enum),
"trait" => Some(Keyword::Trait),
"impl" => Some(Keyword::Impl),
"type" => Some(Keyword::Type),
"const" => Some(Keyword::Const),
"static" => Some(Keyword::Static),
"let" => Some(Keyword::Let),
"mut" => Some(Keyword::Mut),
"pub" => Some(Keyword::Pub),
"mod" => Some(Keyword::Mod),
"module" => Some(Keyword::Module),
"use" => Some(Keyword::Use),
"as" => Some(Keyword::As),
"crate" => Some(Keyword::Crate),
"super" => Some(Keyword::Super),
"self" => Some(Keyword::Self_),
"Self" => Some(Keyword::SelfType),
"if" => Some(Keyword::If),
"else" => Some(Keyword::Else),
"match" => Some(Keyword::Match),
"loop" => Some(Keyword::Loop),
"while" => Some(Keyword::While),
"for" => Some(Keyword::For),
"in" => Some(Keyword::In),
"break" => Some(Keyword::Break),
"continue" => Some(Keyword::Continue),
"return" => Some(Keyword::Return),
"true" => Some(Keyword::True),
"false" => Some(Keyword::False),
"where" => Some(Keyword::Where),
"dyn" => Some(Keyword::Dyn),
"typeof" => Some(Keyword::Typeof),
"sizeof" => Some(Keyword::Sizeof),
"ref" => Some(Keyword::Ref),
"move" => Some(Keyword::Move),
"box" => Some(Keyword::Box),
"unsafe" => Some(Keyword::Unsafe),
"extern" => Some(Keyword::Extern),
"async" => Some(Keyword::Async),
"await" => Some(Keyword::Await),
"with" => Some(Keyword::With),
"effect" => Some(Keyword::Effect),
"handle" => Some(Keyword::Handle),
"resume" => Some(Keyword::Resume),
"perform" => Some(Keyword::Perform),
"ai" => Some(Keyword::AI),
"neural" => Some(Keyword::Neural),
"infer" => Some(Keyword::Infer),
"macro" => Some(Keyword::Macro),
"macro_rules" => Some(Keyword::MacroRules),
"abstract" => Some(Keyword::Abstract),
"become" => Some(Keyword::Become),
"do" => Some(Keyword::Do),
"final" => Some(Keyword::Final),
"override" => Some(Keyword::Override),
"priv" => Some(Keyword::Priv),
"try" => Some(Keyword::Try),
"yield" => Some(Keyword::Yield),
"union" => Some(Keyword::Union),
"default" => Some(Keyword::Default),
"auto" => Some(Keyword::Auto),
_ => None,
}
}
pub const fn is_reserved(&self) -> bool {
matches!(
self,
Keyword::Abstract
| Keyword::Become
| Keyword::Do
| Keyword::Final
| Keyword::Override
| Keyword::Priv
| Keyword::Try
| Keyword::Yield
)
}
pub const fn can_be_raw(&self) -> bool {
!matches!(
self,
Keyword::Crate | Keyword::Self_ | Keyword::SelfType | Keyword::Super
)
}
}
impl fmt::Display for Keyword {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
pub const DSL_NAMES: &[&str] = &[
"sql", "regex", "math", "finance", "glsl", "hlsl", "shell", "json", "xml", "html", "css",
"graphql",
];
pub fn is_dsl_name(name: &str) -> bool {
DSL_NAMES.contains(&name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keyword_roundtrip() {
for kw in [
Keyword::Fn,
Keyword::Struct,
Keyword::Let,
Keyword::If,
Keyword::Match,
] {
let s = kw.as_str();
assert_eq!(Keyword::from_str(s), Some(kw));
}
}
#[test]
fn test_delimiter_chars() {
assert_eq!(Delimiter::Paren.open_char(), '(');
assert_eq!(Delimiter::Paren.close_char(), ')');
assert_eq!(Delimiter::from_open_char('('), Some(Delimiter::Paren));
assert_eq!(Delimiter::from_close_char(')'), Some(Delimiter::Paren));
}
#[test]
fn test_token_glue() {
assert_eq!(
TokenKind::Plus.glue(&TokenKind::Eq),
Some(TokenKind::PlusEq)
);
assert_eq!(
TokenKind::Eq.glue(&TokenKind::Gt),
Some(TokenKind::FatArrow)
);
assert_eq!(
TokenKind::Minus.glue(&TokenKind::Gt),
Some(TokenKind::Arrow)
);
}
}