use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CppTokenKind {
KwClass,
KwNamespace,
KwUsing,
KwVirtual,
KwPublic,
KwProtected,
KwPrivate,
KwTemplate,
KwTypename,
KwOperator,
KwThis,
KwThrow,
KwTry,
KwCatch,
KwNew,
KwDelete,
KwFriend,
KwExplicit,
KwMutable,
KwTypeid,
KwDynamicCast,
KwStaticCast,
KwReinterpretCast,
KwConstCast,
KwWcharT,
KwBool,
KwTrue,
KwFalse,
KwAnd, KwAndEq, KwBitand, KwBitor, KwCompl, KwNot, KwNotEq, KwOr, KwOrEq, KwXor, KwXorEq, KwEnum,
KwStruct,
KwUnion,
KwStatic,
KwModule,
KwTypedef,
KwConst,
KwVolatile,
KwDefault,
KwAsm,
KwContinue,
KwRegister,
KwInline,
KwAlignas,
KwAlignof,
KwChar16T,
KwChar32T,
KwConstexpr,
KwDecltype,
KwNoexcept,
KwNullptr,
KwStaticAssert,
KwThreadLocal,
KwOverride,
KwFinal,
KwAuto_,
KwInline2,
KwConcept,
KwRequires,
KwConsteval,
KwConstinit,
KwCoAwait,
KwCoYield,
KwCoReturn,
KwChar8T,
KwExport,
KwAssume,
Colon, SemiColon, Comma, Dot, LParen, RParen, LBrace, RBrace, LBracket, RBracket, Lt, Gt, Assign,
Scope, DotStar, ArrowStar, LShiftAssign, RShiftAssign, Spaceship,
Arrow, PlusPlus, MinusMinus, Plus, Minus, Star, Slash, Percent, Caret, Ampersand, Pipe, Tilde, Exclaim, LtLt, GtGt, PlusAssign, MinusAssign, StarAssign, SlashAssign, PercentAssign, CaretAssign, AmpersandAssign, PipeAssign, AmpAmp, PipePipe, EqEq, ExclaimEq, LtEq, GtEq, Question,
UserDefinedLiteralSuffix, IntegerLiteralSuffix, FloatLiteralSuffix, CharLiteralPrefix, StringLiteralPrefix, RawStringDelimiter,
LtColon, ColonGt, LtPercent, PercentGt, PercentColon, PercentColonPercentColon,
Hash, HashHash, HeaderName, PPNumeric, PPCharConst, PPString, PPEof, PPNewline, PPWhitespace, PPComment, PPOther, }
impl fmt::Display for CppTokenKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
CppTokenKind::KwClass => "class",
CppTokenKind::KwNamespace => "namespace",
CppTokenKind::KwUsing => "using",
CppTokenKind::KwVirtual => "virtual",
CppTokenKind::KwPublic => "public",
CppTokenKind::KwProtected => "protected",
CppTokenKind::KwPrivate => "private",
CppTokenKind::KwTemplate => "template",
CppTokenKind::KwTypename => "typename",
CppTokenKind::KwOperator => "operator",
CppTokenKind::KwThis => "this",
CppTokenKind::KwThrow => "throw",
CppTokenKind::KwTry => "try",
CppTokenKind::KwCatch => "catch",
CppTokenKind::KwNew => "new",
CppTokenKind::KwDelete => "delete",
CppTokenKind::KwFriend => "friend",
CppTokenKind::KwExplicit => "explicit",
CppTokenKind::KwMutable => "mutable",
CppTokenKind::KwTypeid => "typeid",
CppTokenKind::KwDynamicCast => "dynamic_cast",
CppTokenKind::KwStaticCast => "static_cast",
CppTokenKind::KwReinterpretCast => "reinterpret_cast",
CppTokenKind::KwConstCast => "const_cast",
CppTokenKind::KwWcharT => "wchar_t",
CppTokenKind::KwBool => "bool",
CppTokenKind::KwTrue => "true",
CppTokenKind::KwFalse => "false",
CppTokenKind::KwAnd => "and",
CppTokenKind::KwAndEq => "and_eq",
CppTokenKind::KwBitand => "bitand",
CppTokenKind::KwBitor => "bitor",
CppTokenKind::KwCompl => "compl",
CppTokenKind::KwNot => "not",
CppTokenKind::KwNotEq => "not_eq",
CppTokenKind::KwOr => "or",
CppTokenKind::KwOrEq => "or_eq",
CppTokenKind::KwXor => "xor",
CppTokenKind::KwXorEq => "xor_eq",
CppTokenKind::KwConst => "const",
CppTokenKind::KwVolatile => "volatile",
CppTokenKind::KwDefault => "default",
CppTokenKind::KwAsm => "asm",
CppTokenKind::KwContinue => "continue",
CppTokenKind::KwRegister => "register",
CppTokenKind::KwInline => "inline",
CppTokenKind::KwAlignas => "alignas",
CppTokenKind::KwAlignof => "alignof",
CppTokenKind::KwChar16T => "char16_t",
CppTokenKind::KwChar32T => "char32_t",
CppTokenKind::KwConstexpr => "constexpr",
CppTokenKind::KwDecltype => "decltype",
CppTokenKind::KwNoexcept => "noexcept",
CppTokenKind::KwNullptr => "nullptr",
CppTokenKind::KwStaticAssert => "static_assert",
CppTokenKind::KwThreadLocal => "thread_local",
CppTokenKind::KwOverride => "override",
CppTokenKind::KwFinal => "final",
CppTokenKind::KwAuto_ => "auto",
CppTokenKind::KwInline2 => "inline",
CppTokenKind::KwConcept => "concept",
CppTokenKind::KwRequires => "requires",
CppTokenKind::KwConsteval => "consteval",
CppTokenKind::KwConstinit => "constinit",
CppTokenKind::KwCoAwait => "co_await",
CppTokenKind::KwCoYield => "co_yield",
CppTokenKind::KwCoReturn => "co_return",
CppTokenKind::KwChar8T => "char8_t",
CppTokenKind::KwExport => "export",
CppTokenKind::KwAssume => "assume",
CppTokenKind::Colon => ":",
CppTokenKind::SemiColon => ";",
CppTokenKind::Comma => ",",
CppTokenKind::Dot => ".",
CppTokenKind::LParen => "(",
CppTokenKind::RParen => ")",
CppTokenKind::LBrace => "{",
CppTokenKind::RBrace => "}",
CppTokenKind::LBracket => "[",
CppTokenKind::RBracket => "]",
CppTokenKind::Lt => "<",
CppTokenKind::Gt => ">",
CppTokenKind::Assign => "=",
CppTokenKind::KwEnum => "enum",
CppTokenKind::KwStruct => "struct",
CppTokenKind::KwUnion => "union",
CppTokenKind::KwStatic => "static",
CppTokenKind::KwModule => "module",
CppTokenKind::KwTypedef => "typedef",
CppTokenKind::Scope => "::",
CppTokenKind::DotStar => ".*",
CppTokenKind::ArrowStar => "->*",
CppTokenKind::LShiftAssign => "<<=",
CppTokenKind::RShiftAssign => ">>=",
CppTokenKind::Spaceship => "<=>",
CppTokenKind::UserDefinedLiteralSuffix => "<ud-suffix>",
CppTokenKind::Arrow => "->",
CppTokenKind::PlusPlus => "++",
CppTokenKind::MinusMinus => "--",
CppTokenKind::Plus => "+",
CppTokenKind::Minus => "-",
CppTokenKind::Star => "*",
CppTokenKind::Slash => "/",
CppTokenKind::Percent => "%",
CppTokenKind::Caret => "^",
CppTokenKind::Ampersand => "&",
CppTokenKind::Pipe => "|",
CppTokenKind::Tilde => "~",
CppTokenKind::Exclaim => "!",
CppTokenKind::LtLt => "<<",
CppTokenKind::GtGt => ">>",
CppTokenKind::PlusAssign => "+=",
CppTokenKind::MinusAssign => "-=",
CppTokenKind::StarAssign => "*=",
CppTokenKind::SlashAssign => "/=",
CppTokenKind::PercentAssign => "%=",
CppTokenKind::CaretAssign => "^=",
CppTokenKind::AmpersandAssign => "&=",
CppTokenKind::PipeAssign => "|=",
CppTokenKind::AmpAmp => "&&",
CppTokenKind::PipePipe => "||",
CppTokenKind::EqEq => "==",
CppTokenKind::ExclaimEq => "!=",
CppTokenKind::LtEq => "<=",
CppTokenKind::GtEq => ">=",
CppTokenKind::Question => "?",
CppTokenKind::IntegerLiteralSuffix => "<integer-suffix>",
CppTokenKind::FloatLiteralSuffix => "<float-suffix>",
CppTokenKind::CharLiteralPrefix => "<char-prefix>",
CppTokenKind::StringLiteralPrefix => "<string-prefix>",
CppTokenKind::RawStringDelimiter => "<raw-delim>",
CppTokenKind::LtColon => "<:",
CppTokenKind::ColonGt => ":>",
CppTokenKind::LtPercent => "<%",
CppTokenKind::PercentGt => "%>",
CppTokenKind::PercentColon => "%:",
CppTokenKind::PercentColonPercentColon => "%:%:",
CppTokenKind::Hash => "#",
CppTokenKind::HashHash => "##",
CppTokenKind::HeaderName => "<header>",
CppTokenKind::PPNumeric => "<pp-number>",
CppTokenKind::PPCharConst => "<pp-char>",
CppTokenKind::PPString => "<pp-string>",
CppTokenKind::PPEof => "<pp-eof>",
CppTokenKind::PPNewline => "<pp-newline>",
CppTokenKind::PPWhitespace => "<pp-ws>",
CppTokenKind::PPComment => "<pp-comment>",
CppTokenKind::PPOther => "<pp-other>",
};
write!(f, "{}", s)
}
}
impl CppTokenKind {
pub fn is_keyword(&self) -> bool {
matches!(
self,
CppTokenKind::KwClass
| CppTokenKind::KwNamespace
| CppTokenKind::KwUsing
| CppTokenKind::KwVirtual
| CppTokenKind::KwPublic
| CppTokenKind::KwProtected
| CppTokenKind::KwPrivate
| CppTokenKind::KwTemplate
| CppTokenKind::KwTypename
| CppTokenKind::KwOperator
| CppTokenKind::KwThis
| CppTokenKind::KwThrow
| CppTokenKind::KwTry
| CppTokenKind::KwCatch
| CppTokenKind::KwNew
| CppTokenKind::KwDelete
| CppTokenKind::KwFriend
| CppTokenKind::KwExplicit
| CppTokenKind::KwMutable
| CppTokenKind::KwTypeid
| CppTokenKind::KwDynamicCast
| CppTokenKind::KwStaticCast
| CppTokenKind::KwReinterpretCast
| CppTokenKind::KwConstCast
| CppTokenKind::KwWcharT
| CppTokenKind::KwBool
| CppTokenKind::KwTrue
| CppTokenKind::KwFalse
| CppTokenKind::KwAnd
| CppTokenKind::KwAndEq
| CppTokenKind::KwBitand
| CppTokenKind::KwBitor
| CppTokenKind::KwCompl
| CppTokenKind::KwNot
| CppTokenKind::KwNotEq
| CppTokenKind::KwOr
| CppTokenKind::KwOrEq
| CppTokenKind::KwXor
| CppTokenKind::KwXorEq
| CppTokenKind::KwEnum
| CppTokenKind::KwStruct
| CppTokenKind::KwUnion
| CppTokenKind::KwStatic
| CppTokenKind::KwModule
| CppTokenKind::KwTypedef
| CppTokenKind::KwAlignas
| CppTokenKind::KwAlignof
| CppTokenKind::KwChar16T
| CppTokenKind::KwChar32T
| CppTokenKind::KwConstexpr
| CppTokenKind::KwDecltype
| CppTokenKind::KwNoexcept
| CppTokenKind::KwNullptr
| CppTokenKind::KwStaticAssert
| CppTokenKind::KwThreadLocal
| CppTokenKind::KwOverride
| CppTokenKind::KwFinal
| CppTokenKind::KwAuto_
| CppTokenKind::KwInline2
| CppTokenKind::KwConcept
| CppTokenKind::KwRequires
| CppTokenKind::KwConsteval
| CppTokenKind::KwConstinit
| CppTokenKind::KwCoAwait
| CppTokenKind::KwCoYield
| CppTokenKind::KwCoReturn
| CppTokenKind::KwChar8T
| CppTokenKind::KwExport
| CppTokenKind::KwAssume
)
}
pub fn is_operator(&self) -> bool {
matches!(
self,
CppTokenKind::Colon
| CppTokenKind::SemiColon
| CppTokenKind::Comma
| CppTokenKind::Dot
| CppTokenKind::LParen
| CppTokenKind::RParen
| CppTokenKind::LBrace
| CppTokenKind::RBrace
| CppTokenKind::LBracket
| CppTokenKind::RBracket
| CppTokenKind::Lt
| CppTokenKind::Gt
| CppTokenKind::Assign
| CppTokenKind::Scope
| CppTokenKind::DotStar
| CppTokenKind::ArrowStar
| CppTokenKind::LShiftAssign
| CppTokenKind::RShiftAssign
| CppTokenKind::Spaceship
| CppTokenKind::Arrow
| CppTokenKind::PlusPlus
| CppTokenKind::MinusMinus
| CppTokenKind::Plus
| CppTokenKind::Minus
| CppTokenKind::Star
| CppTokenKind::Slash
| CppTokenKind::Percent
| CppTokenKind::Caret
| CppTokenKind::Ampersand
| CppTokenKind::Pipe
| CppTokenKind::Tilde
| CppTokenKind::Exclaim
| CppTokenKind::LtLt
| CppTokenKind::GtGt
| CppTokenKind::PlusAssign
| CppTokenKind::MinusAssign
| CppTokenKind::StarAssign
| CppTokenKind::SlashAssign
| CppTokenKind::PercentAssign
| CppTokenKind::CaretAssign
| CppTokenKind::AmpersandAssign
| CppTokenKind::PipeAssign
| CppTokenKind::AmpAmp
| CppTokenKind::PipePipe
| CppTokenKind::EqEq
| CppTokenKind::ExclaimEq
| CppTokenKind::LtEq
| CppTokenKind::GtEq
| CppTokenKind::Question
)
}
pub fn is_preprocessing(&self) -> bool {
matches!(
self,
CppTokenKind::Hash
| CppTokenKind::HashHash
| CppTokenKind::HeaderName
| CppTokenKind::PPNumeric
| CppTokenKind::PPCharConst
| CppTokenKind::PPString
| CppTokenKind::PPEof
| CppTokenKind::PPNewline
| CppTokenKind::PPWhitespace
| CppTokenKind::PPComment
| CppTokenKind::PPOther
)
}
pub fn is_digraph(&self) -> bool {
matches!(
self,
CppTokenKind::LtColon
| CppTokenKind::ColonGt
| CppTokenKind::LtPercent
| CppTokenKind::PercentGt
| CppTokenKind::PercentColon
| CppTokenKind::PercentColonPercentColon
)
}
pub fn digraph_to_primary(&self) -> Option<CppTokenKind> {
match self {
CppTokenKind::LtColon => Some(CppTokenKind::LBracket),
CppTokenKind::ColonGt => Some(CppTokenKind::RBracket),
CppTokenKind::LtPercent => Some(CppTokenKind::LBrace),
CppTokenKind::PercentGt => Some(CppTokenKind::RBrace),
CppTokenKind::PercentColon => Some(CppTokenKind::Hash),
CppTokenKind::PercentColonPercentColon => Some(CppTokenKind::HashHash),
_ => None,
}
}
pub fn to_assignment_form(&self) -> Option<CppTokenKind> {
match self {
CppTokenKind::Plus => Some(CppTokenKind::PlusAssign),
CppTokenKind::Minus => Some(CppTokenKind::MinusAssign),
CppTokenKind::Star => Some(CppTokenKind::StarAssign),
CppTokenKind::Slash => Some(CppTokenKind::SlashAssign),
CppTokenKind::Percent => Some(CppTokenKind::PercentAssign),
CppTokenKind::Caret => Some(CppTokenKind::CaretAssign),
CppTokenKind::Ampersand => Some(CppTokenKind::AmpersandAssign),
CppTokenKind::Pipe => Some(CppTokenKind::PipeAssign),
CppTokenKind::LtLt => Some(CppTokenKind::LShiftAssign),
CppTokenKind::GtGt => Some(CppTokenKind::RShiftAssign),
_ => None,
}
}
pub fn from_assignment_form(&self) -> Option<CppTokenKind> {
match self {
CppTokenKind::PlusAssign => Some(CppTokenKind::Plus),
CppTokenKind::MinusAssign => Some(CppTokenKind::Minus),
CppTokenKind::StarAssign => Some(CppTokenKind::Star),
CppTokenKind::SlashAssign => Some(CppTokenKind::Slash),
CppTokenKind::PercentAssign => Some(CppTokenKind::Percent),
CppTokenKind::CaretAssign => Some(CppTokenKind::Caret),
CppTokenKind::AmpersandAssign => Some(CppTokenKind::Ampersand),
CppTokenKind::PipeAssign => Some(CppTokenKind::Pipe),
CppTokenKind::LShiftAssign => Some(CppTokenKind::LtLt),
CppTokenKind::RShiftAssign => Some(CppTokenKind::GtGt),
_ => None,
}
}
pub fn precedence(&self) -> u8 {
match self {
CppTokenKind::Scope => 18,
CppTokenKind::PlusPlus
| CppTokenKind::MinusMinus
| CppTokenKind::LParen
| CppTokenKind::LBracket
| CppTokenKind::Dot
| CppTokenKind::Arrow => 17,
CppTokenKind::Star
| CppTokenKind::Ampersand
| CppTokenKind::Plus
| CppTokenKind::Minus
| CppTokenKind::Tilde
| CppTokenKind::Exclaim => 16, CppTokenKind::Slash | CppTokenKind::Percent => 14, CppTokenKind::LtLt | CppTokenKind::GtGt => 12,
CppTokenKind::Spaceship => 11,
CppTokenKind::Lt | CppTokenKind::Gt | CppTokenKind::LtEq | CppTokenKind::GtEq => 10,
CppTokenKind::EqEq | CppTokenKind::ExclaimEq => 9,
CppTokenKind::Ampersand => 8, CppTokenKind::Caret => 7,
CppTokenKind::Pipe => 6,
CppTokenKind::AmpAmp => 5,
CppTokenKind::PipePipe => 4,
CppTokenKind::Question => 3,
CppTokenKind::Assign
| CppTokenKind::PlusAssign
| CppTokenKind::MinusAssign
| CppTokenKind::StarAssign
| CppTokenKind::SlashAssign
| CppTokenKind::PercentAssign
| CppTokenKind::LShiftAssign
| CppTokenKind::RShiftAssign
| CppTokenKind::AmpersandAssign
| CppTokenKind::CaretAssign
| CppTokenKind::PipeAssign => 2,
CppTokenKind::Comma => 1,
_ => 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum CppStandard {
Cxx98,
Cxx03,
Cxx11,
Cxx14,
Cxx17,
Cxx20,
Cxx23,
GnuXx98,
GnuXx03,
GnuXx11,
GnuXx14,
GnuXx17,
GnuXx20,
GnuXx23,
Cpp11 = 100,
Cpp14 = 101,
Cpp17 = 102,
Cpp20 = 103,
Cpp23 = 104,
}
impl CppStandard {
pub fn as_flag(&self) -> &'static str {
match self {
CppStandard::Cxx98 => "c++98",
CppStandard::Cxx03 => "c++03",
CppStandard::Cxx11 => "c++11",
CppStandard::Cxx14 => "c++14",
CppStandard::Cxx17 => "c++17",
CppStandard::Cxx20 => "c++20",
CppStandard::Cxx23 => "c++23",
CppStandard::Cpp11 => "c++11",
CppStandard::Cpp14 => "c++14",
CppStandard::Cpp17 => "c++17",
CppStandard::Cpp20 => "c++20",
CppStandard::Cpp23 => "c++23",
CppStandard::GnuXx98 => "gnu++98",
CppStandard::GnuXx03 => "gnu++03",
CppStandard::GnuXx11 => "gnu++11",
CppStandard::GnuXx14 => "gnu++14",
CppStandard::GnuXx17 => "gnu++17",
CppStandard::GnuXx20 => "gnu++20",
CppStandard::GnuXx23 => "gnu++23",
}
}
pub fn from_flag(s: &str) -> Option<CppStandard> {
match s {
"c++98" => Some(CppStandard::Cxx98),
"c++03" => Some(CppStandard::Cxx03),
"c++11" | "c++0x" => Some(CppStandard::Cxx11),
"c++14" | "c++1y" => Some(CppStandard::Cxx14),
"c++17" | "c++1z" => Some(CppStandard::Cxx17),
"c++20" | "c++2a" => Some(CppStandard::Cxx20),
"c++23" | "c++2b" => Some(CppStandard::Cxx23),
"gnu++98" => Some(CppStandard::GnuXx98),
"gnu++03" => Some(CppStandard::GnuXx03),
"gnu++11" | "gnu++0x" => Some(CppStandard::GnuXx11),
"gnu++14" | "gnu++1y" => Some(CppStandard::GnuXx14),
"gnu++17" | "gnu++1z" => Some(CppStandard::GnuXx17),
"gnu++20" | "gnu++2a" => Some(CppStandard::GnuXx20),
"gnu++23" | "gnu++2b" => Some(CppStandard::GnuXx23),
_ => None,
}
}
pub fn is_gnu(&self) -> bool {
matches!(
self,
CppStandard::GnuXx98
| CppStandard::GnuXx03
| CppStandard::GnuXx11
| CppStandard::GnuXx14
| CppStandard::GnuXx17
| CppStandard::GnuXx20
| CppStandard::GnuXx23
)
}
pub fn has_constexpr(&self) -> bool {
*self >= CppStandard::Cxx11 || *self >= CppStandard::GnuXx11
}
pub fn has_noexcept(&self) -> bool {
*self >= CppStandard::Cxx11 || *self >= CppStandard::GnuXx11
}
pub fn has_auto(&self) -> bool {
*self >= CppStandard::Cxx11 || *self >= CppStandard::GnuXx11
}
pub fn has_concepts(&self) -> bool {
*self >= CppStandard::Cxx20 || *self >= CppStandard::GnuXx20
}
pub fn has_coroutines(&self) -> bool {
*self >= CppStandard::Cxx20 || *self >= CppStandard::GnuXx20
}
pub fn has_modules(&self) -> bool {
*self >= CppStandard::Cxx20 || *self >= CppStandard::GnuXx20
}
pub fn has_spaceship(&self) -> bool {
*self >= CppStandard::Cxx20 || *self >= CppStandard::GnuXx20
}
pub fn has_bool(&self) -> bool {
true
}
pub fn has_nullptr(&self) -> bool {
*self >= CppStandard::Cxx11 || *self >= CppStandard::GnuXx11
}
pub fn has_char8_t(&self) -> bool {
*self >= CppStandard::Cxx20 || *self >= CppStandard::GnuXx20
}
}
impl fmt::Display for CppStandard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_flag())
}
}
impl Default for CppStandard {
fn default() -> Self {
CppStandard::Cxx17
}
}
pub fn cpp_keyword_lookup(s: &str) -> Option<CppTokenKind> {
match s {
"class" => Some(CppTokenKind::KwClass),
"namespace" => Some(CppTokenKind::KwNamespace),
"using" => Some(CppTokenKind::KwUsing),
"virtual" => Some(CppTokenKind::KwVirtual),
"public" => Some(CppTokenKind::KwPublic),
"protected" => Some(CppTokenKind::KwProtected),
"private" => Some(CppTokenKind::KwPrivate),
"template" => Some(CppTokenKind::KwTemplate),
"typename" => Some(CppTokenKind::KwTypename),
"operator" => Some(CppTokenKind::KwOperator),
"this" => Some(CppTokenKind::KwThis),
"throw" => Some(CppTokenKind::KwThrow),
"try" => Some(CppTokenKind::KwTry),
"catch" => Some(CppTokenKind::KwCatch),
"new" => Some(CppTokenKind::KwNew),
"delete" => Some(CppTokenKind::KwDelete),
"friend" => Some(CppTokenKind::KwFriend),
"explicit" => Some(CppTokenKind::KwExplicit),
"mutable" => Some(CppTokenKind::KwMutable),
"typeid" => Some(CppTokenKind::KwTypeid),
"dynamic_cast" => Some(CppTokenKind::KwDynamicCast),
"static_cast" => Some(CppTokenKind::KwStaticCast),
"reinterpret_cast" => Some(CppTokenKind::KwReinterpretCast),
"const_cast" => Some(CppTokenKind::KwConstCast),
"wchar_t" => Some(CppTokenKind::KwWcharT),
"bool" => Some(CppTokenKind::KwBool),
"true" => Some(CppTokenKind::KwTrue),
"false" => Some(CppTokenKind::KwFalse),
"and" => Some(CppTokenKind::KwAnd),
"and_eq" => Some(CppTokenKind::KwAndEq),
"bitand" => Some(CppTokenKind::KwBitand),
"bitor" => Some(CppTokenKind::KwBitor),
"compl" => Some(CppTokenKind::KwCompl),
"not" => Some(CppTokenKind::KwNot),
"not_eq" => Some(CppTokenKind::KwNotEq),
"or" => Some(CppTokenKind::KwOr),
"or_eq" => Some(CppTokenKind::KwOrEq),
"xor" => Some(CppTokenKind::KwXor),
"xor_eq" => Some(CppTokenKind::KwXorEq),
"alignas" => Some(CppTokenKind::KwAlignas),
"alignof" => Some(CppTokenKind::KwAlignof),
"char16_t" => Some(CppTokenKind::KwChar16T),
"char32_t" => Some(CppTokenKind::KwChar32T),
"constexpr" => Some(CppTokenKind::KwConstexpr),
"decltype" => Some(CppTokenKind::KwDecltype),
"noexcept" => Some(CppTokenKind::KwNoexcept),
"nullptr" => Some(CppTokenKind::KwNullptr),
"static_assert" => Some(CppTokenKind::KwStaticAssert),
"thread_local" => Some(CppTokenKind::KwThreadLocal),
"override" => Some(CppTokenKind::KwOverride),
"final" => Some(CppTokenKind::KwFinal),
"auto" => Some(CppTokenKind::KwAuto_),
"concept" => Some(CppTokenKind::KwConcept),
"requires" => Some(CppTokenKind::KwRequires),
"consteval" => Some(CppTokenKind::KwConsteval),
"constinit" => Some(CppTokenKind::KwConstinit),
"co_await" => Some(CppTokenKind::KwCoAwait),
"co_yield" => Some(CppTokenKind::KwCoYield),
"co_return" => Some(CppTokenKind::KwCoReturn),
"char8_t" => Some(CppTokenKind::KwChar8T),
"export" => Some(CppTokenKind::KwExport),
_ => None,
}
}
pub fn cpp_operator_lookup(s: &str) -> Option<CppTokenKind> {
match s {
"::" => Some(CppTokenKind::Scope),
".*" => Some(CppTokenKind::DotStar),
"->*" => Some(CppTokenKind::ArrowStar),
"->" => Some(CppTokenKind::Arrow),
"<<=" => Some(CppTokenKind::LShiftAssign),
">>=" => Some(CppTokenKind::RShiftAssign),
"<=>" => Some(CppTokenKind::Spaceship),
"++" => Some(CppTokenKind::PlusPlus),
"--" => Some(CppTokenKind::MinusMinus),
"<<" => Some(CppTokenKind::LtLt),
">>" => Some(CppTokenKind::GtGt),
"<=" => Some(CppTokenKind::LtEq),
">=" => Some(CppTokenKind::GtEq),
"==" => Some(CppTokenKind::EqEq),
"!=" => Some(CppTokenKind::ExclaimEq),
"&&" => Some(CppTokenKind::AmpAmp),
"||" => Some(CppTokenKind::PipePipe),
"+=" => Some(CppTokenKind::PlusAssign),
"-=" => Some(CppTokenKind::MinusAssign),
"*=" => Some(CppTokenKind::StarAssign),
"/=" => Some(CppTokenKind::SlashAssign),
"%=" => Some(CppTokenKind::PercentAssign),
"^=" => Some(CppTokenKind::CaretAssign),
"&=" => Some(CppTokenKind::AmpersandAssign),
"|=" => Some(CppTokenKind::PipeAssign),
"+" => Some(CppTokenKind::Plus),
"-" => Some(CppTokenKind::Minus),
"*" => Some(CppTokenKind::Star),
"/" => Some(CppTokenKind::Slash),
"%" => Some(CppTokenKind::Percent),
"^" => Some(CppTokenKind::Caret),
"&" => Some(CppTokenKind::Ampersand),
"|" => Some(CppTokenKind::Pipe),
"~" => Some(CppTokenKind::Tilde),
"!" => Some(CppTokenKind::Exclaim),
"=" => Some(CppTokenKind::Assign),
"<" => Some(CppTokenKind::Lt),
">" => Some(CppTokenKind::Gt),
"." => Some(CppTokenKind::Dot),
":" => Some(CppTokenKind::Colon),
";" => Some(CppTokenKind::SemiColon),
"," => Some(CppTokenKind::Comma),
"(" => Some(CppTokenKind::LParen),
")" => Some(CppTokenKind::RParen),
"{" => Some(CppTokenKind::LBrace),
"}" => Some(CppTokenKind::RBrace),
"[" => Some(CppTokenKind::LBracket),
"]" => Some(CppTokenKind::RBracket),
"?" => Some(CppTokenKind::Question),
"<:" => Some(CppTokenKind::LtColon),
":>" => Some(CppTokenKind::ColonGt),
"<%" => Some(CppTokenKind::LtPercent),
"%>" => Some(CppTokenKind::PercentGt),
"%:" => Some(CppTokenKind::PercentColon),
"%:%:" => Some(CppTokenKind::PercentColonPercentColon),
_ => None,
}
}
pub fn parse_integer_suffix(s: &str) -> Option<&str> {
let s = s.trim();
match s {
"u" | "U" => Some("u"),
"l" | "L" => Some("l"),
"ll" | "LL" | "Ll" | "lL" => Some("ll"),
"ul" | "uL" | "Ul" | "UL" => Some("ul"),
"lu" | "lU" | "Lu" | "LU" => Some("lu"),
"ull" | "uLL" | "Ull" | "ULL" => Some("ull"),
"llu" | "llU" | "LLu" | "LLU" => Some("llu"),
"z" | "Z" => Some("z"),
"uz" | "uZ" | "Uz" | "UZ" => Some("uz"),
"zu" | "zU" | "Zu" | "ZU" => Some("zu"),
_ => None,
}
}
pub fn parse_float_suffix(s: &str) -> Option<&str> {
match s {
"f" | "F" => Some("f"),
"l" | "L" => Some("l"),
"f16" | "F16" => Some("f16"),
"f32" | "F32" => Some("f32"),
"f64" | "F64" => Some("f64"),
"f128" | "F128" => Some("f128"),
"bf16" | "BF16" => Some("bf16"),
_ => None,
}
}
pub fn parse_char_prefix(s: &str) -> Option<&str> {
match s {
"u8" => Some("u8"),
"u" => Some("u"),
"U" => Some("U"),
"L" => Some("L"),
_ => None,
}
}
pub fn parse_string_prefix(s: &str) -> Option<&str> {
match s {
"u8" => Some("u8"),
"u" => Some("u"),
"U" => Some("U"),
"L" => Some("L"),
"R" => Some("R"),
"u8R" | "Ru8" => Some("u8R"),
"uR" | "Ru" => Some("uR"),
"UR" | "RU" => Some("UR"),
"LR" | "RL" => Some("LR"),
_ => None,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SourceLocation {
pub offset: usize,
pub line: usize,
pub column: usize,
}
impl SourceLocation {
pub fn new(offset: usize, line: usize, column: usize) -> Self {
Self {
offset,
line,
column,
}
}
pub fn unknown() -> Self {
Self {
offset: 0,
line: 0,
column: 0,
}
}
pub fn is_unknown(&self) -> bool {
self.line == 0
}
}
impl fmt::Display for SourceLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_unknown() {
write!(f, "<unknown location>")
} else {
write!(f, "line {}:{}", self.line, self.column)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SourceRange {
pub start: SourceLocation,
pub end: SourceLocation,
}
impl SourceRange {
pub fn new(start: SourceLocation, end: SourceLocation) -> Self {
Self { start, end }
}
pub fn unknown() -> Self {
Self {
start: SourceLocation::unknown(),
end: SourceLocation::unknown(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CppToken {
pub kind: CppTokenKind,
pub text: String,
pub location: SourceLocation,
pub from_macro: bool,
}
impl CppToken {
pub fn new(kind: CppTokenKind, text: &str, location: SourceLocation) -> Self {
Self {
kind,
text: text.to_string(),
location,
from_macro: false,
}
}
pub fn is_keyword(&self) -> bool {
self.kind.is_keyword()
}
pub fn is_operator(&self) -> bool {
self.kind.is_operator()
}
pub fn is_digraph(&self) -> bool {
self.kind.is_digraph()
}
pub fn is_preprocessing(&self) -> bool {
self.kind.is_preprocessing()
}
}
impl fmt::Display for CppToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} '{}'", self.kind, self.text)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AccessSpecifier {
Public,
Protected,
Private,
None,
}
impl AccessSpecifier {
pub fn as_str(&self) -> &'static str {
match self {
AccessSpecifier::Public => "public",
AccessSpecifier::Protected => "protected",
AccessSpecifier::Private => "private",
AccessSpecifier::None => "",
}
}
pub fn from_token(tk: CppTokenKind) -> Option<AccessSpecifier> {
match tk {
CppTokenKind::KwPublic => Some(AccessSpecifier::Public),
CppTokenKind::KwProtected => Some(AccessSpecifier::Protected),
CppTokenKind::KwPrivate => Some(AccessSpecifier::Private),
_ => None,
}
}
}
impl fmt::Display for AccessSpecifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cpp_keyword_lookup_basic() {
assert_eq!(cpp_keyword_lookup("class"), Some(CppTokenKind::KwClass));
assert_eq!(
cpp_keyword_lookup("namespace"),
Some(CppTokenKind::KwNamespace)
);
assert_eq!(cpp_keyword_lookup("virtual"), Some(CppTokenKind::KwVirtual));
assert_eq!(
cpp_keyword_lookup("template"),
Some(CppTokenKind::KwTemplate)
);
assert_eq!(cpp_keyword_lookup("nullptr"), Some(CppTokenKind::KwNullptr));
assert_eq!(
cpp_keyword_lookup("constexpr"),
Some(CppTokenKind::KwConstexpr)
);
assert_eq!(cpp_keyword_lookup("concept"), Some(CppTokenKind::KwConcept));
}
#[test]
fn test_cpp_keyword_lookup_alternative_tokens() {
assert_eq!(cpp_keyword_lookup("and"), Some(CppTokenKind::KwAnd));
assert_eq!(cpp_keyword_lookup("or"), Some(CppTokenKind::KwOr));
assert_eq!(cpp_keyword_lookup("not"), Some(CppTokenKind::KwNot));
assert_eq!(cpp_keyword_lookup("xor"), Some(CppTokenKind::KwXor));
}
#[test]
fn test_cpp_keyword_lookup_coroutines() {
assert_eq!(
cpp_keyword_lookup("co_await"),
Some(CppTokenKind::KwCoAwait)
);
assert_eq!(
cpp_keyword_lookup("co_yield"),
Some(CppTokenKind::KwCoYield)
);
assert_eq!(
cpp_keyword_lookup("co_return"),
Some(CppTokenKind::KwCoReturn)
);
}
#[test]
fn test_cpp_keyword_lookup_not_c() {
assert_eq!(cpp_keyword_lookup("if"), None);
assert_eq!(cpp_keyword_lookup("int"), None);
assert_eq!(cpp_keyword_lookup("return"), None);
}
#[test]
fn test_cpp_standard_flags() {
assert_eq!(CppStandard::Cxx17.as_flag(), "c++17");
assert_eq!(CppStandard::Cxx20.as_flag(), "c++20");
assert_eq!(CppStandard::GnuXx17.as_flag(), "gnu++17");
}
#[test]
fn test_cpp_standard_from_flag() {
assert_eq!(CppStandard::from_flag("c++17"), Some(CppStandard::Cxx17));
assert_eq!(CppStandard::from_flag("c++20"), Some(CppStandard::Cxx20));
assert_eq!(
CppStandard::from_flag("gnu++11"),
Some(CppStandard::GnuXx11)
);
assert_eq!(CppStandard::from_flag("invalid"), None);
}
#[test]
fn test_cpp_standard_features() {
let cxx17 = CppStandard::Cxx17;
assert!(cxx17.has_constexpr());
assert!(cxx17.has_noexcept());
assert!(cxx17.has_auto());
assert!(cxx17.has_nullptr());
assert!(cxx17.has_bool());
assert!(!cxx17.has_concepts());
assert!(!cxx17.has_coroutines());
assert!(!cxx17.has_modules());
assert!(!cxx17.has_char8_t());
let cxx20 = CppStandard::Cxx20;
assert!(cxx20.has_concepts());
assert!(cxx20.has_coroutines());
assert!(cxx20.has_modules());
assert!(cxx20.has_char8_t());
}
#[test]
fn test_access_specifier() {
assert_eq!(
AccessSpecifier::from_token(CppTokenKind::KwPublic),
Some(AccessSpecifier::Public)
);
assert_eq!(
AccessSpecifier::from_token(CppTokenKind::KwPrivate),
Some(AccessSpecifier::Private)
);
assert_eq!(AccessSpecifier::from_token(CppTokenKind::KwClass), None);
assert_eq!(AccessSpecifier::Public.as_str(), "public");
assert_eq!(AccessSpecifier::None.as_str(), "");
}
#[test]
fn test_cpp_token_display() {
assert_eq!(format!("{}", CppTokenKind::KwClass), "class");
assert_eq!(format!("{}", CppTokenKind::Scope), "::");
assert_eq!(format!("{}", CppTokenKind::Spaceship), "<=>");
assert_eq!(format!("{}", CppTokenKind::KwNullptr), "nullptr");
}
#[test]
fn test_operator_lookup() {
assert_eq!(cpp_operator_lookup("+"), Some(CppTokenKind::Plus));
assert_eq!(cpp_operator_lookup("::"), Some(CppTokenKind::Scope));
assert_eq!(cpp_operator_lookup("->*"), Some(CppTokenKind::ArrowStar));
assert_eq!(cpp_operator_lookup("<=>"), Some(CppTokenKind::Spaceship));
assert_eq!(cpp_operator_lookup("->"), Some(CppTokenKind::Arrow));
assert_eq!(cpp_operator_lookup("++"), Some(CppTokenKind::PlusPlus));
assert_eq!(cpp_operator_lookup("--"), Some(CppTokenKind::MinusMinus));
assert_eq!(cpp_operator_lookup("=="), Some(CppTokenKind::EqEq));
assert_eq!(cpp_operator_lookup("!="), Some(CppTokenKind::ExclaimEq));
assert_eq!(cpp_operator_lookup("&&"), Some(CppTokenKind::AmpAmp));
assert_eq!(cpp_operator_lookup("||"), Some(CppTokenKind::PipePipe));
assert_eq!(cpp_operator_lookup("<<="), Some(CppTokenKind::LShiftAssign));
assert_eq!(cpp_operator_lookup(">>="), Some(CppTokenKind::RShiftAssign));
assert_eq!(cpp_operator_lookup("invalid"), None);
}
#[test]
fn test_digraph_lookup() {
assert_eq!(cpp_operator_lookup("<:"), Some(CppTokenKind::LtColon));
assert_eq!(cpp_operator_lookup(":>"), Some(CppTokenKind::ColonGt));
assert_eq!(cpp_operator_lookup("<%"), Some(CppTokenKind::LtPercent));
assert_eq!(cpp_operator_lookup("%>"), Some(CppTokenKind::PercentGt));
assert_eq!(cpp_operator_lookup("%:"), Some(CppTokenKind::PercentColon));
assert_eq!(
cpp_operator_lookup("%:%:"),
Some(CppTokenKind::PercentColonPercentColon)
);
}
#[test]
fn test_digraph_to_primary() {
assert_eq!(
CppTokenKind::LtColon.digraph_to_primary(),
Some(CppTokenKind::LBracket)
);
assert_eq!(
CppTokenKind::ColonGt.digraph_to_primary(),
Some(CppTokenKind::RBracket)
);
assert_eq!(
CppTokenKind::LtPercent.digraph_to_primary(),
Some(CppTokenKind::LBrace)
);
assert_eq!(
CppTokenKind::PercentGt.digraph_to_primary(),
Some(CppTokenKind::RBrace)
);
assert_eq!(
CppTokenKind::PercentColon.digraph_to_primary(),
Some(CppTokenKind::Hash)
);
assert_eq!(
CppTokenKind::PercentColonPercentColon.digraph_to_primary(),
Some(CppTokenKind::HashHash)
);
assert_eq!(CppTokenKind::Plus.digraph_to_primary(), None);
}
#[test]
fn test_assignment_form_conversion() {
assert_eq!(
CppTokenKind::Plus.to_assignment_form(),
Some(CppTokenKind::PlusAssign)
);
assert_eq!(
CppTokenKind::LtLt.to_assignment_form(),
Some(CppTokenKind::LShiftAssign)
);
assert_eq!(
CppTokenKind::PlusAssign.from_assignment_form(),
Some(CppTokenKind::Plus)
);
assert_eq!(
CppTokenKind::LShiftAssign.from_assignment_form(),
Some(CppTokenKind::LtLt)
);
assert_eq!(CppTokenKind::Scope.to_assignment_form(), None);
}
#[test]
fn test_token_is_keyword() {
assert!(CppTokenKind::KwClass.is_keyword());
assert!(CppTokenKind::KwConstexpr.is_keyword());
assert!(CppTokenKind::KwConcept.is_keyword());
assert!(CppTokenKind::KwCoAwait.is_keyword());
assert!(!CppTokenKind::Plus.is_keyword());
assert!(!CppTokenKind::Scope.is_keyword());
}
#[test]
fn test_token_is_operator() {
assert!(CppTokenKind::Plus.is_operator());
assert!(CppTokenKind::Scope.is_operator());
assert!(CppTokenKind::Spaceship.is_operator());
assert!(!CppTokenKind::KwClass.is_operator());
assert!(!CppTokenKind::KwNullptr.is_operator());
}
#[test]
fn test_token_is_preprocessing() {
assert!(CppTokenKind::Hash.is_preprocessing());
assert!(CppTokenKind::HashHash.is_preprocessing());
assert!(CppTokenKind::PPNumeric.is_preprocessing());
assert!(!CppTokenKind::Plus.is_preprocessing());
assert!(!CppTokenKind::KwClass.is_preprocessing());
}
#[test]
fn test_token_is_digraph() {
assert!(CppTokenKind::LtColon.is_digraph());
assert!(CppTokenKind::PercentColon.is_digraph());
assert!(!CppTokenKind::Plus.is_digraph());
assert!(!CppTokenKind::Scope.is_digraph());
}
#[test]
fn test_precedence() {
assert_eq!(CppTokenKind::Scope.precedence(), 18);
assert_eq!(CppTokenKind::PlusPlus.precedence(), 17);
assert_eq!(CppTokenKind::Slash.precedence(), 14);
assert_eq!(CppTokenKind::Spaceship.precedence(), 11);
assert_eq!(CppTokenKind::AmpAmp.precedence(), 5);
assert_eq!(CppTokenKind::Assign.precedence(), 2);
assert_eq!(CppTokenKind::Comma.precedence(), 1);
assert_eq!(CppTokenKind::KwClass.precedence(), 0);
}
#[test]
fn test_integer_suffix_parsing() {
assert_eq!(parse_integer_suffix("u"), Some("u"));
assert_eq!(parse_integer_suffix("U"), Some("u"));
assert_eq!(parse_integer_suffix("l"), Some("l"));
assert_eq!(parse_integer_suffix("L"), Some("l"));
assert_eq!(parse_integer_suffix("ll"), Some("ll"));
assert_eq!(parse_integer_suffix("ULL"), Some("ull"));
assert_eq!(parse_integer_suffix("ul"), Some("ul"));
assert_eq!(parse_integer_suffix("z"), Some("z"));
assert_eq!(parse_integer_suffix("uz"), Some("uz"));
assert_eq!(parse_integer_suffix("xyz"), None);
assert_eq!(parse_integer_suffix("f"), None);
}
#[test]
fn test_float_suffix_parsing() {
assert_eq!(parse_float_suffix("f"), Some("f"));
assert_eq!(parse_float_suffix("F"), Some("f"));
assert_eq!(parse_float_suffix("l"), Some("l"));
assert_eq!(parse_float_suffix("L"), Some("l"));
assert_eq!(parse_float_suffix("f16"), Some("f16"));
assert_eq!(parse_float_suffix("bf16"), Some("bf16"));
assert_eq!(parse_float_suffix("xyz"), None);
}
#[test]
fn test_char_prefix() {
assert_eq!(parse_char_prefix("u8"), Some("u8"));
assert_eq!(parse_char_prefix("u"), Some("u"));
assert_eq!(parse_char_prefix("U"), Some("U"));
assert_eq!(parse_char_prefix("L"), Some("L"));
assert_eq!(parse_char_prefix("x"), None);
}
#[test]
fn test_string_prefix() {
assert_eq!(parse_string_prefix("u8"), Some("u8"));
assert_eq!(parse_string_prefix("R"), Some("R"));
assert_eq!(parse_string_prefix("u8R"), Some("u8R"));
assert_eq!(parse_string_prefix("LR"), Some("LR"));
assert_eq!(parse_string_prefix("UR"), Some("UR"));
assert_eq!(parse_string_prefix("xyz"), None);
}
#[test]
fn test_source_location() {
let loc = SourceLocation::new(42, 3, 15);
assert_eq!(loc.offset, 42);
assert_eq!(loc.line, 3);
assert_eq!(loc.column, 15);
assert!(!loc.is_unknown());
assert_eq!(format!("{}", loc), "line 3:15");
let unknown = SourceLocation::unknown();
assert!(unknown.is_unknown());
assert_eq!(format!("{}", unknown), "<unknown location>");
}
#[test]
fn test_cpp_token_struct() {
let loc = SourceLocation::new(0, 1, 1);
let token = CppToken::new(CppTokenKind::KwClass, "class", loc);
assert_eq!(token.kind, CppTokenKind::KwClass);
assert_eq!(token.text, "class");
assert!(token.is_keyword());
assert!(!token.is_operator());
assert!(!token.from_macro);
}
#[test]
fn test_new_operator_tokens() {
assert_eq!(format!("{}", CppTokenKind::Arrow), "->");
assert_eq!(format!("{}", CppTokenKind::PlusPlus), "++");
assert_eq!(format!("{}", CppTokenKind::MinusMinus), "--");
assert_eq!(format!("{}", CppTokenKind::AmpAmp), "&&");
assert_eq!(format!("{}", CppTokenKind::PipePipe), "||");
assert_eq!(format!("{}", CppTokenKind::EqEq), "==");
assert_eq!(format!("{}", CppTokenKind::LtEq), "<=");
assert_eq!(format!("{}", CppTokenKind::GtEq), ">=");
}
#[test]
fn test_pp_tokens() {
assert_eq!(format!("{}", CppTokenKind::Hash), "#");
assert_eq!(format!("{}", CppTokenKind::HashHash), "##");
assert!(CppTokenKind::PPNumeric.is_preprocessing());
assert!(CppTokenKind::PPEof.is_preprocessing());
}
}