use super::ManchesterError;
#[derive(Debug, Clone, PartialEq)]
pub enum Token {
Ident(String),
And,
Or,
Not,
Some,
Only,
Min,
Max,
Exactly,
Value,
LBrace,
RBrace,
LParen,
RParen,
Number(u32),
Eof,
}
impl std::fmt::Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Token::Ident(s) => write!(f, "identifier `{s}`"),
Token::And => write!(f, "`and`"),
Token::Or => write!(f, "`or`"),
Token::Not => write!(f, "`not`"),
Token::Some => write!(f, "`some`"),
Token::Only => write!(f, "`only`"),
Token::Min => write!(f, "`min`"),
Token::Max => write!(f, "`max`"),
Token::Exactly => write!(f, "`exactly`"),
Token::Value => write!(f, "`value`"),
Token::LBrace => write!(f, "`{{`"),
Token::RBrace => write!(f, "`}}`"),
Token::LParen => write!(f, "`(`"),
Token::RParen => write!(f, "`)`"),
Token::Number(n) => write!(f, "number `{n}`"),
Token::Eof => write!(f, "end of input"),
}
}
}
fn classify_ident(raw: &str) -> Token {
match raw {
"and" => Token::And,
"or" => Token::Or,
"not" => Token::Not,
"some" => Token::Some,
"only" => Token::Only,
"min" => Token::Min,
"max" => Token::Max,
"exactly" => Token::Exactly,
"value" => Token::Value,
_ => Token::Ident(raw.to_string()),
}
}
#[inline]
fn is_ident_char(b: u8) -> bool {
b.is_ascii_alphanumeric() || b == b'_' || b == b':'
}
#[inline]
fn is_ident_start(b: u8) -> bool {
b.is_ascii_alphabetic() || b == b'_'
}
pub fn tokenize(input: &str) -> Result<Vec<(Token, usize)>, ManchesterError> {
let bytes = input.as_bytes();
let len = bytes.len();
let mut pos = 0usize;
let mut tokens: Vec<(Token, usize)> = Vec::with_capacity(len / 3 + 8);
while pos < len {
if bytes[pos].is_ascii_whitespace() {
pos += 1;
continue;
}
let start = pos;
let b = bytes[pos];
if is_ident_start(b) {
while pos < len && is_ident_char(bytes[pos]) {
pos += 1;
}
let raw = &input[start..pos];
tokens.push((classify_ident(raw), start));
} else if b.is_ascii_digit() {
while pos < len && bytes[pos].is_ascii_digit() {
pos += 1;
}
let raw = &input[start..pos];
let n: u32 = raw.parse().map_err(|_| ManchesterError::LexError {
pos: start,
msg: format!("integer literal `{raw}` overflows u32"),
})?;
tokens.push((Token::Number(n), start));
} else {
pos += 1; match b {
b'{' => tokens.push((Token::LBrace, start)),
b'}' => tokens.push((Token::RBrace, start)),
b'(' => tokens.push((Token::LParen, start)),
b')' => tokens.push((Token::RParen, start)),
other => {
return Err(ManchesterError::LexError {
pos: start,
msg: format!("unexpected character `{}`", other as char),
});
}
}
}
}
tokens.push((Token::Eof, len));
Ok(tokens)
}