use crate::commands::primitives::PrimitiveIdentifier;
use crate::tex::catcodes::{CategoryCodeScheme, CommandCode};
use crate::tex::characters::Character;
use crate::tex::tokens::control_sequences::{CSName, InternedCSName};
use std::fmt::Write;
use std::marker::PhantomData;
use std::num::NonZeroU32;
pub mod control_sequences;
pub mod token_lists;
pub trait Token: Clone + Eq + 'static + std::fmt::Debug + Sized {
type CS: CSName<Self::Char>;
type Char: Character;
fn to_enum(&self) -> StandardToken<Self::Char, Self::CS>;
fn from_cs(cs: Self::CS) -> Self;
fn space() -> Self;
fn primitive(id: PrimitiveIdentifier) -> Self;
fn argument_marker(i: u8) -> Self;
fn eof() -> Self;
fn from_char_cat(c: Self::Char, cat: CommandCode) -> Self;
fn char_value(&self) -> Option<Self::Char> {
match self.to_enum() {
StandardToken::Character(c, _) => Some(c),
_ => None,
}
}
fn command_code(&self) -> CommandCode {
match self.to_enum() {
StandardToken::ControlSequence(_) => CommandCode::Escape,
StandardToken::Character(_, cat) => cat,
StandardToken::Primitive(_) => CommandCode::Primitive,
}
}
fn is_cs_or_active(&self) -> bool {
matches!(
self.to_enum(),
StandardToken::ControlSequence(_)
| StandardToken::Character(_, CommandCode::Active)
| StandardToken::Primitive(_)
)
}
fn is_cs(&self, name: &Self::CS) -> bool {
match self.to_enum() {
StandardToken::ControlSequence(cs) => cs == *name,
_ => false,
}
}
fn is_primitive(&self) -> Option<PrimitiveIdentifier> {
match self.to_enum() {
StandardToken::Primitive(id) => Some(id),
_ => None,
}
}
fn is_argument_marker(&self) -> Option<u8> {
match self.to_enum() {
StandardToken::Character(c, CommandCode::Argument) => Some(c.try_into().ok().unwrap()),
_ => None,
}
}
fn display_fmt<W: Write>(
&self,
int: &<Self::CS as CSName<Self::Char>>::Handler,
cc: &CategoryCodeScheme<Self::Char>,
escapechar: Option<Self::Char>,
f: &mut W,
) -> std::fmt::Result {
match self.to_enum() {
StandardToken::Character(_, CommandCode::Space) => f.write_char(' '),
StandardToken::Character(c, _) => {
c.display_fmt(f);
Ok(())
}
StandardToken::ControlSequence(cs) => cs.display_fmt(int, cc, escapechar, f),
StandardToken::Primitive(id) => write!(
f,
"{}pdfprimitive {}",
Self::Char::display_opt(escapechar),
id.display(escapechar)
),
}
}
fn display<'a>(
&'a self,
int: &'a <Self::CS as CSName<Self::Char>>::Handler,
cc: &'a CategoryCodeScheme<Self::Char>,
escapechar: Option<Self::Char>,
) -> DisplayToken<'a, Self> {
DisplayToken {
tk: self,
int,
cc,
escapechar,
}
}
}
pub struct DisplayToken<'a, T: Token> {
tk: &'a T,
int: &'a <T::CS as CSName<T::Char>>::Handler,
cc: &'a CategoryCodeScheme<T::Char>,
escapechar: Option<T::Char>,
}
impl<'a, T: Token> std::fmt::Display for DisplayToken<'a, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.tk.display_fmt(self.int, self.cc, self.escapechar, f)
}
}
#[derive(Clone, Copy, Eq, Debug)]
pub enum StandardToken<Char: Character, CS: CSName<Char>> {
ControlSequence(CS),
Character(Char, CommandCode),
Primitive(PrimitiveIdentifier),
}
impl<Char: Character, CS: CSName<Char>> PartialEq for StandardToken<Char, CS> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(StandardToken::ControlSequence(a), StandardToken::ControlSequence(b)) => a == b,
(
StandardToken::Character(_, CommandCode::Space),
StandardToken::Character(_, CommandCode::Space),
) => true,
(StandardToken::Character(a1, a2), StandardToken::Character(b1, b2)) => {
a1 == b1 && a2 == b2
}
(StandardToken::Primitive(a), StandardToken::Primitive(b)) => a == b,
_ => false,
}
}
}
impl<Char: Character, CS: CSName<Char>> Token for StandardToken<Char, CS> {
type CS = CS;
type Char = Char;
fn to_enum(&self) -> StandardToken<Char, CS> {
self.clone()
}
fn from_cs(cs: CS) -> Self {
StandardToken::ControlSequence(cs)
}
fn space() -> Self {
StandardToken::Character(Char::from(32), CommandCode::Space)
}
fn eof() -> Self {
StandardToken::Character(Char::from(0), CommandCode::EOF)
}
fn from_char_cat(c: Char, cat: CommandCode) -> Self {
StandardToken::Character(c, cat)
}
fn primitive(id: PrimitiveIdentifier) -> Self {
Self::Primitive(id)
}
fn argument_marker(i: u8) -> Self {
Self::Character(Char::from(i), CommandCode::Argument)
}
}
#[derive(Clone, Copy, Eq, Debug)]
pub struct CompactToken(NonZeroU32);
impl CompactToken {
fn is_string(&self) -> bool {
self.0.get() < 0x8000_0000
}
fn as_string(&self) -> Option<InternedCSName<u8>> {
if self.is_string() {
Some((self.0, PhantomData))
} else {
None
}
}
fn commandcode_value(&self) -> u8 {
((self.0.get() & 0x00FF_0000) >> 16) as u8
}
fn code(&self) -> CommandCode {
CommandCode::try_from(self.commandcode_value()).unwrap()
}
fn u8(&self) -> u8 {
(self.0.get() & 0x0000_00FF) as u8
}
}
impl PartialEq for CompactToken {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0 || {
if self.is_string() || other.is_string() {
return false;
}
let cc1 = self.code();
let cc2 = other.code();
if cc1 == CommandCode::Space && cc2 == CommandCode::Space {
return true;
}
if cc1 != cc2 {
return false;
}
self.u8() == other.u8()
}
}
}
impl Token for CompactToken {
type CS = InternedCSName<u8>; type Char = u8;
fn to_enum(&self) -> StandardToken<u8, InternedCSName<u8>> {
match self.as_string() {
Some(s) => StandardToken::ControlSequence(s),
None => match self.is_primitive() {
Some(i) => StandardToken::Primitive(i),
None => StandardToken::Character(self.u8(), self.code()),
},
}
}
fn from_cs(cs: Self::CS) -> Self {
Self(cs.0)
}
fn from_char_cat(c: u8, cat: CommandCode) -> Self {
Self(NonZeroU32::new(0x8000_0000 | ((cat.as_byte() as u32) << 16) | (c as u32)).unwrap())
}
fn space() -> Self {
Self::from_char_cat(32, CommandCode::Space)
}
fn eof() -> Self {
Self::from_char_cat(0, CommandCode::EOF)
}
fn primitive(id: PrimitiveIdentifier) -> Self {
Self(
NonZeroU32::new(
0x8000_0000
| ((CommandCode::Primitive.as_byte() as u32) << 16)
| (id.as_u16() as u32),
)
.unwrap(),
)
}
fn is_primitive(&self) -> Option<PrimitiveIdentifier> {
if !self.is_string()
&& (((self.0.get() & 0x00FF_0000) >> 16) as u8) == CommandCode::Primitive.as_byte()
{
PrimitiveIdentifier::try_from_u16((self.0.get() & 0x0000_FFFF) as u16)
} else {
None
}
}
fn argument_marker(i: u8) -> Self {
Self::from_char_cat(i, CommandCode::Argument)
}
fn command_code(&self) -> CommandCode {
if self.is_string() {
CommandCode::Escape
} else {
self.code()
}
}
fn char_value(&self) -> Option<Self::Char> {
if self.is_string() {
None
} else {
Some(self.u8())
}
}
fn is_cs_or_active(&self) -> bool {
self.is_string() || {
let cc = ((self.0.get() & 0x00FF_0000) >> 16) as u8;
cc == CommandCode::Active.as_byte() || cc == CommandCode::Primitive.as_byte()
}
}
fn is_cs(&self, name: &Self::CS) -> bool {
self.0 == name.0
}
fn is_argument_marker(&self) -> Option<u8> {
if !self.is_string()
&& (((self.0.get() & 0x00FF_0000) >> 16) as u8) == CommandCode::Argument.as_byte()
{
Some(self.u8())
} else {
None
}
}
}