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}