use std::sync::atomic::{AtomicU64, Ordering};
use crate::intern::{InternedStr, StringInterner};
use crate::source::SourceLocation;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub struct TokenId(pub u64);
static TOKEN_ID_COUNTER: AtomicU64 = AtomicU64::new(1);
impl TokenId {
pub fn next() -> Self {
Self(TOKEN_ID_COUNTER.fetch_add(1, Ordering::Relaxed))
}
pub const INVALID: Self = Self(0);
pub fn is_valid(&self) -> bool {
self.0 != 0
}
}
impl std::fmt::Display for TokenId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "TokenId({})", self.0)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum MacroInvocationKind {
Object,
Function {
args: Vec<Vec<Token>>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub struct MacroBeginInfo {
pub marker_id: TokenId,
pub trigger_token_id: TokenId,
pub macro_name: InternedStr,
pub kind: MacroInvocationKind,
pub call_loc: SourceLocation,
pub is_wrapped: bool,
pub preserve_call: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct MacroEndInfo {
pub begin_marker_id: TokenId,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum CommentKind {
Line,
Block,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Comment {
pub kind: CommentKind,
pub text: String,
pub loc: SourceLocation,
}
impl Comment {
pub fn new(kind: CommentKind, text: String, loc: SourceLocation) -> Self {
Self { kind, text, loc }
}
pub fn is_doc(&self) -> bool {
false
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TokenKind {
IntLit(i64),
UIntLit(u64),
FloatLit(f64),
CharLit(u8),
WideCharLit(u32),
StringLit(Vec<u8>),
WideStringLit(Vec<u32>),
Ident(InternedStr),
KwAuto,
KwExtern,
KwRegister,
KwStatic,
KwTypedef,
KwChar,
KwDouble,
KwFloat,
KwInt,
KwLong,
KwShort,
KwSigned,
KwUnsigned,
KwVoid,
KwConst,
KwVolatile,
KwRestrict,
KwStruct,
KwUnion,
KwEnum,
KwBreak,
KwCase,
KwContinue,
KwDefault,
KwDo,
KwElse,
KwFor,
KwGoto,
KwIf,
KwReturn,
KwSwitch,
KwWhile,
KwInline,
KwSizeof,
KwBool,
KwComplex,
KwImaginary,
KwAlignas,
KwAlignof,
KwAtomic,
KwGeneric,
KwNoreturn,
KwStaticAssert,
KwThreadLocal,
KwFloat16,
KwFloat32,
KwFloat64,
KwFloat128,
KwFloat32x,
KwFloat64x,
KwInt128,
KwInline2, KwInline3, KwSigned2, KwConst2, KwConst3, KwVolatile2, KwVolatile3, KwRestrict2, KwRestrict3, KwBool2, KwAlignof2, KwAlignof3, KwTypeof, KwTypeof2, KwTypeof3, KwAttribute, KwAttribute2, KwAsm, KwAsm2, KwAsm3, KwExtension,
KwThread,
Plus, Minus, Star, Slash, Percent, Amp, Pipe, Caret, Tilde, LtLt, GtGt, Bang, AmpAmp, PipePipe, Lt, Gt, LtEq, GtEq, EqEq, BangEq, Eq, PlusEq, MinusEq, StarEq, SlashEq, PercentEq, AmpEq, PipeEq, CaretEq, LtLtEq, GtGtEq, PlusPlus, MinusMinus, Question, Colon, Arrow, Dot, Ellipsis,
Comma, Semi, LParen, RParen, LBracket, RBracket, LBrace, RBrace,
Hash, HashHash, Backslash,
Eof,
Newline,
Space,
MacroBegin(Box<MacroBeginInfo>),
MacroEnd(MacroEndInfo),
}
impl TokenKind {
pub fn from_keyword(s: &str) -> Option<TokenKind> {
match s {
"auto" => Some(TokenKind::KwAuto),
"extern" => Some(TokenKind::KwExtern),
"register" => Some(TokenKind::KwRegister),
"static" => Some(TokenKind::KwStatic),
"typedef" => Some(TokenKind::KwTypedef),
"char" => Some(TokenKind::KwChar),
"double" => Some(TokenKind::KwDouble),
"float" => Some(TokenKind::KwFloat),
"int" => Some(TokenKind::KwInt),
"long" => Some(TokenKind::KwLong),
"short" => Some(TokenKind::KwShort),
"signed" => Some(TokenKind::KwSigned),
"unsigned" => Some(TokenKind::KwUnsigned),
"void" => Some(TokenKind::KwVoid),
"const" => Some(TokenKind::KwConst),
"volatile" => Some(TokenKind::KwVolatile),
"restrict" => Some(TokenKind::KwRestrict),
"struct" => Some(TokenKind::KwStruct),
"union" => Some(TokenKind::KwUnion),
"enum" => Some(TokenKind::KwEnum),
"break" => Some(TokenKind::KwBreak),
"case" => Some(TokenKind::KwCase),
"continue" => Some(TokenKind::KwContinue),
"default" => Some(TokenKind::KwDefault),
"do" => Some(TokenKind::KwDo),
"else" => Some(TokenKind::KwElse),
"for" => Some(TokenKind::KwFor),
"goto" => Some(TokenKind::KwGoto),
"if" => Some(TokenKind::KwIf),
"return" => Some(TokenKind::KwReturn),
"switch" => Some(TokenKind::KwSwitch),
"while" => Some(TokenKind::KwWhile),
"inline" => Some(TokenKind::KwInline),
"__inline" => Some(TokenKind::KwInline2),
"__inline__" => Some(TokenKind::KwInline3),
"sizeof" => Some(TokenKind::KwSizeof),
"_Bool" => Some(TokenKind::KwBool),
"bool" => Some(TokenKind::KwBool2),
"_Complex" => Some(TokenKind::KwComplex),
"_Imaginary" => Some(TokenKind::KwImaginary),
"_Alignas" => Some(TokenKind::KwAlignas),
"_Alignof" => Some(TokenKind::KwAlignof),
"__alignof" => Some(TokenKind::KwAlignof2),
"__alignof__" => Some(TokenKind::KwAlignof3),
"_Atomic" => Some(TokenKind::KwAtomic),
"_Generic" => Some(TokenKind::KwGeneric),
"_Noreturn" => Some(TokenKind::KwNoreturn),
"_Static_assert" => Some(TokenKind::KwStaticAssert),
"_Thread_local" => Some(TokenKind::KwThreadLocal),
"__thread" => Some(TokenKind::KwThread),
"_Float16" => Some(TokenKind::KwFloat16),
"_Float32" => Some(TokenKind::KwFloat32),
"_Float64" => Some(TokenKind::KwFloat64),
"_Float128" => Some(TokenKind::KwFloat128),
"_Float32x" => Some(TokenKind::KwFloat32x),
"_Float64x" => Some(TokenKind::KwFloat64x),
"__int128" => Some(TokenKind::KwInt128),
"__signed__" => Some(TokenKind::KwSigned2),
"__const" => Some(TokenKind::KwConst2),
"__const__" => Some(TokenKind::KwConst3),
"__volatile" => Some(TokenKind::KwVolatile2),
"__volatile__" => Some(TokenKind::KwVolatile3),
"__restrict" => Some(TokenKind::KwRestrict2),
"__restrict__" => Some(TokenKind::KwRestrict3),
"typeof" => Some(TokenKind::KwTypeof),
"__typeof" => Some(TokenKind::KwTypeof2),
"__typeof__" => Some(TokenKind::KwTypeof3),
"__attribute" => Some(TokenKind::KwAttribute),
"__attribute__" => Some(TokenKind::KwAttribute2),
"asm" => Some(TokenKind::KwAsm),
"__asm" => Some(TokenKind::KwAsm2),
"__asm__" => Some(TokenKind::KwAsm3),
"__extension__" => Some(TokenKind::KwExtension),
_ => None,
}
}
pub fn is_keyword(&self) -> bool {
matches!(
self,
TokenKind::KwAuto
| TokenKind::KwBreak
| TokenKind::KwCase
| TokenKind::KwChar
| TokenKind::KwConst
| TokenKind::KwConst2
| TokenKind::KwConst3
| TokenKind::KwContinue
| TokenKind::KwDefault
| TokenKind::KwDo
| TokenKind::KwDouble
| TokenKind::KwElse
| TokenKind::KwEnum
| TokenKind::KwExtern
| TokenKind::KwFloat
| TokenKind::KwFor
| TokenKind::KwGoto
| TokenKind::KwIf
| TokenKind::KwInline
| TokenKind::KwInline2
| TokenKind::KwInline3
| TokenKind::KwInt
| TokenKind::KwLong
| TokenKind::KwRegister
| TokenKind::KwRestrict
| TokenKind::KwRestrict2
| TokenKind::KwRestrict3
| TokenKind::KwReturn
| TokenKind::KwShort
| TokenKind::KwSigned
| TokenKind::KwSigned2
| TokenKind::KwSizeof
| TokenKind::KwStatic
| TokenKind::KwStruct
| TokenKind::KwSwitch
| TokenKind::KwTypedef
| TokenKind::KwUnion
| TokenKind::KwUnsigned
| TokenKind::KwVoid
| TokenKind::KwVolatile
| TokenKind::KwVolatile2
| TokenKind::KwVolatile3
| TokenKind::KwWhile
| TokenKind::KwBool
| TokenKind::KwBool2
| TokenKind::KwComplex
| TokenKind::KwImaginary
| TokenKind::KwAlignas
| TokenKind::KwAlignof
| TokenKind::KwAlignof2
| TokenKind::KwAlignof3
| TokenKind::KwAtomic
| TokenKind::KwGeneric
| TokenKind::KwNoreturn
| TokenKind::KwStaticAssert
| TokenKind::KwThreadLocal
| TokenKind::KwTypeof
| TokenKind::KwTypeof2
| TokenKind::KwTypeof3
| TokenKind::KwAttribute
| TokenKind::KwAttribute2
| TokenKind::KwAsm
| TokenKind::KwAsm2
| TokenKind::KwAsm3
| TokenKind::KwExtension
| TokenKind::KwThread
| TokenKind::KwInt128
)
}
pub fn keyword_str(&self) -> Option<&'static str> {
match self {
TokenKind::KwAuto => Some("auto"),
TokenKind::KwExtern => Some("extern"),
TokenKind::KwRegister => Some("register"),
TokenKind::KwStatic => Some("static"),
TokenKind::KwTypedef => Some("typedef"),
TokenKind::KwChar => Some("char"),
TokenKind::KwDouble => Some("double"),
TokenKind::KwFloat => Some("float"),
TokenKind::KwInt => Some("int"),
TokenKind::KwLong => Some("long"),
TokenKind::KwShort => Some("short"),
TokenKind::KwSigned => Some("signed"),
TokenKind::KwUnsigned => Some("unsigned"),
TokenKind::KwVoid => Some("void"),
TokenKind::KwConst => Some("const"),
TokenKind::KwVolatile => Some("volatile"),
TokenKind::KwRestrict => Some("restrict"),
TokenKind::KwStruct => Some("struct"),
TokenKind::KwUnion => Some("union"),
TokenKind::KwEnum => Some("enum"),
TokenKind::KwBreak => Some("break"),
TokenKind::KwCase => Some("case"),
TokenKind::KwContinue => Some("continue"),
TokenKind::KwDefault => Some("default"),
TokenKind::KwDo => Some("do"),
TokenKind::KwElse => Some("else"),
TokenKind::KwFor => Some("for"),
TokenKind::KwGoto => Some("goto"),
TokenKind::KwIf => Some("if"),
TokenKind::KwReturn => Some("return"),
TokenKind::KwSwitch => Some("switch"),
TokenKind::KwWhile => Some("while"),
TokenKind::KwInline => Some("inline"),
TokenKind::KwSizeof => Some("sizeof"),
TokenKind::KwBool => Some("_Bool"),
TokenKind::KwComplex => Some("_Complex"),
TokenKind::KwImaginary => Some("_Imaginary"),
TokenKind::KwAlignas => Some("_Alignas"),
TokenKind::KwAlignof => Some("_Alignof"),
TokenKind::KwAtomic => Some("_Atomic"),
TokenKind::KwGeneric => Some("_Generic"),
TokenKind::KwNoreturn => Some("_Noreturn"),
TokenKind::KwStaticAssert => Some("_Static_assert"),
TokenKind::KwThreadLocal => Some("_Thread_local"),
TokenKind::KwFloat16 => Some("_Float16"),
TokenKind::KwFloat32 => Some("_Float32"),
TokenKind::KwFloat64 => Some("_Float64"),
TokenKind::KwFloat128 => Some("_Float128"),
TokenKind::KwFloat32x => Some("_Float32x"),
TokenKind::KwFloat64x => Some("_Float64x"),
TokenKind::KwInline2 => Some("__inline"),
TokenKind::KwInline3 => Some("__inline__"),
TokenKind::KwSigned2 => Some("__signed__"),
TokenKind::KwConst2 => Some("__const"),
TokenKind::KwConst3 => Some("__const__"),
TokenKind::KwVolatile2 => Some("__volatile"),
TokenKind::KwVolatile3 => Some("__volatile__"),
TokenKind::KwRestrict2 => Some("__restrict"),
TokenKind::KwRestrict3 => Some("__restrict__"),
TokenKind::KwBool2 => Some("bool"),
TokenKind::KwAlignof2 => Some("__alignof"),
TokenKind::KwAlignof3 => Some("__alignof__"),
TokenKind::KwTypeof => Some("typeof"),
TokenKind::KwTypeof2 => Some("__typeof"),
TokenKind::KwTypeof3 => Some("__typeof__"),
TokenKind::KwAttribute => Some("__attribute"),
TokenKind::KwAttribute2 => Some("__attribute__"),
TokenKind::KwAsm => Some("asm"),
TokenKind::KwAsm2 => Some("__asm"),
TokenKind::KwAsm3 => Some("__asm__"),
TokenKind::KwExtension => Some("__extension__"),
TokenKind::KwThread => Some("__thread"),
TokenKind::KwInt128 => Some("__int128"),
_ => None,
}
}
pub fn format(&self, interner: &StringInterner) -> String {
match self {
TokenKind::Ident(id) => interner.get(*id).to_string(),
TokenKind::IntLit(n) => n.to_string(),
TokenKind::UIntLit(n) => format!("{}u", n),
TokenKind::FloatLit(f) => f.to_string(),
TokenKind::CharLit(c) => format!("'{}'", escape_char(*c)),
TokenKind::WideCharLit(c) => format!("L'{}'", escape_wide_char(*c)),
TokenKind::StringLit(s) => format!("\"{}\"", escape_string(s)),
TokenKind::WideStringLit(s) => format!("L\"{}\"", escape_wide_string(s)),
TokenKind::KwAuto => "auto".to_string(),
TokenKind::KwExtern => "extern".to_string(),
TokenKind::KwRegister => "register".to_string(),
TokenKind::KwStatic => "static".to_string(),
TokenKind::KwTypedef => "typedef".to_string(),
TokenKind::KwChar => "char".to_string(),
TokenKind::KwDouble => "double".to_string(),
TokenKind::KwFloat => "float".to_string(),
TokenKind::KwInt => "int".to_string(),
TokenKind::KwLong => "long".to_string(),
TokenKind::KwShort => "short".to_string(),
TokenKind::KwSigned => "signed".to_string(),
TokenKind::KwUnsigned => "unsigned".to_string(),
TokenKind::KwVoid => "void".to_string(),
TokenKind::KwConst => "const".to_string(),
TokenKind::KwVolatile => "volatile".to_string(),
TokenKind::KwRestrict => "restrict".to_string(),
TokenKind::KwStruct => "struct".to_string(),
TokenKind::KwUnion => "union".to_string(),
TokenKind::KwEnum => "enum".to_string(),
TokenKind::KwBreak => "break".to_string(),
TokenKind::KwCase => "case".to_string(),
TokenKind::KwContinue => "continue".to_string(),
TokenKind::KwDefault => "default".to_string(),
TokenKind::KwDo => "do".to_string(),
TokenKind::KwElse => "else".to_string(),
TokenKind::KwFor => "for".to_string(),
TokenKind::KwGoto => "goto".to_string(),
TokenKind::KwIf => "if".to_string(),
TokenKind::KwReturn => "return".to_string(),
TokenKind::KwSwitch => "switch".to_string(),
TokenKind::KwWhile => "while".to_string(),
TokenKind::KwInline => "inline".to_string(),
TokenKind::KwSizeof => "sizeof".to_string(),
TokenKind::KwBool => "_Bool".to_string(),
TokenKind::KwComplex => "_Complex".to_string(),
TokenKind::KwImaginary => "_Imaginary".to_string(),
TokenKind::KwAlignas => "_Alignas".to_string(),
TokenKind::KwAlignof => "_Alignof".to_string(),
TokenKind::KwAtomic => "_Atomic".to_string(),
TokenKind::KwGeneric => "_Generic".to_string(),
TokenKind::KwNoreturn => "_Noreturn".to_string(),
TokenKind::KwStaticAssert => "_Static_assert".to_string(),
TokenKind::KwThreadLocal => "_Thread_local".to_string(),
TokenKind::KwFloat16 => "_Float16".to_string(),
TokenKind::KwFloat32 => "_Float32".to_string(),
TokenKind::KwFloat64 => "_Float64".to_string(),
TokenKind::KwFloat128 => "_Float128".to_string(),
TokenKind::KwFloat32x => "_Float32x".to_string(),
TokenKind::KwFloat64x => "_Float64x".to_string(),
TokenKind::KwInline2 => "__inline".to_string(),
TokenKind::KwInline3 => "__inline__".to_string(),
TokenKind::KwSigned2 => "__signed__".to_string(),
TokenKind::KwConst2 => "__const".to_string(),
TokenKind::KwConst3 => "__const__".to_string(),
TokenKind::KwVolatile2 => "__volatile".to_string(),
TokenKind::KwVolatile3 => "__volatile__".to_string(),
TokenKind::KwRestrict2 => "__restrict".to_string(),
TokenKind::KwRestrict3 => "__restrict__".to_string(),
TokenKind::KwBool2 => "bool".to_string(),
TokenKind::KwAlignof2 => "__alignof".to_string(),
TokenKind::KwAlignof3 => "__alignof__".to_string(),
TokenKind::KwTypeof => "typeof".to_string(),
TokenKind::KwTypeof2 => "__typeof".to_string(),
TokenKind::KwTypeof3 => "__typeof__".to_string(),
TokenKind::KwAttribute => "__attribute".to_string(),
TokenKind::KwAttribute2 => "__attribute__".to_string(),
TokenKind::KwAsm => "asm".to_string(),
TokenKind::KwAsm2 => "__asm".to_string(),
TokenKind::KwAsm3 => "__asm__".to_string(),
TokenKind::KwExtension => "__extension__".to_string(),
TokenKind::KwThread => "__thread".to_string(),
TokenKind::KwInt128 => "__int128".to_string(),
TokenKind::Plus => "+".to_string(),
TokenKind::Minus => "-".to_string(),
TokenKind::Star => "*".to_string(),
TokenKind::Slash => "/".to_string(),
TokenKind::Percent => "%".to_string(),
TokenKind::Amp => "&".to_string(),
TokenKind::Pipe => "|".to_string(),
TokenKind::Caret => "^".to_string(),
TokenKind::Tilde => "~".to_string(),
TokenKind::LtLt => "<<".to_string(),
TokenKind::GtGt => ">>".to_string(),
TokenKind::Bang => "!".to_string(),
TokenKind::AmpAmp => "&&".to_string(),
TokenKind::PipePipe => "||".to_string(),
TokenKind::Lt => "<".to_string(),
TokenKind::Gt => ">".to_string(),
TokenKind::LtEq => "<=".to_string(),
TokenKind::GtEq => ">=".to_string(),
TokenKind::EqEq => "==".to_string(),
TokenKind::BangEq => "!=".to_string(),
TokenKind::Eq => "=".to_string(),
TokenKind::PlusEq => "+=".to_string(),
TokenKind::MinusEq => "-=".to_string(),
TokenKind::StarEq => "*=".to_string(),
TokenKind::SlashEq => "/=".to_string(),
TokenKind::PercentEq => "%=".to_string(),
TokenKind::AmpEq => "&=".to_string(),
TokenKind::PipeEq => "|=".to_string(),
TokenKind::CaretEq => "^=".to_string(),
TokenKind::LtLtEq => "<<=".to_string(),
TokenKind::GtGtEq => ">>=".to_string(),
TokenKind::PlusPlus => "++".to_string(),
TokenKind::MinusMinus => "--".to_string(),
TokenKind::Question => "?".to_string(),
TokenKind::Colon => ":".to_string(),
TokenKind::Arrow => "->".to_string(),
TokenKind::Dot => ".".to_string(),
TokenKind::Ellipsis => "...".to_string(),
TokenKind::Comma => ",".to_string(),
TokenKind::Semi => ";".to_string(),
TokenKind::LParen => "(".to_string(),
TokenKind::RParen => ")".to_string(),
TokenKind::LBracket => "[".to_string(),
TokenKind::RBracket => "]".to_string(),
TokenKind::LBrace => "{".to_string(),
TokenKind::RBrace => "}".to_string(),
TokenKind::Hash => "#".to_string(),
TokenKind::HashHash => "##".to_string(),
TokenKind::Backslash => "\\".to_string(),
TokenKind::Newline => "\n".to_string(),
TokenKind::Eof => "".to_string(),
TokenKind::Space => " ".to_string(),
TokenKind::MacroBegin(info) => {
format!("/*<MACRO_BEGIN:{}>*/", interner.get(info.macro_name))
}
TokenKind::MacroEnd(info) => {
format!("/*<MACRO_END:{}>*/", info.begin_marker_id)
}
}
}
}
fn escape_char(c: u8) -> String {
match c {
b'\n' => "\\n".to_string(),
b'\r' => "\\r".to_string(),
b'\t' => "\\t".to_string(),
b'\\' => "\\\\".to_string(),
b'\'' => "\\'".to_string(),
c if c.is_ascii_graphic() || c == b' ' => (c as char).to_string(),
c => format!("\\x{:02x}", c),
}
}
fn escape_wide_char(c: u32) -> String {
if let Some(ch) = char::from_u32(c) {
match ch {
'\n' => "\\n".to_string(),
'\r' => "\\r".to_string(),
'\t' => "\\t".to_string(),
'\\' => "\\\\".to_string(),
'\'' => "\\'".to_string(),
c if c.is_ascii_graphic() || c == ' ' => c.to_string(),
c if c as u32 <= 0xFFFF => format!("\\u{:04x}", c as u32),
c => format!("\\U{:08x}", c as u32),
}
} else {
format!("\\U{:08x}", c)
}
}
fn escape_string(s: &[u8]) -> String {
s.iter().map(|&c| escape_char(c)).collect()
}
fn escape_wide_string(s: &[u32]) -> String {
s.iter().map(|&c| escape_wide_char(c)).collect()
}
#[derive(Debug, Clone, PartialEq)]
pub struct Token {
pub id: TokenId,
pub kind: TokenKind,
pub loc: SourceLocation,
pub leading_comments: Vec<Comment>,
}
impl Default for Token {
fn default() -> Self {
Self {
id: TokenId::default(),
kind: TokenKind::Eof,
loc: SourceLocation::default(),
leading_comments: Vec::new(),
}
}
}
impl Token {
pub fn new(kind: TokenKind, loc: SourceLocation) -> Self {
Self {
id: TokenId::next(),
kind,
loc,
leading_comments: Vec::new(),
}
}
pub fn with_comments(kind: TokenKind, loc: SourceLocation, comments: Vec<Comment>) -> Self {
Self {
id: TokenId::next(),
kind,
loc,
leading_comments: comments,
}
}
pub fn clone_with_new_id(&self) -> Self {
Self {
id: TokenId::next(),
kind: self.kind.clone(),
loc: self.loc.clone(),
leading_comments: self.leading_comments.clone(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keyword_lookup() {
assert_eq!(TokenKind::from_keyword("int"), Some(TokenKind::KwInt));
assert_eq!(TokenKind::from_keyword("if"), Some(TokenKind::KwIf));
assert_eq!(TokenKind::from_keyword("foo"), None);
}
#[test]
fn test_inline_variants() {
assert_eq!(TokenKind::from_keyword("inline"), Some(TokenKind::KwInline));
assert_eq!(TokenKind::from_keyword("__inline"), Some(TokenKind::KwInline2));
assert_eq!(TokenKind::from_keyword("__inline__"), Some(TokenKind::KwInline3));
}
#[test]
fn test_gcc_extension_keywords() {
assert_eq!(TokenKind::from_keyword("const"), Some(TokenKind::KwConst));
assert_eq!(TokenKind::from_keyword("__const"), Some(TokenKind::KwConst2));
assert_eq!(TokenKind::from_keyword("__const__"), Some(TokenKind::KwConst3));
assert_eq!(TokenKind::from_keyword("volatile"), Some(TokenKind::KwVolatile));
assert_eq!(TokenKind::from_keyword("__volatile"), Some(TokenKind::KwVolatile2));
assert_eq!(TokenKind::from_keyword("__volatile__"), Some(TokenKind::KwVolatile3));
assert_eq!(TokenKind::from_keyword("restrict"), Some(TokenKind::KwRestrict));
assert_eq!(TokenKind::from_keyword("__restrict"), Some(TokenKind::KwRestrict2));
assert_eq!(TokenKind::from_keyword("__restrict__"), Some(TokenKind::KwRestrict3));
assert_eq!(TokenKind::from_keyword("typeof"), Some(TokenKind::KwTypeof));
assert_eq!(TokenKind::from_keyword("__typeof"), Some(TokenKind::KwTypeof2));
assert_eq!(TokenKind::from_keyword("__typeof__"), Some(TokenKind::KwTypeof3));
assert_eq!(TokenKind::from_keyword("__attribute"), Some(TokenKind::KwAttribute));
assert_eq!(TokenKind::from_keyword("__attribute__"), Some(TokenKind::KwAttribute2));
assert_eq!(TokenKind::from_keyword("asm"), Some(TokenKind::KwAsm));
assert_eq!(TokenKind::from_keyword("__asm"), Some(TokenKind::KwAsm2));
assert_eq!(TokenKind::from_keyword("__asm__"), Some(TokenKind::KwAsm3));
}
#[test]
fn test_token_id_uniqueness() {
let id1 = TokenId::next();
let id2 = TokenId::next();
let id3 = TokenId::next();
assert_ne!(id1, id2);
assert_ne!(id2, id3);
assert_ne!(id1, id3);
}
#[test]
fn test_token_id_invalid() {
assert!(!TokenId::INVALID.is_valid());
assert!(TokenId::next().is_valid());
}
#[test]
fn test_token_has_unique_id() {
let loc = SourceLocation::default();
let t1 = Token::new(TokenKind::KwInt, loc.clone());
let t2 = Token::new(TokenKind::KwInt, loc.clone());
assert_ne!(t1.id, t2.id);
}
#[test]
fn test_clone_with_new_id() {
let loc = SourceLocation::default();
let t1 = Token::new(TokenKind::KwInt, loc);
let t2 = t1.clone_with_new_id();
assert_eq!(t1.kind, t2.kind);
assert_ne!(t1.id, t2.id);
}
#[test]
fn test_clone_preserves_id() {
let loc = SourceLocation::default();
let t1 = Token::new(TokenKind::KwInt, loc);
let t2 = t1.clone();
assert_eq!(t1.id, t2.id);
}
}