use rudb_common::Span;
pub const NOT_A_KEYWORD: u16 = u16::MAX;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum Kind {
Identifier,
QuotedIdentifier,
Keyword,
Number,
String,
Operator,
Terminator,
EndOfInput,
}
impl Kind {
pub const fn is_identifier(self) -> bool {
matches!(self, Kind::Identifier | Kind::QuotedIdentifier)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Flags(u8);
impl Flags {
pub const NEWLINE: Flags = Flags(1 << 0);
pub const BLOCK_COMMENT: Flags = Flags(1 << 1);
pub const UNTERMINATED: Flags = Flags(1 << 2);
pub const fn has(self, other: Flags) -> bool {
self.0 & other.0 == other.0
}
pub const fn with(self, other: Flags) -> Flags {
Flags(self.0 | other.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Token {
pub kind: Kind,
pub flags: Flags,
pub keyword: u16,
pub start: u32,
pub end: u32,
}
impl Token {
pub const fn span(self) -> Span {
Span::new(self.start, self.end)
}
pub fn text(self, query: &str) -> &str {
&query[self.start as usize..self.end as usize]
}
}
#[cfg(test)]
mod tests {
use super::{Flags, Kind, Token};
#[test]
fn a_token_is_twelve_bytes() {
assert_eq!(size_of::<Token>(), 12);
}
#[test]
fn the_flags_combine_and_read_back() {
let flags = Flags::default().with(Flags::NEWLINE).with(Flags::BLOCK_COMMENT);
assert!(flags.has(Flags::NEWLINE));
assert!(flags.has(Flags::BLOCK_COMMENT));
assert!(!flags.has(Flags::UNTERMINATED));
assert!(!Flags::default().has(Flags::NEWLINE));
}
#[test]
fn both_spellings_of_a_name_are_identifiers() {
assert!(Kind::Identifier.is_identifier());
assert!(Kind::QuotedIdentifier.is_identifier());
assert!(!Kind::Keyword.is_identifier());
assert!(!Kind::String.is_identifier());
}
}