1use crate::commands::primitives::PrimitiveIdentifier;
15use crate::tex::catcodes::{CategoryCodeScheme, CommandCode};
16use crate::tex::characters::Character;
17use crate::tex::tokens::control_sequences::{CSName, InternedCSName};
18use std::fmt::Write;
19use std::marker::PhantomData;
20use std::num::NonZeroU32;
21
22pub mod control_sequences;
23pub mod token_lists;
24
25pub trait Token: Clone + Eq + 'static + std::fmt::Debug + Sized {
28 type CS: CSName<Self::Char>;
30 type Char: Character;
32 fn to_enum(&self) -> StandardToken<Self::Char, Self::CS>;
34 fn from_cs(cs: Self::CS) -> Self;
36 fn space() -> Self;
38
39 fn primitive(id: PrimitiveIdentifier) -> Self;
41 fn argument_marker(i: u8) -> Self;
43 fn eof() -> Self;
45 fn from_char_cat(c: Self::Char, cat: CommandCode) -> Self;
48 fn char_value(&self) -> Option<Self::Char> {
51 match self.to_enum() {
52 StandardToken::Character(c, _) => Some(c),
53 _ => None,
54 }
55 }
56 fn command_code(&self) -> CommandCode {
59 match self.to_enum() {
60 StandardToken::ControlSequence(_) => CommandCode::Escape,
61 StandardToken::Character(_, cat) => cat,
62 StandardToken::Primitive(_) => CommandCode::Primitive,
63 }
64 }
65
66 fn is_cs_or_active(&self) -> bool {
69 matches!(
70 self.to_enum(),
71 StandardToken::ControlSequence(_)
72 | StandardToken::Character(_, CommandCode::Active)
73 | StandardToken::Primitive(_)
74 )
75 }
76
77 fn is_cs(&self, name: &Self::CS) -> bool {
80 match self.to_enum() {
81 StandardToken::ControlSequence(cs) => cs == *name,
82 _ => false,
83 }
84 }
85 fn is_primitive(&self) -> Option<PrimitiveIdentifier> {
86 match self.to_enum() {
87 StandardToken::Primitive(id) => Some(id),
88 _ => None,
89 }
90 }
91 fn is_argument_marker(&self) -> Option<u8> {
93 match self.to_enum() {
94 StandardToken::Character(c, CommandCode::Argument) => Some(c.try_into().ok().unwrap()),
95 _ => None,
96 }
97 }
98
99 fn display_fmt<W: Write>(
105 &self,
106 int: &<Self::CS as CSName<Self::Char>>::Handler,
107 cc: &CategoryCodeScheme<Self::Char>,
108 escapechar: Option<Self::Char>,
109 f: &mut W,
110 ) -> std::fmt::Result {
111 match self.to_enum() {
112 StandardToken::Character(_, CommandCode::Space) => f.write_char(' '),
113 StandardToken::Character(c, _) => {
114 c.display_fmt(f);
115 Ok(())
116 }
117 StandardToken::ControlSequence(cs) => cs.display_fmt(int, cc, escapechar, f),
118 StandardToken::Primitive(id) => write!(
119 f,
120 "{}pdfprimitive {}",
121 Self::Char::display_opt(escapechar),
122 id.display(escapechar)
123 ),
124 }
125 }
126 fn display<'a>(
128 &'a self,
129 int: &'a <Self::CS as CSName<Self::Char>>::Handler,
130 cc: &'a CategoryCodeScheme<Self::Char>,
131 escapechar: Option<Self::Char>,
132 ) -> DisplayToken<'a, Self> {
133 DisplayToken {
134 tk: self,
135 int,
136 cc,
137 escapechar,
138 }
139 }
140}
141
142pub struct DisplayToken<'a, T: Token> {
143 tk: &'a T,
144 int: &'a <T::CS as CSName<T::Char>>::Handler,
145 cc: &'a CategoryCodeScheme<T::Char>,
146 escapechar: Option<T::Char>,
147}
148impl<'a, T: Token> std::fmt::Display for DisplayToken<'a, T> {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 self.tk.display_fmt(self.int, self.cc, self.escapechar, f)
151 }
152}
153
154#[derive(Clone, Copy, Eq, Debug)]
159pub enum StandardToken<Char: Character, CS: CSName<Char>> {
160 ControlSequence(CS),
161 Character(Char, CommandCode),
162 Primitive(PrimitiveIdentifier),
163}
164impl<Char: Character, CS: CSName<Char>> PartialEq for StandardToken<Char, CS> {
165 fn eq(&self, other: &Self) -> bool {
166 match (self, other) {
167 (StandardToken::ControlSequence(a), StandardToken::ControlSequence(b)) => a == b,
168 (
169 StandardToken::Character(_, CommandCode::Space),
170 StandardToken::Character(_, CommandCode::Space),
171 ) => true,
172 (StandardToken::Character(a1, a2), StandardToken::Character(b1, b2)) => {
173 a1 == b1 && a2 == b2
174 }
175 (StandardToken::Primitive(a), StandardToken::Primitive(b)) => a == b,
176 _ => false,
177 }
178 }
179}
180impl<Char: Character, CS: CSName<Char>> Token for StandardToken<Char, CS> {
181 type CS = CS;
182 type Char = Char;
183
184 fn to_enum(&self) -> StandardToken<Char, CS> {
185 self.clone()
186 }
187
188 fn from_cs(cs: CS) -> Self {
189 StandardToken::ControlSequence(cs)
190 }
191
192 fn space() -> Self {
193 StandardToken::Character(Char::from(32), CommandCode::Space)
194 }
195
196 fn eof() -> Self {
197 StandardToken::Character(Char::from(0), CommandCode::EOF)
198 }
199
200 fn from_char_cat(c: Char, cat: CommandCode) -> Self {
201 StandardToken::Character(c, cat)
202 }
203
204 fn primitive(id: PrimitiveIdentifier) -> Self {
205 Self::Primitive(id)
206 }
207
208 fn argument_marker(i: u8) -> Self {
209 Self::Character(Char::from(i), CommandCode::Argument)
210 }
211}
212
213#[derive(Clone, Copy, Eq, Debug)]
224pub struct CompactToken(NonZeroU32);
225impl CompactToken {
226 fn is_string(&self) -> bool {
227 self.0.get() < 0x8000_0000
228 }
229
230 fn as_string(&self) -> Option<InternedCSName<u8>> {
231 if self.is_string() {
232 Some((self.0, PhantomData))
233 } else {
235 None
236 }
237 }
238
239 fn commandcode_value(&self) -> u8 {
240 ((self.0.get() & 0x00FF_0000) >> 16) as u8
241 }
242
243 fn code(&self) -> CommandCode {
244 CommandCode::try_from(self.commandcode_value()).unwrap()
245 }
246
247 fn u8(&self) -> u8 {
248 (self.0.get() & 0x0000_00FF) as u8
249 }
250}
251impl PartialEq for CompactToken {
252 fn eq(&self, other: &Self) -> bool {
253 self.0 == other.0 || {
254 if self.is_string() || other.is_string() {
255 return false;
256 }
257 let cc1 = self.code();
258 let cc2 = other.code();
259 if cc1 == CommandCode::Space && cc2 == CommandCode::Space {
260 return true;
261 }
262 if cc1 != cc2 {
263 return false;
264 }
265 self.u8() == other.u8()
266 }
267 }
268}
269impl Token for CompactToken {
270 type CS = InternedCSName<u8>; type Char = u8;
272 fn to_enum(&self) -> StandardToken<u8, InternedCSName<u8>> {
274 match self.as_string() {
275 Some(s) => StandardToken::ControlSequence(s),
276 None => match self.is_primitive() {
277 Some(i) => StandardToken::Primitive(i),
278 None => StandardToken::Character(self.u8(), self.code()),
279 },
280 }
281 }
282
283 fn from_cs(cs: Self::CS) -> Self {
284 Self(cs.0)
285 }
286
287 fn from_char_cat(c: u8, cat: CommandCode) -> Self {
288 Self(NonZeroU32::new(0x8000_0000 | ((cat.as_byte() as u32) << 16) | (c as u32)).unwrap())
289 }
290
291 fn space() -> Self {
292 Self::from_char_cat(32, CommandCode::Space)
293 }
294
295 fn eof() -> Self {
296 Self::from_char_cat(0, CommandCode::EOF)
297 }
298
299 fn primitive(id: PrimitiveIdentifier) -> Self {
300 Self(
301 NonZeroU32::new(
302 0x8000_0000
303 | ((CommandCode::Primitive.as_byte() as u32) << 16)
304 | (id.as_u16() as u32),
305 )
306 .unwrap(),
307 )
308 }
309 fn is_primitive(&self) -> Option<PrimitiveIdentifier> {
310 if !self.is_string()
311 && (((self.0.get() & 0x00FF_0000) >> 16) as u8) == CommandCode::Primitive.as_byte()
312 {
313 PrimitiveIdentifier::try_from_u16((self.0.get() & 0x0000_FFFF) as u16)
314 } else {
315 None
316 }
317 }
318
319 fn argument_marker(i: u8) -> Self {
320 Self::from_char_cat(i, CommandCode::Argument)
321 }
322
323 fn command_code(&self) -> CommandCode {
324 if self.is_string() {
325 CommandCode::Escape
326 } else {
327 self.code()
328 }
329 }
330
331 fn char_value(&self) -> Option<Self::Char> {
332 if self.is_string() {
333 None
334 } else {
335 Some(self.u8())
336 }
337 }
338
339 fn is_cs_or_active(&self) -> bool {
340 self.is_string() || {
341 let cc = ((self.0.get() & 0x00FF_0000) >> 16) as u8;
342 cc == CommandCode::Active.as_byte() || cc == CommandCode::Primitive.as_byte()
343 }
344 }
345
346 fn is_cs(&self, name: &Self::CS) -> bool {
347 self.0 == name.0
348 }
349
350 fn is_argument_marker(&self) -> Option<u8> {
351 if !self.is_string()
352 && (((self.0.get() & 0x00FF_0000) >> 16) as u8) == CommandCode::Argument.as_byte()
353 {
354 Some(self.u8())
355 } else {
356 None
357 }
358 }
359}