Skip to main content

tex_engine/tex/
nodes.rs

1/*! [TeX Nodes](NodeTrait) that end up in the final document.
2
3Split into three types: [vertical](vertical::VNode), [horizontal](horizontal::HNode) and [math](math::MathNode)
4nodes, each with a corresponding [`NodeList`] type.
5*/
6pub mod boxes;
7pub mod horizontal;
8pub mod math;
9pub mod vertical;
10
11use crate::commands::primitives::PrimitiveIdentifier;
12use crate::engine::filesystem::SourceRef;
13use crate::engine::{EngineReferences, EngineTypes};
14use crate::tex::nodes::boxes::TeXBox;
15use crate::tex::nodes::horizontal::{HNode, HorizontalNodeListType};
16use crate::tex::nodes::math::{MathNode, MathNodeList, MathNodeListType, UnresolvedMathFontStyle};
17use crate::tex::nodes::vertical::{VNode, VerticalNodeListType};
18use crate::tex::numerics::Skip;
19use crate::utils::errors::TeXResult;
20use crate::utils::Ptr;
21use std::convert::Infallible;
22use std::fmt::{Debug, Display, Formatter, Write};
23use std::marker::PhantomData;
24
25/// Common trait for all nodes that end up in the final document.
26pub trait NodeTrait<ET: EngineTypes>: Debug + Clone {
27    /// Returns the height of the node.
28    fn height(&self) -> ET::Dim;
29    /// Returns the depth of the node.
30    fn depth(&self) -> ET::Dim;
31    /// Returns the width of the node.
32    fn width(&self) -> ET::Dim;
33    /// Returns the type of the node, as returned by `\lastnodetype`.
34    fn nodetype(&self) -> NodeType;
35    /// Produces a human-readable string; since nodes are deeply nested, takes an
36    /// additional `indent` value to indent the string
37    fn display_fmt(&self, indent: usize, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
38
39    /// Returns a helper struct that implements [`Display`] and uses [`Self::display_fmt`]
40    /// to yield a human-readable string.
41    fn display(&self) -> DisplayNode<ET, Self> {
42        DisplayNode(self, PhantomData)
43    }
44    /// Whether this node is "opaque"; meaning: When considering a list of nodes (e.g. in `\unskip`
45    /// or `\lastbox`, this node should not be considered. Useful for annotation/marker nodes
46    /// some engine wants to insert, without impacting algorithms that inspect e.g. the last node
47    /// of the current list.
48    fn opaque(&self) -> bool {
49        false
50    }
51    fn sourceref(&self) -> Option<(&SourceRef<ET>, &SourceRef<ET>)> {
52        None
53    }
54}
55
56/// Produces a `\n` followed by `indent`-many spaces - i.e. does the indentation for
57/// [`crate::tex::nodes::NodeTrait::display_fmt`].
58pub fn display_do_indent(indent: usize, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59    f.write_char('\n')?;
60    for _ in 0..indent {
61        f.write_char(' ')?;
62    }
63    Ok(())
64}
65
66/// Helper struct that implements [`Display`] and uses [`NodeTrait::display_fmt`] to yield a
67/// human-readable string.
68pub struct DisplayNode<'a, ET: EngineTypes, N: NodeTrait<ET>>(&'a N, PhantomData<ET>);
69impl<'a, ET: EngineTypes, N: NodeTrait<ET>> Display for DisplayNode<'a, ET, N> {
70    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71        self.0.display_fmt(0, f)
72    }
73}
74
75/// Trait to implement for engine-specific new node types. Needs to implement [`NodeTrait`]
76/// and [`Into`]`<`[`ET::CustomNode`](EngineTypes::CustomNode)`>`. Is implemented by `()` for engines that do not have
77/// additional node types beyond the default ones.
78pub trait CustomNodeTrait<ET: EngineTypes>: NodeTrait<ET>
79where
80    Self: Into<ET::CustomNode>,
81{
82    /// Return this node as a [`VNode`].
83    fn into_v(self) -> VNode<ET> {
84        VNode::Custom(self.into())
85    }
86    /// Return this node as an [`HNode`].
87    fn into_h(self) -> HNode<ET> {
88        HNode::Custom(self.into())
89    }
90    /// Return this node as a [`MathNode`].
91    fn into_math(self) -> MathNode<ET, UnresolvedMathFontStyle<ET>> {
92        MathNode::Custom(self.into())
93    }
94}
95
96impl<ET: EngineTypes<CustomNode = Infallible>> NodeTrait<ET> for Infallible {
97    fn height(&self) -> ET::Dim {
98        ET::Dim::default()
99    }
100    fn depth(&self) -> ET::Dim {
101        ET::Dim::default()
102    }
103    fn width(&self) -> ET::Dim {
104        ET::Dim::default()
105    }
106    fn nodetype(&self) -> NodeType {
107        NodeType::WhatsIt
108    }
109    fn display_fmt(&self, _indent: usize, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110        Ok(())
111    }
112    fn opaque(&self) -> bool {
113        true
114    }
115}
116impl<ET: EngineTypes<CustomNode = Infallible>> CustomNodeTrait<ET> for Infallible {}
117
118#[cfg(feature = "multithreaded")]
119pub type WhatsitFunction<ET> =
120    dyn FnOnce(&mut EngineReferences<ET>) -> TeXResult<(), ET> + Send + Sync;
121#[cfg(not(feature = "multithreaded"))]
122pub type WhatsitFunction<ET> = dyn FnOnce(&mut EngineReferences<ET>) -> TeXResult<(), ET>;
123
124#[cfg(feature = "multithreaded")]
125type WhatsitF<ET> = Ptr<std::sync::RwLock<Option<Box<WhatsitFunction<ET>>>>>;
126#[cfg(not(feature = "multithreaded"))]
127type WhatsitF<ET> = Ptr<std::cell::RefCell<Option<Box<WhatsitFunction<ET>>>>>;
128/// A Whatsit [node](NodeTrait), essentially representing a callback to the engine to be executed
129/// at shipout, as produced by e.g. `\special` or `\write`.
130#[derive(Clone)]
131pub struct WhatsitNode<ET: EngineTypes>(String, WhatsitF<ET>);
132impl<ET: EngineTypes> std::fmt::Debug for WhatsitNode<ET> {
133    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134        write!(f, "<whatsit {}>", self.0)
135    }
136}
137impl<ET: EngineTypes> WhatsitNode<ET> {
138    /// Create a new Whatsit node produce by the primitive
139    /// [Whatsit command](crate::commands::PrimitiveCommand::Whatsit) with the given name,
140    /// with the function provided, to be executed at shipout.
141    pub fn new(f: Box<WhatsitFunction<ET>>, name: PrimitiveIdentifier) -> Self {
142        #[cfg(feature = "multithreaded")]
143        let i = std::sync::RwLock::new(Some(f));
144        #[cfg(not(feature = "multithreaded"))]
145        let i = std::cell::RefCell::new(Some(f));
146        WhatsitNode(name.display::<ET::Char>(None).to_string(), Ptr::new(i))
147    }
148    /// Run this Whatsit node's function at shipout, if it has not been run yet.
149    /// If it has been run already, this is a no-op.
150
151    #[cfg(feature = "multithreaded")]
152    pub fn call(self, engine: &mut EngineReferences<ET>) -> TeXResult<(), ET> {
153        let mut lock = self.1.write().unwrap();
154        if let Some(f) = std::mem::take(&mut *lock) {
155            f(engine)
156        } else {
157            Ok(())
158        }
159    }
160    #[cfg(not(feature = "multithreaded"))]
161    pub fn call(self, engine: &mut EngineReferences<ET>) -> TeXResult<(), ET> {
162        if let Some(f) = self.1.replace(None) {
163            f(engine)
164        } else {
165            Ok(())
166        }
167    }
168}
169
170/// A `\leaders` node, as produced by `\leaders`, `\cleaders` or `\xleaders`.
171#[derive(Clone, Debug)]
172pub struct Leaders<ET: EngineTypes> {
173    /// `\leaders`, `\cleaders` or `\xleaders`.
174    pub tp: LeaderType,
175    /// The node to be repeated for the given skip length; a box or a rule.
176    pub body: LeaderBody<ET>,
177    /// The skip length along which the body should be repeated.
178    pub skip: LeaderSkip<ET>,
179}
180impl<ET: EngineTypes> NodeTrait<ET> for Leaders<ET> {
181    fn display_fmt(&self, indent: usize, f: &mut Formatter<'_>) -> std::fmt::Result {
182        display_do_indent(indent, f)?;
183        write!(f, "<leaders")?;
184        match self.tp {
185            LeaderType::Normal => {}
186            LeaderType::C => write!(f, " type=c")?,
187            LeaderType::X => write!(f, " type=x")?,
188        }
189        match self.skip {
190            LeaderSkip::HSkip(s) => write!(f, " hskip={}", s)?,
191            LeaderSkip::HFil => write!(f, " hfil")?,
192            LeaderSkip::HFill => write!(f, " hfill")?,
193            LeaderSkip::VSkip(s) => write!(f, " vskip={}", s)?,
194            LeaderSkip::VFil => write!(f, " vfil")?,
195            LeaderSkip::VFill => write!(f, " vfill")?,
196        }
197        write!(f, ">")?;
198        //self.bx.readable_fmt(indent+2, f)?;
199        display_do_indent(indent, f)?;
200        write!(f, "</leaders>")
201    }
202    fn height(&self) -> ET::Dim {
203        if self.skip.is_h() {
204            ET::Dim::default() //self.bx.height()
205        } else {
206            match self.skip {
207                LeaderSkip::VSkip(s) => s.base,
208                _ => ET::Dim::default(),
209            }
210        }
211    }
212    fn width(&self) -> ET::Dim {
213        if self.skip.is_h() {
214            match self.skip {
215                LeaderSkip::HSkip(s) => s.base,
216                _ => ET::Dim::default(),
217            }
218        } else {
219            ET::Dim::default() //self.bx.width()
220        }
221    }
222    fn depth(&self) -> ET::Dim {
223        ET::Dim::default()
224        //self.bx.depth()
225    }
226    fn nodetype(&self) -> NodeType {
227        NodeType::Glue
228    }
229}
230
231/// The type of a [`Leaders`] node
232/// (i.e. whether it was produced by `\leaders`, `\cleaders` or `\xleaders`).
233#[derive(Debug, Clone, Copy, PartialEq, Eq)]
234pub enum LeaderType {
235    Normal,
236    C,
237    X,
238}
239
240/// The body of a [`Leaders`] node;
241/// either a box or a rule.
242#[derive(Clone, Debug)]
243pub enum LeaderBody<ET: EngineTypes> {
244    Box(TeXBox<ET>),
245    Rule {
246        width: Option<ET::Dim>,
247        height: Option<ET::Dim>,
248        depth: Option<ET::Dim>,
249    },
250}
251
252/// The skip length along which a [`Leaders`] node
253/// should be repeated.
254#[derive(Clone, Debug)]
255pub enum LeaderSkip<ET: EngineTypes> {
256    HSkip(Skip<ET::Dim>),
257    HFil,
258    HFill,
259    VSkip(Skip<ET::Dim>),
260    VFil,
261    VFill,
262}
263impl<ET: EngineTypes> LeaderSkip<ET> {
264    /// Whether this is a horizontal skip.
265    pub fn is_h(&self) -> bool {
266        use LeaderSkip::*;
267        matches!(self, HSkip(_) | HFil | HFill)
268    }
269}
270
271/// The type of a [`Node`](crate::tex::nodes::NodeTrait), as returned
272/// by `\lastnodetype`
273#[derive(Clone, Copy, Eq, PartialEq, Debug)]
274pub enum NodeType {
275    /// A character node, e.g. `a` or `1`.
276    Char = 0,
277    /// A horizontal list node, e.g. `\hbox{...}`.
278    HList = 1,
279    /// A vertical list node, e.g. `\vbox{...}`.
280    VList = 2,
281    /// A rule node, e.g. `\hrule`.
282    Rule = 3,
283    /// An insertion node as produced by `\insert`.
284    Insertion = 4,
285    /// A mark node as produced by `\mark`.
286    Mark = 5,
287    /// An adjust node as produced by `\vadjust`.
288    Adjust = 6,
289    /// A ligature node as produced by the ligaturing algorithm.
290    Ligature = 7,
291    /// A discretionary node as produced by the discretionary algorithm.
292    Discretionary = 8,
293    /// A whatsit node, e.g. `\special`.
294    WhatsIt = 9,
295    /// A math node.
296    Math = 10,
297    /// A glue node, e.g. `\hskip`.
298    Glue = 11,
299    /// A kern node, e.g. `\kern`.
300    Kern = 12,
301    /// A penalty node as produced by `\penalty`.
302    Penalty = 13,
303    /// An unset node, e.g. `\unskip`.
304    Unset = 14,
305    /// A math character node, e.g. `a` or `1` in math mode.
306    MathChar = 15,
307}
308impl NodeType {
309    /// Returns the numeric value of the node type, as returned by `\lastnodetype`.
310    pub fn to_u8(&self) -> u8 {
311        *self as u8
312    }
313}
314
315/// A node list, i.e. an open list of [`Node`](crate::tex::nodes::NodeTrait)s
316/// currently being built.
317#[derive(Clone, Debug)]
318pub enum NodeList<ET: EngineTypes> {
319    /// A vertical list.
320    Vertical {
321        tp: VerticalNodeListType<ET>,
322        children: Vec<VNode<ET>>,
323    },
324    /// A horizontal list.
325    Horizontal {
326        tp: HorizontalNodeListType<ET>,
327        children: Vec<HNode<ET>>,
328    },
329    /// A math list.
330    Math {
331        children: MathNodeList<ET>,
332        start: SourceRef<ET>,
333        tp: MathNodeListType<ET>,
334    },
335}
336impl<ET: EngineTypes> NodeList<ET> {
337    /// Create a new math list.
338    pub fn new_math(start: SourceRef<ET>) -> Self {
339        NodeList::Math {
340            children: MathNodeList::default(),
341            start,
342            tp: MathNodeListType::Target(ListTarget::none()),
343        }
344    }
345}
346
347/// Once a box is closed, something is supposed to happen to it; most commonly, it is just added to the parent
348/// node list; but occasionally, something else should happen to it - e.g. `\setbox5\hbox{...}` implies that
349/// after the node list of the box is closed, it should be put into the box register with index 5 instead.
350/// This struct abstracts over that by carrying a continuation function to be called when the box is closed.
351///
352/// TODO: rethink this in light of [`ListTarget`].
353pub struct BoxTarget<ET: EngineTypes>(
354    Option<Box<dyn FnOnce(&mut EngineReferences<ET>, TeXBox<ET>) -> TeXResult<(), ET>>>,
355);
356impl<ET: EngineTypes> crate::tex::nodes::BoxTarget<ET> {
357    /// Create a new box target from the given continuation function.
358    pub fn new<F: FnOnce(&mut EngineReferences<ET>, TeXBox<ET>) -> TeXResult<(), ET> + 'static>(
359        f: F,
360    ) -> Self {
361        crate::tex::nodes::BoxTarget(Some(Box::new(f)))
362    }
363    /// Execute the continuation function (once the box is closed)
364    pub fn call(self, engine: &mut EngineReferences<ET>, bx: TeXBox<ET>) -> TeXResult<(), ET> {
365        match self.0 {
366            Some(f) => f(engine, bx),
367            None => unreachable!(),
368        }
369    }
370    /// Create a trivial box target
371    pub fn none() -> Self {
372        crate::tex::nodes::BoxTarget(None)
373    }
374    /// Whether the continuation function has not yet been called
375    pub fn is_some(&self) -> bool {
376        self.0.is_some()
377    }
378}
379impl<ET: EngineTypes> Debug for crate::tex::nodes::BoxTarget<ET> {
380    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
381        match self.0 {
382            None => f.write_str("BoxTarget(None)"),
383            Some(_) => f.write_str("BoxTarget(Some(_))"),
384        }
385    }
386}
387impl<ET: EngineTypes> Clone for crate::tex::nodes::BoxTarget<ET> {
388    fn clone(&self) -> Self {
389        crate::tex::nodes::BoxTarget(None)
390    }
391}
392
393/// Once a node list is closed, something is supposed to happen to it; most commonly, it is a box-list;
394/// but occasionally, something else should happen to it - e.g. `\vadjust{...}` adds the list's contents
395/// as a vertical adjustment, `a^{...}` adds it to the superscript-field of the `a`.character etc.
396/// This struct abstracts over that by carrying a continuation function to be called when the list is closed.
397///
398/// TODO: rethink this in light of [`BoxTarget`].
399pub struct ListTarget<ET: EngineTypes, N>(
400    Option<Box<dyn FnOnce(&mut EngineReferences<ET>, Vec<N>, SourceRef<ET>) -> TeXResult<(), ET>>>,
401);
402impl<ET: EngineTypes, N> ListTarget<ET, N> {
403    /// Create a new list target from the given continuation function.
404    pub fn new<
405        F: FnOnce(&mut EngineReferences<ET>, Vec<N>, SourceRef<ET>) -> TeXResult<(), ET> + 'static,
406    >(
407        f: F,
408    ) -> Self {
409        ListTarget(Some(Box::new(f)))
410    }
411    /// Execute the continuation function (once the list is closed)
412    pub fn call(
413        self,
414        engine: &mut EngineReferences<ET>,
415        v: Vec<N>,
416        start: SourceRef<ET>,
417    ) -> TeXResult<(), ET> {
418        match self.0 {
419            Some(f) => f(engine, v, start),
420            None => unreachable!(),
421        }
422    }
423    /// Create a trivial list target
424    pub fn none() -> Self {
425        ListTarget(None)
426    }
427    /// Whether the continuation function has not yet been called
428    pub fn is_some(&self) -> bool {
429        self.0.is_some()
430    }
431}
432impl<ET: EngineTypes, N> Debug for ListTarget<ET, N> {
433    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
434        match self.0 {
435            None => f.write_str("ListTarget(None)"),
436            Some(_) => f.write_str("ListTarget(Some(_))"),
437        }
438    }
439}
440impl<ET: EngineTypes, N> Clone for ListTarget<ET, N> {
441    fn clone(&self) -> Self {
442        ListTarget(None)
443    }
444}