use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TexToken {
ControlSeq(String),
BeginGroup,
EndGroup,
Param(u8),
DeferredParam(u8),
Char(char),
Space,
Comment(String),
MathShift,
AlignTab,
Superscript,
Subscript,
ActiveChar(char),
EndOfInput,
}
impl TexToken {
pub fn is_begin_group(&self) -> bool {
matches!(self, TexToken::BeginGroup)
}
pub fn is_end_group(&self) -> bool {
matches!(self, TexToken::EndGroup)
}
pub fn is_control_seq(&self) -> bool {
matches!(self, TexToken::ControlSeq(_))
}
pub fn is_space(&self) -> bool {
matches!(self, TexToken::Space)
}
pub fn as_control_seq(&self) -> Option<&str> {
match self {
TexToken::ControlSeq(name) => Some(name),
_ => None,
}
}
pub fn is_cs(&self, name: &str) -> bool {
matches!(self, TexToken::ControlSeq(n) if n == name)
}
}
impl fmt::Display for TexToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TexToken::ControlSeq(name) => write!(f, "\\{}", name),
TexToken::BeginGroup => write!(f, "{{"),
TexToken::EndGroup => write!(f, "}}"),
TexToken::Param(n) => write!(f, "#{}", n),
TexToken::DeferredParam(n) => write!(f, "##{}", n),
TexToken::Char(c) => write!(f, "{}", c),
TexToken::Space => write!(f, " "),
TexToken::Comment(text) => write!(f, "%{}", text),
TexToken::MathShift => write!(f, "$"),
TexToken::AlignTab => write!(f, "&"),
TexToken::Superscript => write!(f, "^"),
TexToken::Subscript => write!(f, "_"),
TexToken::ActiveChar(c) => write!(f, "{}", c),
TexToken::EndOfInput => Ok(()),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct TokenList(pub Vec<TexToken>);
impl TokenList {
pub fn new() -> Self {
TokenList(Vec::new())
}
pub fn from_vec(tokens: Vec<TexToken>) -> Self {
TokenList(tokens)
}
pub fn push(&mut self, token: TexToken) {
self.0.push(token);
}
pub fn into_inner(self) -> Vec<TexToken> {
self.0
}
pub fn as_slice(&self) -> &[TexToken] {
&self.0
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
}
impl fmt::Display for TokenList {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for token in &self.0 {
write!(f, "{}", token)?;
}
Ok(())
}
}
impl IntoIterator for TokenList {
type Item = TexToken;
type IntoIter = std::vec::IntoIter<TexToken>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a TokenList {
type Item = &'a TexToken;
type IntoIter = std::slice::Iter<'a, TexToken>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_token_display() {
assert_eq!(format!("{}", TexToken::ControlSeq("frac".into())), "\\frac");
assert_eq!(format!("{}", TexToken::BeginGroup), "{");
assert_eq!(format!("{}", TexToken::EndGroup), "}");
assert_eq!(format!("{}", TexToken::Param(1)), "#1");
assert_eq!(format!("{}", TexToken::Char('x')), "x");
assert_eq!(format!("{}", TexToken::Space), " ");
}
#[test]
fn test_token_list_display() {
let tokens = TokenList::from_vec(vec![
TexToken::ControlSeq("frac".into()),
TexToken::BeginGroup,
TexToken::Char('a'),
TexToken::EndGroup,
TexToken::BeginGroup,
TexToken::Char('b'),
TexToken::EndGroup,
]);
assert_eq!(format!("{}", tokens), "\\frac{a}{b}");
}
}