use std::sync::Arc;
#[derive(Debug, Clone, PartialEq)]
pub enum StringPart {
Literal(Arc<str>),
Variable(Arc<str>),
Expression(Arc<str>),
MethodCall(Arc<str>),
ArraySlice(Arc<str>),
}
#[derive(Debug, Clone, PartialEq)]
pub enum TokenType {
Division,
RegexMatch,
Substitution,
Transliteration,
QuoteRegex,
StringLiteral,
QuoteSingle,
QuoteDouble,
QuoteWords,
QuoteCommand,
InterpolatedString(Vec<StringPart>),
HeredocStart,
HeredocBody(Arc<str>),
FormatBody(Arc<str>),
Version(Arc<str>),
Pod,
DataMarker(Arc<str>),
DataBody(Arc<str>),
UnknownRest,
Identifier(Arc<str>),
Number(Arc<str>),
Operator(Arc<str>),
Keyword(Arc<str>),
LeftParen,
RightParen,
LeftBracket,
RightBracket,
LeftBrace,
RightBrace,
Semicolon,
Comma,
Colon,
Arrow,
FatComma,
Whitespace,
Newline,
Comment(Arc<str>),
EOF,
Error(Arc<str>),
}
impl TokenType {
pub fn is_trivia(&self) -> bool {
matches!(self, Self::Whitespace | Self::Newline | Self::Comment(_))
}
pub fn is_recovery_token(&self) -> bool {
matches!(self, Self::UnknownRest | Self::Error(_))
}
}
#[derive(Debug, Clone)]
pub struct Token {
pub token_type: TokenType,
pub text: Arc<str>,
pub start: usize,
pub end: usize,
}
impl Token {
pub fn new(token_type: TokenType, text: impl Into<Arc<str>>, start: usize, end: usize) -> Self {
Self { token_type, text: text.into(), start, end }
}
pub fn len(&self) -> usize {
self.end - self.start
}
pub fn is_empty(&self) -> bool {
self.start == self.end
}
}
#[cfg(test)]
mod tests {
use super::TokenType;
use std::sync::Arc;
#[test]
fn token_type_trivia_classifier_matches_expected_variants() {
assert!(TokenType::Whitespace.is_trivia());
assert!(TokenType::Newline.is_trivia());
assert!(TokenType::Comment(Arc::from("# note")).is_trivia());
assert!(!TokenType::Identifier(Arc::from("foo")).is_trivia());
}
#[test]
fn token_type_recovery_classifier_matches_expected_variants() {
assert!(TokenType::UnknownRest.is_recovery_token());
assert!(TokenType::Error(Arc::from("oops")).is_recovery_token());
assert!(!TokenType::EOF.is_recovery_token());
}
}