use rucc_base::Symbol;
use rucc_diag::Span;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PpTokenKind {
Ident,
Number,
CharConst,
StringLit,
HeaderName,
Punct(Punct),
Other,
Eof,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
pub struct TokenFlags(u8);
impl TokenFlags {
pub const START_OF_LINE: TokenFlags = TokenFlags(1);
pub const LEADING_SPACE: TokenFlags = TokenFlags(2);
pub const SPLICED: TokenFlags = TokenFlags(4);
pub const DIGRAPH: TokenFlags = TokenFlags(8);
pub const EMPTY: TokenFlags = TokenFlags(0);
#[inline]
#[must_use]
pub const fn has(self, other: TokenFlags) -> bool {
self.0 & other.0 == other.0
}
#[inline]
#[must_use]
pub const fn with(self, other: TokenFlags) -> TokenFlags {
TokenFlags(self.0 | other.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PpToken {
pub kind: PpTokenKind,
pub flags: TokenFlags,
pub value: Option<Symbol>,
pub span: Span,
}
impl PpToken {
#[inline]
#[must_use]
pub const fn is_eof(self) -> bool {
matches!(self.kind, PpTokenKind::Eof)
}
#[inline]
#[must_use]
pub const fn punct(self) -> Option<Punct> {
match self.kind {
PpTokenKind::Punct(p) => Some(p),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum Punct {
LBracket,
RBracket,
LParen,
RParen,
LBrace,
RBrace,
Dot,
Ellipsis,
Arrow,
PlusPlus,
MinusMinus,
Amp,
Star,
Plus,
Minus,
Tilde,
Bang,
Slash,
Percent,
Shl,
Shr,
Lt,
Gt,
Le,
Ge,
EqEq,
Ne,
Caret,
Pipe,
AmpAmp,
PipePipe,
Question,
Colon,
ColonColon,
Semi,
Eq,
StarEq,
SlashEq,
PercentEq,
PlusEq,
MinusEq,
ShlEq,
ShrEq,
AmpEq,
CaretEq,
PipeEq,
Comma,
Hash,
HashHash,
}
impl Punct {
#[must_use]
pub const fn as_str(self) -> &'static str {
match self {
Punct::LBracket => "[",
Punct::RBracket => "]",
Punct::LParen => "(",
Punct::RParen => ")",
Punct::LBrace => "{",
Punct::RBrace => "}",
Punct::Dot => ".",
Punct::Ellipsis => "...",
Punct::Arrow => "->",
Punct::PlusPlus => "++",
Punct::MinusMinus => "--",
Punct::Amp => "&",
Punct::Star => "*",
Punct::Plus => "+",
Punct::Minus => "-",
Punct::Tilde => "~",
Punct::Bang => "!",
Punct::Slash => "/",
Punct::Percent => "%",
Punct::Shl => "<<",
Punct::Shr => ">>",
Punct::Lt => "<",
Punct::Gt => ">",
Punct::Le => "<=",
Punct::Ge => ">=",
Punct::EqEq => "==",
Punct::Ne => "!=",
Punct::Caret => "^",
Punct::Pipe => "|",
Punct::AmpAmp => "&&",
Punct::PipePipe => "||",
Punct::Question => "?",
Punct::Colon => ":",
Punct::ColonColon => "::",
Punct::Semi => ";",
Punct::Eq => "=",
Punct::StarEq => "*=",
Punct::SlashEq => "/=",
Punct::PercentEq => "%=",
Punct::PlusEq => "+=",
Punct::MinusEq => "-=",
Punct::ShlEq => "<<=",
Punct::ShrEq => ">>=",
Punct::AmpEq => "&=",
Punct::CaretEq => "^=",
Punct::PipeEq => "|=",
Punct::Comma => ",",
Punct::Hash => "#",
Punct::HashHash => "##",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_pp_token_is_sixteen_bytes() {
assert_eq!(size_of::<PpToken>(), 16);
}
#[test]
fn flags_are_a_set() {
let f = TokenFlags::EMPTY.with(TokenFlags::START_OF_LINE).with(TokenFlags::LEADING_SPACE);
assert!(f.has(TokenFlags::START_OF_LINE));
assert!(f.has(TokenFlags::LEADING_SPACE));
assert!(!f.has(TokenFlags::SPLICED));
}
#[test]
fn every_punctuator_spells_something() {
for p in [Punct::LBracket, Punct::HashHash, Punct::ColonColon, Punct::ShrEq] {
assert!(!p.as_str().is_empty());
}
}
}