1pub mod lexer;
4pub mod trace;
5use crate::types::CatCode;
6use std::{fmt::Display, num};
7use texcraft_stdext::collections::interner;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub struct CsName(num::NonZeroU32);
16
17impl CsName {
18 #[inline]
19 pub fn to_usize(&self) -> usize {
20 self.0.get() as usize
21 }
22
23 pub fn try_from_usize(u: usize) -> Option<CsName> {
24 let u = match u32::try_from(u) {
25 Ok(u) => u,
26 Err(_) => return None,
27 };
28 num::NonZeroU32::new(u).map(CsName)
29 }
30}
31
32pub type CsNameInterner = interner::Interner<CsName>;
34
35impl interner::Key for CsName {
36 fn try_from_usize(index: usize) -> Option<Self> {
37 num::NonZeroU32::try_from_usize(index).map(CsName)
38 }
39
40 fn into_usize(self) -> usize {
41 self.0.into_usize()
42 }
43}
44
45#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
48pub enum Value {
49 BeginGroup(char),
50 EndGroup(char),
51 MathShift(char),
52 AlignmentTab(char),
53 Parameter(char),
54 Superscript(char),
55 Subscript(char),
56 Space(char),
57 Letter(char),
58 Other(char),
59 CommandRef(CommandRef),
60}
61
62#[derive(Debug, Eq, PartialEq, Clone, Copy, Hash)]
64#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
65pub enum CommandRef {
66 ControlSequence(CsName),
67 ActiveCharacter(char),
68}
69
70impl CommandRef {
71 pub fn to_string(&self, cs_name_interner: &CsNameInterner) -> String {
72 match self {
73 CommandRef::ControlSequence(cs_name) => {
74 format!("\\{}", cs_name_interner.resolve(*cs_name).unwrap())
75 }
76 CommandRef::ActiveCharacter(c) => format!("{c}"),
77 }
78 }
79}
80
81impl Value {
82 pub fn new(c: char, cat_code: CatCode) -> Value {
83 match cat_code {
84 CatCode::BeginGroup => Value::BeginGroup(c),
85 CatCode::EndGroup => Value::EndGroup(c),
86 CatCode::MathShift => Value::MathShift(c),
87 CatCode::AlignmentTab => Value::AlignmentTab(c),
88 CatCode::Parameter => Value::Parameter(c),
89 CatCode::Superscript => Value::Superscript(c),
90 CatCode::Subscript => Value::Subscript(c),
91 CatCode::Space => Value::Space(c),
92 CatCode::Letter => Value::Letter(c),
93 CatCode::Other => Value::Other(c),
94 CatCode::Active => Value::CommandRef(CommandRef::ActiveCharacter(c)),
95 _ => panic!("raw cat code not allowed"),
96 }
97 }
98
99 pub fn char(&self) -> Option<char> {
100 Some(self.char_and_cat_code()?.0)
101 }
102
103 pub fn cat_code(&self) -> Option<CatCode> {
104 Some(self.char_and_cat_code()?.1)
105 }
106
107 pub fn char_and_cat_code(&self) -> Option<(char, CatCode)> {
108 let (c, code) = match self {
109 Value::BeginGroup(c) => (c, CatCode::BeginGroup),
110 Value::EndGroup(c) => (c, CatCode::EndGroup),
111 Value::MathShift(c) => (c, CatCode::MathShift),
112 Value::AlignmentTab(c) => (c, CatCode::AlignmentTab),
113 Value::Parameter(c) => (c, CatCode::Parameter),
114 Value::Superscript(c) => (c, CatCode::Superscript),
115 Value::Subscript(c) => (c, CatCode::Subscript),
116 Value::Space(c) => (c, CatCode::Space),
117 Value::Letter(c) => (c, CatCode::Letter),
118 Value::Other(c) => (c, CatCode::Other),
119 Value::CommandRef(command_ref) => match command_ref {
120 CommandRef::ControlSequence(_) => return None,
121 CommandRef::ActiveCharacter(c) => (c, CatCode::Active),
122 },
123 };
124 Some((*c, code))
125 }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
130#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
131pub struct Token {
132 value: Value,
133 trace_key: trace::Key,
134}
135
136impl std::fmt::Display for Token {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 match &self.value {
139 Value::CommandRef(_) => {
140 write![f, "todo"] }
142 _ => {
143 write![f, "{}", self.char().unwrap()]
144 }
145 }
146 }
147}
148
149macro_rules! token_constructor {
150 ($name: ident, $value: expr) => {
151 pub fn $name(c: char, trace_key: trace::Key) -> Token {
152 Token {
153 value: $value(c),
154 trace_key,
155 }
156 }
157 };
158}
159
160impl Token {
161 token_constructor!(new_begin_group, Value::BeginGroup);
162 token_constructor!(new_end_group, Value::EndGroup);
163 token_constructor!(new_math_shift, Value::MathShift);
164 token_constructor!(new_alignment_tab, Value::AlignmentTab);
165 token_constructor!(new_parameter, Value::Parameter);
166 token_constructor!(new_superscript, Value::Superscript);
167 token_constructor!(new_subscript, Value::Subscript);
168 token_constructor!(new_space, Value::Space);
169 token_constructor!(new_letter, Value::Letter);
170 token_constructor!(new_other, Value::Other);
171
172 pub fn new_command_ref(command_ref: CommandRef, trace_key: trace::Key) -> Token {
173 Token {
174 value: Value::CommandRef(command_ref),
175 trace_key,
176 }
177 }
178
179 pub fn new_active_character(c: char, trace_key: trace::Key) -> Token {
180 Token {
181 value: Value::CommandRef(CommandRef::ActiveCharacter(c)),
182 trace_key,
183 }
184 }
185
186 pub fn new_control_sequence(name: CsName, trace_key: trace::Key) -> Token {
187 Token {
188 value: Value::CommandRef(CommandRef::ControlSequence(name)),
189 trace_key,
190 }
191 }
192
193 pub fn new_from_value(value: Value, trace_key: trace::Key) -> Token {
194 Token { value, trace_key }
195 }
196
197 #[inline]
198 pub fn value(&self) -> Value {
199 self.value
200 }
201
202 #[inline]
203 pub fn trace_key(&self) -> trace::Key {
204 self.trace_key
205 }
206
207 pub fn char(&self) -> Option<char> {
208 self.value.char()
209 }
210
211 pub fn cat_code(&self) -> Option<CatCode> {
212 self.value.cat_code()
213 }
214 pub fn char_and_cat_code(&self) -> Option<(char, CatCode)> {
215 self.value.char_and_cat_code()
216 }
217}
218
219#[derive(Default)]
220enum PendingWhitespace {
221 #[default]
222 NotStarted,
223 None,
224 Space,
225 Newlines(usize),
226}
227
228impl PendingWhitespace {
229 fn reset(&mut self) {
230 *self = PendingWhitespace::None;
231 }
232
233 fn add_space(&mut self) {
234 *self = match self {
235 PendingWhitespace::NotStarted => PendingWhitespace::NotStarted,
236 PendingWhitespace::None => PendingWhitespace::Space,
237 PendingWhitespace::Space => PendingWhitespace::Space,
238 PendingWhitespace::Newlines(n) => PendingWhitespace::Newlines(*n),
239 }
240 }
241
242 fn add_newline(&mut self) {
243 *self = match self {
244 PendingWhitespace::NotStarted => PendingWhitespace::NotStarted,
245 PendingWhitespace::None => PendingWhitespace::Newlines(1),
246 PendingWhitespace::Space => PendingWhitespace::Newlines(1),
247 PendingWhitespace::Newlines(n) => PendingWhitespace::Newlines(*n + 1),
248 }
249 }
250
251 fn new_paragraph(&mut self) {
252 *self = match self {
253 PendingWhitespace::NotStarted => PendingWhitespace::NotStarted,
254 PendingWhitespace::None | PendingWhitespace::Space | PendingWhitespace::Newlines(1) => {
255 PendingWhitespace::Newlines(2)
256 }
257 PendingWhitespace::Newlines(n) => PendingWhitespace::Newlines(*n),
258 }
259 }
260}
261
262impl Display for PendingWhitespace {
263 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
264 match self {
265 PendingWhitespace::NotStarted | PendingWhitespace::None => Ok(()),
266 PendingWhitespace::Space => {
267 write!(f, " ")
268 }
269 PendingWhitespace::Newlines(n) => {
270 for _ in 0..*n {
271 writeln!(f)?;
272 }
273 Ok(())
274 }
275 }
276 }
277}
278
279#[derive(Default)]
281pub struct Writer {
282 pending_whitespace: PendingWhitespace,
283}
284
285impl Writer {
286 pub fn write(
288 &mut self,
289 io_writer: &mut dyn std::io::Write,
290 interner: &CsNameInterner,
291 value: Value,
292 ) -> Result<(), std::io::Error> {
293 match &value {
294 Value::CommandRef(CommandRef::ControlSequence(s)) => {
295 write!(
296 io_writer,
297 "{}\\{}",
298 self.pending_whitespace,
299 interner.resolve(*s).unwrap()
300 )?;
301 self.pending_whitespace.reset();
302 }
303 Value::Space(_) => self.pending_whitespace.add_space(),
304 _ => {
305 write!(
306 io_writer,
307 "{}{}",
308 self.pending_whitespace,
309 value.char().unwrap()
310 )?;
311 self.pending_whitespace.reset();
312 }
313 }
314 io_writer.flush()
315 }
316
317 pub fn add_newline(&mut self) {
318 self.pending_whitespace.add_newline();
319 }
320
321 pub fn start_paragraph(&mut self) {
322 self.pending_whitespace.new_paragraph();
323 }
324}
325
326pub fn write_tokens<'a, T>(tokens: T, interner: &CsNameInterner) -> String
328where
329 T: IntoIterator<Item = &'a Token>,
330{
331 let mut buffer: Vec<u8> = Default::default();
332 let mut writer: Writer = Default::default();
333 for token in tokens.into_iter() {
334 writer.write(&mut buffer, interner, token.value()).unwrap();
335 }
336 std::str::from_utf8(&buffer).unwrap().into()
337}
338
339pub fn write_token_values<'a, T>(values: T, interner: &CsNameInterner) -> String
341where
342 T: IntoIterator<Item = &'a Value>,
343{
344 let mut buffer: Vec<u8> = Default::default();
345 let mut writer: Writer = Default::default();
346 for value in values.into_iter() {
347 writer.write(&mut buffer, interner, *value).unwrap();
348 }
349 std::str::from_utf8(&buffer).unwrap().into()
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 enum Instruction {
357 ControlSequence(&'static str),
358 Character(char, CatCode),
359 Newline,
360 NewParagraph,
361 }
362
363 fn writer_test(input: Vec<Instruction>, want: &str) {
364 let mut buffer: Vec<u8> = Default::default();
365 let mut writer: Writer = Default::default();
366 let mut interner = CsNameInterner::default();
367 for pre_interned_token in input {
368 match pre_interned_token {
369 Instruction::ControlSequence(name) => {
370 let cs_name = interner.get_or_intern(name);
371 let token = Token::new_control_sequence(cs_name, trace::Key::dummy());
372 writer.write(&mut buffer, &interner, token.value()).unwrap();
373 }
374 Instruction::Character(c, code) => {
375 let token = Token::new_from_value(Value::new(c, code), trace::Key::dummy());
376 writer.write(&mut buffer, &interner, token.value()).unwrap();
377 }
378 Instruction::Newline => {
379 writer.add_newline();
380 }
381 Instruction::NewParagraph => {
382 writer.start_paragraph();
383 }
384 };
385 }
386 let got: String = std::str::from_utf8(&buffer).unwrap().into();
387 let want = want.to_string();
388
389 if got != want {
390 println!("Output is different:");
391 println!("------[got]-------");
392 println!("{}", got);
393 println!("------[want]------");
394 println!("{}", want);
395 println!("-----------------");
396 panic!("write_tokens test failed");
397 }
398 }
399
400 macro_rules! write_tokens_tests {
401 ($( ($name: ident, $input: expr, $want: expr), )+) => {
402 $(
403 #[test]
404 fn $name() {
405 writer_test($input, $want);
406 }
407 )+
408 };
409 }
410
411 write_tokens_tests!(
412 (blank, vec!(), ""),
413 (
414 trim_whitespace_from_start,
415 vec![
416 Instruction::Character('\n', CatCode::Space),
417 Instruction::Character('\n', CatCode::Space),
418 Instruction::Character('\n', CatCode::Space),
419 Instruction::Character('H', CatCode::Letter),
420 ],
421 "H"
422 ),
423 (
424 trim_whitespace_from_end,
425 vec![
426 Instruction::Character('H', CatCode::Letter),
427 Instruction::Character('\n', CatCode::Space),
428 Instruction::Character('\n', CatCode::Space),
429 Instruction::Character('\n', CatCode::Space),
430 ],
431 "H"
432 ),
433 (
434 trim_whitespace_from_middle_1,
435 vec![
436 Instruction::Character('H', CatCode::Letter),
437 Instruction::Character(' ', CatCode::Space),
438 Instruction::Character(' ', CatCode::Space),
439 Instruction::Character('W', CatCode::Letter),
440 ],
441 "H W"
442 ),
443 (
444 trim_whitespace_from_middle_2,
445 vec![
446 Instruction::Character('H', CatCode::Letter),
447 Instruction::Character('\n', CatCode::Space),
448 Instruction::Character(' ', CatCode::Space),
449 Instruction::Character('\n', CatCode::Space),
450 Instruction::Character('W', CatCode::Letter),
451 ],
452 "H W"
453 ),
454 (
455 trim_whitespace_from_middle_3,
456 vec![
457 Instruction::Character('H', CatCode::Letter),
458 Instruction::Character('\n', CatCode::Space),
459 Instruction::Character('\n', CatCode::Space),
460 Instruction::Character('\n', CatCode::Space),
461 Instruction::Character('W', CatCode::Letter),
462 ],
463 "H W"
464 ),
465 (
466 control_sequence,
467 vec![Instruction::ControlSequence("HelloWorld"),],
468 "\\HelloWorld"
469 ),
470 (
471 newline_1,
472 vec![
473 Instruction::Character('H', CatCode::Letter),
474 Instruction::Newline,
475 Instruction::Character('W', CatCode::Letter),
476 ],
477 "H\nW"
478 ),
479 (
480 newline_2,
481 vec![
482 Instruction::Character('H', CatCode::Letter),
483 Instruction::Newline,
484 Instruction::Character(' ', CatCode::Space),
485 Instruction::Newline,
486 Instruction::Character('W', CatCode::Letter),
487 ],
488 "H\n\nW"
489 ),
490 (
491 newline_3,
492 vec![
493 Instruction::Character('H', CatCode::Letter),
494 Instruction::Newline,
495 Instruction::Character(' ', CatCode::Space),
496 Instruction::Newline,
497 Instruction::Character(' ', CatCode::Space),
498 Instruction::Newline,
499 Instruction::Character('W', CatCode::Letter),
500 ],
501 "H\n\n\nW"
502 ),
503 (
504 par_1,
505 vec![
506 Instruction::Character('H', CatCode::Letter),
507 Instruction::NewParagraph,
508 Instruction::NewParagraph,
509 Instruction::Character('W', CatCode::Letter),
510 ],
511 "H\n\nW"
512 ),
513 (
514 par_2,
515 vec![
516 Instruction::Character('H', CatCode::Letter),
517 Instruction::NewParagraph,
518 Instruction::NewParagraph,
519 Instruction::NewParagraph,
520 Instruction::Character('W', CatCode::Letter),
521 ],
522 "H\n\nW"
523 ),
524 );
525
526 #[test]
527 fn token_size() {
528 assert_eq!(std::mem::size_of::<CommandRef>(), 8);
529 assert_eq!(std::mem::size_of::<Value>(), 8);
530 assert_eq!(std::mem::size_of::<Token>(), 12);
531 assert_eq!(std::mem::size_of::<Result<Token, ()>>(), 12);
532 assert_eq!(std::mem::size_of::<Result<Option<Token>, ()>>(), 12);
533 assert_eq!(std::mem::size_of::<crate::prelude::Result<Token>>(), 12);
534 assert_eq!(
535 std::mem::size_of::<crate::prelude::Result<Option<Token>>>(),
536 12
537 );
538 }
539}