dvi/transforms.rs
1//! Transforms on DVI data.
2//!
3//! The DVI format can represent the same document
4//! in multiple different ways.
5//! For example this list of operations:
6//! ```
7//! vec![
8//! dvi::Op::SetVar(dvi::Var::X, 3),
9//! dvi::Op::Move(dvi::Var::X),
10//! dvi::Op::TypesetChar{char: 'D' as u32, move_h: false},
11//! ];
12//! ```
13//! describes the same document as this list of operations:
14//! ```
15//! vec![
16//! dvi::Op::Right(6),
17//! dvi::Op::TypesetChar{char: 'D' as u32, move_h: false},
18//! ];
19//! ```
20//! In both cases, the result of the DVI operations is a document
21//! with the single character D typeset at the coordinate (6,0).
22//!
23//! This module contains _transforms_ that change the DVI representation
24//! of documents without changing the actual meaning of the document.
25//! You can think of transforms as being like optimization passes in
26//! an optimizing compiler: the output program behaves the same,
27//! but its code is different.
28//!
29//! There are at least two reasons why one would want to perform a DVI transform:
30//!
31//! 1. To **optimize** the DVI in some way; for example, to reduce
32//! the size of the DVI file.
33//! Knuth performs an optmization of this type in TeX.2021.604-615.
34//!
35//! 2. To **normalize** the DVI file so that a DVI produced by one program
36//! will match the DVI produced by another program.
37use super::*;
38
39/// Transform that removes uses of the [`Var`] variables.
40///
41/// This transform removes all [`Op::Move`] and [`Op::SetVar`]
42/// operations and thus removes all uses of the _w_, _x_, _y_ and
43/// _z_ variables.
44/// All [`Op::Move`] and [`Op::SetVar`] are replaced by [`Op::Right`]
45/// and [`Op::Down`] operations, with the correct payload.
46///
47/// This transform undoes the optimization that Knuth performs in
48/// TeX.2021.604-615.
49///
50/// ```
51/// use dvi::transforms::VarRemover;
52/// let ops_1 = vec![
53/// dvi::Op::SetVar(dvi::Var::X, 3),
54/// dvi::Op::Push,
55/// dvi::Op::SetVar(dvi::Var::X, 5),
56/// dvi::Op::Move(dvi::Var::X),
57/// dvi::Op::Pop,
58/// dvi::Op::Move(dvi::Var::X),
59/// ];
60/// let ops_2: Vec<dvi::Op> = VarRemover::new(ops_1).collect();
61/// assert_eq![
62/// ops_2,
63/// vec![
64/// dvi::Op::Right(3),
65/// dvi::Op::Push,
66/// dvi::Op::Right(5),
67/// dvi::Op::Right(5),
68/// dvi::Op::Pop,
69/// dvi::Op::Right(3),
70/// ],
71/// ];
72/// ```
73pub struct VarRemover<I> {
74 iter: I,
75 values: Values,
76}
77
78impl<I: Iterator<Item = Op>> VarRemover<I> {
79 pub fn new<J: IntoIterator<IntoIter = I>>(iter: J) -> Self {
80 Self {
81 iter: iter.into_iter(),
82 values: Default::default(),
83 }
84 }
85}
86
87impl<I: Iterator<Item = Op>> Iterator for VarRemover<I> {
88 type Item = Op;
89
90 fn next(&mut self) -> Option<Self::Item> {
91 let op = self.iter.next()?;
92 self.values.update(&op);
93 Some(match op {
94 Op::Move(var) | Op::SetVar(var, _) => {
95 let value = self.values.var(var);
96 match var {
97 Var::W | Var::X => Op::Right(value),
98 Var::Y | Var::Z => Op::Down(value),
99 }
100 }
101 _ => op,
102 })
103 }
104}